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.

Files changed (215) hide show
  1. ansible/_internal/__init__.py +1 -4
  2. ansible/_internal/_ansiballz/_builder.py +1 -3
  3. ansible/_internal/_collection_proxy.py +7 -9
  4. ansible/_internal/_json/__init__.py +3 -4
  5. ansible/_internal/_templating/_engine.py +1 -1
  6. ansible/_internal/_templating/_jinja_plugins.py +1 -2
  7. ansible/_internal/_wrapt.py +105 -301
  8. ansible/cli/__init__.py +11 -10
  9. ansible/cli/adhoc.py +1 -2
  10. ansible/cli/arguments/option_helpers.py +1 -1
  11. ansible/cli/config.py +5 -6
  12. ansible/cli/doc.py +67 -67
  13. ansible/cli/galaxy.py +15 -24
  14. ansible/cli/inventory.py +0 -1
  15. ansible/cli/playbook.py +0 -1
  16. ansible/cli/pull.py +0 -1
  17. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
  18. ansible/config/base.yml +1 -25
  19. ansible/config/manager.py +0 -2
  20. ansible/executor/play_iterator.py +42 -20
  21. ansible/executor/playbook_executor.py +0 -9
  22. ansible/executor/task_executor.py +26 -18
  23. ansible/executor/task_queue_manager.py +1 -3
  24. ansible/galaxy/api.py +33 -80
  25. ansible/galaxy/collection/__init__.py +11 -21
  26. ansible/galaxy/dependency_resolution/__init__.py +10 -9
  27. ansible/galaxy/dependency_resolution/dataclasses.py +86 -70
  28. ansible/galaxy/dependency_resolution/providers.py +54 -134
  29. ansible/galaxy/dependency_resolution/versioning.py +2 -4
  30. ansible/galaxy/role.py +1 -33
  31. ansible/inventory/manager.py +2 -3
  32. ansible/keyword_desc.yml +0 -3
  33. ansible/module_utils/_internal/_datatag/__init__.py +2 -10
  34. ansible/module_utils/_internal/_no_six.py +86 -0
  35. ansible/module_utils/_text.py +28 -8
  36. ansible/module_utils/ansible_release.py +2 -2
  37. ansible/module_utils/basic.py +26 -23
  38. ansible/module_utils/common/_collections_compat.py +11 -2
  39. ansible/module_utils/common/collections.py +8 -3
  40. ansible/module_utils/common/dict_transformations.py +1 -2
  41. ansible/module_utils/common/network.py +4 -2
  42. ansible/module_utils/common/parameters.py +32 -41
  43. ansible/module_utils/common/text/converters.py +109 -23
  44. ansible/module_utils/common/text/formatters.py +6 -2
  45. ansible/module_utils/common/validation.py +11 -9
  46. ansible/module_utils/connection.py +8 -3
  47. ansible/module_utils/facts/hardware/linux.py +23 -7
  48. ansible/module_utils/facts/hardware/netbsd.py +1 -1
  49. ansible/module_utils/facts/hardware/sunos.py +2 -1
  50. ansible/module_utils/facts/packages.py +6 -2
  51. ansible/module_utils/facts/system/distribution.py +2 -1
  52. ansible/module_utils/facts/system/env.py +6 -3
  53. ansible/module_utils/facts/system/local.py +3 -1
  54. ansible/module_utils/parsing/convert_bool.py +6 -2
  55. ansible/module_utils/service.py +2 -3
  56. ansible/module_utils/six/__init__.py +11 -6
  57. ansible/module_utils/yumdnf.py +0 -5
  58. ansible/modules/apt.py +18 -13
  59. ansible/modules/apt_repository.py +1 -1
  60. ansible/modules/assemble.py +5 -9
  61. ansible/modules/blockinfile.py +39 -23
  62. ansible/modules/cron.py +26 -35
  63. ansible/modules/deb822_repository.py +83 -12
  64. ansible/modules/dnf.py +3 -7
  65. ansible/modules/dnf5.py +4 -6
  66. ansible/modules/expect.py +0 -3
  67. ansible/modules/find.py +1 -2
  68. ansible/modules/get_url.py +1 -1
  69. ansible/modules/git.py +4 -5
  70. ansible/modules/include_vars.py +1 -1
  71. ansible/modules/known_hosts.py +7 -1
  72. ansible/modules/lineinfile.py +71 -63
  73. ansible/modules/package_facts.py +1 -1
  74. ansible/modules/pip.py +8 -2
  75. ansible/modules/replace.py +6 -6
  76. ansible/modules/service.py +3 -4
  77. ansible/modules/stat.py +20 -0
  78. ansible/modules/uri.py +9 -10
  79. ansible/modules/user.py +1 -2
  80. ansible/modules/wait_for.py +2 -2
  81. ansible/modules/wait_for_connection.py +2 -1
  82. ansible/modules/yum_repository.py +1 -16
  83. ansible/parsing/dataloader.py +24 -31
  84. ansible/parsing/vault/__init__.py +1 -2
  85. ansible/playbook/base.py +8 -56
  86. ansible/playbook/block.py +0 -60
  87. ansible/playbook/collectionsearch.py +1 -2
  88. ansible/playbook/handler.py +1 -7
  89. ansible/playbook/helpers.py +0 -7
  90. ansible/playbook/included_file.py +1 -1
  91. ansible/playbook/play.py +102 -36
  92. ansible/playbook/play_context.py +4 -0
  93. ansible/playbook/role/__init__.py +10 -65
  94. ansible/playbook/role/definition.py +3 -4
  95. ansible/playbook/role/include.py +2 -3
  96. ansible/playbook/role/metadata.py +1 -12
  97. ansible/playbook/role/requirement.py +1 -2
  98. ansible/playbook/role_include.py +1 -2
  99. ansible/playbook/taggable.py +16 -5
  100. ansible/playbook/task.py +11 -50
  101. ansible/plugins/action/__init__.py +20 -19
  102. ansible/plugins/action/add_host.py +1 -2
  103. ansible/plugins/action/fetch.py +3 -5
  104. ansible/plugins/action/group_by.py +1 -2
  105. ansible/plugins/action/include_vars.py +20 -22
  106. ansible/plugins/action/script.py +1 -3
  107. ansible/plugins/action/template.py +1 -2
  108. ansible/plugins/action/uri.py +4 -2
  109. ansible/plugins/cache/__init__.py +1 -0
  110. ansible/plugins/callback/__init__.py +13 -6
  111. ansible/plugins/connection/__init__.py +3 -7
  112. ansible/plugins/connection/local.py +2 -3
  113. ansible/plugins/connection/psrp.py +0 -2
  114. ansible/plugins/connection/ssh.py +2 -7
  115. ansible/plugins/connection/winrm.py +0 -2
  116. ansible/plugins/doc_fragments/result_format_callback.py +15 -0
  117. ansible/plugins/filter/core.py +4 -5
  118. ansible/plugins/filter/encryption.py +3 -27
  119. ansible/plugins/filter/mathstuff.py +1 -2
  120. ansible/plugins/filter/to_nice_yaml.yml +31 -3
  121. ansible/plugins/filter/to_yaml.yml +29 -12
  122. ansible/plugins/inventory/__init__.py +1 -2
  123. ansible/plugins/inventory/toml.py +3 -6
  124. ansible/plugins/inventory/yaml.py +1 -2
  125. ansible/plugins/loader.py +3 -4
  126. ansible/plugins/lookup/password.py +1 -2
  127. ansible/plugins/lookup/subelements.py +2 -3
  128. ansible/plugins/lookup/url.py +1 -1
  129. ansible/plugins/lookup/varnames.py +1 -2
  130. ansible/plugins/shell/__init__.py +9 -4
  131. ansible/plugins/shell/powershell.py +8 -24
  132. ansible/plugins/strategy/__init__.py +5 -2
  133. ansible/plugins/test/core.py +4 -1
  134. ansible/plugins/test/falsy.yml +1 -1
  135. ansible/plugins/test/regex.yml +18 -6
  136. ansible/plugins/test/truthy.yml +1 -1
  137. ansible/release.py +2 -2
  138. ansible/template/__init__.py +3 -7
  139. ansible/utils/collection_loader/_collection_config.py +5 -0
  140. ansible/utils/collection_loader/_collection_finder.py +11 -14
  141. ansible/utils/context_objects.py +7 -4
  142. ansible/utils/display.py +7 -6
  143. ansible/utils/encrypt.py +0 -5
  144. ansible/utils/helpers.py +6 -2
  145. ansible/utils/jsonrpc.py +7 -3
  146. ansible/utils/plugin_docs.py +49 -38
  147. ansible/utils/ssh_functions.py +0 -19
  148. ansible/utils/unsafe_proxy.py +7 -7
  149. ansible/vars/clean.py +2 -3
  150. ansible/vars/manager.py +28 -22
  151. ansible/vars/plugins.py +1 -31
  152. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/METADATA +4 -4
  153. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/RECORD +213 -214
  154. ansible_test/_data/completion/docker.txt +7 -7
  155. ansible_test/_data/completion/network.txt +0 -1
  156. ansible_test/_data/completion/remote.txt +4 -4
  157. ansible_test/_data/requirements/ansible-test.txt +1 -1
  158. ansible_test/_data/requirements/ansible.txt +1 -1
  159. ansible_test/_data/requirements/sanity.ansible-doc.txt +2 -2
  160. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  161. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  162. ansible_test/_data/requirements/sanity.import.txt +1 -1
  163. ansible_test/_data/requirements/sanity.integration-aliases.txt +1 -1
  164. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  165. ansible_test/_data/requirements/sanity.pylint.txt +6 -6
  166. ansible_test/_data/requirements/sanity.runtime-metadata.txt +1 -1
  167. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
  168. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  169. ansible_test/_internal/cache.py +2 -5
  170. ansible_test/_internal/cli/compat.py +1 -1
  171. ansible_test/_internal/commands/coverage/combine.py +1 -3
  172. ansible_test/_internal/commands/integration/__init__.py +3 -7
  173. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  174. ansible_test/_internal/commands/integration/coverage.py +1 -3
  175. ansible_test/_internal/commands/integration/filters.py +5 -10
  176. ansible_test/_internal/commands/sanity/pylint.py +11 -0
  177. ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
  178. ansible_test/_internal/commands/units/__init__.py +1 -13
  179. ansible_test/_internal/compat/packaging.py +2 -2
  180. ansible_test/_internal/compat/yaml.py +2 -2
  181. ansible_test/_internal/completion.py +2 -5
  182. ansible_test/_internal/config.py +2 -7
  183. ansible_test/_internal/coverage_util.py +1 -1
  184. ansible_test/_internal/delegation.py +2 -0
  185. ansible_test/_internal/docker_util.py +1 -1
  186. ansible_test/_internal/host_profiles.py +6 -11
  187. ansible_test/_internal/provider/__init__.py +2 -5
  188. ansible_test/_internal/provisioning.py +2 -5
  189. ansible_test/_internal/pypi_proxy.py +1 -1
  190. ansible_test/_internal/python_requirements.py +1 -1
  191. ansible_test/_internal/target.py +2 -6
  192. ansible_test/_internal/thread.py +1 -4
  193. ansible_test/_internal/util.py +9 -14
  194. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
  195. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +48 -45
  196. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +9 -7
  197. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +51 -37
  198. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
  199. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
  200. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
  201. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
  202. ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
  203. ansible_test/_util/target/common/constants.py +2 -2
  204. ansible_test/_util/target/setup/bootstrap.sh +0 -6
  205. ansible/utils/py3compat.py +0 -27
  206. ansible_test/_data/pytest/config/legacy.ini +0 -4
  207. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/WHEEL +0 -0
  208. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/entry_points.txt +0 -0
  209. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/COPYING +0 -0
  210. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  211. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  212. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  213. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  214. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  215. {ansible_core-2.19.4rc1.dist-info → ansible_core-2.20.0.dist-info}/top_level.txt +0 -0
