From e91a22631081122fb5e4ef7f9f75513c88a7b3d1 Mon Sep 17 00:00:00 2001 From: Domen Date: Thu, 2 Feb 2023 12:43:45 +0100 Subject: [PATCH 01/10] Add mypy to CI file --- .github/workflows/ci-test.yml | 50 ++++++-------- Makefile | 5 ++ examples/dns_config.yml | 2 +- mypy.requirements | 1 + plugins/module_utils/arguments.py | 3 +- plugins/module_utils/rest_client.py | 18 ++++- plugins/module_utils/role.py | 43 ++++++++---- plugins/module_utils/task_tag.py | 7 +- plugins/module_utils/utils.py | 7 +- plugins/module_utils/vm.py | 1 - pyproject.toml | 70 ++++++++++++++++++++ tests/unit/plugins/module_utils/test_disk.py | 1 - 12 files changed, 155 insertions(+), 53 deletions(-) create mode 100644 mypy.requirements create mode 100644 pyproject.toml diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index b2d0e413..5959e9e3 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -8,41 +8,29 @@ concurrency: group: ${{ github.ref_name }} cancel-in-progress: true jobs: - docs: + mypy: + name: Type checks (mypy) runs-on: [self-hosted] container: python:3.10-slim-buster steps: - - name: Checkout + - name: Check out code uses: actions/checkout@v3 - - run: pip3 install -r sanity.requirements -r test.requirements -r docs.requirements - - run: apt update - - run: apt install -y git make - # ansible-test needs special directory structure. - - run: mkdir -p $WORKDIR - - run: cp -a ./ $WORKDIR - # - - run: cd $WORKDIR && make docs - - run: cd $WORKDIR && ls -al docs/build/html - sanity-test: - runs-on: [self-hosted] - container: python:3.10-slim-buster - steps: - - name: Checkout - uses: actions/checkout@v3 - - run: pip3 install -r sanity.requirements -r test.requirements -r docs.requirements - - run: apt update - - run: apt install -y git make - - run: pip install ansible-core==2.13.1 - # ansible-test needs special directory structure. - - run: mkdir -p $WORKDIR - - run: cp -a ./ $WORKDIR - # Same as "make sanity" - # TODO reuse Makefile - - run: cd $WORKDIR && black -t py38 --check --diff --color plugins tests/unit - - run: cd $WORKDIR && ansible-lint - - run: cd $WORKDIR && flake8 --exclude tests/output/ - - run: cd $WORKDIR && ansible-test sanity + - name: Install requirements + shell: bash + run: | + apt update + pip install --upgrade pip wheel + pip install mypy==0.991 + + - name: Link repo into the correct structure and run mypy + shell: bash + run: | + set -eux + mkdir -p $WORKDIR + cp -a ./ $WORKDIR + cd $WORKDIR + mypy -p plugins units-test: runs-on: [self-hosted] @@ -179,4 +167,4 @@ jobs: - run: apt install -y smbclient - run: mkdir -p $WORKDIR - run: cp -a ./ $WORKDIR - - run: cd $WORKDIR/tests/integration/cleanup && ./smb_cleanup.sh ${{ secrets.SMB_SERVER }} ${{ secrets.SMB_SHARE }} "${{ secrets.SMB_USERNAME }}" ${{ secrets.SMB_PASSWORD }} \ No newline at end of file + - run: cd $WORKDIR/tests/integration/cleanup && ./smb_cleanup.sh ${{ secrets.SMB_SERVER }} ${{ secrets.SMB_SHARE }} "${{ secrets.SMB_USERNAME }}" ${{ secrets.SMB_PASSWORD }} diff --git a/Makefile b/Makefile index fe525c82..ff88e436 100644 --- a/Makefile +++ b/Makefile @@ -73,3 +73,8 @@ integration: ## Run integration tests docs: ## Build collection documentation pip install -r docs.requirements $(MAKE) -C docs -f Makefile.custom docs + +.PHONY: mypy +mypy: ## Run mypy hint checker + pip install -r mypy.requirements + mypy -p plugins diff --git a/examples/dns_config.yml b/examples/dns_config.yml index 792ff6fb..b527e48b 100644 --- a/examples/dns_config.yml +++ b/examples/dns_config.yml @@ -24,4 +24,4 @@ dns_servers: - 0.0.0.1 - 0.0.1.0 - state: before # or after + state: before # or after diff --git a/mypy.requirements b/mypy.requirements new file mode 100644 index 00000000..35950914 --- /dev/null +++ b/mypy.requirements @@ -0,0 +1 @@ +mypy==0.991 diff --git a/plugins/module_utils/arguments.py b/plugins/module_utils/arguments.py index 5e5b5ef4..65ec7d56 100644 --- a/plugins/module_utils/arguments.py +++ b/plugins/module_utils/arguments.py @@ -8,6 +8,7 @@ __metaclass__ = type from ansible.module_utils.basic import env_fallback +from typing import Any # TODO - env from /etc/environment is loaded # But when env is set in bash session, env seems to be lost on ssh connection to localhost. @@ -46,5 +47,5 @@ ) -def get_spec(*param_names): +def get_spec(*param_names: str) -> dict[Any, Any]: return dict((p, SHARED_SPECS[p]) for p in param_names) diff --git a/plugins/module_utils/rest_client.py b/plugins/module_utils/rest_client.py index 7e730481..09a2c412 100644 --- a/plugins/module_utils/rest_client.py +++ b/plugins/module_utils/rest_client.py @@ -4,12 +4,16 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function +from __future__ import annotations from . import errors from . import utils +from ..module_utils.client import Client __metaclass__ = type +from typing import Any, Union + def _query(original=None): # Make sure the query isn't equal to None @@ -18,10 +22,12 @@ def _query(original=None): class RestClient: - def __init__(self, client): + def __init__(self, client: Client): self.client = client - def list_records(self, endpoint, query=None, timeout=None): + def list_records( + self, endpoint: str, query: dict[Any, Any] = None, timeout: float = None + ) -> list[Any]: """Results are obtained so that first off, all records are obtained and then filtered manually""" try: @@ -30,7 +36,13 @@ def list_records(self, endpoint, query=None, timeout=None): raise errors.ScaleComputingError(f"Request timed out: {e}") return utils.filter_results(records, query) - def get_record(self, endpoint, query=None, must_exist=False, timeout=None): + def get_record( + self, + endpoint: str, + query: dict[Any, Any] = None, + must_exist: bool = False, + timeout: float = None, + ) -> Union[dict[Any, Any], None]: records = self.list_records(endpoint=endpoint, query=query, timeout=timeout) if len(records) > 1: raise errors.ScaleComputingError( diff --git a/plugins/module_utils/role.py b/plugins/module_utils/role.py index 46708171..0e76f0e2 100644 --- a/plugins/module_utils/role.py +++ b/plugins/module_utils/role.py @@ -5,24 +5,33 @@ from __future__ import absolute_import, division, print_function +from __future__ import annotations __metaclass__ = type from ..module_utils.utils import PayloadMapper from ..module_utils.rest_client import RestClient +from typing import Union, Any, TypedDict + + +# Use for type hinting. +class TypedDNSConfigToAnsible(TypedDict): + uuid: str + name: str + class Role(PayloadMapper): - def __init__(self, uuid, name): + def __init__(self, uuid: str, name: str): self.uuid = uuid self.name = name @classmethod - def from_ansible(cls): + def from_ansible(cls, ansible_data: dict[Any, Any]) -> None: pass @classmethod - def from_hypercore(cls, role_dict): + def from_hypercore(cls, role_dict: dict[Any, Any]) -> Union[Role, None]: if not role_dict: # In case for get_record, return None if no result is found return None return cls( @@ -30,20 +39,22 @@ def from_hypercore(cls, role_dict): name=role_dict["name"], ) - def to_hypercore(self): + def to_hypercore(self) -> None: pass - def to_ansible(self): + def to_ansible(self) -> TypedDNSConfigToAnsible: return dict( uuid=self.uuid, name=self.name, ) - def __eq__(self, other): + def __eq__(self, other: object) -> bool: """ One User is equal to another if it has ALL attributes exactly the same. This method is used only in tests. """ + if not isinstance(other, Role): + return NotImplemented return all( ( self.uuid == other.uuid, @@ -52,17 +63,25 @@ def __eq__(self, other): ) @classmethod - def get_role_from_uuid(cls, role_uuid, rest_client: RestClient, must_exist=False): + def get_role_from_uuid( + cls, role_uuid: str, rest_client: RestClient, must_exist: bool = False + ) -> Union[Role, None]: hypercore_dict = rest_client.get_record( "/rest/v1/Role/{0}".format(role_uuid), must_exist=must_exist ) - role = cls.from_hypercore(hypercore_dict) - return role + if hypercore_dict: + role = cls.from_hypercore(hypercore_dict) + return role + return None @classmethod - def get_role_from_name(cls, role_name, rest_client: RestClient, must_exist=False): + def get_role_from_name( + cls, role_name: str, rest_client: RestClient, must_exist: bool = False + ) -> Union[Role, None]: hypercore_dict = rest_client.get_record( "/rest/v1/Role", {"name": role_name}, must_exist=must_exist ) - role = cls.from_hypercore(hypercore_dict) - return role + if hypercore_dict: + role = cls.from_hypercore(hypercore_dict) + return role + return None diff --git a/plugins/module_utils/task_tag.py b/plugins/module_utils/task_tag.py index 8540627b..3e01574d 100644 --- a/plugins/module_utils/task_tag.py +++ b/plugins/module_utils/task_tag.py @@ -11,11 +11,16 @@ from time import sleep from ..module_utils import errors +from ..module_utils.rest_client import RestClient + +from typing import Any class TaskTag: @classmethod - def wait_task(cls, rest_client, task, check_mode=False): + def wait_task( + cls, rest_client: RestClient, task: dict[Any, Any], check_mode: bool = False + ): if check_mode: return if type(task) != dict: diff --git a/plugins/module_utils/utils.py b/plugins/module_utils/utils.py index 9e2f478e..e0c697b5 100644 --- a/plugins/module_utils/utils.py +++ b/plugins/module_utils/utils.py @@ -11,6 +11,7 @@ import uuid from ..module_utils.errors import InvalidUuidFormatError +from typing import Any # Used in case of check mode @@ -27,7 +28,9 @@ def validate_uuid(value): raise InvalidUuidFormatError(value) -def get_query(input, *field_names, ansible_hypercore_map): +def get_query( + input: dict[Any, Any], *field_names: str, ansible_hypercore_map: dict[Any, Any] +): """ Wrapps filter_dict and transform_ansible_to_hypercore_query. Prefer to use 'get_query' over filter_dict even if there's no mapping between hypercore and ansible columns for the sake of verbosity and consistency @@ -113,5 +116,5 @@ def is_superset(superset, candidate): return True -def filter_results(results, filter_data): +def filter_results(results, filter_data) -> list[Any]: return [element for element in results if is_superset(element, filter_data)] diff --git a/plugins/module_utils/vm.py b/plugins/module_utils/vm.py index 2ff754dd..fda6af82 100644 --- a/plugins/module_utils/vm.py +++ b/plugins/module_utils/vm.py @@ -133,7 +133,6 @@ def __init__( was_shutdown_tried=False, # Has shutdown request already been tried machine_type=None, ): - self.operating_system = operating_system self.uuid = uuid self.node_uuid = node_uuid diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..167b7e70 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,70 @@ +[tool.mypy] +strict = true +pretty = true +show_error_codes = true +show_error_context = true +show_column_numbers = true +warn_unused_configs = true +color_output = true + +namespace_packages = true +explicit_package_bases = true + +# ignores for dependencies without type information +# ansible basic does not have hints implemented +[[tool.mypy.overrides]] +module = [ + "ansible.*", +] +ignore_missing_imports = true + +# Overrides error codes for incomplete modules / module_utils +# Remove once hints are added +[[tool.mypy.overrides]] +module = [ + "plugins.module_utils.arguments", + "plugins.module_utils.disk", + "plugins.module_utils.errors", + "plugins.module_utils.iso", + "plugins.module_utils.nic", + "plugins.module_utils.node", + "plugins.module_utils.user", + "plugins.module_utils.dns_config", + "plugins.module_utils.remote_cluster", + "plugins.module_utils.replication", + "plugins.module_utils.snapshot_schedule", + "plugins.module_utils.state", + "plugins.module_utils.task_tag", + "plugins.module_utils.type", + "plugins.module_utils.utils", + "plugins.module_utils.vm", + "plugins.module_utils.rest_client", + "plugins.module_utils.client", + "plugins.modules.api", + "plugins.modules.iso_info", + "plugins.modules.iso", + "plugins.modules.node_info", + "plugins.modules.remote_cluster_info", + "plugins.modules.snapshot_schedule", + "plugins.modules.snapshot_schedule_info", + "plugins.modules.task_wait", + "plugins.modules.user_info", + "plugins.modules.user", + "plugins.modules.vm_boot_devices", + "plugins.modules.vm_clone", + "plugins.modules.vm_disk", + "plugins.modules.vm_export", + "plugins.modules.vm_import", + "plugins.modules.vm_info", + "plugins.modules.vm_nic_info", + "plugins.modules.vm_nic", + "plugins.modules.vm_node_affinity", + "plugins.modules.vm_params", + "plugins.modules.vm_replication", + "plugins.modules.vm_replication_info", + "plugins.modules.vm", + "plugins.modules.dns_config", + "plugins.modules.dns_config_info", + "plugins.inventory.*" +] +disable_error_code = ["no-untyped-def", "no-untyped-call", "assignment", "type-arg", "var-annotated", "import", "misc", "arg-type", "dict-item", "override", "union-attr", "valid-type"] diff --git a/tests/unit/plugins/module_utils/test_disk.py b/tests/unit/plugins/module_utils/test_disk.py index fab0e154..36589fff 100644 --- a/tests/unit/plugins/module_utils/test_disk.py +++ b/tests/unit/plugins/module_utils/test_disk.py @@ -229,7 +229,6 @@ def test_equal(self): assert disk1 == disk2 def test_post_and_patch_payload(self): - vm = VM( uuid="id", name="VM-name", From d20b3de608d949683db5da5447fab58613e23c63 Mon Sep 17 00:00:00 2001 From: Domen Date: Thu, 2 Feb 2023 12:48:49 +0100 Subject: [PATCH 02/10] Adding sanity and unit tests back to CI file --- .github/workflows/ci-test.yml | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index 5959e9e3..b0e77263 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -32,6 +32,42 @@ jobs: cd $WORKDIR mypy -p plugins +docs: + runs-on: [self-hosted] + container: python:3.10-slim-buster + steps: + - name: Checkout + uses: actions/checkout@v3 + - run: pip3 install -r sanity.requirements -r test.requirements -r docs.requirements + - run: apt update + - run: apt install -y git make + # ansible-test needs special directory structure. + - run: mkdir -p $WORKDIR + - run: cp -a ./ $WORKDIR + # + - run: cd $WORKDIR && make docs + - run: cd $WORKDIR && ls -al docs/build/html + + sanity-test: + runs-on: [self-hosted] + container: python:3.10-slim-buster + steps: + - name: Checkout + uses: actions/checkout@v3 + - run: pip3 install -r sanity.requirements -r test.requirements -r docs.requirements + - run: apt update + - run: apt install -y git make + - run: pip install ansible-core==2.13.1 + # ansible-test needs special directory structure. + - run: mkdir -p $WORKDIR + - run: cp -a ./ $WORKDIR + # Same as "make sanity" + # TODO reuse Makefile + - run: cd $WORKDIR && black -t py38 --check --diff --color plugins tests/unit + - run: cd $WORKDIR && ansible-lint + - run: cd $WORKDIR && flake8 --exclude tests/output/ + - run: cd $WORKDIR && ansible-test sanity + units-test: runs-on: [self-hosted] container: python:3.10-slim-buster From ce31a15b472beaf696417014398ab6529d6b6e01 Mon Sep 17 00:00:00 2001 From: Domen Date: Thu, 2 Feb 2023 12:49:34 +0100 Subject: [PATCH 03/10] Sanity fix --- .github/workflows/ci-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index b0e77263..be8b354a 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -32,7 +32,7 @@ jobs: cd $WORKDIR mypy -p plugins -docs: + docs: runs-on: [self-hosted] container: python:3.10-slim-buster steps: From 3447c2e914f686c1606c0189031148b764e394e2 Mon Sep 17 00:00:00 2001 From: Domen Date: Thu, 2 Feb 2023 14:20:37 +0100 Subject: [PATCH 04/10] Comments to typed classes file --- plugins/module_utils/typed_classes.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/module_utils/typed_classes.py b/plugins/module_utils/typed_classes.py index 0a466107..d80f33b1 100644 --- a/plugins/module_utils/typed_classes.py +++ b/plugins/module_utils/typed_classes.py @@ -10,8 +10,9 @@ from typing import TypedDict, Union, Any +# Typed Classes use for Python hints. -# Use for type hinting. +# Registration to ansible return dict. class TypedRegistrationToAnsible(TypedDict): company_name: Union[str, None] contact: Union[str, None] @@ -19,7 +20,7 @@ class TypedRegistrationToAnsible(TypedDict): email: Union[str, None] -# Use for type hinting. +# Registration from ansible input dict. class TypedRegistrationFromAnsible(TypedDict): company_name: Union[str, None] contact: Union[str, None] @@ -27,18 +28,18 @@ class TypedRegistrationFromAnsible(TypedDict): email: Union[str, None] -# Use for type hinting. +# Task tag return dict. class TypedTaskTag(TypedDict): createdUUID: str taskTag: str -# Use for type hinting. +# DNSConfig to ansible return dict. class TypedDNSConfigToAnsible(TypedDict): uuid: str name: str - +# Ansible module return Diff dict {before:{} after:{}} class TypedDiff(TypedDict): before: Union[dict[Any, Any], TypedRegistrationToAnsible, None] after: Union[dict[Any, Any], TypedRegistrationToAnsible, None] From f438e7b8611c4c9f46817e72031558c43591d10a Mon Sep 17 00:00:00 2001 From: Domen Date: Thu, 2 Feb 2023 14:21:57 +0100 Subject: [PATCH 05/10] Sanity fix --- plugins/module_utils/typed_classes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/module_utils/typed_classes.py b/plugins/module_utils/typed_classes.py index d80f33b1..9a9547f1 100644 --- a/plugins/module_utils/typed_classes.py +++ b/plugins/module_utils/typed_classes.py @@ -12,6 +12,7 @@ # Typed Classes use for Python hints. + # Registration to ansible return dict. class TypedRegistrationToAnsible(TypedDict): company_name: Union[str, None] @@ -39,6 +40,7 @@ class TypedDNSConfigToAnsible(TypedDict): uuid: str name: str + # Ansible module return Diff dict {before:{} after:{}} class TypedDiff(TypedDict): before: Union[dict[Any, Any], TypedRegistrationToAnsible, None] From 9999d629d95b0c5f56bce8144ed29185ff9e03a8 Mon Sep 17 00:00:00 2001 From: Justin Cinkelj Date: Fri, 3 Feb 2023 01:39:11 +0100 Subject: [PATCH 06/10] mypy fixes Signed-off-by: Justin Cinkelj --- plugins/module_utils/typed_classes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/module_utils/typed_classes.py b/plugins/module_utils/typed_classes.py index 9a9547f1..f7c37281 100644 --- a/plugins/module_utils/typed_classes.py +++ b/plugins/module_utils/typed_classes.py @@ -8,7 +8,7 @@ __metaclass__ = type -from typing import TypedDict, Union, Any +from typing import TypedDict, Union, Any, Dict # Typed Classes use for Python hints. @@ -43,5 +43,5 @@ class TypedDNSConfigToAnsible(TypedDict): # Ansible module return Diff dict {before:{} after:{}} class TypedDiff(TypedDict): - before: Union[dict[Any, Any], TypedRegistrationToAnsible, None] - after: Union[dict[Any, Any], TypedRegistrationToAnsible, None] + before: Union[Dict[Any, Any], TypedRegistrationToAnsible, None] + after: Union[Dict[Any, Any], TypedRegistrationToAnsible, None] From cb2d9ac1ab51b364e831a970260a612992324811 Mon Sep 17 00:00:00 2001 From: Justin Cinkelj Date: Fri, 3 Feb 2023 08:57:41 +0100 Subject: [PATCH 07/10] Ignore recently added files Signed-off-by: Justin Cinkelj --- pyproject.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 167b7e70..794c5f89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,8 @@ module = [ "plugins.module_utils.vm", "plugins.module_utils.rest_client", "plugins.module_utils.client", + "plugins.module_utils.time_server", + "plugins.module_utils.time_zone", "plugins.modules.api", "plugins.modules.iso_info", "plugins.modules.iso", @@ -65,6 +67,10 @@ module = [ "plugins.modules.vm", "plugins.modules.dns_config", "plugins.modules.dns_config_info", + "plugins.modules.time_server", + "plugins.modules.time_server_info", + "plugins.modules.time_zone", + "plugins.modules.time_zone_info", "plugins.inventory.*" ] disable_error_code = ["no-untyped-def", "no-untyped-call", "assignment", "type-arg", "var-annotated", "import", "misc", "arg-type", "dict-item", "override", "union-attr", "valid-type"] From 2448b47cd1fa00dcdc512834151516070e3347bb Mon Sep 17 00:00:00 2001 From: Domen Date: Fri, 3 Feb 2023 10:23:46 +0100 Subject: [PATCH 08/10] dict to Dict --- plugins/module_utils/arguments.py | 4 ++-- plugins/module_utils/registration.py | 6 +++--- plugins/module_utils/rest_client.py | 8 ++++---- plugins/module_utils/role.py | 6 +++--- plugins/module_utils/utils.py | 4 ++-- tests/unit/plugins/module_utils/test_time_server.py | 1 - tests/unit/plugins/module_utils/test_time_zone.py | 1 - 7 files changed, 14 insertions(+), 16 deletions(-) diff --git a/plugins/module_utils/arguments.py b/plugins/module_utils/arguments.py index 65ec7d56..dd204ea4 100644 --- a/plugins/module_utils/arguments.py +++ b/plugins/module_utils/arguments.py @@ -8,7 +8,7 @@ __metaclass__ = type from ansible.module_utils.basic import env_fallback -from typing import Any +from typing import Any, Dict # TODO - env from /etc/environment is loaded # But when env is set in bash session, env seems to be lost on ssh connection to localhost. @@ -47,5 +47,5 @@ ) -def get_spec(*param_names: str) -> dict[Any, Any]: +def get_spec(*param_names: str) -> Dict[Any, Any]: return dict((p, SHARED_SPECS[p]) for p in param_names) diff --git a/plugins/module_utils/registration.py b/plugins/module_utils/registration.py index b68a4f86..68dfc4da 100644 --- a/plugins/module_utils/registration.py +++ b/plugins/module_utils/registration.py @@ -17,7 +17,7 @@ TypedRegistrationFromAnsible, TypedRegistrationToAnsible, ) -from typing import Union, Any +from typing import Union, Any, Dict class Registration(PayloadMapper): @@ -52,7 +52,7 @@ def get(cls, rest_client: RestClient) -> Union[Registration, None]: return None @classmethod - def from_hypercore(cls, hypercore_data: dict[Any, Any]) -> Registration: + def from_hypercore(cls, hypercore_data: Dict[Any, Any]) -> Registration: try: obj = cls() obj.uuid = hypercore_data["uuid"] @@ -77,7 +77,7 @@ def from_ansible(cls, ansible_data: TypedRegistrationFromAnsible) -> Registratio obj.email = ansible_data.get("email", None) return obj - def to_hypercore(self) -> dict[Any, Any]: + def to_hypercore(self) -> Dict[Any, Any]: hypercore_dict = dict() if self.company_name: hypercore_dict["companyName"] = self.company_name diff --git a/plugins/module_utils/rest_client.py b/plugins/module_utils/rest_client.py index 54fa2f66..fb52cfe4 100644 --- a/plugins/module_utils/rest_client.py +++ b/plugins/module_utils/rest_client.py @@ -13,7 +13,7 @@ __metaclass__ = type -from typing import Any, Union +from typing import Any, Union, Dict def _query(original=None): @@ -27,7 +27,7 @@ def __init__(self, client: Client): self.client = client def list_records( - self, endpoint: str, query: dict[Any, Any] = None, timeout: float = None + self, endpoint: str, query: Dict[Any, Any] = None, timeout: float = None ) -> list[Any]: """Results are obtained so that first off, all records are obtained and then filtered manually""" @@ -40,10 +40,10 @@ def list_records( def get_record( self, endpoint: str, - query: dict[Any, Any] = None, + query: Dict[Any, Any] = None, must_exist: bool = False, timeout: float = None, - ) -> Union[dict[Any, Any], None]: + ) -> Union[Dict[Any, Any], None]: records = self.list_records(endpoint=endpoint, query=query, timeout=timeout) if len(records) > 1: raise errors.ScaleComputingError( diff --git a/plugins/module_utils/role.py b/plugins/module_utils/role.py index a891b9a5..0e7199c3 100644 --- a/plugins/module_utils/role.py +++ b/plugins/module_utils/role.py @@ -13,7 +13,7 @@ from ..module_utils.rest_client import RestClient from ..module_utils.typed_classes import TypedDNSConfigToAnsible -from typing import Union, Any +from typing import Union, Any, Dict class Role(PayloadMapper): @@ -22,11 +22,11 @@ def __init__(self, uuid: str, name: str): self.name = name @classmethod - def from_ansible(cls, ansible_data: dict[Any, Any]) -> None: + def from_ansible(cls, ansible_data: Dict[Any, Any]) -> None: pass @classmethod - def from_hypercore(cls, hypercore_data: dict[Any, Any]) -> Union[Role, None]: + def from_hypercore(cls, hypercore_data: Dict[Any, Any]) -> Union[Role, None]: if not hypercore_data: # In case for get_record, return None if no result is found return None diff --git a/plugins/module_utils/utils.py b/plugins/module_utils/utils.py index 1c31dd2b..7ded4c27 100644 --- a/plugins/module_utils/utils.py +++ b/plugins/module_utils/utils.py @@ -11,7 +11,7 @@ import uuid from ..module_utils.errors import InvalidUuidFormatError -from typing import Union, Any +from typing import Union, Any, Dict from ..module_utils.typed_classes import TypedTaskTag, TypedRegistrationToAnsible @@ -30,7 +30,7 @@ def validate_uuid(value): def get_query( - input: dict[Any, Any], *field_names: str, ansible_hypercore_map: dict[Any, Any] + input: Dict[Any, Any], *field_names: str, ansible_hypercore_map: Dict[Any, Any] ): """ Wrapps filter_dict and transform_ansible_to_hypercore_query. Prefer to use 'get_query' over filter_dict diff --git a/tests/unit/plugins/module_utils/test_time_server.py b/tests/unit/plugins/module_utils/test_time_server.py index 5eac82ad..f6d02e0f 100644 --- a/tests/unit/plugins/module_utils/test_time_server.py +++ b/tests/unit/plugins/module_utils/test_time_server.py @@ -76,7 +76,6 @@ def test_get_state(self, rest_client): rest_client.list_records.return_value = [self.from_hypercore_dict] result = TimeServer.get_state(rest_client) - print(result) assert result == { "uuid": "test", "host": "pool.ntp.org", diff --git a/tests/unit/plugins/module_utils/test_time_zone.py b/tests/unit/plugins/module_utils/test_time_zone.py index f6b03730..db8b6dff 100644 --- a/tests/unit/plugins/module_utils/test_time_zone.py +++ b/tests/unit/plugins/module_utils/test_time_zone.py @@ -76,7 +76,6 @@ def test_get_state(self, rest_client): rest_client.list_records.return_value = [self.from_hypercore_dict] result = TimeZone.get_state(rest_client) - print(result) assert result == { "uuid": "test", "time_zone": "US/Eastern", From 25504780e5b420cbfa56e0dfff0a6ccc7ab804be Mon Sep 17 00:00:00 2001 From: Domen Date: Fri, 3 Feb 2023 10:34:55 +0100 Subject: [PATCH 09/10] adding import annotations to utils --- plugins/module_utils/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/module_utils/utils.py b/plugins/module_utils/utils.py index 7ded4c27..be2ea36b 100644 --- a/plugins/module_utils/utils.py +++ b/plugins/module_utils/utils.py @@ -4,6 +4,7 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function +from __future__ import annotations from abc import abstractmethod __metaclass__ = type From 0c0648c67be4df41fc9ea0a734ff580d8c81c90c Mon Sep 17 00:00:00 2001 From: Domen Date: Fri, 3 Feb 2023 10:52:24 +0100 Subject: [PATCH 10/10] Adding import annotations --- plugins/module_utils/arguments.py | 5 +++-- plugins/module_utils/registration.py | 6 +++--- plugins/module_utils/rest_client.py | 8 ++++---- plugins/module_utils/role.py | 20 +++++++++----------- plugins/module_utils/utils.py | 4 ++-- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/plugins/module_utils/arguments.py b/plugins/module_utils/arguments.py index dd204ea4..76f894bb 100644 --- a/plugins/module_utils/arguments.py +++ b/plugins/module_utils/arguments.py @@ -4,11 +4,12 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function +from __future__ import annotations __metaclass__ = type from ansible.module_utils.basic import env_fallback -from typing import Any, Dict +from typing import Any # TODO - env from /etc/environment is loaded # But when env is set in bash session, env seems to be lost on ssh connection to localhost. @@ -47,5 +48,5 @@ ) -def get_spec(*param_names: str) -> Dict[Any, Any]: +def get_spec(*param_names: str) -> dict[Any, Any]: return dict((p, SHARED_SPECS[p]) for p in param_names) diff --git a/plugins/module_utils/registration.py b/plugins/module_utils/registration.py index 68dfc4da..b68a4f86 100644 --- a/plugins/module_utils/registration.py +++ b/plugins/module_utils/registration.py @@ -17,7 +17,7 @@ TypedRegistrationFromAnsible, TypedRegistrationToAnsible, ) -from typing import Union, Any, Dict +from typing import Union, Any class Registration(PayloadMapper): @@ -52,7 +52,7 @@ def get(cls, rest_client: RestClient) -> Union[Registration, None]: return None @classmethod - def from_hypercore(cls, hypercore_data: Dict[Any, Any]) -> Registration: + def from_hypercore(cls, hypercore_data: dict[Any, Any]) -> Registration: try: obj = cls() obj.uuid = hypercore_data["uuid"] @@ -77,7 +77,7 @@ def from_ansible(cls, ansible_data: TypedRegistrationFromAnsible) -> Registratio obj.email = ansible_data.get("email", None) return obj - def to_hypercore(self) -> Dict[Any, Any]: + def to_hypercore(self) -> dict[Any, Any]: hypercore_dict = dict() if self.company_name: hypercore_dict["companyName"] = self.company_name diff --git a/plugins/module_utils/rest_client.py b/plugins/module_utils/rest_client.py index fb52cfe4..54fa2f66 100644 --- a/plugins/module_utils/rest_client.py +++ b/plugins/module_utils/rest_client.py @@ -13,7 +13,7 @@ __metaclass__ = type -from typing import Any, Union, Dict +from typing import Any, Union def _query(original=None): @@ -27,7 +27,7 @@ def __init__(self, client: Client): self.client = client def list_records( - self, endpoint: str, query: Dict[Any, Any] = None, timeout: float = None + self, endpoint: str, query: dict[Any, Any] = None, timeout: float = None ) -> list[Any]: """Results are obtained so that first off, all records are obtained and then filtered manually""" @@ -40,10 +40,10 @@ def list_records( def get_record( self, endpoint: str, - query: Dict[Any, Any] = None, + query: dict[Any, Any] = None, must_exist: bool = False, timeout: float = None, - ) -> Union[Dict[Any, Any], None]: + ) -> Union[dict[Any, Any], None]: records = self.list_records(endpoint=endpoint, query=query, timeout=timeout) if len(records) > 1: raise errors.ScaleComputingError( diff --git a/plugins/module_utils/role.py b/plugins/module_utils/role.py index 0e7199c3..1ebd2feb 100644 --- a/plugins/module_utils/role.py +++ b/plugins/module_utils/role.py @@ -13,7 +13,7 @@ from ..module_utils.rest_client import RestClient from ..module_utils.typed_classes import TypedDNSConfigToAnsible -from typing import Union, Any, Dict +from typing import Union, Any class Role(PayloadMapper): @@ -22,11 +22,13 @@ def __init__(self, uuid: str, name: str): self.name = name @classmethod - def from_ansible(cls, ansible_data: Dict[Any, Any]) -> None: + def from_ansible(cls, ansible_data: dict[Any, Any]) -> None: pass @classmethod - def from_hypercore(cls, hypercore_data: Dict[Any, Any]) -> Union[Role, None]: + def from_hypercore( + cls, hypercore_data: Union[dict[Any, Any], None] + ) -> Union[Role, None]: if not hypercore_data: # In case for get_record, return None if no result is found return None @@ -65,10 +67,8 @@ def get_role_from_uuid( hypercore_dict = rest_client.get_record( "/rest/v1/Role/{0}".format(role_uuid), must_exist=must_exist ) - if hypercore_dict: - role = cls.from_hypercore(hypercore_dict) - return role - return None + role = cls.from_hypercore(hypercore_dict) + return role @classmethod def get_role_from_name( @@ -77,7 +77,5 @@ def get_role_from_name( hypercore_dict = rest_client.get_record( "/rest/v1/Role", {"name": role_name}, must_exist=must_exist ) - if hypercore_dict: - role = cls.from_hypercore(hypercore_dict) - return role - return None + role = cls.from_hypercore(hypercore_dict) + return role diff --git a/plugins/module_utils/utils.py b/plugins/module_utils/utils.py index be2ea36b..377c981a 100644 --- a/plugins/module_utils/utils.py +++ b/plugins/module_utils/utils.py @@ -12,7 +12,7 @@ import uuid from ..module_utils.errors import InvalidUuidFormatError -from typing import Union, Any, Dict +from typing import Union, Any from ..module_utils.typed_classes import TypedTaskTag, TypedRegistrationToAnsible @@ -31,7 +31,7 @@ def validate_uuid(value): def get_query( - input: Dict[Any, Any], *field_names: str, ansible_hypercore_map: Dict[Any, Any] + input: dict[Any, Any], *field_names: str, ansible_hypercore_map: dict[Any, Any] ): """ Wrapps filter_dict and transform_ansible_to_hypercore_query. Prefer to use 'get_query' over filter_dict