ansible-core 2.19.0b3__py3-none-any.whl → 2.19.0b4__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.
Files changed (36) hide show
  1. ansible/_internal/__init__.py +1 -1
  2. ansible/_internal/_json/__init__.py +1 -1
  3. ansible/_internal/_testing.py +26 -0
  4. ansible/cli/arguments/option_helpers.py +6 -2
  5. ansible/config/base.yml +5 -23
  6. ansible/config/manager.py +142 -101
  7. ansible/constants.py +1 -1
  8. ansible/inventory/manager.py +1 -0
  9. ansible/module_utils/ansible_release.py +1 -1
  10. ansible/module_utils/facts/virtual/linux.py +1 -1
  11. ansible/module_utils/parsing/convert_bool.py +6 -0
  12. ansible/modules/assemble.py +4 -4
  13. ansible/modules/cron.py +3 -5
  14. ansible/modules/dnf5.py +1 -0
  15. ansible/modules/git.py +1 -6
  16. ansible/modules/pip.py +2 -4
  17. ansible/modules/sysvinit.py +3 -3
  18. ansible/plugins/action/__init__.py +1 -1
  19. ansible/plugins/action/template.py +3 -0
  20. ansible/plugins/loader.py +3 -2
  21. ansible/release.py +1 -1
  22. ansible/utils/collection_loader/__init__.py +2 -0
  23. ansible/utils/display.py +4 -0
  24. ansible/utils/plugin_docs.py +2 -1
  25. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info}/METADATA +3 -2
  26. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info}/RECORD +36 -35
  27. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info}/WHEEL +1 -1
  28. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +12 -1
  29. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info}/entry_points.txt +0 -0
  30. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info/licenses}/COPYING +0 -0
  31. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/Apache-License.txt +0 -0
  32. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/BSD-3-Clause.txt +0 -0
  33. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/MIT-license.txt +0 -0
  34. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/PSF-license.txt +0 -0
  35. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info/licenses/licenses}/simplified_bsd.txt +0 -0
  36. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b4.dist-info}/top_level.txt +0 -0
@@ -18,7 +18,7 @@ def get_controller_serialize_map() -> dict[type, t.Callable]:
18
18
  return {
19
19
  _lazy_containers._AnsibleLazyTemplateDict: _profiles._JSONSerializationProfile.discard_tags,
20
20
  _lazy_containers._AnsibleLazyTemplateList: _profiles._JSONSerializationProfile.discard_tags,
21
- EncryptedString: str, # preserves tags since this is an intance of EncryptedString; if tags should be discarded from str, another entry will handle it
21
+ EncryptedString: str, # preserves tags since this is an instance of EncryptedString; if tags should be discarded from str, another entry will handle it
22
22
  }
23
23
 
24
24
 
@@ -152,7 +152,7 @@ class AnsibleVariableVisitor:
152
152
  result: _T
153
153
 
154
154
  # DTFIX-RELEASE: the visitor is ignoring dict/mapping keys except for debugging and schema-aware checking, it should be doing type checks on keys
155
- # keep in mind the allowed types for keys is a more restrictive set than for values (str and taggged str only, not EncryptedString)
155
+ # keep in mind the allowed types for keys is a more restrictive set than for values (str and tagged str only, not EncryptedString)
156
156
  # DTFIX-RELEASE: some type lists being consulted (the ones from datatag) are probably too permissive, and perhaps should not be dynamic
157
157
 
158
158
  if (result := self._early_visit(value, value_type)) is not _sentinel:
@@ -0,0 +1,26 @@
1
+ """
2
+ Testing utilities for use in integration tests, not unit tests or non-test code.
3
+ Provides better error behavior than Python's `assert` statement.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import contextlib
9
+ import typing as t
10
+
11
+
12
+ class _Checker:
13
+ @staticmethod
14
+ def check(value: object, msg: str | None = 'Value is not truthy.') -> None:
15
+ """Raise an `AssertionError` if the given `value` is not truthy."""
16
+ if not value:
17
+ raise AssertionError(msg)
18
+
19
+
20
+ @contextlib.contextmanager
21
+ def hard_fail_context(msg: str) -> t.Generator[_Checker]:
22
+ """Enter a context which converts all exceptions to `BaseException` and provides a `Checker` instance for making assertions."""
23
+ try:
24
+ yield _Checker()
25
+ except BaseException as ex:
26
+ raise BaseException(f"Hard failure: {msg}") from ex
@@ -535,13 +535,17 @@ def _tagged_type_factory(name: str, func: t.Callable[[str], object], /) -> t.Cal
535
535
  def tag_value(value: str) -> object:
536
536
  result = func(value)
537
537
 
538
- if result is value:
538
+ if result is value or func is str:
539
539
  # Values which are not mutated are automatically trusted for templating.
540
540
  # The `is` reference equality is critically important, as other types may only alter the tags, so object equality is
541
541
  # not sufficient to prevent them being tagged as trusted when they should not.
542
+ # Explicitly include all usages using the `str` type factory since it strips tags.
542
543
  result = TrustedAsTemplate().tag(result)
543
544
 
544
- return Origin(description=f'<CLI option {name!r}>').tag(result)
545
+ if not (origin := Origin.get_tag(value)):
546
+ origin = Origin(description=f'<CLI option {name!r}>')
547
+
548
+ return origin.tag(result)
545
549
 
546
550
  tag_value._name = name # simplify debugging by attaching the argument name to the function
547
551
 
ansible/config/base.yml CHANGED
@@ -757,7 +757,7 @@ DEFAULT_HASH_BEHAVIOUR:
757
757
  - {key: hash_behaviour, section: defaults}
758
758
  DEFAULT_HOST_LIST:
759
759
  name: Inventory Source
760
- default: /etc/ansible/hosts
760
+ default: [/etc/ansible/hosts]
761
761
  description: Comma-separated list of Ansible inventory sources
762
762
  env:
763
763
  - name: ANSIBLE_INVENTORY
@@ -1054,7 +1054,7 @@ DEFAULT_ROLES_PATH:
1054
1054
  yaml: {key: defaults.roles_path}
1055
1055
  DEFAULT_SELINUX_SPECIAL_FS:
1056
1056
  name: Problematic file systems
1057
- default: fuse, nfs, vboxsf, ramfs, 9p, vfat
1057
+ default: [fuse, nfs, vboxsf, ramfs, 9p, vfat]
1058
1058
  description:
1059
1059
  - "Some filesystems do not support safe operations and/or return inconsistent errors,
1060
1060
  this setting makes Ansible 'tolerate' those in the list without causing fatal errors."
@@ -1199,15 +1199,6 @@ DEFAULT_VARS_PLUGIN_PATH:
1199
1199
  ini:
1200
1200
  - {key: vars_plugins, section: defaults}
1201
1201
  type: pathspec
1202
- # TODO: unused?
1203
- #DEFAULT_VAR_COMPRESSION_LEVEL:
1204
- # default: 0
1205
- # description: 'TODO: write it'
1206
- # env: [{name: ANSIBLE_VAR_COMPRESSION_LEVEL}]
1207
- # ini:
1208
- # - {key: var_compression_level, section: defaults}
1209
- # type: integer
1210
- # yaml: {key: defaults.var_compression_level}
1211
1202
  DEFAULT_VAULT_ID_MATCH:
1212
1203
  name: Force vault id match
1213
1204
  default: False
@@ -1333,7 +1324,7 @@ DISPLAY_SKIPPED_HOSTS:
1333
1324
  type: boolean
1334
1325
  DISPLAY_TRACEBACK:
1335
1326
  name: Control traceback display
1336
- default: never
1327
+ default: [never]
1337
1328
  description: When to include tracebacks in extended error messages
1338
1329
  env:
1339
1330
  - name: ANSIBLE_DISPLAY_TRACEBACK
@@ -1480,15 +1471,6 @@ GALAXY_COLLECTIONS_PATH_WARNING:
1480
1471
  ini:
1481
1472
  - {key: collections_path_warning, section: galaxy}
1482
1473
  version_added: "2.16"
1483
- # TODO: unused?
1484
- #GALAXY_SCMS:
1485
- # name: Galaxy SCMS
1486
- # default: git, hg
1487
- # description: Available galaxy source control management systems.
1488
- # env: [{name: ANSIBLE_GALAXY_SCMS}]
1489
- # ini:
1490
- # - {key: scms, section: galaxy}
1491
- # type: list
1492
1474
  GALAXY_SERVER:
1493
1475
  default: https://galaxy.ansible.com
1494
1476
  description: "URL to prepend when roles don't specify the full URI, assume they are referencing this server as the source."
@@ -1731,7 +1713,7 @@ INVENTORY_EXPORT:
1731
1713
  type: bool
1732
1714
  INVENTORY_IGNORE_EXTS:
1733
1715
  name: Inventory ignore extensions
1734
- default: "{{(REJECT_EXTS + ('.orig', '.cfg', '.retry'))}}"
1716
+ default: "{{ REJECT_EXTS + ['.orig', '.cfg', '.retry'] }}"
1735
1717
  description: List of extensions to ignore when using a directory as an inventory source.
1736
1718
  env: [{name: ANSIBLE_INVENTORY_IGNORE}]
1737
1719
  ini:
@@ -1788,7 +1770,7 @@ INJECT_FACTS_AS_VARS:
1788
1770
  version_added: "2.5"
1789
1771
  MODULE_IGNORE_EXTS:
1790
1772
  name: Module ignore extensions
1791
- default: "{{(REJECT_EXTS + ('.yaml', '.yml', '.ini'))}}"
1773
+ default: "{{ REJECT_EXTS + ['.yaml', '.yml', '.ini'] }}"
1792
1774
  description:
1793
1775
  - List of extensions to ignore when looking for modules to load.
1794
1776
  - This is for rejecting script and binary module fallback extensions.
ansible/config/manager.py CHANGED
@@ -16,11 +16,12 @@ import typing as t
16
16
  from collections.abc import Mapping, Sequence
17
17
  from jinja2.nativetypes import NativeEnvironment
18
18
 
19
+ from ansible._internal._datatag import _tags
19
20
  from ansible.errors import AnsibleOptionsError, AnsibleError, AnsibleUndefinedConfigEntry, AnsibleRequiredOptionError
21
+ from ansible.module_utils._internal._datatag import AnsibleTagHelper
20
22
  from ansible.module_utils.common.sentinel import Sentinel
21
23
  from ansible.module_utils.common.text.converters import to_text, to_bytes, to_native
22
24
  from ansible.module_utils.common.yaml import yaml_load
23
- from ansible.module_utils.six import string_types
24
25
  from ansible.module_utils.parsing.convert_bool import boolean
25
26
  from ansible.parsing.quoting import unquote
26
27
  from ansible.utils.path import cleanup_tmp_file, makedirs_safe, unfrackpath
@@ -50,6 +51,14 @@ GALAXY_SERVER_ADDITIONAL = {
50
51
  }
51
52
 
52
53
 
54
+ @t.runtime_checkable
55
+ class _EncryptedStringProtocol(t.Protocol):
56
+ """Protocol representing an `EncryptedString`, since it cannot be imported here."""
57
+ # DTFIX-FUTURE: collapse this with the one in collection loader, once we can
58
+
59
+ def _decrypt(self) -> str: ...
60
+
61
+
53
62
  def _get_config_label(plugin_type: str, plugin_name: str, config: str) -> str:
54
63
  """Return a label for the given config."""
55
64
  entry = f'{config!r}'
@@ -65,133 +74,157 @@ def _get_config_label(plugin_type: str, plugin_name: str, config: str) -> str:
65
74
  return entry
66
75
 
67
76
 
68
- # FIXME: see if we can unify in module_utils with similar function used by argspec
69
- def ensure_type(value, value_type, origin=None, origin_ftype=None):
70
- """ return a configuration variable with casting
71
- :arg value: The value to ensure correct typing of
72
- :kwarg value_type: The type of the value. This can be any of the following strings:
73
- :boolean: sets the value to a True or False value
74
- :bool: Same as 'boolean'
75
- :integer: Sets the value to an integer or raises a ValueType error
76
- :int: Same as 'integer'
77
- :float: Sets the value to a float or raises a ValueType error
78
- :list: Treats the value as a comma separated list. Split the value
79
- and return it as a python list.
80
- :none: Sets the value to None
81
- :path: Expands any environment variables and tilde's in the value.
82
- :tmppath: Create a unique temporary directory inside of the directory
83
- specified by value and return its path.
84
- :temppath: Same as 'tmppath'
85
- :tmp: Same as 'tmppath'
86
- :pathlist: Treat the value as a typical PATH string. (On POSIX, this
87
- means comma separated strings.) Split the value and then expand
88
- each part for environment variables and tildes.
89
- :pathspec: Treat the value as a PATH string. Expands any environment variables
90
- tildes's in the value.
91
- :str: Sets the value to string types.
92
- :string: Same as 'str'
77
+ def ensure_type(value: object, value_type: str | None, origin: str | None = None, origin_ftype: str | None = None) -> t.Any:
78
+ """
79
+ Converts `value` to the requested `value_type`; raises `ValueError` for failed conversions.
80
+
81
+ Values for `value_type` are:
82
+
83
+ * boolean/bool: Return a `bool` by applying non-strict `bool` filter rules:
84
+ 'y', 'yes', 'on', '1', 'true', 't', 1, 1.0, True return True, any other value is False.
85
+ * integer/int: Return an `int`. Accepts any `str` parseable by `int` or numeric value with a zero mantissa (including `bool`).
86
+ * float: Return a `float`. Accepts any `str` parseable by `float` or numeric value (including `bool`).
87
+ * list: Return a `list`. Accepts `list` or `Sequence`. Also accepts, `str`, splitting on ',' while stripping whitespace and unquoting items.
88
+ * none: Return `None`. Accepts only the string "None".
89
+ * path: Return a resolved path. Accepts `str`.
90
+ * temppath/tmppath/tmp: Return a unique temporary directory inside the resolved path specified by the value.
91
+ * pathspec: Return a `list` of resolved paths. Accepts a `list` or `Sequence`. Also accepts `str`, splitting on ':'.
92
+ * pathlist: Return a `list` of resolved paths. Accepts a `list` or `Sequence`. Also accepts `str`, splitting on `,` while stripping whitespace from paths.
93
+ * dictionary/dict: Return a `dict`. Accepts `dict` or `Mapping`.
94
+ * string/str: Return a `str`. Accepts `bool`, `int`, `float`, `complex` or `str`.
95
+
96
+ Path resolution ensures paths are `str` with expansion of '{{CWD}}', environment variables and '~'.
97
+ Non-absolute paths are expanded relative to the basedir from `origin`, if specified.
98
+
99
+ No conversion is performed if `value_type` is unknown or `value` is `None`.
100
+ When `origin_ftype` is "ini", a `str` result will be unquoted.
93
101
  """