@@ -52,7 +52,6 @@ import re
52
52
 
53
53
  from ansible.errors import AnsibleError
54
54
  from ansible.module_utils.common.text.converters import to_native
55
- from ansible.module_utils.six import string_types
56
55
  from ansible.plugins.lookup import LookupBase
57
56
 
58
57
 
@@ -69,7 +68,7 @@ class LookupModule(LookupBase):
69
68
  variable_names = list(variables.keys())
70
69
  for term in terms:
71
70
 
72
- if not isinstance(term, string_types):
71
+ if not isinstance(term, str):
73
72
  raise AnsibleError('Invalid setting identifier, "%s" is not a string, it is a %s' % (term, type(term)))
74
73
 
75
74
  try:
@@ -24,12 +24,12 @@ import secrets
24
24
  import shlex
25
25
  import time
26
26
 
27
- from collections.abc import Mapping, Sequence
28
-
29
27
  from ansible.errors import AnsibleError
30
28
  from ansible.module_utils.common.text.converters import to_native
31
- from ansible.module_utils.six import text_type, string_types
32
29
  from ansible.plugins import AnsiblePlugin
30
+ from ansible.utils.display import Display
31
+
32
+ display = Display()
33
33
 
34
34
  _USER_HOME_PATH_RE = re.compile(r'^~[_.A-Za-z0-9][-_.A-Za-z0-9]*$')
