diff --git a/ci/linting.sh b/ci/linting.sh index 7db1eed..50845f8 100755 --- a/ci/linting.sh +++ b/ci/linting.sh @@ -10,7 +10,7 @@ pyright . shopt -s globstar echo "Running black" -black --check . +black --check --diff . echo "Running pyflakes" pyflakes . diff --git a/examples/gpg/gpg.py b/examples/gpg/gpg.py index abe8039..8930c31 100644 --- a/examples/gpg/gpg.py +++ b/examples/gpg/gpg.py @@ -141,7 +141,7 @@ def evaluate_promise(self, promiser, attributes, metadata): f"Importing ascii key for user id '{user_id}' into gpg homedir '{promiser}'" ) if self.gpg_import_ascii(promiser, key["ascii"]): - if result != Result.NOTKEPT: + if result != Result.NOT_KEPT: result = Result.REPAIRED else: self.log_error(f"Unable to import key for user id '{user_id}'") diff --git a/libraries/python/cfengine_module_library.py b/libraries/python/cfengine_module_library.py index da6a28e..3aa02eb 100644 --- a/libraries/python/cfengine_module_library.py +++ b/libraries/python/cfengine_module_library.py @@ -21,6 +21,7 @@ import traceback from copy import copy from collections import OrderedDict +from typing import Any _LOG_LEVELS = { level: idx @@ -91,6 +92,17 @@ def __init__(self, d): for key, value in d.items(): setattr(self, key, value) + # Python only calls __getattr__ as a fallback when normal attribute + # lookup (__getattribute__) has already failed, so attributes set via + # setattr() in __init__ are never affected. The -> Any return type + # tells pyright that dynamic attribute access is valid. + def __getattr__(self, name) -> Any: + raise AttributeError( + "'{}' object has no attribute '{}'".format( + self.__class__.__qualname__, name + ) + ) + def __repr__(self): return "{}({})".format( self.__class__.__qualname__, diff --git a/promise-types/ansible/ansible_promise.py b/promise-types/ansible/ansible_promise.py index fea29db..610d5ec 100644 --- a/promise-types/ansible/ansible_promise.py +++ b/promise-types/ansible/ansible_promise.py @@ -96,9 +96,9 @@ def validate_promise(self, promiser: str, attributes: Dict, metadata: Dict): return def evaluate_promise( - self, safe_promiser: str, attributes: Dict, metadata: Dict + self, promiser: str, attributes: Dict, metadata: Dict ) -> Tuple[str, List[str]]: - model = self.create_attribute_object(safe_promiser, attributes) + model = self.create_attribute_object(promiser, attributes) classes = [] result = Result.KEPT @@ -149,9 +149,7 @@ def evaluate_promise( exit_code = pbex.run() if exit_code != 0: - classes.append( - "{safe_promiser}_failed".format(safe_promiser=safe_promiser) - ) + classes.append("{safe_promiser}_failed".format(safe_promiser=promiser)) result = Result.NOT_KEPT elif callback.changed: result = Result.REPAIRED diff --git a/promise-types/iptables/iptables.py b/promise-types/iptables/iptables.py index 2448922..ba2d86b 100644 --- a/promise-types/iptables/iptables.py +++ b/promise-types/iptables/iptables.py @@ -231,7 +231,7 @@ def evaluate_promise(self, promiser: str, attributes: Dict, metadata: Dict): return result, classes - def evaluate_command_policy(self, executable, table, chain, target) -> Result: + def evaluate_command_policy(self, executable, table, chain, target) -> str: policy_rules = self._iptables_policy_rules_of(executable, table, chain) assert len(policy_rules) == 1 and len(policy_rules[0].split()) >= 1 diff --git a/promise-types/json/json_promise_type.py b/promise-types/json/json_promise_type.py index b72260c..4048b9b 100644 --- a/promise-types/json/json_promise_type.py +++ b/promise-types/json/json_promise_type.py @@ -39,7 +39,7 @@ def __init__(self, **kwargs): self.types ) # for now, the only valid attributes are the types. - def create_attribute_object(self, attributes): + def create_attribute_object(self, promiser, attributes): data = {t: None for t in self.valid_attributes} for attr, val in attributes.items(): data[attr] = val @@ -73,7 +73,7 @@ def validate_promise(self, promiser, attributes, metadata): if colon and not field: raise ValidationError("Invalid syntax: field specified but empty") - model = self.create_attribute_object(attributes) + model = self.create_attribute_object(promiser, attributes) if ( model.object and isinstance(model.object, str) @@ -113,7 +113,7 @@ def validate_promise(self, promiser, attributes, metadata): ) def evaluate_promise(self, promiser, attributes, metadata): - model = self.create_attribute_object(attributes) + model = self.create_attribute_object(promiser, attributes) filename, _, field = promiser.partition(":") if os.path.exists(filename) and not os.path.isfile(filename): diff --git a/promise-types/sshd/sshd_promise_type.py b/promise-types/sshd/sshd_promise_type.py index d573a09..44aa77c 100644 --- a/promise-types/sshd/sshd_promise_type.py +++ b/promise-types/sshd/sshd_promise_type.py @@ -3,8 +3,7 @@ import subprocess import tempfile -from cfengine_module_library import PromiseModule, ValidationError, Result - +from cfengine_module_library import PromiseModule, Result, ValidationError BASE_CONFIG = "/etc/ssh/sshd_config" DROP_IN_DIR = "/etc/ssh/sshd_config.d/" diff --git a/promise-types/systemd/systemd.py b/promise-types/systemd/systemd.py index 25f9c7d..c20ad3f 100644 --- a/promise-types/systemd/systemd.py +++ b/promise-types/systemd/systemd.py @@ -79,9 +79,9 @@ def prepare_promiser_and_attributes(self, promiser, attributes): return (safe_promiser, attributes) def evaluate_promise( - self, safe_promiser: str, attributes: Dict, metadata: Dict + self, promiser: str, attributes: Dict, metadata: Dict ) -> Tuple[str, List[str]]: - model = self.create_attribute_object(safe_promiser, attributes) + model = self.create_attribute_object(promiser, attributes) # get the status of the service try: output = self._exec_command( @@ -106,13 +106,13 @@ def evaluate_promise( self.log_error(e.stderr.strip()) return ( Result.NOT_KEPT, - ["{safe_promiser}_show_failed".format(safe_promiser=safe_promiser)], + ["{safe_promiser}_show_failed".format(safe_promiser=promiser)], ) # apply the changes if model.state == SystemdPromiseTypeStates.ABSENT.value: - return self._service_absent(model, safe_promiser, service_status) + return self._service_absent(model, promiser, service_status) else: - return self._service_present(model, safe_promiser, service_status) + return self._service_present(model, promiser, service_status) def _service_absent( self, model: AttributeObject, safe_promiser: str, service_status: dict