94
102
 
95
- errmsg = ''
96
- basedir = None
97
- if origin and os.path.isabs(origin) and os.path.exists(to_bytes(origin)):
98
- basedir = origin
103
+ if value is None:
104
+ return None
105
+
106
+ original_value = value
107
+ copy_tags = value_type not in ('temppath', 'tmppath', 'tmp')
108
+
109
+ value = _ensure_type(value, value_type, origin)
110
+
111
+ if copy_tags and value is not original_value:
112
+ if isinstance(value, list):
113
+ value = [AnsibleTagHelper.tag_copy(original_value, item) for item in value]
114
+
115
+ value = AnsibleTagHelper.tag_copy(original_value, value)
116
+
117
+ if isinstance(value, str) and origin_ftype and origin_ftype == 'ini':
118
+ value = unquote(value)
119
+
120
+ return value
121
+
122
+
123
+ def _ensure_type(value: object, value_type: str | None, origin: str | None = None) -> t.Any:
124
+ """Internal implementation for `ensure_type`, call that function instead."""
125
+ original_value = value
126
+ basedir = origin if origin and os.path.isabs(origin) and os.path.exists(to_bytes(origin)) else None
99
127
 
100
128
  if value_type:
101
129
  value_type = value_type.lower()
102
130
 
103
- if value is not None:
104
- if value_type in ('boolean', 'bool'):
105
- value = boolean(value, strict=False)
131
+ match value_type:
132
+ case 'boolean' | 'bool':
133
+ return boolean(value, strict=False)
134
+
135
+ case 'integer' | 'int':
136
+ if isinstance(value, int): # handle both int and bool (which is an int)
137
+ return int(value)
106
138
 
107
- elif value_type in ('integer', 'int'):
108
- if not isinstance(value, int):
139
+ if isinstance(value, (float, str)):
109
140
  try:
141
+ # use Decimal for all other source type conversions; non-zero mantissa is a failure
110
142
  if (decimal_value := decimal.Decimal(value)) == (int_part := int(decimal_value)):
111
- value = int_part
112
- else:
113
- errmsg = 'int'
114
- except decimal.DecimalException:
115
- errmsg = 'int'
143
+ return int_part
144
+ except (decimal.DecimalException, ValueError):
145
+ pass
146
+
147
+ case 'float':
148
+ if isinstance(value, float):
149
+ return value
116
150
 
117
- elif value_type == 'float':
118
- if not isinstance(value, float):
119
- value = float(value)
151
+ if isinstance(value, (int, str)):
152
+ try:
153
+ return float(value)
154
+ except ValueError:
155
+ pass
156
+
157
+ case 'list':
158
+ if isinstance(value, list):
159
+ return value
120
160
 
121
- elif value_type == 'list':
122
- if isinstance(value, string_types):
123
- value = [unquote(x.strip()) for x in value.split(',')]
124
- elif not isinstance(value, Sequence):
125
- errmsg = 'list'
161
+ if isinstance(value, str):
162
+ return [unquote(x.strip()) for x in value.split(',')]
126
163
 
127
- elif value_type == 'none':
164
+ if isinstance(value, Sequence) and not isinstance(value, bytes):
165
+ return list(value)
166
+
167
+ case 'none':
128
168
  if value == "None":
129
- value = None
169
+ return None
130
170
 
131
- if value is not None:
132
- errmsg = 'None'
171
+ case 'path':
172
+ if isinstance(value, str):
173
+ return resolve_path(value, basedir=basedir)
133
174
 
134
- elif value_type == 'path':
135
- if isinstance(value, string_types):
175
+ case 'temppath' | 'tmppath' | 'tmp':
176
+ if isinstance(value, str):
136
177
  value = resolve_path(value, basedir=basedir)
137
- else:
138
- errmsg = 'path'
139
178
 
140
- elif value_type in ('tmp', 'temppath', 'tmppath'):
141
- if isinstance(value, string_types):
142
- value = resolve_path(value, basedir=basedir)
143
179
  if not os.path.exists(value):
144
180
  makedirs_safe(value, 0o700)
181
+
145
182
  prefix = 'ansible-local-%s' % os.getpid()
146
183
  value = tempfile.mkdtemp(prefix=prefix, dir=value)
147
184
  atexit.register(cleanup_tmp_file, value, warn=True)
148
- else:
149
- errmsg = 'temppath'
150
185
 