35
35
 
@@ -84,7 +84,7 @@ class ShellBase(AnsiblePlugin):
84
84
  return 'ansible-tmp-%s-%s-%s' % (time.time(), os.getpid(), secrets.randbelow(2**48))
85
85
 
86
86
  def env_prefix(self, **kwargs):
87
- return ' '.join(['%s=%s' % (k, self.quote(text_type(v))) for k, v in kwargs.items()])
87
+ return ' '.join(['%s=%s' % (k, self.quote(str(v))) for k, v in kwargs.items()])
88
88
 
89
89
  def join_path(self, *args):
90
90
  return os.path.join(*args)
@@ -272,6 +272,11 @@ class ShellBase(AnsiblePlugin):
272
272
 
273
273
  def wrap_for_exec(self, cmd):
274
274
  """wrap script execution with any necessary decoration (eg '&' for quoted powershell script paths)"""
275
+ display.deprecated(
276
+ msg='The Shell.wrap_for_exec method is deprecated.',
277
+ help_text="Contact plugin author to update their plugin to not use this method.",
278
+ version='2.24',
279
+ )
275
280
  return cmd
276
281
 
277
282
  def quote(self, cmd):
@@ -192,7 +192,7 @@ class ShellModule(ShellBase):
192
192
 
193
193
  def join_path(self, *args):
