diff --git a/changelogs/fragments/module_utils-yaml-fix.yml b/changelogs/fragments/module_utils-yaml-fix.yml new file mode 100644 index 00000000000..56ab05b6f58 --- /dev/null +++ b/changelogs/fragments/module_utils-yaml-fix.yml @@ -0,0 +1,3 @@ +bugfixes: + - module_utils.common.yaml - The ``SafeLoader``, ``SafeDumper`` and ``Parser`` classes now fallback to ``object`` when ``yaml`` is not available. + This fixes tracebacks when inheriting from these classes without requiring a ``HAS_YAML`` guard around class definitions. diff --git a/lib/ansible/module_utils/common/yaml.py b/lib/ansible/module_utils/common/yaml.py index 68f837568aa..f91bf1234d5 100644 --- a/lib/ansible/module_utils/common/yaml.py +++ b/lib/ansible/module_utils/common/yaml.py @@ -11,34 +11,38 @@ __metaclass__ = type from functools import partial as _partial -import ansible.module_utils.compat.typing as t - HAS_LIBYAML = False + try: import yaml as _yaml except ImportError: HAS_YAML = False - SafeLoader = None - SafeDumper = None - Parser = None - yaml_load = None - yaml_load_all = None - yaml_dump = None - yaml_dump_all = None else: HAS_YAML = True + +if HAS_YAML: try: - SafeLoader = _yaml.CSafeLoader - SafeDumper = _yaml.CSafeDumper - Parser = _yaml.cyaml.CParser + from yaml import CSafeLoader as SafeLoader + from yaml import CSafeDumper as SafeDumper + from yaml.cyaml import CParser as Parser + HAS_LIBYAML = True except AttributeError: - SafeLoader = _yaml.SafeLoader # type: t.Type[_yaml.CSafeLoader] | t.Type[_yaml.SafeLoader] # type: ignore[no-redef] - SafeDumper = _yaml.SafeDumper # type: t.Type[_yaml.CSafeDumper] | t.Type[_yaml.SafeDumper] # type: ignore[no-redef] - Parser = _yaml.parser.Parser # type: t.Type[_yaml.cyaml.CParser] | t.Type[_yaml.parser.Parser] # type: ignore[no-redef] + from yaml import SafeLoader # type: ignore[misc] + from yaml import SafeDumper # type: ignore[misc] + from yaml.parser import Parser # type: ignore[misc] yaml_load = _partial(_yaml.load, Loader=SafeLoader) yaml_load_all = _partial(_yaml.load_all, Loader=SafeLoader) yaml_dump = _partial(_yaml.dump, Dumper=SafeDumper) yaml_dump_all = _partial(_yaml.dump_all, Dumper=SafeDumper) +else: + SafeLoader = object # type: ignore[assignment,misc] + SafeDumper = object # type: ignore[assignment,misc] + Parser = object # type: ignore[assignment,misc] + + yaml_load = None # type: ignore[assignment] + yaml_load_all = None # type: ignore[assignment] + yaml_dump = None # type: ignore[assignment] + yaml_dump_all = None # type: ignore[assignment] diff --git a/lib/ansible/parsing/yaml/loader.py b/lib/ansible/parsing/yaml/loader.py index e2989a49eda..15bde79aab0 100644 --- a/lib/ansible/parsing/yaml/loader.py +++ b/lib/ansible/parsing/yaml/loader.py @@ -27,20 +27,19 @@ from ansible.module_utils.common.yaml import HAS_LIBYAML, Parser if HAS_LIBYAML: class AnsibleLoader(Parser, AnsibleConstructor, Resolver): # type: ignore[misc] # pylint: disable=inconsistent-mro def __init__(self, stream, file_name=None, vault_secrets=None): - Parser.__init__(self, stream) # pylint: disable=non-parent-init-called + Parser.__init__(self, stream) AnsibleConstructor.__init__(self, file_name=file_name, vault_secrets=vault_secrets) Resolver.__init__(self) else: from yaml.composer import Composer from yaml.reader import Reader from yaml.scanner import Scanner - from yaml.parser import Parser class AnsibleLoader(Reader, Scanner, Parser, Composer, AnsibleConstructor, Resolver): # type: ignore[misc,no-redef] # pylint: disable=inconsistent-mro def __init__(self, stream, file_name=None, vault_secrets=None): Reader.__init__(self, stream) Scanner.__init__(self) - Parser.__init__(self) # type: ignore[call-arg] # pylint: disable=non-parent-init-called + Parser.__init__(self) Composer.__init__(self) AnsibleConstructor.__init__(self, file_name=file_name, vault_secrets=vault_secrets) Resolver.__init__(self) diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 3b3d5b4f613..005434e84aa 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -331,15 +331,3 @@ lib/ansible/module_utils/compat/_selectors2.py mypy-3.8:assignment # vendored c lib/ansible/module_utils/compat/_selectors2.py mypy-3.9:assignment # vendored code lib/ansible/module_utils/compat/_selectors2.py mypy-3.10:assignment # vendored code lib/ansible/module_utils/compat/_selectors2.py mypy-2.7:attr-defined # vendored code -lib/ansible/parsing/yaml/dumper.py mypy-3.8:misc -lib/ansible/parsing/yaml/dumper.py mypy-3.9:misc -lib/ansible/parsing/yaml/dumper.py mypy-3.10:misc -lib/ansible/parsing/yaml/dumper.py mypy-3.8:valid-type -lib/ansible/parsing/yaml/dumper.py mypy-3.9:valid-type -lib/ansible/parsing/yaml/dumper.py mypy-3.10:valid-type -lib/ansible/parsing/yaml/loader.py mypy-3.8:misc -lib/ansible/parsing/yaml/loader.py mypy-3.9:misc -lib/ansible/parsing/yaml/loader.py mypy-3.10:misc -lib/ansible/parsing/yaml/loader.py mypy-3.8:valid-type -lib/ansible/parsing/yaml/loader.py mypy-3.9:valid-type -lib/ansible/parsing/yaml/loader.py mypy-3.10:valid-type