151
- elif value_type == 'pathspec':
152
- if isinstance(value, string_types):
186
+ return value
187
+
188
+ case 'pathspec':
189
+ if isinstance(value, str):
153
190
  value = value.split(os.pathsep)
154
191
 
155
- if isinstance(value, Sequence):
156
- value = [resolve_path(x, basedir=basedir) for x in value]
157
- else:
158
- errmsg = 'pathspec'
192
+ if isinstance(value, Sequence) and not isinstance(value, bytes) and all(isinstance(x, str) for x in value):
193
+ return [resolve_path(x, basedir=basedir) for x in value]
159
194
 
160
- elif value_type == 'pathlist':
161
- if isinstance(value, string_types):
195
+ case 'pathlist':
196
+ if isinstance(value, str):
162
197
  value = [x.strip() for x in value.split(',')]
163
198
 
164
- if isinstance(value, Sequence):
165
- value = [resolve_path(x, basedir=basedir) for x in value]
166
- else:
167
- errmsg = 'pathlist'
199
+ if isinstance(value, Sequence) and not isinstance(value, bytes) and all(isinstance(x, str) for x in value):
200
+ return [resolve_path(x, basedir=basedir) for x in value]
168
201
 
169
- elif value_type in ('dict', 'dictionary'):
170
- if not isinstance(value, Mapping):
171
- errmsg = 'dictionary'
202
+ case 'dictionary' | 'dict':
203
+ if isinstance(value, dict):
204
+ return value
172
205
 
173
- elif value_type in ('str', 'string'):
174
- if isinstance(value, (string_types, bool, int, float, complex)):
175
- value = to_text(value, errors='surrogate_or_strict')
176
- if origin_ftype and origin_ftype == 'ini':
177
- value = unquote(value)
178
- else:
179
- errmsg = 'string'
206
+ if isinstance(value, Mapping):
207
+ return dict(value)
208
+
209
+ case 'string' | 'str':
210
+ if isinstance(value, str):
211
+ return value
180
212
 
181
- # defaults to string type
182
- elif isinstance(value, (string_types)):
183
- value = to_text(value, errors='surrogate_or_strict')
184
- if origin_ftype and origin_ftype == 'ini':
185
- value = unquote(value)
213
+ if isinstance(value, (bool, int, float, complex)):
214
+ return str(value)
186
215
 
187
- if errmsg:
188
- raise ValueError(f'Invalid type provided for {errmsg!r}: {value!r}')
216
+ if isinstance(value, _EncryptedStringProtocol):
217
+ return value._decrypt()
189
218
 
190
- return to_text(value, errors='surrogate_or_strict', nonstring='passthru')
219
+ case _:
220
+ # FIXME: define and document a pass-through value_type (None, 'raw', 'object', '', ...) and then deprecate acceptance of unknown types
221
+ return value # return non-str values of unknown value_type as-is
222
+
223
+ raise ValueError(f'Invalid value provided for {value_type!r}: {original_value!r}')
191
224
 
192
225
 
193
226
  # FIXME: see if this can live in utils/path
194
- def resolve_path(path, basedir=None):
227
+ def resolve_path(path: str, basedir: str | None = None) -> str:
195
228
  """ resolve relative or 'variable' paths """
196
229
  if '{{CWD}}' in path: # allow users to force CWD using 'magic' {{CWD}}
197
230
  path = path.replace('{{CWD}}', os.getcwd())
@@ -304,11 +337,13 @@ def _add_base_defs_deprecations(base_defs):
304
337
  process(entry)
305
338
 
306
339
 
307
- class ConfigManager(object):
340
+ class ConfigManager:
308
341
 
309
342
  DEPRECATED = [] # type: list[tuple[str, dict[str, str]]]
310
343
  WARNINGS = set() # type: set[str]
311
344
 
345
+ _errors: list[tuple[str, Exception]]
346
+
312
347
  def __init__(self, conf_file=None, defs_file=None):
313
348
 
314
349
  self._base_defs = {}
@@ -329,6 +364,9 @@ class ConfigManager(object):
329
364
  # initialize parser and read config
330
365
  self._parse_config_file()
331
366
 
367
+ self._errors = []
368
+ """Deferred errors that will be turned into warnings."""
369
+
332
370
  # ensure we always have config def entry
333
371
  self._base_defs['CONFIG_FILE'] = {'default': None, 'type': 'path'}
334
372
 
@@ -368,16 +406,16 @@ class ConfigManager(object):
368
406
  defs = dict((k, server_config_def(server_key, k, req, value_type)) for k, req, value_type in GALAXY_SERVER_DEF)
369
407
  self.initialize_plugin_configuration_definitions('galaxy_server', server_key, defs)
370
408
 
371
- def template_default(self, value, variables):
372
- if isinstance(value, string_types) and (value.startswith('{{') and value.endswith('}}')) and variables is not None:
409
+ def template_default(self, value, variables, key_name: str = '<unknown>'):
410
+ if isinstance(value, str) and (value.startswith('{{') and value.endswith('}}')) and variables is not None:
373
411
  # template default values if possible
374
412
  # NOTE: cannot use is_template due to circular dep
375
413
  try:
376
414
  # FIXME: This really should be using an immutable sandboxed native environment, not just native environment
377
- t = NativeEnvironment().from_string(value)
378
- value = t.render(variables)
379
- except Exception:
380
- pass # not templatable
415
+ template = NativeEnvironment().from_string(value)
416
+ value = template.render(variables)
417
+ except Exception as ex:
418
+ self._errors.append((f'Failed to template default for config {key_name}.', ex))
381
419
  return value
382
420
 
383
421
  def _read_config_yaml_file(self, yml_file):
@@ -631,7 +669,7 @@ class ConfigManager(object):
631
669
  raise AnsibleRequiredOptionError(f"Required config {_get_config_label(plugin_type, plugin_name, config)} not provided.")
632
670
  else:
633
671
  origin = 'default'
634
- value = self.template_default(defs[config].get('default'), variables)
672
+ value = self.template_default(defs[config].get('default'), variables, key_name=_get_config_label(plugin_type, plugin_name, config))
635
673
 
636
674
  try:
637
675
  # ensure correct type, can raise exceptions on mismatched types
@@ -658,7 +696,7 @@ class ConfigManager(object):
658
696
 
659
697
  if isinstance(defs[config]['choices'], Mapping):
660
698
  valid = ', '.join([to_text(k) for k in defs[config]['choices'].keys()])
661
- elif isinstance(defs[config]['choices'], string_types):
699
+ elif isinstance(defs[config]['choices'], str):
662
700
  valid = defs[config]['choices']
663
701
  elif isinstance(defs[config]['choices'], Sequence):
664
702
  valid = ', '.join([to_text(c) for c in defs[config]['choices']])
@@ -674,6 +712,9 @@ class ConfigManager(object):
674
712
  else:
675
713
  raise AnsibleUndefinedConfigEntry(f'No config definition exists for {_get_config_label(plugin_type, plugin_name, config)}.')
676
714
 
715
+ if not _tags.Origin.is_tagged_on(value):
716
+ value = _tags.Origin(description=f'<Config {origin}>').tag(value)
717
+
677
718
  return value, origin
678
719
 
679
720
  def initialize_plugin_configuration_definitions(self, plugin_type, name, defs):
ansible/constants.py CHANGED
@@ -60,7 +60,7 @@ COLOR_CODES = {
60
60
  'magenta': u'0;35', 'bright magenta': u'1;35',
61
61
  'normal': u'0',
62
62
  }
63
- REJECT_EXTS = ('.pyc', '.pyo', '.swp', '.bak', '~', '.rpm', '.md', '.txt', '.rst')
63
+ REJECT_EXTS = ['.pyc', '.pyo', '.swp', '.bak', '~', '.rpm', '.md', '.txt', '.rst'] # this is concatenated with other config settings as lists; cannot be tuple
64
64
  BOOL_TRUE = BOOLEANS_TRUE
65
65
  COLLECTION_PTYPE_COMPAT = {'module': 'modules'}
66
66
 
@@ -313,6 +313,7 @@ class InventoryManager(object):
313
313
  ex.obj = origin
314
314
  failures.append({'src': source, 'plugin': plugin_name, 'exc': ex})
315
315
  except Exception as ex:
316
+ # DTFIX-RELEASE: fix this error handling to correctly deal with messaging
316
317
  try:
317
318
  # omit line number to prevent contextual display of script or possibly sensitive info
318
319
  raise AnsibleError(str(ex), obj=origin) from ex
@@ -17,6 +17,6 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = '2.19.0b3'
20
+ __version__ = '2.19.0b4'
21
21
  __author__ = 'Ansible, Inc.'
22
22
  __codename__ = "What Is and What Should Never Be"
@@ -151,7 +151,7 @@ class LinuxVirtual(Virtual):
151
151
  sys_vendor = get_file_content('/sys/devices/virtual/dmi/id/sys_vendor')
152
152
  product_family = get_file_content('/sys/devices/virtual/dmi/id/product_family')
153
153
 
154
- if product_name in ('KVM', 'KVM Server', 'Bochs', 'AHV'):
154
+ if product_name in ('KVM', 'KVM Server', 'Bochs', 'AHV', 'CloudStack KVM Hypervisor'):
155
155
  guest_tech.add('kvm')
156
156
  if not found_virt:
157
157
  virtual_facts['virtualization_type'] = 'kvm'
@@ -3,6 +3,8 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import collections.abc as c
7
+
6
8
  from ansible.module_utils.six import binary_type, text_type
7
9
  from ansible.module_utils.common.text.converters import to_text
8
10
 
@@ -17,9 +19,13 @@ def boolean(value, strict=True):
17
19
  return value
18
20
 
19
21
  normalized_value = value