194
194
  # use normpath() to remove doubled slashed and convert forward to backslashes
195
- parts = [ntpath.normpath(self._unquote(arg)) for arg in args]
195
+ parts = [ntpath.normpath(arg) for arg in args]
196
196
 
197
197
  # Because ntpath.join treats any component that begins with a backslash as an absolute path,
198
198
  # we have to strip slashes from at least the beginning, otherwise join will ignore all previous
@@ -210,7 +210,6 @@ class ShellModule(ShellBase):
210
210
 
211
211
  def path_has_trailing_slash(self, path):
212
212
  # Allow Windows paths to be specified using either slash.
213
- path = self._unquote(path)
214
213
  return path.endswith('/') or path.endswith('\\')
215
214
 
216
215
  def chmod(self, paths, mode):
@@ -223,11 +222,11 @@ class ShellModule(ShellBase):
223
222
  raise NotImplementedError('set_user_facl is not implemented for Powershell')
224
223
 
225
224
  def remove(self, path, recurse=False):
226
- path = self._escape(self._unquote(path))
225
+ quoted_path = self._escape(path)
227
226
  if recurse:
228
- return self._encode_script("""Remove-Item '%s' -Force -Recurse;""" % path)
227
+ return self._encode_script("""Remove-Item '%s' -Force -Recurse;""" % quoted_path)
229
228
  else:
230
- return self._encode_script("""Remove-Item '%s' -Force;""" % path)
229
+ return self._encode_script("""Remove-Item '%s' -Force;""" % quoted_path)
231
230
 
