Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add warning: If component has update() but no verify() #471

Merged
merged 4 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
context. This variable contains a comment block that signals that
the file content is generated by batou. (#356)
- Development: update Sphinx to 5.3.0 to fix readthedocs build failures.
- Component object model: Class variable .instances renamed to ._instances
to avoid collisions with user-applications.

## 2.5.0b3 (2024-08-05)
-----------------------
Expand Down
37 changes: 37 additions & 0 deletions src/batou/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,43 @@ def report(self):
output.tabular("Root", self.root_name, red=True)


class ComponentWithUpdateWithoutVerify(ConfigurationError):
"""Some components have an update method but no verify method."""

sort_key = (5, "without_verify")

@classmethod
def from_context(cls, components, roots):
self = cls()
self.components = []
for component in components:
self.components.append(repr(component.__class__.__name__))
self.roots = []
for root in roots:
self.roots.append(root.name)
return self

def __str__(self):
out_str = "Some components have an update method but no verify method:"
for idx, component in enumerate(self.components):
out_str += f"\n {component}"
out_str += f"\nRoot: {self.roots[idx]}"
out_str += f"\nThe update() method may not be called by batou if the verify() method is missing."
return out_str

def report(self):
output.error(
f"Some components have an update method but no verify method:"
)
for component in self.components:
output.line(f" {component}", red=True)
output.tabular("Root", self.root_name, red=True)
output.line(
f"The update() method may not be called by batou if the verify() method is missing.",
red=True,
)


class UnsatisfiedResources(ConfigurationError):
"""Some required resources were never provided."""

Expand Down
4 changes: 2 additions & 2 deletions src/batou/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ class Component(object):
#: After the configuration phase, this list is checked for
#: components that have component._prepared == False and
#: warns about them.
instances: List["Component"] = []
_instances: List["Component"] = []

@property
def defdir(self):
Expand Down Expand Up @@ -229,7 +229,7 @@ def __init__(self, namevar=None, **kw):

self._init_breadcrumbs = init_breadcrumbs

Component.instances.append(self)
Component._instances.append(self)
self.timer = batou.utils.Timer(self.__class__.__name__)
# Are any keyword arguments undefined attributes?
# This is a somewhat rough implementation as it allows overriding
Expand Down
32 changes: 30 additions & 2 deletions src/batou/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import batou.vfs
from batou import (
ComponentLoadingError,
ComponentWithUpdateWithoutVerify,
ConfigurationError,
CycleErrorDetected,
DuplicateHostError,
Expand Down Expand Up @@ -492,13 +493,14 @@ def configure(self):

while working_set:
exceptions = []
components_without_verify = []
previous_working_sets.append(working_set.copy())
retry = set()
self.resources.dirty_dependencies.clear()

for root in working_set:
try:
Component.instances.clear()
Component._instances.clear()
self.resources.reset_component_resources(root)
root.overrides = self.overrides.get(root.name, {})
root.prepare()
Expand All @@ -518,8 +520,10 @@ def configure(self):
)
)
else:
# warnings: does not fail the deployment
# 1. unprepared component warning
unprepared_components = []
for component in Component.instances:
for component in Component._instances:
if not component._prepared:
unprepared_components.append(component)
if unprepared_components:
Expand All @@ -531,6 +535,21 @@ def configure(self):
)
# exceptions.append(unused_exception)
output.warn(str(unused_exception))

# 2. a component has .update() but no .verify()
def has_original_update_method(component):
return type(component).update == Component.update

def has_original_verify_method(component):
return type(component).verify == Component.verify

for component in Component._instances:
if (
not has_original_update_method(component)
and has_original_verify_method(component)
and (component not in components_without_verify)
):
components_without_verify.append((component, root))
# configured this component successfully
# we won't have to retry it later
continue
Expand Down Expand Up @@ -573,6 +592,15 @@ def configure(self):

working_set = retry

# warn if a component has .update() but no .verify()
if components_without_verify:
component_without_verify_exception = (
ComponentWithUpdateWithoutVerify.from_context(
components_without_verify, root
)
)
output.warn(str(component_without_verify_exception))

# We managed to converge on a working set. However, some resource were
# provided but never used. We're rather picky here and report this as
# an error.
Expand Down
Loading