ansible-core 2.19.4rc1__py3-none-any.whl → 2.20.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ansible-core might be problematic. Click here for more details.
- ansible/_internal/__init__.py +1 -4
- ansible/_internal/_ansiballz/_builder.py +1 -3
- ansible/_internal/_collection_proxy.py +7 -9
- ansible/_internal/_json/__init__.py +3 -4
- ansible/_internal/_templating/_engine.py +1 -1
- ansible/_internal/_templating/_jinja_plugins.py +1 -2
- ansible/_internal/_wrapt.py +105 -301
- ansible/cli/__init__.py +11 -10
- ansible/cli/adhoc.py +1 -2
- ansible/cli/arguments/option_helpers.py +1 -1
- ansible/cli/config.py +5 -6
- ansible/cli/doc.py +67 -67
- ansible/cli/galaxy.py +15 -24
- ansible/cli/inventory.py +0 -1
- ansible/cli/playbook.py +0 -1
- ansible/cli/pull.py +0 -1
- ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
- ansible/config/base.yml +1 -25
- ansible/config/manager.py +0 -2
- ansible/executor/play_iterator.py +42 -20
- ansible/executor/playbook_executor.py +0 -9
- ansible/executor/task_executor.py +26 -18
- ansible/executor/task_queue_manager.py +1 -3
- ansible/galaxy/api.py +33 -80
- ansible/galaxy/collection/__init__.py +11 -21
- ansible/galaxy/dependency_resolution/__init__.py +10 -9
- ansible/galaxy/dependency_resolution/dataclasses.py +86 -70
- ansible/galaxy/dependency_resolution/providers.py +54 -134
- ansible/galaxy/dependency_resolution/versioning.py +2 -4
- ansible/galaxy/role.py +1 -33
- ansible/inventory/manager.py +2 -3
- ansible/keyword_desc.yml +0 -3
- ansible/module_utils/_internal/_datatag/__init__.py +2 -10
- ansible/module_utils/_internal/_no_six.py +86 -0
- ansible/module_utils/_text.py +28 -8
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/basic.py +26 -23
- ansible/module_utils/common/_collections_compat.py +11 -2
- ansible/module_utils/common/collections.py +8 -3
- ansible/module_utils/common/dict_transformations.py +1 -2
- ansible/module_utils/common/network.py +4 -2
- ansible/module_utils/common/parameters.py +32 -41
- ansible/module_utils/common/text/converters.py +109 -23
- ansible/module_utils/common/text/formatters.py +6 -2
- ansible/module_utils/common/validation.py +11 -9
- ansible/module_utils/connection.py +8 -3
- ansible/module_utils/facts/hardware/linux.py +23 -7
- ansible/module_utils/facts/hardware/netbsd.py +1 -1
- ansible/module_utils/facts/hardware/sunos.py +2 -1
- ansible/module_utils/facts/packages.py +6 -2
- ansible/module_utils/facts/system/distribution.py +2 -1
- ansible/module_utils/facts/system/env.py +6 -3
- ansible/module_utils/facts/system/local.py +3 -1
- ansible/module_utils/parsing/convert_bool.py +6 -2
- ansible/module_utils/service.py +2 -3
- ansible/module_utils/six/__init__.py +11 -6
- ansible/module_utils/yumdnf.py +0 -5
- ansible/modules/apt.py +18 -13
- ansible/modules/apt_repository.py +1 -1
- ansible/modules/assemble.py +5 -9
- ansible/modules/blockinfile.py +39 -23
- ansible/modules/cron.py +26 -35
- ansible/modules/deb822_repository.py +83 -12
- ansible/modules/dnf.py +3 -7
- ansible/modules/dnf5.py +4 -6
- ansible/modules/expect.py +0 -3
- ansible/modules/find.py +1 -2
- ansible/modules/get_url.py +1 -1
- ansible/modules/git.py +4 -5
- ansible/modules/include_vars.py +1 -1
- ansible/modules/known_hosts.py +7 -1
- ansible/modules/lineinfile.py +71 -63
- ansible/modules/package_facts.py +1 -1
- ansible/modules/pip.py +8 -2
- ansible/modules/replace.py +6 -6
- ansible/modules/service.py +3 -4
- ansible/modules/stat.py +20 -0
- ansible/modules/uri.py +9 -10
- ansible/modules/user.py +1 -2
- ansible/modules/wait_for.py +2 -2
- ansible/modules/wait_for_connection.py +2 -1
- ansible/modules/yum_repository.py +1 -16
- ansible/parsing/dataloader.py +24 -31
- ansible/parsing/vault/__init__.py +1 -2
- ansible/playbook/base.py +8 -56
- ansible/playbook/block.py +0 -60
- ansible/playbook/collectionsearch.py +1 -2
- ansible/playbook/handler.py +1 -7
- ansible/playbook/helpers.py +0 -7
- ansible/playbook/included_file.py +1 -1
- ansible/playbook/play.py +102 -36
- ansible/playbook/play_context.py +4 -0
- ansible/playbook/role/__init__.py +10 -65
- ansible/playbook/role/definition.py +3 -4
- ansible/playbook/role/include.py +2 -3
- ansible/playbook/role/metadata.py +1 -12
- ansible/playbook/role/requirement.py +1 -2
- ansible/playbook/role_include.py +1 -2
- ansible/playbook/taggable.py +16 -5
- ansible/playbook/task.py +11 -50
- ansible/plugins/action/__init__.py +20 -19
- ansible/plugins/action/add_host.py +1 -2
- ansible/plugins/action/fetch.py +3 -5
- ansible/plugins/action/group_by.py +1 -2
- ansible/plugins/action/include_vars.py +20 -22
- ansible/plugins/action/script.py +1 -3
- ansible/plugins/action/template.py +1 -2
- ansible/plugins/action/uri.py +4 -2
- ansible/plugins/cache/__init__.py +1 -0
- ansible/plugins/callback/__init__.py +13 -6
- ansible/plugins/connection/__init__.py +3 -7
- ansible/plugins/connection/local.py +2 -3
- ansible/plugins/connection/psrp.py +0 -2
- ansible/plugins/connection/ssh.py +2 -7
- ansible/plugins/connection/winrm.py +0 -2
- ansible/plugins/doc_fragments/result_format_callback.py +15 -0
- ansible/plugins/filter/core.py +4 -5
- ansible/plugins/filter/encryption.py +3 -27
- ansible/plugins/filter/mathstuff.py +1 -2
- ansible/plugins/filter/to_nice_yaml.yml +31 -3
- ansible/plugins/filter/to_yaml.yml +29 -12
- ansible/plugins/inventory/__init__.py +1 -2
- ansible/plugins/inventory/toml.py +3 -6
- ansible/plugins/inventory/yaml.py +1 -2
- ansible/plugins/loader.py +3 -4
- ansible/plugins/lookup/password.py +1 -2
- ansible/plugins/lookup/subelements.py +2 -3
- ansible/plugins/lookup/url.py +1 -1
- ansible/plugins/lookup/varnames.py +1 -2
- ansible/plugins/shell/__init__.py +9 -4
- ansible/plugins/shell/powershell.py +8 -24
- ansible/plugins/strategy/__init__.py +5 -2
- ansible/plugins/test/core.py +4 -1
- ansible/plugins/test/falsy.yml +1 -1
- ansible/plugins/test/regex.yml +18 -6
- ansible/plugins/test/truthy.yml +1 -1
- ansible/release.py +2 -2
- ansible/template/__init__.py +3 -7
- ansible/utils/collection_loader/_collection_config.py +5 -0
- ansible/utils/collection_loader/_collection_finder.py +11 -14
- ansible/utils/context_objects.py +7 -4
- ansible/utils/display.py +7 -6
- ansible/utils/encrypt.py +0 -5
- ansible/utils/helpers.py +6 -2
- ansible/utils/jsonrpc.py +7 -3
- ansible/utils/plugin_docs.py +49 -38
- ansible/utils/ssh_functions.py +0 -19
- ansible/utils/unsafe_proxy.py +7 -7
- ansible/vars/clean.py +2 -3
- ansible/vars/manager.py +28 -22
- ansible/vars/plugins.py +1 -31
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/METADATA +4 -4
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/RECORD +213 -214
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/network.txt +0 -1
- ansible_test/_data/completion/remote.txt +4 -4
- ansible_test/_data/requirements/ansible-test.txt +1 -1
- ansible_test/_data/requirements/ansible.txt +1 -1
- ansible_test/_data/requirements/sanity.ansible-doc.txt +2 -2
- ansible_test/_data/requirements/sanity.changelog.txt +2 -2
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.import.txt +1 -1
- ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +6 -6
- ansible_test/_data/requirements/sanity.runtime-metadata.txt +1 -1
- ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_internal/cache.py +2 -5
- ansible_test/_internal/cli/compat.py +1 -1
- ansible_test/_internal/commands/coverage/combine.py +1 -3
- ansible_test/_internal/commands/integration/__init__.py +3 -7
- ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
- ansible_test/_internal/commands/integration/coverage.py +1 -3
- ansible_test/_internal/commands/integration/filters.py +5 -10
- ansible_test/_internal/commands/sanity/pylint.py +11 -0
- ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
- ansible_test/_internal/commands/units/__init__.py +1 -13
- ansible_test/_internal/compat/packaging.py +2 -2
- ansible_test/_internal/compat/yaml.py +2 -2
- ansible_test/_internal/completion.py +2 -5
- ansible_test/_internal/config.py +2 -7
- ansible_test/_internal/coverage_util.py +1 -1
- ansible_test/_internal/delegation.py +2 -0
- ansible_test/_internal/docker_util.py +1 -1
- ansible_test/_internal/host_profiles.py +6 -11
- ansible_test/_internal/provider/__init__.py +2 -5
- ansible_test/_internal/provisioning.py +2 -5
- ansible_test/_internal/pypi_proxy.py +1 -1
- ansible_test/_internal/python_requirements.py +1 -1
- ansible_test/_internal/target.py +2 -6
- ansible_test/_internal/thread.py +1 -4
- ansible_test/_internal/util.py +9 -14
- ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +48 -45
- ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +9 -7
- ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +51 -37
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
- ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
- ansible_test/_util/target/common/constants.py +2 -2
- ansible_test/_util/target/setup/bootstrap.sh +0 -6
- ansible/utils/py3compat.py +0 -27
- ansible_test/_data/pytest/config/legacy.ini +0 -4
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/WHEEL +0 -0
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/top_level.txt +0 -0
ansible_test/_internal/target.py
CHANGED
|
@@ -65,7 +65,7 @@ def walk_completion_targets(targets: c.Iterable[CompletionTarget], prefix: str,
|
|
|
65
65
|
return tuple(sorted(matches))
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def walk_internal_targets(
|
|
68
|
+
def walk_internal_targets[TCompletionTarget: CompletionTarget](
|
|
69
69
|
targets: c.Iterable[TCompletionTarget],
|
|
70
70
|
includes: t.Optional[list[str]] = None,
|
|
71
71
|
excludes: t.Optional[list[str]] = None,
|
|
@@ -87,7 +87,7 @@ def walk_internal_targets(
|
|
|
87
87
|
return tuple(sorted(internal_targets, key=lambda sort_target: sort_target.name))
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def filter_targets(
|
|
90
|
+
def filter_targets[TCompletionTarget: CompletionTarget](
|
|
91
91
|
targets: c.Iterable[TCompletionTarget],
|
|
92
92
|
patterns: list[str],
|
|
93
93
|
include: bool = True,
|
|
@@ -711,7 +711,3 @@ class TargetPatternsNotMatched(ApplicationError):
|
|
|
711
711
|
message = 'Target pattern not matched: %s' % self.patterns[0]
|
|
712
712
|
|
|
713
713
|
super().__init__(message)
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
TCompletionTarget = t.TypeVar('TCompletionTarget', bound=CompletionTarget)
|
|
717
|
-
TIntegrationTarget = t.TypeVar('TIntegrationTarget', bound=IntegrationTarget)
|
ansible_test/_internal/thread.py
CHANGED
|
@@ -11,9 +11,6 @@ import queue
|
|
|
11
11
|
import typing as t
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
TCallable = t.TypeVar('TCallable', bound=t.Callable[..., t.Any])
|
|
15
|
-
|
|
16
|
-
|
|
17
14
|
class WrappedThread(threading.Thread):
|
|
18
15
|
"""Wrapper around Thread which captures results and exceptions."""
|
|
19
16
|
|
|
@@ -50,7 +47,7 @@ class WrappedThread(threading.Thread):
|
|
|
50
47
|
return result
|
|
51
48
|
|
|
52
49
|
|
|
53
|
-
def mutex(func: TCallable) -> TCallable:
|
|
50
|
+
def mutex[TCallable: t.Callable[..., t.Any]](func: TCallable) -> TCallable:
|
|
54
51
|
"""Enforce exclusive access on a decorated function."""
|
|
55
52
|
lock = threading.Lock()
|
|
56
53
|
|
ansible_test/_internal/util.py
CHANGED
|
@@ -57,11 +57,6 @@ from .constants import (
|
|
|
57
57
|
SUPPORTED_PYTHON_VERSIONS,
|
|
58
58
|
)
|
|
59
59
|
|
|
60
|
-
C = t.TypeVar('C')
|
|
61
|
-
TBase = t.TypeVar('TBase')
|
|
62
|
-
TKey = t.TypeVar('TKey')
|
|
63
|
-
TValue = t.TypeVar('TValue')
|
|
64
|
-
|
|
65
60
|
PYTHON_PATHS: dict[str, str] = {}
|
|
66
61
|
|
|
67
62
|
COVERAGE_CONFIG_NAME = 'coveragerc'
|
|
@@ -180,7 +175,7 @@ def is_valid_identifier(value: str) -> bool:
|
|
|
180
175
|
return value.isidentifier() and not keyword.iskeyword(value)
|
|
181
176
|
|
|
182
177
|
|
|
183
|
-
def cache(func: c.Callable[[], TValue]) -> c.Callable[[], TValue]:
|
|
178
|
+
def cache[TValue](func: c.Callable[[], TValue]) -> c.Callable[[], TValue]:
|
|
184
179
|
"""Enforce exclusive access on a decorated function and cache the result."""
|
|
185
180
|
storage: dict[None, TValue] = {}
|
|
186
181
|
sentinel = object()
|
|
@@ -313,7 +308,7 @@ def read_lines_without_comments(path: str, remove_blank_lines: bool = False, opt
|
|
|
313
308
|
return lines
|
|
314
309
|
|
|
315
310
|
|
|
316
|
-
def exclude_none_values(data: dict[TKey, t.Optional[TValue]]) -> dict[TKey, TValue]:
|
|
311
|
+
def exclude_none_values[TKey, TValue](data: dict[TKey, t.Optional[TValue]]) -> dict[TKey, TValue]:
|
|
317
312
|
"""Return the provided dictionary with any None values excluded."""
|
|
318
313
|
return dict((key, value) for key, value in data.items() if value is not None)
|
|
319
314
|
|
|
@@ -1059,7 +1054,7 @@ def format_command_output(stdout: str | None, stderr: str | None) -> str:
|
|
|
1059
1054
|
return message
|
|
1060
1055
|
|
|
1061
1056
|
|
|
1062
|
-
def retry(func: t.Callable[...,
|
|
1057
|
+
def retry[T](func: t.Callable[..., T], ex_type: t.Type[BaseException] = SubprocessError, sleep: int = 10, attempts: int = 10, warn: bool = True) -> T:
|
|
1063
1058
|
"""Retry the specified function on failure."""
|
|
1064
1059
|
for dummy in range(1, attempts):
|
|
1065
1060
|
try:
|
|
@@ -1092,7 +1087,7 @@ def parse_to_list_of_dict(pattern: str, value: str) -> list[dict[str, str]]:
|
|
|
1092
1087
|
return matched
|
|
1093
1088
|
|
|
1094
1089
|
|
|
1095
|
-
def get_subclasses(class_type: t.Type[C]) -> list[t.Type[C]]:
|
|
1090
|
+
def get_subclasses[C](class_type: t.Type[C]) -> list[t.Type[C]]:
|
|
1096
1091
|
"""Returns a list of types that are concrete subclasses of the given type."""
|
|
1097
1092
|
subclasses: set[t.Type[C]] = set()
|
|
1098
1093
|
queue: list[t.Type[C]] = [class_type]
|
|
@@ -1168,7 +1163,7 @@ def import_plugins(directory: str, root: t.Optional[str] = None) -> None:
|
|
|
1168
1163
|
load_module(module_path, name)
|
|
1169
1164
|
|
|
1170
1165
|
|
|
1171
|
-
def load_plugins(base_type: t.Type[C], database: dict[str, t.Type[C]]) -> None:
|
|
1166
|
+
def load_plugins[C](base_type: t.Type[C], database: dict[str, t.Type[C]]) -> None:
|
|
1172
1167
|
"""
|
|
1173
1168
|
Load plugins of the specified type and track them in the specified database.
|
|
1174
1169
|
Only plugins which have already been imported will be loaded.
|
|
@@ -1195,19 +1190,19 @@ def sanitize_host_name(name: str) -> str:
|
|
|
1195
1190
|
return re.sub('[^A-Za-z0-9]+', '-', name)[:63].strip('-')
|
|
1196
1191
|
|
|
1197
1192
|
|
|
1198
|
-
def get_generic_type(base_type: t.Type, generic_base_type: t.Type[TValue]) -> t.Optional[t.Type[TValue]]:
|
|
1193
|
+
def get_generic_type[TValue](base_type: t.Type, generic_base_type: t.Type[TValue]) -> t.Optional[t.Type[TValue]]:
|
|
1199
1194
|
"""Return the generic type arg derived from the generic_base_type type that is associated with the base_type type, if any, otherwise return None."""
|
|
1200
1195
|
# noinspection PyUnresolvedReferences
|
|
1201
1196
|
type_arg = t.get_args(base_type.__orig_bases__[0])[0]
|
|
1202
1197
|
return None if isinstance(type_arg, generic_base_type) else type_arg
|
|
1203
1198
|
|
|
1204
1199
|
|
|
1205
|
-
def get_type_associations(base_type: t.Type[TBase], generic_base_type: t.Type[TValue]) -> list[tuple[t.Type[TValue], t.Type[TBase]]]:
|
|
1200
|
+
def get_type_associations[TBase, TValue](base_type: t.Type[TBase], generic_base_type: t.Type[TValue]) -> list[tuple[t.Type[TValue], t.Type[TBase]]]:
|
|
1206
1201
|
"""Create and return a list of tuples associating generic_base_type derived types with a corresponding base_type derived type."""
|
|
1207
1202
|
return [item for item in [(get_generic_type(sc_type, generic_base_type), sc_type) for sc_type in get_subclasses(base_type)] if item[1]]
|
|
1208
1203
|
|
|
1209
1204
|
|
|
1210
|
-
def get_type_map(base_type: t.Type[TBase], generic_base_type: t.Type[TValue]) -> dict[t.Type[TValue], t.Type[TBase]]:
|
|
1205
|
+
def get_type_map[TBase, TValue](base_type: t.Type[TBase], generic_base_type: t.Type[TValue]) -> dict[t.Type[TValue], t.Type[TBase]]:
|
|
1211
1206
|
"""Create and return a mapping of generic_base_type derived types to base_type derived types."""
|
|
1212
1207
|
return {item[0]: item[1] for item in get_type_associations(base_type, generic_base_type)}
|
|
1213
1208
|
|
|
@@ -1228,7 +1223,7 @@ def verify_sys_executable(path: str) -> t.Optional[str]:
|
|
|
1228
1223
|
return expected_executable
|
|
1229
1224
|
|
|
1230
1225
|
|
|
1231
|
-
def type_guard(sequence: c.Sequence[t.Any], guard_type: t.Type[C]) -> t.TypeGuard[c.Sequence[C]]:
|
|
1226
|
+
def type_guard[C](sequence: c.Sequence[t.Any], guard_type: t.Type[C]) -> t.TypeGuard[c.Sequence[C]]:
|
|
1232
1227
|
"""
|
|
1233
1228
|
Raises an exception if any item in the given sequence does not match the specified guard type.
|
|
1234
1229
|
Use with assert so that type checkers are aware of the type guard.
|
|
@@ -17,14 +17,13 @@ from voluptuous import Required, Schema, Invalid
|
|
|
17
17
|
from voluptuous.humanize import humanize_error
|
|
18
18
|
|
|
19
19
|
from ansible.module_utils.compat.version import StrictVersion, LooseVersion
|
|
20
|
-
from ansible.module_utils.six import string_types
|
|
21
20
|
from ansible.utils.collection_loader import AnsibleCollectionRef
|
|
22
21
|
from ansible.utils.version import SemanticVersion
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
def fqcr(value):
|
|
26
25
|
"""Validate a FQCR."""
|
|
27
|
-
if not isinstance(value,
|
|
26
|
+
if not isinstance(value, str):
|
|
28
27
|
raise Invalid('Must be a string that is a FQCR')
|
|
29
28
|
if not AnsibleCollectionRef.is_valid_fqcr(value):
|
|
30
29
|
raise Invalid('Must be a FQCR')
|
|
@@ -33,7 +32,7 @@ def fqcr(value):
|
|
|
33
32
|
|
|
34
33
|
def fqcr_or_shortname(value):
|
|
35
34
|
"""Validate a FQCR or a shortname."""
|
|
36
|
-
if not isinstance(value,
|
|
35
|
+
if not isinstance(value, str):
|
|
37
36
|
raise Invalid('Must be a string that is a FQCR or a short name')
|
|
38
37
|
if '.' in value and not AnsibleCollectionRef.is_valid_fqcr(value):
|
|
39
38
|
raise Invalid('Must be a FQCR or a short name')
|
|
@@ -48,7 +47,7 @@ def isodate(value, check_deprecation_date=False, is_tombstone=False):
|
|
|
48
47
|
else:
|
|
49
48
|
# make sure we have a string
|
|
50
49
|
msg = 'Expected ISO 8601 date string (YYYY-MM-DD), or YAML date'
|
|
51
|
-
if not isinstance(value,
|
|
50
|
+
if not isinstance(value, str):
|
|
52
51
|
raise Invalid(msg)
|
|
53
52
|
# From Python 3.7 in, there is datetime.date.fromisoformat(). For older versions,
|
|
54
53
|
# we have to do things manually.
|
|
@@ -80,7 +79,7 @@ def removal_version(value, is_ansible, current_version=None, is_tombstone=False)
|
|
|
80
79
|
'Removal version must be a string' if is_ansible else
|
|
81
80
|
'Removal version must be a semantic version (https://semver.org/)'
|
|
82
81
|
)
|
|
83
|
-
if not isinstance(value,
|
|
82
|
+
if not isinstance(value, str):
|
|
84
83
|
raise Invalid(msg)
|
|
85
84
|
try:
|
|
86
85
|
if is_ansible:
|
|
@@ -191,7 +190,7 @@ def validate_metadata_file(path, is_ansible, check_deprecation_dates=False):
|
|
|
191
190
|
'removal_version': partial(removal_version, is_ansible=is_ansible,
|
|
192
191
|
current_version=current_version),
|
|
193
192
|
'removal_date': partial(isodate, check_deprecation_date=check_deprecation_dates),
|
|
194
|
-
'warning_text':
|
|
193
|
+
'warning_text': str,
|
|
195
194
|
}
|
|
196
195
|
),
|
|
197
196
|
avoid_additional_data
|
|
@@ -204,7 +203,7 @@ def validate_metadata_file(path, is_ansible, check_deprecation_dates=False):
|
|
|
204
203
|
'removal_version': partial(removal_version, is_ansible=is_ansible,
|
|
205
204
|
current_version=current_version, is_tombstone=True),
|
|
206
205
|
'removal_date': partial(isodate, is_tombstone=True),
|
|
207
|
-
'warning_text':
|
|
206
|
+
'warning_text': str,
|
|
208
207
|
}
|
|
209
208
|
),
|
|
210
209
|
avoid_additional_data
|
|
@@ -228,18 +227,15 @@ def validate_metadata_file(path, is_ansible, check_deprecation_dates=False):
|
|
|
228
227
|
# Adjusted schema for module_utils
|
|
229
228
|
plugin_routing_schema_mu = Any(
|
|
230
229
|
plugins_routing_common_schema.extend({
|
|
231
|
-
('redirect'):
|
|
230
|
+
('redirect'): str}
|
|
232
231
|
),
|
|
233
232
|
)
|
|
234
233
|
|
|
235
|
-
list_dict_plugin_routing_schema = [{
|
|
236
|
-
for str_type in string_types]
|
|
234
|
+
list_dict_plugin_routing_schema = [{str: plugin_routing_schema}]
|
|
237
235
|
|
|
238
|
-
list_dict_plugin_routing_schema_mu = [{
|
|
239
|
-
for str_type in string_types]
|
|
236
|
+
list_dict_plugin_routing_schema_mu = [{str: plugin_routing_schema_mu}]
|
|
240
237
|
|
|
241
|
-
list_dict_plugin_routing_schema_modules = [{
|
|
242
|
-
for str_type in string_types]
|
|
238
|
+
list_dict_plugin_routing_schema_modules = [{str: plugin_routing_schema_modules}]
|
|
243
239
|
|
|
244
240
|
plugin_schema = Schema({
|
|
245
241
|
('action'): Any(None, *list_dict_plugin_routing_schema),
|
|
@@ -267,13 +263,12 @@ def validate_metadata_file(path, is_ansible, check_deprecation_dates=False):
|
|
|
267
263
|
|
|
268
264
|
import_redirection_schema = Any(
|
|
269
265
|
Schema({
|
|
270
|
-
('redirect'):
|
|
266
|
+
('redirect'): str,
|
|
271
267
|
# import_redirect doesn't currently support deprecation
|
|
272
268
|
}, extra=PREVENT_EXTRA)
|
|
273
269
|
)
|
|
274
270
|
|
|
275
|
-
list_dict_import_redirection_schema = [{
|
|
276
|
-
for str_type in string_types]
|
|
271
|
+
list_dict_import_redirection_schema = [{str: import_redirection_schema}]
|
|
277
272
|
|
|
278
273
|
# action_groups schema
|
|
279
274
|
|
|
@@ -289,7 +284,7 @@ def validate_metadata_file(path, is_ansible, check_deprecation_dates=False):
|
|
|
289
284
|
}, extra=PREVENT_EXTRA)
|
|
290
285
|
}, extra=PREVENT_EXTRA)
|
|
291
286
|
action_group_schema = All([metadata_dict, fqcr_or_shortname], at_most_one_dict)
|
|
292
|
-
list_dict_action_groups_schema = [{
|
|
287
|
+
list_dict_action_groups_schema = [{str: action_group_schema}]
|
|
293
288
|
|
|
294
289
|
# top level schema
|
|
295
290
|
|
|
@@ -298,7 +293,7 @@ def validate_metadata_file(path, is_ansible, check_deprecation_dates=False):
|
|
|
298
293
|
('plugin_routing'): Any(plugin_schema),
|
|
299
294
|
('import_redirection'): Any(None, *list_dict_import_redirection_schema),
|
|
300
295
|
# requires_ansible: In the future we should validate this with SpecifierSet
|
|
301
|
-
('requires_ansible'):
|
|
296
|
+
('requires_ansible'): str,
|
|
302
297
|
('action_groups'): Any(*list_dict_action_groups_schema),
|
|
303
298
|
}, extra=PREVENT_EXTRA)
|
|
304
299
|
|
|
@@ -11,9 +11,11 @@ import functools
|
|
|
11
11
|
import pathlib
|
|
12
12
|
import re
|
|
13
13
|
|
|
14
|
-
import astroid
|
|
15
|
-
import astroid.
|
|
14
|
+
import astroid.bases
|
|
15
|
+
import astroid.exceptions
|
|
16
|
+
import astroid.nodes
|
|
16
17
|
import astroid.typing
|
|
18
|
+
import astroid.util
|
|
17
19
|
|
|
18
20
|
import pylint.lint
|
|
19
21
|
import pylint.checkers
|
|
@@ -42,7 +44,7 @@ class DeprecationCallArgs:
|
|
|
42
44
|
|
|
43
45
|
def all_args_dynamic(self) -> bool:
|
|
44
46
|
"""True if all args are dynamic or None, otherwise False."""
|
|
45
|
-
return all(arg is None or isinstance(arg, astroid.NodeNG) for arg in dataclasses.asdict(self).values())
|
|
47
|
+
return all(arg is None or isinstance(arg, astroid.nodes.NodeNG) for arg in dataclasses.asdict(self).values())
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
@@ -177,7 +179,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
177
179
|
def __init__(self, *args, **kwargs) -> None:
|
|
178
180
|
super().__init__(*args, **kwargs)
|
|
179
181
|
|
|
180
|
-
self.module_cache: dict[str, astroid.Module] = {}
|
|
182
|
+
self.module_cache: dict[str, astroid.nodes.Module] = {}
|
|
181
183
|
|
|
182
184
|
@functools.cached_property
|
|
183
185
|
def collection_name(self) -> str | None:
|
|
@@ -226,7 +228,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
226
228
|
return None
|
|
227
229
|
|
|
228
230
|
@pylint.checkers.utils.only_required_for_messages(*(msgs.keys()))
|
|
229
|
-
def visit_call(self, node: astroid.Call) -> None:
|
|
231
|
+
def visit_call(self, node: astroid.nodes.Call) -> None:
|
|
230
232
|
"""Visit a call node."""
|
|
231
233
|
if inferred := self.infer(node.func):
|
|
232
234
|
name = self.get_fully_qualified_name(inferred)
|
|
@@ -234,50 +236,50 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
234
236
|
if args := self.DEPRECATION_FUNCTIONS.get(name):
|
|
235
237
|
self.check_call(node, name, args)
|
|
236
238
|
|
|
237
|
-
def infer(self, node: astroid.NodeNG) -> astroid.NodeNG | None:
|
|
239
|
+
def infer(self, node: astroid.nodes.NodeNG) -> astroid.nodes.NodeNG | None:
|
|
238
240
|
"""Return the inferred node from the given node, or `None` if it cannot be unambiguously inferred."""
|
|
239
241
|
names: list[str] = []
|
|
240
|
-
target: astroid.NodeNG | None = node
|
|
242
|
+
target: astroid.nodes.NodeNG | None = node
|
|
241
243
|
inferred: astroid.typing.InferenceResult | None = None
|
|
242
244
|
|
|
243
245
|
while target:
|
|
244
246
|
if inferred := astroid.util.safe_infer(target):
|
|
245
247
|
break
|
|
246
248
|
|
|
247
|
-
if isinstance(target, astroid.Call):
|
|
249
|
+
if isinstance(target, astroid.nodes.Call):
|
|
248
250
|
inferred = self.infer(target.func)
|
|
249
251
|
break
|
|
250
252
|
|
|
251
|
-
if isinstance(target, astroid.FunctionDef):
|
|
253
|
+
if isinstance(target, astroid.nodes.FunctionDef):
|
|
252
254
|
inferred = target
|
|
253
255
|
break
|
|
254
256
|
|
|
255
|
-
if isinstance(target, astroid.Name):
|
|
257
|
+
if isinstance(target, astroid.nodes.Name):
|
|
256
258
|
target = self.infer_name(target)
|
|
257
|
-
elif isinstance(target, astroid.AssignName) and isinstance(target.parent, astroid.Assign):
|
|
259
|
+
elif isinstance(target, astroid.nodes.AssignName) and isinstance(target.parent, astroid.nodes.Assign):
|
|
258
260
|
target = target.parent.value
|
|
259
|
-
elif isinstance(target, astroid.Attribute):
|
|
261
|
+
elif isinstance(target, astroid.nodes.Attribute):
|
|
260
262
|
names.append(target.attrname)
|
|
261
263
|
target = target.expr
|
|
262
264
|
else:
|
|
263
265
|
break
|
|
264
266
|
|
|
265
267
|
for name in reversed(names):
|
|
266
|
-
if isinstance(inferred, astroid.Instance):
|
|
268
|
+
if isinstance(inferred, astroid.bases.Instance):
|
|
267
269
|
try:
|
|
268
270
|
attr = next(iter(inferred.getattr(name)), None)
|
|
269
|
-
except astroid.AttributeInferenceError:
|
|
271
|
+
except astroid.exceptions.AttributeInferenceError:
|
|
270
272
|
break
|
|
271
273
|
|
|
272
|
-
if isinstance(attr, astroid.AssignAttr):
|
|
274
|
+
if isinstance(attr, astroid.nodes.AssignAttr):
|
|
273
275
|
inferred = self.get_ansible_module(attr)
|
|
274
276
|
continue
|
|
275
277
|
|
|
276
|
-
if isinstance(attr, astroid.FunctionDef):
|
|
278
|
+
if isinstance(attr, astroid.nodes.FunctionDef):
|
|
277
279
|
inferred = attr
|
|
278
280
|
continue
|
|
279
281
|
|
|
280
|
-
if not isinstance(inferred, (astroid.Module, astroid.ClassDef)):
|
|
282
|
+
if not isinstance(inferred, (astroid.nodes.Module, astroid.nodes.ClassDef)):
|
|
281
283
|
inferred = None
|
|
282
284
|
break
|
|
283
285
|
|
|
@@ -288,15 +290,15 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
288
290
|
else:
|
|
289
291
|
inferred = self.infer(inferred)
|
|
290
292
|
|
|
291
|
-
if isinstance(inferred, astroid.FunctionDef) and isinstance(inferred.parent, astroid.ClassDef):
|
|
292
|
-
inferred = astroid.BoundMethod(inferred, inferred.parent)
|
|
293
|
+
if isinstance(inferred, astroid.nodes.FunctionDef) and isinstance(inferred.parent, astroid.nodes.ClassDef):
|
|
294
|
+
inferred = astroid.bases.BoundMethod(inferred, inferred.parent)
|
|
293
295
|
|
|
294
296
|
return inferred
|
|
295
297
|
|
|
296
|
-
def infer_name(self, node: astroid.Name) -> astroid.NodeNG | None:
|
|
298
|
+
def infer_name(self, node: astroid.nodes.Name) -> astroid.nodes.NodeNG | None:
|
|
297
299
|
"""Infer the node referenced by the given name, or `None` if it cannot be unambiguously inferred."""
|
|
298
300
|
scope = node.scope()
|
|
299
|
-
inferred: astroid.NodeNG | None = None
|
|
301
|
+
inferred: astroid.nodes.NodeNG | None = None
|
|
300
302
|
name = node.name
|
|
301
303
|
|
|
302
304
|
while scope:
|
|
@@ -306,12 +308,12 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
306
308
|
scope = scope.parent.scope() if scope.parent else None
|
|
307
309
|
continue
|
|
308
310
|
|
|
309
|
-
if isinstance(assignment, astroid.AssignName) and isinstance(assignment.parent, astroid.Assign):
|
|
311
|
+
if isinstance(assignment, astroid.nodes.AssignName) and isinstance(assignment.parent, astroid.nodes.Assign):
|
|
310
312
|
inferred = assignment.parent.value
|
|
311
313
|
elif (
|
|
312
|
-
isinstance(scope, astroid.FunctionDef)
|
|
313
|
-
and isinstance(assignment, astroid.AssignName)
|
|
314
|
-
and isinstance(assignment.parent, astroid.Arguments)
|
|
314
|
+
isinstance(scope, astroid.nodes.FunctionDef)
|
|
315
|
+
and isinstance(assignment, astroid.nodes.AssignName)
|
|
316
|
+
and isinstance(assignment.parent, astroid.nodes.Arguments)
|
|
315
317
|
and assignment.parent.annotations
|
|
316
318
|
):
|
|
317
319
|
idx, _node = assignment.parent.find_argname(name)
|
|
@@ -322,12 +324,12 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
322
324
|
except IndexError:
|
|
323
325
|
pass
|
|
324
326
|
else:
|
|
325
|
-
if isinstance(annotation, astroid.Name):
|
|
327
|
+
if isinstance(annotation, astroid.nodes.Name):
|
|
326
328
|
name = annotation.name
|
|
327
329
|
continue
|
|
328
|
-
elif isinstance(assignment, astroid.ClassDef):
|
|
330
|
+
elif isinstance(assignment, astroid.nodes.ClassDef):
|
|
329
331
|
inferred = assignment
|
|
330
|
-
elif isinstance(assignment, astroid.ImportFrom):
|
|
332
|
+
elif isinstance(assignment, astroid.nodes.ImportFrom):
|
|
331
333
|
if module := self.get_module(assignment):
|
|
332
334
|
name = assignment.real_name(name)
|
|
333
335
|
scope = module.scope()
|
|
@@ -337,7 +339,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
337
339
|
|
|
338
340
|
return inferred
|
|
339
341
|
|
|
340
|
-
def get_module(self, node: astroid.ImportFrom) -> astroid.Module | None:
|
|
342
|
+
def get_module(self, node: astroid.nodes.ImportFrom) -> astroid.nodes.Module | None:
|
|
341
343
|
"""Import the requested module if possible and cache the result."""
|
|
342
344
|
module_name = pylint.checkers.utils.get_import_name(node, node.modname)
|
|
343
345
|
|
|
@@ -357,21 +359,21 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
357
359
|
return module
|
|
358
360
|
|
|
359
361
|
@staticmethod
|
|
360
|
-
def get_fully_qualified_name(node: astroid.NodeNG) -> str | None:
|
|
362
|
+
def get_fully_qualified_name(node: astroid.nodes.NodeNG) -> str | None:
|
|
361
363
|
"""Return the fully qualified name of the given inferred node."""
|
|
362
364
|
parent = node.parent
|
|
363
365
|
parts: tuple[str, ...] | None
|
|
364
366
|
|
|
365
|
-
if isinstance(node, astroid.FunctionDef) and isinstance(parent, astroid.Module):
|
|
367
|
+
if isinstance(node, astroid.nodes.FunctionDef) and isinstance(parent, astroid.nodes.Module):
|
|
366
368
|
parts = (parent.name, node.name)
|
|
367
|
-
elif isinstance(node, astroid.BoundMethod) and isinstance(parent, astroid.ClassDef) and isinstance(parent.parent, astroid.Module):
|
|
369
|
+
elif isinstance(node, astroid.bases.BoundMethod) and isinstance(parent, astroid.nodes.ClassDef) and isinstance(parent.parent, astroid.nodes.Module):
|
|
368
370
|
parts = (parent.parent.name, parent.name, node.name)
|
|
369
371
|
else:
|
|
370
372
|
parts = None
|
|
371
373
|
|
|
372
374
|
return '.'.join(parts) if parts else None
|
|
373
375
|
|
|
374
|
-
def check_call(self, node: astroid.Call, name: str, args: tuple[str, ...]) -> None:
|
|
376
|
+
def check_call(self, node: astroid.nodes.Call, name: str, args: tuple[str, ...]) -> None:
|
|
375
377
|
"""Check the given deprecation call node for valid arguments."""
|
|
376
378
|
call_args = self.get_deprecation_call_args(node, args)
|
|
377
379
|
|
|
@@ -400,7 +402,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
400
402
|
self.check_version(node, name, call_args)
|
|
401
403
|
|
|
402
404
|
@staticmethod
|
|
403
|
-
def get_deprecation_call_args(node: astroid.Call, args: tuple[str, ...]) -> DeprecationCallArgs:
|
|
405
|
+
def get_deprecation_call_args(node: astroid.nodes.Call, args: tuple[str, ...]) -> DeprecationCallArgs:
|
|
404
406
|
"""Get the deprecation call arguments from the given node."""
|
|
405
407
|
fields: dict[str, object] = {}
|
|
406
408
|
|
|
@@ -413,12 +415,12 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
413
415
|
fields[keyword.arg] = keyword.value
|
|
414
416
|
|
|
415
417
|
for key, value in fields.items():
|
|
416
|
-
if isinstance(value, astroid.Const):
|
|
418
|
+
if isinstance(value, astroid.nodes.Const):
|
|
417
419
|
fields[key] = value.value
|
|
418
420
|
|
|
419
421
|
return DeprecationCallArgs(**fields)
|
|
420
422
|
|
|
421
|
-
def check_collection_name(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
423
|
+
def check_collection_name(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
422
424
|
"""Check the collection name provided to the given call node."""
|
|
423
425
|
deprecator_requirement = self.is_deprecator_required()
|
|
424
426
|
|
|
@@ -459,14 +461,14 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
459
461
|
if args.collection_name and args.collection_name != expected_collection_name:
|
|
460
462
|
self.add_message('wrong-collection-deprecated', node=node, args=(args.collection_name, name))
|
|
461
463
|
|
|
462
|
-
def check_version(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
464
|
+
def check_version(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
463
465
|
"""Check the version provided to the given call node."""
|
|
464
466
|
if self.collection_name:
|
|
465
467
|
self.check_collection_version(node, name, args)
|
|
466
468
|
else:
|
|
467
469
|
self.check_core_version(node, name, args)
|
|
468
470
|
|
|
469
|
-
def check_core_version(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
471
|
+
def check_core_version(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
470
472
|
"""Check the core version provided to the given call node."""
|
|
471
473
|
try:
|
|
472
474
|
if not isinstance(args.version, str) or not args.version:
|
|
@@ -480,7 +482,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
480
482
|
if self.ANSIBLE_VERSION >= strict_version:
|
|
481
483
|
self.add_message('ansible-deprecated-version', node=node, args=(args.version, name))
|
|
482
484
|
|
|
483
|
-
def check_collection_version(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
485
|
+
def check_collection_version(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
484
486
|
"""Check the collection version provided to the given call node."""
|
|
485
487
|
try:
|
|
486
488
|
if not isinstance(args.version, str) or not args.version:
|
|
@@ -497,7 +499,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
497
499
|
if semantic_version.major != 0 and (semantic_version.minor != 0 or semantic_version.patch != 0):
|
|
498
500
|
self.add_message('removal-version-must-be-major', node=node, args=(args.version,))
|
|
499
501
|
|
|
500
|
-
def check_date(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
502
|
+
def check_date(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
|
501
503
|
"""Check the date provided to the given call node."""
|
|
502
504
|
try:
|
|
503
505
|
date_parsed = self.parse_isodate(args.date)
|
|
@@ -515,18 +517,19 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
|
|
515
517
|
|
|
516
518
|
raise TypeError(type(value))
|
|
517
519
|
|
|
518
|
-
def get_ansible_module(self, node: astroid.AssignAttr) -> astroid.Instance | None:
|
|
520
|
+
def get_ansible_module(self, node: astroid.nodes.AssignAttr) -> astroid.bases.Instance | None:
|
|
519
521
|
"""Infer an AnsibleModule instance node from the given assignment."""
|
|
520
|
-
if isinstance(node.parent, astroid.Assign) and isinstance(node.parent.type_annotation, astroid.Name):
|
|
522
|
+
if isinstance(node.parent, astroid.nodes.Assign) and isinstance(node.parent.type_annotation, astroid.nodes.Name):
|
|
521
523
|
inferred = self.infer_name(node.parent.type_annotation)
|
|
522
|
-
elif isinstance(node.parent, astroid.Assign) and isinstance(node.parent.parent, astroid.FunctionDef) and
|
|
524
|
+
elif (isinstance(node.parent, astroid.nodes.Assign) and isinstance(node.parent.parent, astroid.nodes.FunctionDef) and
|
|
525
|
+
isinstance(node.parent.value, astroid.nodes.Name)):
|
|
523
526
|
inferred = self.infer_name(node.parent.value)
|
|
524
|
-
elif isinstance(node.parent, astroid.AnnAssign) and isinstance(node.parent.annotation, astroid.Name):
|
|
527
|
+
elif isinstance(node.parent, astroid.nodes.AnnAssign) and isinstance(node.parent.annotation, astroid.nodes.Name):
|
|
525
528
|
inferred = self.infer_name(node.parent.annotation)
|
|
526
529
|
else:
|
|
527
530
|
inferred = None
|
|
528
531
|
|
|
529
|
-
if isinstance(inferred, astroid.ClassDef) and inferred.name == 'AnsibleModule':
|
|
532
|
+
if isinstance(inferred, astroid.nodes.ClassDef) and inferred.name == 'AnsibleModule':
|
|
530
533
|
return inferred.instantiate_class()
|
|
531
534
|
|
|
532
535
|
return None
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
# -*- coding: utf-8 -*-
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
import astroid
|
|
8
|
+
import astroid.bases
|
|
9
|
+
import astroid.exceptions
|
|
10
|
+
import astroid.nodes
|
|
9
11
|
|
|
10
12
|
try:
|
|
11
13
|
from pylint.checkers.utils import check_messages
|
|
@@ -39,22 +41,22 @@ class AnsibleStringFormatChecker(BaseChecker):
|
|
|
39
41
|
def visit_call(self, node):
|
|
40
42
|
"""Visit a call node."""
|
|
41
43
|
func = utils.safe_infer(node.func)
|
|
42
|
-
if (isinstance(func, astroid.BoundMethod)
|
|
43
|
-
and isinstance(func.bound, astroid.Instance)
|
|
44
|
+
if (isinstance(func, astroid.bases.BoundMethod)
|
|
45
|
+
and isinstance(func.bound, astroid.bases.Instance)
|
|
44
46
|
and func.bound.name in ('str', 'unicode', 'bytes')):
|
|
45
47
|
if func.name == 'format':
|
|
46
48
|
self._check_new_format(node, func)
|
|
47
49
|
|
|
48
50
|
def _check_new_format(self, node, func):
|
|
49
51
|
""" Check the new string formatting """
|
|
50
|
-
if (isinstance(node.func, astroid.Attribute)
|
|
51
|
-
and not isinstance(node.func.expr, astroid.Const)):
|
|
52
|
+
if (isinstance(node.func, astroid.nodes.Attribute)
|
|
53
|
+
and not isinstance(node.func.expr, astroid.nodes.Const)):
|
|
52
54
|
return
|
|
53
55
|
try:
|
|
54
56
|
strnode = next(func.bound.infer())
|
|
55
|
-
except astroid.InferenceError:
|
|
57
|
+
except astroid.exceptions.InferenceError:
|
|
56
58
|
return
|
|
57
|
-
if not isinstance(strnode, astroid.Const):
|
|
59
|
+
if not isinstance(strnode, astroid.nodes.Const):
|
|
58
60
|
return
|
|
59
61
|
|
|
60
62
|
if isinstance(strnode.value, bytes):
|