232
231
  def mkdtemp(
233
232
  self,
@@ -240,7 +239,6 @@ class ShellModule(ShellBase):
240
239
  # compatibility in case other action plugins outside Ansible calls this.
241
240
  if not basefile:
242
241
  basefile = self.__class__._generate_temp_dir_name()
243
- basefile = self._escape(self._unquote(basefile))
244
242
  basetmpdir = self._escape(tmpdir if tmpdir else self.get_option('remote_tmp'))
245
243
 
246
244
  script = f"""
@@ -263,7 +261,6 @@ class ShellModule(ShellBase):
263
261
  if not basefile:
264
262
  basefile = self.__class__._generate_temp_dir_name()
265
263
 
266
- basefile = self._unquote(basefile)
267
264
  basetmpdir = tmpdir if tmpdir else self.get_option('remote_tmp')
268
265
 
269
266
  script, stdin = _bootstrap_powershell_script("powershell_mkdtemp.ps1", {
@@ -283,7 +280,6 @@ class ShellModule(ShellBase):
283
280
  ) -> str:
284
281
  # This is not called in Ansible anymore but it is kept for backwards
285
282
  # compatibility in case other actions plugins outside Ansible called this.
286
- user_home_path = self._unquote(user_home_path)
287
283
  if user_home_path == '~':
288
284
  script = 'Write-Output (Get-Location).Path'
289
285
  elif user_home_path.startswith('~\\'):
@@ -297,7 +293,6 @@ class ShellModule(ShellBase):
297
293
  user_home_path: str,
298
294
  username: str = '',
299
295
  ) -> _ShellCommand:
300
- user_home_path = self._unquote(user_home_path)
301
296
  script, stdin = _bootstrap_powershell_script("powershell_expand_user.ps1", {
302
297
  'Path': user_home_path,
303
298
  })
@@ -308,7 +303,7 @@ class ShellModule(ShellBase):
308
303
  )
309
304
 
310
305
  def exists(self, path):
311
- path = self._escape(self._unquote(path))
306
+ path = self._escape(path)
312
307
  script = """
313
308
  If (Test-Path '%s')
314
309
  {
@@ -329,7 +324,7 @@ class ShellModule(ShellBase):
329
324
  version="2.23",
330
325
  help_text="Use `ActionBase._execute_remote_stat()` instead.",
331
326
  )
332
- path = self._escape(self._unquote(path))
327
+ path = self._escape(path)
333
328
  script = """
334
329
  If (Test-Path -PathType Leaf '%(path)s')
335
330
  {
@@ -364,7 +359,7 @@ class ShellModule(ShellBase):
364
359
  if arg_path:
365
360
  # Running a module without the exec_wrapper and with an argument
366
361
  # file.
367
- script_path = self._unquote(cmd_parts[0])
362
+ script_path = cmd_parts[0]
368
363
  if not script_path.lower().endswith('.ps1'):
369
364
  script_path += '.ps1'
370
365
 
@@ -387,7 +382,6 @@ class ShellModule(ShellBase):
387
382
  cmd_parts.insert(0, shebang[2:])
388
383
  elif not shebang:
389
384
  # The module is assumed to be a binary
390
- cmd_parts[0] = self._unquote(cmd_parts[0])
391
385
  cmd_parts.append(arg_path)
392
386
  script = """
393
387
  Try
@@ -428,19 +422,9 @@ class ShellModule(ShellBase):
428
422
  return self._encode_script(script, preserve_rc=False)
429
423
 
430
424
  def wrap_for_exec(self, cmd):
425
+ super().wrap_for_exec(cmd)
431
426
  return '& %s; exit $LASTEXITCODE' % cmd
432
427
 
433
- def _unquote(self, value):
434
- """Remove any matching quotes that wrap the given value."""
435
- value = to_text(value or '')
436
- m = re.match(r'^\s*?\'(.*?)\'\s*?$', value)
437
- if m:
438
- return m.group(1)
439
- m = re.match(r'^\s*?"(.*?)"\s*?$', value)
440
- if m:
441
- return m.group(1)
442
- return value
443
-
444
428
  def _escape(self, value):
445
429
  """Return value escaped for use in PowerShell single quotes."""
446
430
  # There are 5 chars that need to be escaped in a single quote.
@@ -53,6 +53,9 @@ from ansible.utils.sentinel import Sentinel
53
53
  from ansible.utils.vars import combine_vars
54
54
  from ansible.vars.clean import strip_internal_keys, module_response_deepcopy
55
55
 
56
+ if t.TYPE_CHECKING:
57
+ from ansible.playbook.role_include import IncludeRole
58
+
56
59
  display = Display()
57
60
 
58
61
  __all__ = ['StrategyBase']
@@ -581,7 +584,7 @@ class StrategyBase:
581
584
  self._variable_manager.set_nonpersistent_facts(
582
585
  original_host.name,
583
586
  dict(
584
- ansible_failed_task=original_task.serialize(),
587
+ ansible_failed_task=original_task.dump_attrs(),
585
588
  ansible_failed_result=task_result._return_data,
586
589
  ),
587
590
  )
@@ -799,7 +802,7 @@ class StrategyBase:
799
802
 
800
803
  return ret_results
801
804
 
802
- def _copy_included_file(self, included_file: IncludedFile) -> IncludedFile:
805
+ def _copy_included_file(self, included_file: IncludedFile) -> TaskInclude | IncludeRole:
803
806
  """
804
807
  A proven safe and performant way to create a copy of an included file
805
808
  """
@@ -142,6 +142,9 @@ def regex(value='', pattern='', ignorecase=False, multiline=False, match_type='s
142
142
  This is likely only useful for `search` and `match` which already
143
143
  have their own filters.
144
144
  """
145
+ valid_match_types = ('search', 'match', 'fullmatch')
146
+ if match_type not in valid_match_types:
147
+ raise errors.AnsibleTemplatePluginError(f"Invalid match_type specified. Expected one of: {', '.join(valid_match_types)}.", obj=match_type)
145
148
  value = to_text(value, errors='surrogate_or_strict')
146
149
  flags = 0
147
150
  if ignorecase:
@@ -149,7 +152,7 @@ def regex(value='', pattern='', ignorecase=False, multiline=False, match_type='s
149
152
  if multiline:
150
153
  flags |= re.M
151
154
  _re = re.compile(pattern, flags=flags)
152
- return bool(getattr(_re, match_type, 'search')(value))
155
+ return bool(getattr(_re, match_type)(value))
153
156
 
154
157
 
155
158
  @accept_args_markers
@@ -5,7 +5,7 @@ DOCUMENTATION:
5
5
  short_description: Pythonic false
6
6
  description:
7
7
  - This check is a more Python version of what is 'false'.
8
- - It is the opposite of 'truthy'.
8
+ - It is the opposite of P(ansible.builtin.truthy#test).
9
9
  options:
10
10
  _input:
11
11
  description: An expression that can be expressed in a boolean context.
@@ -3,7 +3,7 @@ DOCUMENTATION:
3
3
  author: Ansible Core
4
4
  short_description: Does string match regular expression from the start
5
5
  description:
6
- - Compare string against regular expression using Python's match or search functions.
6
+ - Compare string against regular expression using Python's match, fullmatch or search functions.
7
7
  options:
8
8
  _input:
9
9
  description: String to match.
@@ -22,14 +22,26 @@ DOCUMENTATION:
22
22
  type: boolean
23
23
  default: False
24
24
  match_type:
25
- description: Decide which function to be used to do the matching.
25
+ description:
26
+ - Decide which function to be used to do the matching.
26
27
  type: string
27
- choices: [match, search]
28
+ choices: [match, search, fullmatch]
28
29
  default: search
29
30
 
30
- EXAMPLES: |
31
- url: "https://example.com/users/foo/resources/bar"
32
- foundmatch: url is regex("example\.com/\w+/foo")
31
+ EXAMPLES:
32
+ - name: check if string matches regex
33
+ assert:
34
+ that:
35
+ - 'url is regex("example\.com/\w+/foo")'
36
+ vars:
37
+ url: "https://example.com/users/foo/resources/bar"
38
+
39
+ - name: check if string matches regex ignoring case
40
+ assert:
41
+ that:
42
+ - 'url is regex("EXAMPLE\.COM/\w+/foo", ignorecase=True)'
43
+ vars:
44
+ url: "https://Example.com/users/foo/resources/bar"
33
45
 
34
46
  RETURN:
35
47
  _value:
@@ -20,5 +20,5 @@ EXAMPLES: |
20
20
  thisisfalse: '{{ "" is truthy }}'
21
21
  RETURN:
22
22
  _value:
23
- description: Returns V(True) if the condition is not "Python truthy", V(False) otherwise.
23
+ description: Returns V(True) if the condition is "Python truthy", V(False) otherwise.
24
24
  type: boolean
ansible/release.py CHANGED
@@ -17,6 +17,6 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = '2.19.4rc1'
20
+ __version__ = '2.20.0'
21
21
  __author__ = 'Ansible, Inc.'
22
- __codename__ = "What Is and What Should Never Be"
22
+ __codename__ = "Good Times Bad Times"
@@ -25,7 +25,6 @@ if _t.TYPE_CHECKING: # pragma: nocover
25
25
 
26
26
  _display: _t.Final[_Display] = _Display()
27
27
  _UNSET = _t.cast(_t.Any, object())
28
- _TTrustable = _t.TypeVar('_TTrustable', bound=str | _io.IOBase | _t.TextIO | _t.BinaryIO)
29
28
  _TRUSTABLE_TYPES = (str, _io.IOBase)
30
29
 
31
30
  AnsibleUndefined = _jinja_common.UndefinedMarker
@@ -361,7 +360,7 @@ def generate_ansible_template_vars(
361
360
  return _template_vars.generate_ansible_template_vars(path=path, fullpath=fullpath, dest_path=dest_path, include_ansible_managed=True)
362
361
 
363
362
 
364
- def trust_as_template(value: _TTrustable) -> _TTrustable:
363
+ def trust_as_template[T: str | _io.IOBase | _t.TextIO | _t.BinaryIO](value: T) -> T:
365
364
  """
366
365
  Returns `value` tagged as trusted for templating.
367
366
  Raises a `TypeError` if `value` is not a supported type.
@@ -385,10 +384,7 @@ def is_trusted_as_template(value: object) -> bool:
385
384
  return isinstance(value, _TRUSTABLE_TYPES) and _tags.TrustedAsTemplate.is_tagged_on(value)
386
385
 
387
386
 
388
- _TCallable = _t.TypeVar('_TCallable', bound=_t.Callable)
389
-
390
-
391
- def accept_args_markers(plugin: _TCallable) -> _TCallable:
387
+ def accept_args_markers[T: _t.Callable](plugin: T) -> T:
392
388
  """
393
389
  A decorator to mark a Jinja plugin as capable of handling `Marker` values for its top-level arguments.
394
390
  Non-decorated plugin invocation is skipped when a top-level argument is a `Marker`, with the first such value substituted as the plugin result.
@@ -399,7 +395,7 @@ def accept_args_markers(plugin: _TCallable) -> _TCallable:
399
395
  return plugin
400
396
 
401
397
 
402
- def accept_lazy_markers(plugin: _TCallable) -> _TCallable:
398
+ def accept_lazy_markers[T: _t.Callable](plugin: T) -> T:
403
399
  """
404
400
  A decorator to mark a Jinja plugin as capable of handling `Marker` values retrieved from lazy containers.
405
401
  Non-decorated plugins will trigger a `MarkerError` exception when attempting to retrieve a `Marker` from a lazy container.
@@ -62,6 +62,11 @@ class _AnsibleCollectionConfig(type):
62
62
  cls._require_finder()
63
63
  return [_to_text(p) for p in cls._collection_finder._n_collection_paths]
64
64
 
65
+ @property
66
+ def _internal_collections(cls):
67
+ cls._require_finder()
68
+ return cls._collection_finder._internal_collections
69
+
65
70
  @property
66
71
  def default_collection(cls):
67
72
  return cls._default_collection
@@ -26,20 +26,16 @@ from . import _to_bytes, _to_text
26
26
  from ._collection_config import AnsibleCollectionConfig
27
27
 
28
28
  try:
29
- try:
30
- # Available on Python >= 3.11
31
- # We ignore the import error that will trigger when running mypy with
32
- # older Python versions.
33
- from importlib.resources.abc import TraversableResources # type: ignore[import]
34
- except ImportError:
35
- # Used with Python 3.9 and 3.10 only
36
- # This member is still available as an alias up until Python 3.14 but
37
- # is deprecated as of Python 3.12.
38
- from importlib.abc import TraversableResources # deprecated: description='TraversableResources move' python_version='3.10'
29
+ # Available on Python >= 3.11
30
+ # We ignore the import error that will trigger when running mypy with
31
+ # older Python versions.
32
+ from importlib.resources.abc import TraversableResources # type: ignore[import]
39
33
  except ImportError:
40
- # Python < 3.9
41
- # deprecated: description='TraversableResources fallback' python_version='3.8'
42
- TraversableResources = object # type: ignore[assignment,misc]
34
+ # Used with Python 3.9 and 3.10 only
35
+ # This member is still available as an alias up until Python 3.14 but
36
+ # is deprecated as of Python 3.12.
37
+ # deprecated: description='TraversableResources move' python_version='3.10'
38
+ from importlib.abc import TraversableResources # type: ignore[assignment,no-redef]
43
39
 
44
40
  # NB: this supports import sanity test providing a different impl
45
41
  try:
@@ -186,7 +182,7 @@ class _AnsibleTraversableResources(TraversableResources):
186
182
 
187
183
 
188
184
  class _AnsibleCollectionFinder:
189
- def __init__(self, paths=None, scan_sys_paths=True):
185
+ def __init__(self, paths=None, scan_sys_paths=True, internal_collections=None):
190
186
  # TODO: accept metadata loader override
191
187
  self._ansible_pkg_path = _to_text(os.path.dirname(_to_bytes(sys.modules['ansible'].__file__)))
192
188
 
@@ -213,6 +209,7 @@ class _AnsibleCollectionFinder:
213
209
  if p not in good_paths and os.path.isdir(_to_bytes(os.path.join(p, 'ansible_collections'))):
214
210
  good_paths.append(p)
215
211
 
212
+ self._internal_collections = internal_collections
216
213
  self._n_configured_paths = good_paths
217
214
  self._n_cached_collection_paths = None
218
215
  self._n_cached_collection_qualified_paths = None
@@ -9,14 +9,14 @@ from __future__ import annotations
9
9
  from abc import ABCMeta
10
10
  from collections.abc import Container, Mapping, Sequence, Set
11
11
 
12
+ from ansible.module_utils._internal import _no_six
12
13
  from ansible.module_utils.common.collections import ImmutableDict
13
- from ansible.module_utils.six import add_metaclass, binary_type, text_type
14
14
  from ansible.utils.singleton import Singleton
15
15
 
16
16
 
17
17
  def _make_immutable(obj):
18
18
  """Recursively convert a container and objects inside of it into immutable data types"""
19
- if isinstance(obj, (text_type, binary_type)):
19
+ if isinstance(obj, (str, bytes)):
20
20
  # Strings first because they are also sequences
21
21
  return obj
22
22
  elif isinstance(obj, Mapping):
@@ -79,11 +79,14 @@ class CLIArgs(ImmutableDict):
79
79
  return cls(vars(options))
80
80
 
81
81
 
82
- @add_metaclass(_ABCSingleton)
83
- class GlobalCLIArgs(CLIArgs):
82
+ class GlobalCLIArgs(CLIArgs, metaclass=_ABCSingleton):
84
83
  """
85
84
  Globally hold a parsed copy of cli arguments.
86
85
 
87
86
  Only one of these exist per program as it is for global context
88
87
  """
89
88
  pass
89
+
90
+
91
+ def __getattr__(importable_name):
92
+ return _no_six.deprecate(importable_name, __name__, "binary_type", "text_type", "add_metaclass")
ansible/utils/display.py CHANGED
@@ -52,11 +52,10 @@ from ansible.constants import config
52
52
  from ansible.errors import AnsibleAssertionError, AnsiblePromptInterrupt, AnsiblePromptNoninteractive, AnsibleError
53
53
  from ansible._internal._errors import _error_utils, _error_factory
54
54
  from ansible._internal import _display_utils
55
- from ansible.module_utils._internal import _deprecator, _messages
55
+ from ansible.module_utils._internal import _deprecator, _messages, _no_six
56
56
  from ansible.module_utils.common.text.converters import to_bytes, to_text
57
57
  from ansible.module_utils.datatag import deprecator_from_collection_name
58
58
  from ansible._internal._datatag._tags import TrustedAsTemplate
59
- from ansible.module_utils.six import text_type
60
59
  from ansible.module_utils._internal import _traceback, _errors
61
60
  from ansible.utils.color import stringc
62
61
  from ansible.utils.multiprocessing import context as multiprocessing_context
@@ -66,8 +65,6 @@ if t.TYPE_CHECKING:
66
65
  # avoid circular import at runtime
67
66
  from ansible.executor.task_queue_manager import FinalQueue
68
67
 
69
- P = t.ParamSpec('P')
70
-
71
68
  _LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
72
69
  # Set argtypes, to avoid segfault if the wrong type is provided,
73
70
  # restype is assumed to be c_int
@@ -118,7 +115,7 @@ def get_text_width(text: str) -> int:
118
115
  character and using wcwidth individually, falling back to a value of 0
119
116
  for non-printable wide characters.
120
117
  """
121
- if not isinstance(text, text_type):
118
+ if not isinstance(text, str):
122
119
  raise TypeError('get_text_width requires text, not %s' % type(text))
123
120
 
124
121
  try:
@@ -398,7 +395,7 @@ class Display(metaclass=Singleton):
398
395
  self.b_cowsay = b_cow_path
399
396
 
400
397
  @staticmethod
401
- def _proxy(
398
+ def _proxy[**P](
402
399
  func: c.Callable[t.Concatenate[Display, P], None]
403
400
  ) -> c.Callable[..., None]:
404
401
  @wraps(func)
@@ -1144,3 +1141,7 @@ def _report_config_warnings(deprecator: _messages.PluginInfo) -> None:
1144
1141
  # emit any warnings or deprecations
1145
1142
  # in the event config fails before display is up, we'll lose warnings -- but that's OK, since everything is broken anyway
1146
1143
  _report_config_warnings(_deprecator.ANSIBLE_CORE_DEPRECATOR)
1144
+
1145
+
1146
+ def __getattr__(importable_name):
1147
+ return _no_six.deprecate(importable_name, __name__, "text_type")
ansible/utils/encrypt.py CHANGED
@@ -176,11 +176,6 @@ class PasslibHash(BaseHash):
176
176
  return to_text(result, errors='strict')
177
177
 
178
178
 
179
- def passlib_or_crypt(secret, algorithm, salt=None, salt_size=None, rounds=None, ident=None):
180
- display.deprecated("passlib_or_crypt API is deprecated in favor of do_encrypt", version='2.20')
181
- return do_encrypt(secret, algorithm, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident)
182
-
183
-
184
179
  def do_encrypt(result, encrypt, salt_size=None, salt=None, ident=None, rounds=None):
185
180
  if PASSLIB_AVAILABLE:
186
181
  return PasslibHash(encrypt).hash(result, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident)
ansible/utils/helpers.py CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- from ansible.module_utils.six import string_types
20
+ from ansible.module_utils._internal import _no_six
21
21
 
22
22
 
23
23
  def pct_to_int(value, num_items, min_value=1):
@@ -25,7 +25,7 @@ def pct_to_int(value, num_items, min_value=1):
25
25
  Converts a given value to a percentage if specified as "x%",
26
26
  otherwise converts the given value to an integer.
27
27
  """
28
- if isinstance(value, string_types) and value.endswith('%'):
28
+ if isinstance(value, str) and value.endswith('%'):
29
29
  value_pct = int(value.replace("%", ""))
30
30
  return int((value_pct / 100.0) * num_items) or min_value
31
31
  else:
@@ -47,3 +47,7 @@ def deduplicate_list(original_list):
47
47
  """
48
48
  seen = set()
49
49
  return [x for x in original_list if x not in seen and not seen.add(x)]
50
+
51
+
52
+ def __getattr__(importable_name):
53
+ return _no_six.deprecate(importable_name, __name__, "string_types")
ansible/utils/jsonrpc.py CHANGED
@@ -7,9 +7,9 @@ import json
7
7
  import pickle
8
8
  import traceback
9
9
 
10
+ from ansible.module_utils._internal import _no_six
10
11
  from ansible.module_utils.common.text.converters import to_text
11
12
  from ansible.module_utils.connection import ConnectionError
12
- from ansible.module_utils.six import binary_type, text_type
13
13
  from ansible.utils.display import Display
14
14
 
15
15
  display = Display()
@@ -79,9 +79,9 @@ class JsonRpcServer(object):
79
79
 
80
80
  def response(self, result=None):
81
81
  response = self.header()
82
- if isinstance(result, binary_type):
82
+ if isinstance(result, bytes):
83
83
  result = to_text(result)
84
- if not isinstance(result, text_type):
84
+ if not isinstance(result, str):
85
85
  response["result_type"] = "pickle"
86
86
  result = to_text(pickle.dumps(result), errors='surrogateescape')
87
87
  response['result'] = result
@@ -110,3 +110,7 @@ class JsonRpcServer(object):
110
110
 
111
111
  def internal_error(self, data=None):
112
112
  return self.error(-32603, 'Internal error', data)
113
+
114
+
115
+ def __getattr__(importable_name):
116
+ return _no_six.deprecate(importable_name, __name__, "binary_type", "text_type")