From cbc6f6eed3dd6725f9618a8b9682616dc9dd7c3d Mon Sep 17 00:00:00 2001 From: Alexei Znamensky <103110+russoz@users.noreply.github.com> Date: Tue, 26 May 2026 01:50:34 +1200 Subject: [PATCH] multiple: replace `namedtuple` with dataclass (#12094) * refactor(multiple): replace namedtuple with dataclass Co-Authored-By: Claude Sonnet 4.6 * chore(changelog): add fragment for PR 12094 Co-Authored-By: Claude Sonnet 4.6 * Update comment. --------- Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Felix Fontein --- .../fragments/12094-namedtuple-to-dataclass.yml | 6 ++++++ plugins/inventory/opennebula.py | 13 +++++++++---- plugins/modules/homebrew_services.py | 10 +++++----- plugins/modules/one_service.py | 14 ++++++++++---- plugins/modules/one_vm.py | 14 ++++++++++---- plugins/modules/pacman.py | 10 +++++++--- 6 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 changelogs/fragments/12094-namedtuple-to-dataclass.yml diff --git a/changelogs/fragments/12094-namedtuple-to-dataclass.yml b/changelogs/fragments/12094-namedtuple-to-dataclass.yml new file mode 100644 index 0000000000..dcbfead84f --- /dev/null +++ b/changelogs/fragments/12094-namedtuple-to-dataclass.yml @@ -0,0 +1,6 @@ +minor_changes: + - homebrew_services - replace ``NamedTuple`` with dataclass (https://github.com/ansible-collections/community.general/pull/12094). + - one_service - replace function-local ``namedtuple`` with module-level dataclass (https://github.com/ansible-collections/community.general/pull/12094). + - one_vm - replace function-local ``namedtuple`` with module-level dataclass (https://github.com/ansible-collections/community.general/pull/12094). + - opennebula inventory plugin - replace function-local ``namedtuple`` with module-level dataclass (https://github.com/ansible-collections/community.general/pull/12094). + - pacman - replace ``namedtuple`` with dataclass for ``VersionTuple`` (https://github.com/ansible-collections/community.general/pull/12094). diff --git a/plugins/inventory/opennebula.py b/plugins/inventory/opennebula.py index 2dd42a024e..52625213a1 100644 --- a/plugins/inventory/opennebula.py +++ b/plugins/inventory/opennebula.py @@ -90,7 +90,7 @@ except ImportError: HAS_PYONE = False import os -from collections import namedtuple +from dataclasses import dataclass from ansible.errors import AnsibleError from ansible.plugins.inventory import BaseInventoryPlugin, Constructable @@ -98,6 +98,13 @@ from ansible.plugins.inventory import BaseInventoryPlugin, Constructable from ansible_collections.community.general.plugins.plugin_utils._unsafe import make_unsafe +@dataclass +class AuthParams: + url: str + username: str + password: str + + class InventoryModule(BaseInventoryPlugin, Constructable): NAME = "community.general.opennebula" @@ -126,9 +133,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable): except Exception as e: raise AnsibleError(f"Error occurs when reading ONE_AUTH file at '{authfile}'") from e - auth_params = namedtuple("auth", ("url", "username", "password")) - - return auth_params(url=url, username=username, password=password) + return AuthParams(url=url, username=username, password=password) def _get_vm_ipv4(self, vm): nic = vm.TEMPLATE.get("NIC") diff --git a/plugins/modules/homebrew_services.py b/plugins/modules/homebrew_services.py index 43ed827463..ac8d329ad6 100644 --- a/plugins/modules/homebrew_services.py +++ b/plugins/modules/homebrew_services.py @@ -88,6 +88,7 @@ running: import json import typing as t +from dataclasses import dataclass from ansible.module_utils.basic import AnsibleModule @@ -97,16 +98,15 @@ from ansible_collections.community.general.plugins.module_utils._homebrew import ) -# Stores validated arguments for an instance of an action. -# See DOCUMENTATION string for argument-specific information. -class HomebrewServiceArgs(t.NamedTuple): +@dataclass +class HomebrewServiceArgs: name: str state: str brew_path: str -# Stores the state of a Homebrew service. -class HomebrewServiceState(t.NamedTuple): +@dataclass +class HomebrewServiceState: running: bool pid: int | None diff --git a/plugins/modules/one_service.py b/plugins/modules/one_service.py index b4ba1ad5f7..74a77cef7d 100644 --- a/plugins/modules/one_service.py +++ b/plugins/modules/one_service.py @@ -232,11 +232,19 @@ roles: import os import time -from collections import namedtuple +from dataclasses import dataclass from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import open_url + +@dataclass +class AuthParams: + url: str + user: str + password: str + + STATES = ( "PENDING", "DEPLOYING", @@ -728,9 +736,7 @@ def get_connection_info(module): module.fail_json( msg="One or more connection parameters (api_url, api_username, api_password) were not specified" ) - auth_params = namedtuple("auth", ("url", "user", "password")) - - return auth_params(url=url, user=username, password=password) + return AuthParams(url=url, user=username, password=password) def main(): diff --git a/plugins/modules/one_vm.py b/plugins/modules/one_vm.py index 15002c7181..dc2fa5bb42 100644 --- a/plugins/modules/one_vm.py +++ b/plugins/modules/one_vm.py @@ -688,13 +688,21 @@ import copy import os import re import time -from collections import namedtuple +from dataclasses import dataclass from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.common.dict_transformations import dict_merge from ansible_collections.community.general.plugins.module_utils._opennebula import flatten, render + +@dataclass +class AuthParams: + url: str + username: str + password: str + + # Updateconf attributes documentation: https://docs.opennebula.io/6.10/integration_and_development/system_interfaces/api.html#one-vm-updateconf UPDATECONF_ATTRIBUTES = { "OS": ["ARCH", "MACHINE", "KERNEL", "INITRD", "BOOTLOADER", "BOOT", "SD_DISK_BUS", "UUID", "FIRMWARE"], @@ -1632,9 +1640,7 @@ def get_connection_info(module): if not url: module.fail_json(msg="Opennebula API url (api_url) is not specified") - auth_params = namedtuple("auth", ("url", "username", "password")) - - return auth_params(url=url, username=username, password=password) + return AuthParams(url=url, username=username, password=password) def main(): diff --git a/plugins/modules/pacman.py b/plugins/modules/pacman.py index f217427960..7d349d68ed 100644 --- a/plugins/modules/pacman.py +++ b/plugins/modules/pacman.py @@ -275,7 +275,8 @@ EXAMPLES = r""" import re import shlex -from collections import defaultdict, namedtuple +from collections import defaultdict +from dataclasses import dataclass from ansible.module_utils.basic import AnsibleModule @@ -296,7 +297,10 @@ class Package: return f'Package("{self.name}", "{self.source}", {self.source_is_URL})' -VersionTuple = namedtuple("VersionTuple", ["current", "latest"]) +@dataclass +class VersionTuple: + current: str + latest: str class Pacman: @@ -717,7 +721,7 @@ class Pacman: "installed_groups": {groupname: set(pkgnames)}, "available_pkgs": {pkgname: version}, "available_groups": {groupname: set(pkgnames)}, - "upgradable_pkgs": {pkgname: (current_version,latest_version)}, + "upgradable_pkgs": {pkgname: VersionTuple}, "pkg_reasons": {pkgname: reason}, }