22
+
20
23
  if isinstance(value, (text_type, binary_type)):
21
24
  normalized_value = to_text(value, errors='surrogate_or_strict').lower().strip()
22
25
 
26
+ if not isinstance(value, c.Hashable):
27
+ normalized_value = None # prevent unhashable types from bombing, but keep the rest of the existing fallback/error behavior
28
+
23
29
  if normalized_value in BOOLEANS_TRUE:
24
30
  return True
25
31
  elif normalized_value in BOOLEANS_FALSE or not strict:
@@ -181,7 +181,7 @@ def assemble_from_fragments(src_path, delimiter=None, compiled_regexp=None, igno
181
181
  return temp_path
182
182
 
183
183
 
184
- def cleanup(path, result=None):
184
+ def cleanup(module, path, result=None):
185
185
  # cleanup just in case
186
186
  if os.path.exists(path):
187
187
  try:
@@ -189,7 +189,7 @@ def cleanup(path, result=None):
189
189
  except (IOError, OSError) as e:
190
190
  # don't error on possible race conditions, but keep warning
191
191
  if result is not None:
192
- result['warnings'] = ['Unable to remove temp file (%s): %s' % (path, to_native(e))]
192
+ module.warn('Unable to remove temp file (%s): %s' % (path, to_native(e)))
193
193
 
194
194
 
195
195
  def main():
@@ -261,7 +261,7 @@ def main():
261
261
  (rc, out, err) = module.run_command(validate % path)
262
262
  result['validation'] = dict(rc=rc, stdout=out, stderr=err)
263
263
  if rc != 0:
264
- cleanup(path)
264
+ cleanup(module, path)
265
265
  module.fail_json(msg="failed to validate: rc:%s error:%s" % (rc, err))
266
266
  if backup and dest_hash is not None:
267
267
  result['backup_file'] = module.backup_local(dest)
@@ -269,7 +269,7 @@ def main():
269
269
  module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes'])
270
270
  changed = True
271
271
 
272
- cleanup(path, result)
272
+ cleanup(module, path, result)
273
273
 
274
274
  # handle file permissions
275
275
  file_args = module.load_file_common_arguments(module.params)
ansible/modules/cron.py CHANGED
@@ -618,7 +618,6 @@ def main():
618
618
 
619
619
  changed = False
620
620
  res_args = dict()
621
- warnings = list()
622
621
 
623
622
  if cron_file:
624
623
 
@@ -627,8 +626,8 @@ def main():
627
626
 
628
627
  cron_file_basename = os.path.basename(cron_file)
629
628
  if not re.search(r'^[A-Z0-9_-]+$', cron_file_basename, re.I):
630
- warnings.append('Filename portion of cron_file ("%s") should consist' % cron_file_basename +
631
- ' solely of upper- and lower-case letters, digits, underscores, and hyphens')
629
+ module.warn('Filename portion of cron_file ("%s") should consist' % cron_file_basename +
630
+ ' solely of upper- and lower-case letters, digits, underscores, and hyphens')
632
631
 
633
632
  # Ensure all files generated are only writable by the owning user. Primarily relevant for the cron_file option.
634
633
  os.umask(int('022', 8))
@@ -693,7 +692,7 @@ def main():
693
692
  if do_install:
694
693
  for char in ['\r', '\n']:
695
694
  if char in job.strip('\r\n'):
696
- warnings.append('Job should not contain line breaks')
695
+ module.warn('Job should not contain line breaks')
697
696
  break
698
697
 
699
698
  job = crontab.get_cron_job(minute, hour, day, month, weekday, job, special_time, disabled)
@@ -734,7 +733,6 @@ def main():
734
733
  res_args = dict(
735
734
  jobs=crontab.get_jobnames(),
736
735
  envs=crontab.get_envnames(),
737
- warnings=warnings,
738
736
  changed=changed
739
737
  )
740
738
 
ansible/modules/dnf5.py CHANGED
@@ -722,6 +722,7 @@ class Dnf5Module(YumDnf):
722
722
  if self.security:
723
723
  types.append("security")
724
724
  advisory_query.filter_type(types)
725
+ conf.skip_unavailable = True # ignore packages that are of a different type, for backwards compat
725
726
  settings.set_advisory_filter(advisory_query)
726
727
 
727
728
  goal = libdnf5.base.Goal(base)
ansible/modules/git.py CHANGED
@@ -317,11 +317,6 @@ remote_url_changed:
317
317
  returned: success
318
318
  type: bool
319
319
  sample: True
320
- warnings:
321
- description: List of warnings if requested features were not available due to a too old git version.
322
- returned: error
323
- type: str
324
- sample: git version is too old to fully support the depth argument. Falling back to full checkouts.
325
320
  git_dir_now:
326
321
  description: Contains the new path of .git directory if it is changed.
327
322
  returned: success
@@ -1240,7 +1235,7 @@ def main():
1240
1235
  archive_prefix = module.params['archive_prefix']
1241
1236
  separate_git_dir = module.params['separate_git_dir']
1242
1237
 
1243
- result = dict(changed=False, warnings=list())
1238
+ result = dict(changed=False)
1244
1239
 
1245
1240
  if module.params['accept_hostkey']:
1246
1241
  if ssh_opts is not None:
ansible/modules/pip.py CHANGED
@@ -814,10 +814,8 @@ def main():
814
814
  elif requirements:
815
815
  cmd.extend(['-r', requirements])
816
816
  else:
817
- module.exit_json(
818
- changed=False,
819
- warnings=["No valid name or requirements file found."],
820
- )
817
+ module.warn("No valid name or requirements file found.")
818
+ module.exit_json(changed=False)
821
819
 
822
820
  if module.check_mode:
823
821
  if extra_args or requirements or state == 'latest' or not name:
@@ -88,9 +88,9 @@ EXAMPLES = """
88
88
 
89
89
  - name: Sleep for 5 seconds between stop and start command of badly behaving service
90
90
  ansible.builtin.sysvinit:
91
- name: apache2
92
- state: restarted
93
- sleep: 5
91
+ name: apache2
92
+ state: restarted
93
+ sleep: 5
94
94
 
95
95
  - name: Make sure apache2 is started on runlevels 3 and 5
96
96
  ansible.builtin.sysvinit:
@@ -1002,7 +1002,7 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
1002
1002
  # tells the module to ignore options that are not in its argspec.
1003
1003
  module_args['_ansible_ignore_unknown_opts'] = ignore_unknown_opts
1004
1004
 
1005
- # allow user to insert string to add context to remote loggging
1005
+ # allow user to insert string to add context to remote logging
1006
1006
  module_args['_ansible_target_log_info'] = C.config.get_config_value('TARGET_LOG_INFO', variables=task_vars)
1007
1007
 
1008
1008
  module_args['_ansible_tracebacks_for'] = _traceback.traceback_for()
@@ -132,6 +132,9 @@ class ActionModule(ActionBase):
132
132
  data_templar = self._templar.copy_with_new_env(searchpath=searchpath, available_variables=temp_vars)
133
133
  resultant = data_templar.template(template_data, escape_backslashes=False, overrides=overrides)
134
134
 
135
+ if resultant is None:
136
+ resultant = ''
137
+
135
138
  new_task = self._task.copy()
136
139
  # mode is either the mode from task.args or the mode of the source file if the task.args
137
140
  # mode == 'preserve'
ansible/plugins/loader.py CHANGED
@@ -505,7 +505,8 @@ class PluginLoader:
505
505
 
506
506
  # if type name != 'module_doc_fragment':
507
507
  if type_name in C.CONFIGURABLE_PLUGINS and not C.config.has_configuration_definition(type_name, name):
508
- documentation_source = getattr(module, 'DOCUMENTATION', '')
508
+ # trust-tagged source propagates to loaded values; expressions and templates in config require trust
509
+ documentation_source = _tags.TrustedAsTemplate().tag(getattr(module, 'DOCUMENTATION', ''))
509
510
  try:
510
511
  dstring = yaml.load(_tags.Origin(path=path).tag(documentation_source), Loader=AnsibleLoader)
511
512
  except ParserError as e:
@@ -673,7 +674,7 @@ class PluginLoader:
673
674
  # look for any matching extension in the package location (sans filter)
674
675
  found_files = [f
675
676
  for f in glob.iglob(os.path.join(pkg_path, n_resource) + '.*')
676
- if os.path.isfile(f) and not f.endswith(C.MODULE_IGNORE_EXTS)]
677
+ if os.path.isfile(f) and not any(f.endswith(ext) for ext in C.MODULE_IGNORE_EXTS)]
677
678
 
678
679
  if not found_files:
679
680
  return plugin_load_context.nope('failed fuzzy extension match for {0} in {1}'.format(full_name, acr.collection))
ansible/release.py CHANGED
@@ -17,6 +17,6 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = '2.19.0b3'
20
+ __version__ = '2.19.0b4'
21
21
  __author__ = 'Ansible, Inc.'
22
22
  __codename__ = "What Is and What Should Never Be"
@@ -13,6 +13,8 @@ import typing as t
13
13
  class _EncryptedStringProtocol(t.Protocol):
14
14
  """Protocol representing an `EncryptedString`, since it cannot be imported here."""
15
15
 
16
+ # DTFIX-FUTURE: collapse this with the one in config, once we can
17
+
16
18
  def _decrypt(self) -> str: ...
17
19
 
18
20
 
ansible/utils/display.py CHANGED
@@ -1285,6 +1285,10 @@ def format_message(summary: SummaryBase) -> str:
1285
1285
 
1286
1286
  def _report_config_warnings(deprecator: PluginInfo) -> None:
1287
1287
  """Called by config to report warnings/deprecations collected during a config parse."""
1288
+ while config._errors:
1289
+ msg, exception = config._errors.pop()
1290
+ _display.error_as_warning(msg=msg, exception=exception)
1291
+
1288
1292
  while config.WARNINGS:
1289
1293
  warn = config.WARNINGS.pop()
1290
1294
  _display.warning(warn)
@@ -154,7 +154,8 @@ def add_fragments(doc, filename, fragment_loader, is_module=False):
154
154
  unknown_fragments.append(fragment_slug)
155
155
  continue
156
156
 
157
- fragment_yaml = getattr(fragment_class, fragment_var, None)
157
+ # trust-tagged source propagates to loaded values; expressions and templates in config require trust
158
+ fragment_yaml = _tags.TrustedAsTemplate().tag(getattr(fragment_class, fragment_var, None))
158
159
  if fragment_yaml is None:
159
160
  if fragment_var != 'DOCUMENTATION':
160
161
  # if it's asking for something specific that's missing, that's an error
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: ansible-core
3
- Version: 2.19.0b3
3
+ Version: 2.19.0b4
4
4
  Summary: Radically simple IT automation
5
5
  Author: Ansible Project
6
6
  Project-URL: Homepage, https://ansible.com/
@@ -38,6 +38,7 @@ Requires-Dist: PyYAML>=5.1
38
38
  Requires-Dist: cryptography
39
39
  Requires-Dist: packaging
40
40
  Requires-Dist: resolvelib<2.0.0,>=0.5.3
41
+ Dynamic: license-file
41
42
 
42
43
  [![PyPI version](https://img.shields.io/pypi/v/ansible-core.svg)](https://pypi.org/project/ansible-core)
43
44
  [![Docs badge](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.ansible.com/ansible/latest/)
@@ -1,14 +1,15 @@
1
1
  ansible/__init__.py,sha256=lcN6K0RviF9nJ6bUtvmwp26GEEy7CzyYivFJ6JIsziA,1215
2
2
  ansible/__main__.py,sha256=24j-7-YT4lZ2fmV80JD-VRoYBnxR7YoP_VP-orJtDt0,796
3
- ansible/constants.py,sha256=k56yPdrVjMtHb6nRDOX192KcjiixSwdbJPtzIxox3RA,7912
3
+ ansible/constants.py,sha256=qef45QpHi-yFFMvllvNKmqFpXdKKr304e5fEZnjgwZc,7989
4
4
  ansible/context.py,sha256=oKYyfjfWpy8vDeProtqfnqSmuij_t75_5e5t0U_hQ1g,1933
5
5
  ansible/keyword_desc.yml,sha256=5rGCsr-0B8w2D67qBD6q_2WFxfqj9ieb0V_2J-dZJ5E,7547
6
- ansible/release.py,sha256=d4NSvjjXAXUynpLHVr5I_-EukeLwMyyCwiGLUFbowlY,854
7
- ansible/_internal/__init__.py,sha256=Ox3RPuKbwNiMj7aIZj-5JGVbPtKhjlfpb2Ed_AwIoPk,2208
6
+ ansible/release.py,sha256=IXr3OR35RyOVLr9l3eiSI-Lq37a-X2gZPSBHSI-O6r8,854
7
+ ansible/_internal/__init__.py,sha256=D-vFgPGHLjzG97IvyIoyhJu5Hycp6R8LWbm5_9XYfSc,2209
8
8
  ansible/_internal/_ansiballz.py,sha256=OL2FCN0jOjg1a8diutsNcs_mr0dqO7SgJPmrYCKfo1Y,11649
9
9
  ansible/_internal/_collection_proxy.py,sha256=F7sde4HIxwZVszF1bYkbcNvNsy233rAEjn2OV5ENx90,1235
10
10
  ansible/_internal/_locking.py,sha256=8jMXsKSNCKVZRUesZWb5Hx45g9UVVZ9JXUa_fq2004Q,669
11
11
  ansible/_internal/_task.py,sha256=NCEF3sPxt99n4Gk-e00A9Ce52duffThJm0qlmgkm0nQ,3293
12
+ ansible/_internal/_testing.py,sha256=WCEwZk8_NP2f2LoY2zjh6VIw6d8bPoFspvfP3-KQMjQ,825
12
13
  ansible/_internal/_wrapt.py,sha256=CLgu2S5V4NmJ9pPFIKarhqiiwAQxni5ISgr-TxARWuE,37963
13
14
  ansible/_internal/_datatag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
15
  ansible/_internal/_datatag/_tags.py,sha256=8ZLQUX2ggTQluwtLfFbNKa9rKNpxRhqalRzeqSUda2Y,5180
@@ -18,7 +19,7 @@ ansible/_internal/_errors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
18
19
  ansible/_internal/_errors/_captured.py,sha256=UXIzFiUMZVAGYlrc2dBJDmRYYDLgj6zOOSaLth7Br5Q,4919
19
20
  ansible/_internal/_errors/_handler.py,sha256=4ZX27G_jrDsomR28OVC0A9WRponpte5V-QGq5ZcDzeE,3455
20
21
  ansible/_internal/_errors/_utils.py,sha256=LKS9CRb8OobJTlLjs0poHiVqmzIdLB7BIW3m3fzLFbQ,11942
21
- ansible/_internal/_json/__init__.py,sha256=2YuemYC6eksqn2wmkfSQh118EPIZCMYTvH6dB6kJU8A,8826
22
+ ansible/_internal/_json/__init__.py,sha256=nYxJrXf9eqa0-ECe6cdEJBgNF9TUX3YJKsazFbtn17s,8825
22
23
  ansible/_internal/_json/_legacy_encoder.py,sha256=clXiPnJ2aR5McuSbajugTPW2wav1wwklivxDKWZ1iFw,1646
23
24
  ansible/_internal/_json/_profiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
25
  ansible/_internal/_json/_profiles/_cache_persistence.py,sha256=PGtAPvMQdC6yHJcHz3HYkkmRMH8SkE4nOjhIg3SMdRA,1817
@@ -75,7 +76,7 @@ ansible/cli/playbook.py,sha256=yPOYqwH5qcI6uCVI2k4GrSB0thWQLjlauJOD-sFx11I,10546
75
76
  ansible/cli/pull.py,sha256=Bvc59o9KbLRlcKTCb_pzr-vcs2owDuThpQJlK05xYM8,18425
76
77
  ansible/cli/vault.py,sha256=bG_8LZ697ohjjJqb7aDryQmc9ahQggiZA2IPmgyTDnA,23195
77
78
  ansible/cli/arguments/__init__.py,sha256=_4taT82hZKKTzhdXKmIgqxWwuG21XZxF874V2k1e3us,168
78
- ansible/cli/arguments/option_helpers.py,sha256=1-e0cOgvC0PSO-PuPeQmjZNi-ojSOjVeAJO7FNo6Tew,24256
79
+ ansible/cli/arguments/option_helpers.py,sha256=7fJuJ0dq_nC6M1BoOicu92lNhbRwD2379JOniqEbDc0,24445
79
80
  ansible/cli/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
81
  ansible/cli/scripts/ansible_connection_cli_stub.py,sha256=Wz413NyoBudEJdQt6pw2UAB4IveHQma4XoHBzFSENt0,13122
81
82
  ansible/collections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -84,8 +85,8 @@ ansible/compat/__init__.py,sha256=CvyoCuJ9EdeWO3_nj5fBSQ495YP0tCbXhQ6cramBdGY,10
84
85
  ansible/compat/importlib_resources.py,sha256=oCjsu8foADOkMNwRuWiRCjQxO8zEOc-Olc2bKo-Kgh0,572
85
86
  ansible/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
87
  ansible/config/ansible_builtin_runtime.yml,sha256=nwL_-rqEEmpuSHxZH70pJBiEosDKOPkYIboH3_7LVEY,376076
87
- ansible/config/base.yml,sha256=PAt_Sqf-nV4B17PxOaA9L2nwcfcAB0jDU-AwHPiF3i8,89169
88
- ansible/config/manager.py,sha256=sw68hqoHNdEfl9exqZOzXidbGVpzrK4dL-xSGQ9KY_A,29220
88
+ ansible/config/base.yml,sha256=Vy4Y6wDVN-7lnVzvTi2pWbQFZrCn9nkiGejXt5DWGWA,88674
89
+ ansible/config/manager.py,sha256=CC-JhhK4KN07rvXAfiex4sxjFSVtxsLx6E_YnpEOE88,31123
89
90
  ansible/errors/__init__.py,sha256=7nJsA0zFSesC3xVN_qf4-47VQmZ4eWi6R9zTEBW7gUY,16111
90
91
  ansible/executor/__init__.py,sha256=mRvbCJPA-_veSG5ka3v04G5vsarLVDeB3EWFsu6geSI,749
91
92
  ansible/executor/interpreter_discovery.py,sha256=UWeAxnHknJCci2gG3zt6edx5Nj4WbHYfJVcmW_DzItY,3858
@@ -195,10 +196,10 @@ ansible/inventory/data.py,sha256=Hy4-36CWcKEWvTm55fLw888hjmYvmGcQN0myIZRex5A,104
195
196
  ansible/inventory/group.py,sha256=_4q692djOZmJjk2x2UQuS03lSst8zCucJnq9kVZ2uks,9351
196
197
  ansible/inventory/helpers.py,sha256=8DaVEdMhIfan6Fy65AoHO3rILtl3mDzxei4_j8r4fp4,1556
197
198
  ansible/inventory/host.py,sha256=Zzod7oWrsrCfv4v8oOu40ASEsrlk3IIZH4WtGs8rbU4,4654
198
- ansible/inventory/manager.py,sha256=d8-48KFnmr97sPlqmYGSS4ihzatN4UBcl9eUcqtDkMI,31714
199
+ ansible/inventory/manager.py,sha256=bZppxa0x0aLvs801zhn1f0fNQhPszKFcvbAjWZmMjes,31812
199
200
  ansible/module_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
200
201
  ansible/module_utils/_text.py,sha256=VkWgAnSNVCbTQqZgllUObBFsH3uM4EUW5srl1UR9t1g,544
201
- ansible/module_utils/ansible_release.py,sha256=d4NSvjjXAXUynpLHVr5I_-EukeLwMyyCwiGLUFbowlY,854
202
+ ansible/module_utils/ansible_release.py,sha256=IXr3OR35RyOVLr9l3eiSI-Lq37a-X2gZPSBHSI-O6r8,854
202
203
  ansible/module_utils/api.py,sha256=8BmCzQtp9rClsLGlDn4I9iJrUFLCdnoEIxYX59_IL9c,5756
203
204
  ansible/module_utils/basic.py,sha256=e92CZBmokHTcQG-gcmxHZ8efweBMDalcoTAg9rJ-J9M,89711
204
205
  ansible/module_utils/connection.py,sha256=EYcZ-JJ3GnCb8Hhdan2upLJfgfQGvzpPUHNY_qVXyKw,7675
@@ -343,13 +344,13 @@ ansible/module_utils/facts/virtual/base.py,sha256=BijfSGBeCSdDzK2jeeQ2oymgupItCv
343
344
  ansible/module_utils/facts/virtual/dragonfly.py,sha256=fx8MZjy6FqfSpshxnPyGs5B4FezmYFqqTr1XibWWSeE,959
344
345
  ansible/module_utils/facts/virtual/freebsd.py,sha256=Wc3hjsxrjWnLaZFBX3zM4lZpeGy4ZS5BTOXTs9SRN-I,3018
345
346
  ansible/module_utils/facts/virtual/hpux.py,sha256=NLQfUpXE7Gh-eZFfLyugvnnJjWFIGv9xqjC_zV4DLKw,2823
346
- ansible/module_utils/facts/virtual/linux.py,sha256=ifvJuZ6S0IgFSLVH3ajCfkX2eFv2g5cajvid7UFCARE,17822
347
+ ansible/module_utils/facts/virtual/linux.py,sha256=UOtZzsbO6ENkj7-0X1SHOZ8DpyQB8RcZJh3mj4dxBHU,17851
347
348
  ansible/module_utils/facts/virtual/netbsd.py,sha256=53n3E9vowi8kCbFyj7vDeKocZ3OU_TLVSKRJRU8SenE,2896
348
349
  ansible/module_utils/facts/virtual/openbsd.py,sha256=J8Ow7x3J5ZuHFThqAwIdAdTLV1V9vN_U965Q34TAvNA,2785
349
350
  ansible/module_utils/facts/virtual/sunos.py,sha256=9wUiq-2oXlrZbaskVI9c8WmG206AH2j5KO3F5jKo3Ec,5700
350
351
  ansible/module_utils/facts/virtual/sysctl.py,sha256=suvfSdKL5e_AAPCxE_EQRiqNHzpMJYSucT4URyn8Qxw,4741
351
352
  ansible/module_utils/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
352
- ansible/module_utils/parsing/convert_bool.py,sha256=Z445cePUG9tyRr1NXKzKiPlA1U43OGfF_etpAOdRL18,1060
353
+ ansible/module_utils/parsing/convert_bool.py,sha256=WN-LzXRsO96kNig3cngTu_3sjM2xxnoSXAqUfynhGQM,1265
353
354
  ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1,sha256=NYfvSKqsTCC2YQIx-pGPpKKS3BG499v8ZqQHlHiXsOQ,20127
354
355
  ansible/module_utils/powershell/Ansible.ModuleUtils.ArgvParser.psm1,sha256=x9wTV5jOpoCtFbpZW6GoZEELdL3RNOhdY91QOhYxJqk,3327
355
356
  ansible/module_utils/powershell/Ansible.ModuleUtils.Backup.psm1,sha256=ebgpraCNmPwswlLHShgiviTk2thw8ch3ekF5n_I_cXg,1104
@@ -368,19 +369,19 @@ ansible/modules/add_host.py,sha256=VZ3gc-phY5myzGijo6AIB1omD9ykfWpoqRvzleZp3DU,3
368
369
  ansible/modules/apt.py,sha256=eMoHgh-4SWWu6d3VmxybzP-ACTi0sKaJOkMIqrh1Sjo,62708
369
370
  ansible/modules/apt_key.py,sha256=DmmNkVdRrhOCEhItsbXUA3TuAfXZW4A0CGZu4JeqL90,18485
370
371
  ansible/modules/apt_repository.py,sha256=AzJnzlImqU_ZWZVBMi9xAklzfMqqTOuuOH1Jp5m16pY,31529
371
- ansible/modules/assemble.py,sha256=wJNTPSukLSkvvVu2upjBcyAJ6YyDnmY2Q-8qqUAmyKg,9257
372
+ ansible/modules/assemble.py,sha256=4_Cy7mS-4Yhpz0pnj6sVes39DTlgzBLlfTKEsHCQ9D8,9271
372
373
  ansible/modules/assert.py,sha256=B41GXAdBQ6XbYj9G8ENx29fhAlcnyBXm8zDqbd1TnMU,2954
373
374
  ansible/modules/async_status.py,sha256=7qs2cyVvoFDLDWhVYzjsfdLPj89EKKjSDw5vZ1WbPH4,4589
374
375
  ansible/modules/async_wrapper.py,sha256=3nwRroF8BImwYQVd9Yb0jldm_c6hdLpOsou53xiHaew,12011
375
376
  ansible/modules/blockinfile.py,sha256=CkzCw73Kbkvgb763C1x_qUMkf6UdJJsyZC_Kiy85Unw,15450
376
377
  ansible/modules/command.py,sha256=v7KAW_uI4zhBwaKBDZaHNSk3coZlyXoG9xGtDOM4MaY,14037
377
378
  ansible/modules/copy.py,sha256=uLltSKg-0poPjtV9WKhq0w4Z_6wOKm8mNR8How-DHXw,26725
378
- ansible/modules/cron.py,sha256=SruXGH9gwftzXNiql_hz42kiN2BE325JxqvaFB2pe4M,26770
379
+ ansible/modules/cron.py,sha256=trFmqi8l8jADTDyNZn1COe_-GpCi9aNQj8wUCn5O7SA,26709
379
380
  ansible/modules/deb822_repository.py,sha256=kHBZlGGQ_fA3dMxx_NzjC-L2ZuhkEwTTfLS6ZC6qph4,15760
380
381
  ansible/modules/debconf.py,sha256=YAS1yba0yaxPrfFCLFLQwtHxlpriNxiJiwpDnmm3JP0,9362
381
382
  ansible/modules/debug.py,sha256=E2UADFGHgS78KxCiOdXoojX5G4pAAMz32VGHgaPObNs,2908
382
383
  ansible/modules/dnf.py,sha256=BStesAQE40-hZRMwV8IZW5j3yIv1wd1XGXsXjW1jKfw,52289
383
- ansible/modules/dnf5.py,sha256=fbsc1gmG7Egn29LinHihe_28v6gr2TN4rFDEs-B4gy4,31832
384
+ ansible/modules/dnf5.py,sha256=cMAgXt84CwnI4ujiF0gIAcQ3DXtprhLaetOyMr_z3MQ,31943
384
385
  ansible/modules/dpkg_selections.py,sha256=RWtzxNNOfQ5SdwMwnt_1q-IJhLVb-nxNAriJwRHNVuI,2805
385
386
  ansible/modules/expect.py,sha256=yBisXCvL8OY5c_9AibH8xY3elmKebCwoHZAJj-MFPU0,9279
386
387
  ansible/modules/fail.py,sha256=kppam_caBllcF5IcKEYd-Xc--okSAOWNG9dVbpn2CwM,1659
@@ -390,7 +391,7 @@ ansible/modules/find.py,sha256=Ia2QUTCHzc58MiLUOFTkYa_NHgM3rGsuNDtrWVwzvrE,24051
390
391
  ansible/modules/gather_facts.py,sha256=3t2_XMgKuB-U35tnO-JbGEsQ_E31SnZxRQ-B_DVkIu4,3107
391
392
  ansible/modules/get_url.py,sha256=1JW5sLkkWbmXmh4rxNX05XHwn0QkcM10dnqqQ_PXMp8,27372
392
393
  ansible/modules/getent.py,sha256=tq3z0Szq_m2gp4DOgACRvNJzh-tkXGzd2Ew8XxrGRWM,5592
393
- ansible/modules/git.py,sha256=rQ6cMOgmWzfWb6A9oSRMGtE4qWfIYAirkA7GLDNYjgE,57396
394
+ ansible/modules/git.py,sha256=BtZWA4furfNfPefusAD-ihBROKHdRcBucpvpLJttxJU,57126
394
395
  ansible/modules/group.py,sha256=UKbXzwsgAFWWMlgFW8R3CyzthJXXu5JHnHnj7IlqOac,23747
395
396
  ansible/modules/group_by.py,sha256=_RDYbNJS27eosefvwnBCM86GyobaY-h3ZiWkd04qU68,2416
396
397
  ansible/modules/hostname.py,sha256=oklIQ8jwpnRZ2IAxY5igFg1hQFSHisNjXp-6sCQzmko,28181
@@ -409,7 +410,7 @@ ansible/modules/package.py,sha256=ce2O1o3tzPRTyYYvYNw16OLed8ke8f5AAXMGF1vi8XU,37
409
410
  ansible/modules/package_facts.py,sha256=Tvq3ULR8oyIx-lCJzY-wwWlL2gGiwLq9jS5H8Hjmick,17284
410
411
  ansible/modules/pause.py,sha256=VzN_Ay94TYvBPTQRG0s0YTnXJkMTh8CEy2KostS2nVY,3796
411
412
  ansible/modules/ping.py,sha256=80pw8eLBvT2pw8f0V3zxqExQhbsKKoA6YfPBvcncTng,2325
412
- ansible/modules/pip.py,sha256=R8lDkbxIgf_aA_EOCpUI8DdZgNJ9cs-WZu6sTEOOXnw,32760
413
+ ansible/modules/pip.py,sha256=GEtBIzOqZCtQbKwfWfZ-xHzDxtiXdCBe7kH7l-5ghBY,32726
413
414
  ansible/modules/raw.py,sha256=hPToqCii1ZolXVSaPmu-tCouEO1PoDIYjBtevoUvqIE,3741
414
415
  ansible/modules/reboot.py,sha256=P5mbaDi_k-ISjR5M0adW6yeyssahw4QvXYqSN7elypk,4808
415
416
  ansible/modules/replace.py,sha256=jAnGfWD38_zo8gC-rM3LkPwch54w9j044Bpr8mjpP2Q,11683
@@ -426,7 +427,7 @@ ansible/modules/stat.py,sha256=cD2l6TQjwrJhIY-SCgS9W_i6WC6FreSDSudFHmH63R8,18658
426
427
  ansible/modules/subversion.py,sha256=XEhsibJHb5rg_yioveP_c-THxbh0ZMm0d8qApk02MZg,13485
427
428
  ansible/modules/systemd.py,sha256=ZJ8uKx7xsvTQvmBTl5AyyP4KOBNZcjy5rr0CVH4lw2s,24990
428
429
  ansible/modules/systemd_service.py,sha256=ZJ8uKx7xsvTQvmBTl5AyyP4KOBNZcjy5rr0CVH4lw2s,24990
429
- ansible/modules/sysvinit.py,sha256=IUDuk6erHw9uPWU-7HZijJxILlmioZPxbLeu6-ImVEo,13956
430
+ ansible/modules/sysvinit.py,sha256=Nrs5gLyHKZsqtm_ymFO0e0wB2KSDXYfbDt-uNlgUiF0,13962
430
431
  ansible/modules/tempfile.py,sha256=3ZljXNOu06T5ObTAz5Ktm_WKFrJACHwLf-01F7isf5A,3570
431
432
  ansible/modules/template.py,sha256=y1zu-ZZ0P-Cpxjddzk5V5GVpQNNfQL8E5sVeAb2plR0,4537
432
433
  ansible/modules/unarchive.py,sha256=ESs24Ceb9CC1rwF06qtOfg4xZdDLmiDbmNyUjVMLefE,45943
@@ -479,8 +480,8 @@ ansible/playbook/role/metadata.py,sha256=h439HGUucs2gOMUJlp2M0OO0_wnWWlQmTs_sOe8
479
480
  ansible/playbook/role/requirement.py,sha256=CNgLa0J6zZk2YQ_aeALnjQvehkkFXhrK8LQQZs7Ztzc,4173
480
481
  ansible/plugins/__init__.py,sha256=h2YINajRVBLM4G5BYnGCgycC0QCF9qys_Ssb6AAM93c,8456
481
482
  ansible/plugins/list.py,sha256=5lQD2AHH4ysJzPBbgYE3ASJvSZOgKppGlgPrQkmGmv8,8895
482
- ansible/plugins/loader.py,sha256=fCBAXDVvlvGCZKOImHQ48zPmDyUpdzB8t6pCh0jCbzU,81744
483
- ansible/plugins/action/__init__.py,sha256=mJK4hA5jaj24UjeSMxhXvlddHtaCdnv6ssNfpmnuTvM,70112
483
+ ansible/plugins/loader.py,sha256=eUiuCsF3Imwl9105VzxcjCeLHHmtfW2eOBQJPVDleTs,81912
484
+ ansible/plugins/action/__init__.py,sha256=rsCmbq3w1CtXvw0GsiCArK3ab4tJB7_KLoqPkCZF40k,70111
484
485
  ansible/plugins/action/add_host.py,sha256=pz8R7qzCwwinxTx4hEZ3FMsVJpMV3NEfQvn2wqLGLLY,3579
485
486
  ansible/plugins/action/assemble.py,sha256=ePMLNuh7Ie2fp-An9G5yieg9mETqAi0RL5aNK3W47OM,6529
486
487
  ansible/plugins/action/assert.py,sha256=k1kLWyaXNE-XTVWBX1eIHaSZpGg8O8WeJfa62vqzcLM,4007
@@ -504,7 +505,7 @@ ansible/plugins/action/service.py,sha256=7wupKrBwIRnxQZXPHuOiEJaf1KtkgVmfn0ntM5f
504
505
  ansible/plugins/action/set_fact.py,sha256=G1Q0HcTaXFIykNvjSG65tFkif0dMuvMvt3T44OlrB6Y,2186
505
506
  ansible/plugins/action/set_stats.py,sha256=wRmlGzp5TAaLrPlz2O6kL6F_uSGYBaxtnF4jWmB3qeY,2476
506
507
  ansible/plugins/action/shell.py,sha256=pd7Dm6gAZXWNAqANd7ETHSs90le7VXAxe7pB97T_zwU,1527
507
- ansible/plugins/action/template.py,sha256=j7vLyQvhlSKKtM1eimYkJijZVZp_F2O33kpcJKy7668,8406
508
+ ansible/plugins/action/template.py,sha256=yf1OJZN8f4TqmQ2HuFtEM8HFhuxBTR7nKJEak-mz4fQ,8472
508
509
  ansible/plugins/action/unarchive.py,sha256=88h7YM6B9j_2XE5Sp-02o9oKYfnLliIlD5EbVXX3ndY,4859
509
510
  ansible/plugins/action/uri.py,sha256=325UaEu7OY4yXnIj8c1Y0sB_7buzp3oQRoNDVJgwPg0,3981
510
511
  ansible/plugins/action/validate_argument_spec.py,sha256=U-g8PugBg3QTTqkKQwtOS3NNou12yT4NQtXqJ10qBw8,3937
@@ -736,7 +737,7 @@ ansible/utils/_ssh_agent.py,sha256=2WlQqytGHoSkASLBscMfBOv69QAV0YNuBfKak4dh0nk,2
736
737
  ansible/utils/cmd_functions.py,sha256=VmGs5ntdVaaqAJHcCTpGG3rYAAcTNl1b2-Iw4YVOt9Y,2180
737
738
  ansible/utils/color.py,sha256=LjJO_12OsJiavBxwSDVXtLxdTzdwd2YWUp1OJ6KcM2g,4057
738
739
  ansible/utils/context_objects.py,sha256=vYulSJkzR3zxsQF_6_AqbPCCMy8WGC5dSqLFXJZqGIo,3034
739
- ansible/utils/display.py,sha256=RuZlM8cIiYfwfZyjA_08y4sonESPJhqah46mm_JnuBU,48732
740
+ ansible/utils/display.py,sha256=ym8irMsZFDCFD63XJTviZHwnvrO3eX-pJcN_Gh3G8u4,48869
740
741
  ansible/utils/encrypt.py,sha256=j7DcEPr_yHSLYfYEruybjCl7kFk4rKjjTebyGx_-ZbA,7590
741
742
  ansible/utils/fqcn.py,sha256=_wPNWMkR0mqRdkr6fn9FRgEkaCQHw40yardWe97FfEc,1215
742
743
  ansible/utils/galaxy.py,sha256=Un3XgXhx8FoC6tkp1cZ33rmiAaRg634exKruwOVhtdQ,3855
@@ -747,7 +748,7 @@ ansible/utils/listify.py,sha256=kDtcewq4gWi-waCfqgHy-eITDbvXA08Gx_qvgbYI62I,1275
747
748
  ansible/utils/lock.py,sha256=aP6MfrvWHdO_V756hwFsJG1I9QaQyFJq9W0tf7pCN3I,1306
748
749
  ansible/utils/multiprocessing.py,sha256=Xgs3kXqbzVujXah0-R_D6eUcQoiapbQ-0yclNpkAvs4,614
749
750
  ansible/utils/path.py,sha256=RMuCOlqUyDjIlKTAqNhD2N7iuKkL4jnvXNWh9aMtQRw,6051
750
- ansible/utils/plugin_docs.py,sha256=qL-cuwOb4QyOllbgIs3ObCF-ArCVK3p2_3XYlI6qT90,15243
751
+ ansible/utils/plugin_docs.py,sha256=A1Fy5qaZuOnrEm896AdHi3znJkVMFveu4C0MQujSBpE,15383
751
752
  ansible/utils/py3compat.py,sha256=l5x0He1q4RRPwSummCN8wHhD8PxlivcgfthllyI_XCg,578
752
753
  ansible/utils/sentinel.py,sha256=3TBjBlaOB6NfoXt5MFLCHVQLbPIFx03N_glcN3cYdjo,323
753
754
  ansible/utils/shlex.py,sha256=eUCZ0VkxMSEoyXCDspS9E4cI8pQWn83OFCt7sbVLB6g,841
@@ -757,7 +758,7 @@ ansible/utils/unicode.py,sha256=__zbdElrMS9Rrqo9H7tF8zkjKFQCFU8kTtj5cVIITxM,1100
757
758
  ansible/utils/unsafe_proxy.py,sha256=f_oJ0jCOh28Aa4Ps5QuhlBuPJCigVwpNKdfffw3xGC0,2114
758
759
  ansible/utils/vars.py,sha256=RhZssst9c2fRWvlT6YgDW3v45mmea3x1KLucyzKUsE8,11443
759
760
  ansible/utils/version.py,sha256=TKmSzm_MFZVJWpvmOnIEGZzRfeHLwWmeuHj16zGqb2c,7736
760
- ansible/utils/collection_loader/__init__.py,sha256=tPRr-USq3xk7x9zq0OOnrEZzB5B1guOXL2iq0sZuL78,2493
761
+ ansible/utils/collection_loader/__init__.py,sha256=lwLu1LJhWz3cRzFLfNWuFdS-Rhods6aReqOaqoVaDaM,2564
761
762
  ansible/utils/collection_loader/_collection_config.py,sha256=6r5DY_4kKGQz4qk7u9vsrNq__UmGFlCjtAV-HwJsFaM,3000
762
763
  ansible/utils/collection_loader/_collection_finder.py,sha256=3E71MX6FU2OmBeK5BpJrh7lQ4Fg6kuiRMA4WxuO3JqY,55125
763
764
  ansible/utils/collection_loader/_collection_meta.py,sha256=p2eZArsO8RCl4PlN1x2I-7A7Q8HZpdFoeqfMNVaRgiU,1815
@@ -767,6 +768,12 @@ ansible/vars/hostvars.py,sha256=EaRFnZbPqAv8PDMF-aS_v6igoV3MNzix_VwuPviwNLI,4365
767
768
  ansible/vars/manager.py,sha256=pCLAvO_J_IQPBPPnOb6JHhU5t2dTXUYpj3D3eMxmJKw,28041
768
769
  ansible/vars/plugins.py,sha256=Qnk83j898hQ-oiJHaKsel177tkYLwm6cCoHed4GPoiA,4519
769
770
  ansible/vars/reserved.py,sha256=r7qXzXJ5uZdkRYjmyf3uSxU6EHnOVLEiMMnwOM7Q46U,2771
771
+ ansible_core-2.19.0b4.dist-info/licenses/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
772
+ ansible_core-2.19.0b4.dist-info/licenses/licenses/Apache-License.txt,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
773
+ ansible_core-2.19.0b4.dist-info/licenses/licenses/BSD-3-Clause.txt,sha256=la0N3fE3Se8vBiuvUcFKA8b-E41G7flTic6P8CkUroE,1548
774
+ ansible_core-2.19.0b4.dist-info/licenses/licenses/MIT-license.txt,sha256=jLXp2XurnyZKbye40g9tfmLGtVlxh3pPD4n8xNqX8xc,1023
775
+ ansible_core-2.19.0b4.dist-info/licenses/licenses/PSF-license.txt,sha256=g7BC_H1qyg8Q1o5F76Vrm8ChSWYI5-dyj-CdGlNKBUo,2484
776
+ ansible_core-2.19.0b4.dist-info/licenses/licenses/simplified_bsd.txt,sha256=8R5R7R7sOa0h1Fi6RNgFgHowHBfun-OVOMzJ4rKAk2w,1237
770
777
  ansible_test/__init__.py,sha256=20VPOj11c6Ut1Av9RaurgwJvFhMqkWG3vAvcCbecNKw,66
771
778
  ansible_test/_data/ansible.cfg,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
772
779
  ansible_test/_data/coveragerc,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1010,7 +1017,7 @@ ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg,sha256=DJyZi
1010
1017
  ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg,sha256=sp-BrEpQVZebA1FVRtb1WVegyEr5O8zj_F_1NCVuRvc,1942
1011
1018
  ansible_test/_util/controller/sanity/pylint/config/collection.cfg,sha256=h2qhEF9_RvP70katitXYVjePtJDzAsGJPeARQdhBFlE,4669
1012
1019
  ansible_test/_util/controller/sanity/pylint/config/default.cfg,sha256=D33wbIVfOBjkvxgWAPfN48aMZY4jh57xupsBQh6C-kg,4243
1013
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py,sha256=jFEaZu2XqeNOWkvBF4HZ8CIwiDPlJcqBQKHRSpgBqws,19033
1020
+ ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py,sha256=1vTbPAGaA3KAiJ60dKWGrakufqIgJ5o_vwFL-e0GsaU,19447
1014
1021
  ansible_test/_util/controller/sanity/pylint/plugins/deprecated_comment.py,sha256=tmQf_-2VAT2GVfwa9X9ruBcaj0Sz6Ifx4cXmdzJ99SQ,5226
1015
1022
  ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py,sha256=s0AIoK03uqZSTwXSLvd4oXvf4WJ0Ckol5ingitHoMr4,836
1016
1023
  ansible_test/_util/controller/sanity/pylint/plugins/string_format.py,sha256=Mb1Mx8WS4RulsORFgyctlFRR0Sn-PYPy4mVu_GYCD18,2359
@@ -1061,14 +1068,8 @@ ansible_test/config/cloud-config-vultr.ini.template,sha256=XLKHk3lg_8ReQMdWfZzhh
1061
1068
  ansible_test/config/config.yml,sha256=1zdGucnIl6nIecZA7ISIANvqXiHWqq6Dthsk_6MUwNc,2642
1062
1069
  ansible_test/config/inventory.networking.template,sha256=bFNSk8zNQOaZ_twaflrY0XZ9mLwUbRLuNT0BdIFwvn4,1335
1063
1070
  ansible_test/config/inventory.winrm.template,sha256=1QU8W-GFLnYEw8yY9bVIvUAVvJYPM3hyoijf6-M7T00,1098
1064
- ansible_core-2.19.0b3.dist-info/Apache-License.txt,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
1065
- ansible_core-2.19.0b3.dist-info/BSD-3-Clause.txt,sha256=la0N3fE3Se8vBiuvUcFKA8b-E41G7flTic6P8CkUroE,1548
1066
- ansible_core-2.19.0b3.dist-info/COPYING,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
1067
- ansible_core-2.19.0b3.dist-info/METADATA,sha256=wu3SUlMaDRL4qpDfuPRFfzyyG7pCfNE5CljfJFd8tbk,7710
1068
- ansible_core-2.19.0b3.dist-info/MIT-license.txt,sha256=jLXp2XurnyZKbye40g9tfmLGtVlxh3pPD4n8xNqX8xc,1023
1069
- ansible_core-2.19.0b3.dist-info/PSF-license.txt,sha256=g7BC_H1qyg8Q1o5F76Vrm8ChSWYI5-dyj-CdGlNKBUo,2484
1070
- ansible_core-2.19.0b3.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
1071
- ansible_core-2.19.0b3.dist-info/entry_points.txt,sha256=S9yJij5Im6FgRQxzkqSCnPQokC7PcWrDW_NSygZczJU,451
1072
- ansible_core-2.19.0b3.dist-info/simplified_bsd.txt,sha256=8R5R7R7sOa0h1Fi6RNgFgHowHBfun-OVOMzJ4rKAk2w,1237
1073
- ansible_core-2.19.0b3.dist-info/top_level.txt,sha256=IFbRLjAvih1DYzJWg3_F6t4sCzEMxRO7TOMNs6GkYHo,21
1074
- ansible_core-2.19.0b3.dist-info/RECORD,,
1071
+ ansible_core-2.19.0b4.dist-info/METADATA,sha256=R_UmHiGlICZrxQQEitbG7calWkGy_BADvyNT33vYv3c,7732
1072
+ ansible_core-2.19.0b4.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
1073
+ ansible_core-2.19.0b4.dist-info/entry_points.txt,sha256=S9yJij5Im6FgRQxzkqSCnPQokC7PcWrDW_NSygZczJU,451
1074
+ ansible_core-2.19.0b4.dist-info/top_level.txt,sha256=IFbRLjAvih1DYzJWg3_F6t4sCzEMxRO7TOMNs6GkYHo,21
1075
+ ansible_core-2.19.0b4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.1.0)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -39,6 +39,10 @@ class DeprecationCallArgs:
39
39
  removed: object = None # only on Display.deprecated
40
40
  value: object = None # only on deprecate_value
41
41
 
42
+ def all_args_dynamic(self) -> bool:
43
+ """True if all args are dynamic or None, otherwise False."""
44
+ return all(arg is None or isinstance(arg, astroid.NodeNG) for arg in dataclasses.asdict(self).values())
45
+
42
46
 
43
47
  class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
44
48
  """Checks for deprecated calls to ensure proper usage."""
@@ -346,6 +350,10 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
346
350
  self.add_message('ansible-deprecated-date-not-permitted', node=node, args=(name,))
347
351
  return
348
352
 
353
+ if call_args.all_args_dynamic():
354
+ # assume collection maintainers know what they're doing if all args are dynamic
355
+ return
356
+
349
357
  if call_args.version and call_args.date:
350
358
  self.add_message('ansible-deprecated-both-version-and-date', node=node, args=(name,))
351
359
  return
@@ -407,10 +415,13 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
407
415
  self.add_message('ansible-deprecated-unnecessary-collection-name', node=node, args=('deprecator', name,))
408
416
  return
409
417
 
418
+ if args.all_args_dynamic():
419
+ # assume collection maintainers know what they're doing if all args are dynamic
420
+ return
421
+
410
422
  expected_collection_name = 'ansible.builtin' if self.is_ansible_core else self.collection_name
411
423
 
412
424
  if args.collection_name and args.collection_name != expected_collection_name:
413
- # if collection_name is provided and a constant, report when it does not match the expected name
414
425
  self.add_message('wrong-collection-deprecated', node=node, args=(args.collection_name, name))
415
426
 
416
427
  def check_version(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None: