ansible-core 2.19.2__py3-none-any.whl → 2.20.0b1__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 (202) 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/_display_utils.py +145 -0
  5. ansible/_internal/_json/__init__.py +3 -4
  6. ansible/_internal/_templating/_engine.py +1 -1
  7. ansible/_internal/_templating/_jinja_plugins.py +1 -2
  8. ansible/_internal/_wrapt.py +105 -301
  9. ansible/cli/__init__.py +11 -10
  10. ansible/cli/adhoc.py +1 -2
  11. ansible/cli/arguments/option_helpers.py +1 -1
  12. ansible/cli/config.py +5 -6
  13. ansible/cli/doc.py +67 -67
  14. ansible/cli/galaxy.py +15 -24
  15. ansible/cli/inventory.py +0 -1
  16. ansible/cli/playbook.py +0 -1
  17. ansible/cli/pull.py +0 -1
  18. ansible/cli/scripts/ansible_connection_cli_stub.py +1 -1
  19. ansible/config/base.yml +1 -25
  20. ansible/config/manager.py +0 -2
  21. ansible/executor/play_iterator.py +42 -20
  22. ansible/executor/playbook_executor.py +0 -9
  23. ansible/executor/powershell/async_watchdog.ps1 +24 -4
  24. ansible/executor/task_executor.py +32 -22
  25. ansible/executor/task_queue_manager.py +1 -3
  26. ansible/galaxy/api.py +33 -80
  27. ansible/galaxy/collection/__init__.py +4 -17
  28. ansible/galaxy/dependency_resolution/dataclasses.py +0 -10
  29. ansible/galaxy/dependency_resolution/providers.py +1 -2
  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 +27 -24
  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/urls.py +6 -2
  58. ansible/module_utils/yumdnf.py +0 -5
  59. ansible/modules/apt.py +18 -13
  60. ansible/modules/apt_repository.py +1 -1
  61. ansible/modules/assemble.py +5 -9
  62. ansible/modules/blockinfile.py +39 -23
  63. ansible/modules/cron.py +26 -35
  64. ansible/modules/deb822_repository.py +83 -12
  65. ansible/modules/dnf.py +3 -7
  66. ansible/modules/dnf5.py +4 -6
  67. ansible/modules/expect.py +0 -3
  68. ansible/modules/find.py +1 -2
  69. ansible/modules/get_url.py +1 -1
  70. ansible/modules/git.py +4 -5
  71. ansible/modules/include_vars.py +1 -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/mod_args.py +3 -0
  85. ansible/parsing/vault/__init__.py +1 -2
  86. ansible/playbook/base.py +8 -56
  87. ansible/playbook/block.py +0 -60
  88. ansible/playbook/collectionsearch.py +1 -2
  89. ansible/playbook/handler.py +1 -7
  90. ansible/playbook/helpers.py +0 -7
  91. ansible/playbook/included_file.py +1 -1
  92. ansible/playbook/play.py +103 -37
  93. ansible/playbook/play_context.py +4 -0
  94. ansible/playbook/role/__init__.py +10 -65
  95. ansible/playbook/role/definition.py +3 -4
  96. ansible/playbook/role/include.py +2 -3
  97. ansible/playbook/role/metadata.py +1 -12
  98. ansible/playbook/role/requirement.py +1 -2
  99. ansible/playbook/role_include.py +1 -2
  100. ansible/playbook/taggable.py +16 -5
  101. ansible/playbook/task.py +51 -55
  102. ansible/plugins/action/__init__.py +20 -19
  103. ansible/plugins/action/add_host.py +1 -2
  104. ansible/plugins/action/fetch.py +2 -4
  105. ansible/plugins/action/group_by.py +1 -2
  106. ansible/plugins/action/include_vars.py +20 -22
  107. ansible/plugins/action/script.py +1 -3
  108. ansible/plugins/action/template.py +1 -2
  109. ansible/plugins/action/uri.py +4 -2
  110. ansible/plugins/cache/__init__.py +1 -0
  111. ansible/plugins/callback/__init__.py +13 -6
  112. ansible/plugins/connection/__init__.py +3 -7
  113. ansible/plugins/connection/local.py +2 -3
  114. ansible/plugins/connection/psrp.py +0 -2
  115. ansible/plugins/connection/ssh.py +2 -7
  116. ansible/plugins/connection/winrm.py +0 -2
  117. ansible/plugins/doc_fragments/result_format_callback.py +15 -0
  118. ansible/plugins/filter/core.py +4 -5
  119. ansible/plugins/filter/encryption.py +3 -27
  120. ansible/plugins/filter/mathstuff.py +1 -2
  121. ansible/plugins/filter/to_nice_yaml.yml +31 -3
  122. ansible/plugins/filter/to_yaml.yml +29 -12
  123. ansible/plugins/inventory/__init__.py +1 -2
  124. ansible/plugins/inventory/script.py +2 -1
  125. ansible/plugins/inventory/toml.py +3 -6
  126. ansible/plugins/inventory/yaml.py +1 -2
  127. ansible/plugins/list.py +10 -3
  128. ansible/plugins/loader.py +6 -6
  129. ansible/plugins/lookup/password.py +1 -2
  130. ansible/plugins/lookup/subelements.py +2 -3
  131. ansible/plugins/lookup/url.py +1 -1
  132. ansible/plugins/lookup/varnames.py +1 -2
  133. ansible/plugins/shell/__init__.py +9 -4
  134. ansible/plugins/shell/powershell.py +8 -24
  135. ansible/plugins/strategy/__init__.py +6 -3
  136. ansible/plugins/test/core.py +4 -1
  137. ansible/plugins/test/regex.yml +18 -6
  138. ansible/release.py +2 -2
  139. ansible/template/__init__.py +3 -7
  140. ansible/utils/collection_loader/_collection_config.py +5 -0
  141. ansible/utils/collection_loader/_collection_finder.py +11 -14
  142. ansible/utils/context_objects.py +7 -4
  143. ansible/utils/display.py +28 -167
  144. ansible/utils/encrypt.py +0 -5
  145. ansible/utils/helpers.py +6 -2
  146. ansible/utils/jsonrpc.py +7 -3
  147. ansible/utils/plugin_docs.py +49 -38
  148. ansible/utils/ssh_functions.py +0 -19
  149. ansible/utils/unsafe_proxy.py +7 -7
  150. ansible/vars/clean.py +2 -3
  151. ansible/vars/manager.py +27 -20
  152. ansible/vars/plugins.py +1 -31
  153. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/METADATA +3 -3
  154. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/RECORD +200 -200
  155. ansible_test/_data/completion/docker.txt +7 -7
  156. ansible_test/_data/completion/network.txt +0 -1
  157. ansible_test/_data/completion/remote.txt +4 -4
  158. ansible_test/_data/requirements/ansible-test.txt +1 -1
  159. ansible_test/_data/requirements/sanity.changelog.txt +1 -1
  160. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  161. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  162. ansible_test/_internal/cache.py +2 -5
  163. ansible_test/_internal/cli/compat.py +1 -1
  164. ansible_test/_internal/commands/coverage/combine.py +1 -3
  165. ansible_test/_internal/commands/integration/__init__.py +3 -7
  166. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  167. ansible_test/_internal/commands/integration/coverage.py +1 -3
  168. ansible_test/_internal/commands/integration/filters.py +5 -10
  169. ansible_test/_internal/commands/sanity/validate_modules.py +1 -5
  170. ansible_test/_internal/commands/units/__init__.py +1 -13
  171. ansible_test/_internal/completion.py +2 -5
  172. ansible_test/_internal/config.py +2 -7
  173. ansible_test/_internal/coverage_util.py +1 -1
  174. ansible_test/_internal/delegation.py +2 -0
  175. ansible_test/_internal/docker_util.py +1 -1
  176. ansible_test/_internal/host_profiles.py +6 -11
  177. ansible_test/_internal/provider/__init__.py +2 -5
  178. ansible_test/_internal/provisioning.py +2 -5
  179. ansible_test/_internal/pypi_proxy.py +1 -1
  180. ansible_test/_internal/target.py +2 -6
  181. ansible_test/_internal/thread.py +1 -4
  182. ansible_test/_internal/util.py +9 -14
  183. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +14 -19
  184. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +30 -27
  185. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +31 -18
  186. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +1 -2
  187. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +59 -71
  188. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -2
  189. ansible_test/_util/target/cli/ansible_test_cli_stub.py +4 -2
  190. ansible_test/_util/target/common/constants.py +2 -2
  191. ansible_test/_util/target/setup/bootstrap.sh +0 -6
  192. ansible/utils/py3compat.py +0 -27
  193. ansible_test/_data/pytest/config/legacy.ini +0 -4
  194. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/WHEEL +0 -0
  195. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/entry_points.txt +0 -0
  196. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/COPYING +0 -0
  197. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  198. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  199. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  200. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  201. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  202. {ansible_core-2.19.2.dist-info → ansible_core-2.20.0b1.dist-info}/top_level.txt +0 -0
@@ -13,25 +13,13 @@ from ansible.utils.display import Display
13
13
  display = Display()
14
14
 
15
15
 
16
- def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=False, vaultid=None):
16
+ def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=False):
17
17
  if not isinstance(secret, (str, bytes)):
18
18
  raise TypeError(f"Secret passed is required to be a string, instead we got {type(secret)}.")
19
19
 
20
20
  if not isinstance(data, (str, bytes)):
21
21
  raise TypeError(f"Can only vault strings, instead we got {type(data)}.")
22
22
 
23
- if vaultid is not None:
24
- display.deprecated(
25
- msg="Use of undocumented `vaultid`.",
26
- version="2.20",
27
- help_text="Use `vault_id` instead.",
28
- )
29
-
30
- if vault_id == 'filter_default':
31
- vault_id = vaultid
32
- else:
33
- display.warning("Ignoring vaultid as vault_id is already set.")
34
-
35
23
  vs = VaultSecret(to_bytes(secret))
36
24
  vl = VaultLib()
37
25
  try:
@@ -48,11 +36,11 @@ def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=Fal
48
36
 
49
37
 
50
38
  @_template.accept_args_markers
51
- def do_unvault(vault, secret, vault_id='filter_default', vaultid=None):
39
+ def do_unvault(vault, secret, vault_id='filter_default'):
52
40
  if isinstance(vault, VaultExceptionMarker):
53
41
  vault = vault._disarm()
54
42
 
55
- if (first_marker := _template.get_first_marker_arg((vault, secret, vault_id, vaultid), {})) is not None:
43
+ if (first_marker := _template.get_first_marker_arg((vault, secret, vault_id), {})) is not None:
56
44
  return first_marker
57
45
 
58
46
  if not isinstance(secret, (str, bytes)):
@@ -61,18 +49,6 @@ def do_unvault(vault, secret, vault_id='filter_default', vaultid=None):
61
49
  if not isinstance(vault, (str, bytes)):
62
50
  raise TypeError(f"Vault should be in the form of a string, instead we got {type(vault)}.")
63
51
 
64
- if vaultid is not None:
65
- display.deprecated(
66
- msg="Use of undocumented `vaultid`.",
67
- version="2.20",
68
- help_text="Use `vault_id` instead.",
69
- )
70
-
71
- if vault_id == 'filter_default':
72
- vault_id = vaultid
73
- else:
74
- display.warning("Ignoring vaultid as vault_id is already set.")
75
-
76
52
  vs = VaultSecret(to_bytes(secret))
77
53
  vl = VaultLib([(vault_id, vs)])
78
54
 
@@ -29,7 +29,6 @@ from jinja2.filters import pass_environment
29
29
 
30
30
  from ansible.errors import AnsibleError
31
31
  from ansible.module_utils.common.text import formatters
32
- from ansible.module_utils.six import binary_type, text_type
33
32
  from ansible.utils.display import Display
34
33
 
35
34
  try:
@@ -180,7 +179,7 @@ def rekey_on_member(data, key, duplicates='error'):
180
179
 
181
180
  if isinstance(data, Mapping):
182
181
  iterate_over = data.values()
183
- elif isinstance(data, Iterable) and not isinstance(data, (text_type, binary_type)):
182
+ elif isinstance(data, Iterable) and not isinstance(data, (str, bytes)):
184
183
  iterate_over = data
185
184
  else:
186
185
  raise AnsibleError("Type is not a valid list, set, or dict")
@@ -1,5 +1,5 @@
1
1
  DOCUMENTATION:
2
- name: to_yaml
2
+ name: to_nice_yaml
3
3
  author: core team
4
4
  version_added: 'historical'
5
5
  short_description: Convert variable to YAML string
@@ -20,10 +20,38 @@ DOCUMENTATION:
20
20
  description: Affects sorting of dictionary keys.
21
21
  default: True
22
22
  type: bool
23
- #default_style=None, canonical=None, width=None, line_break=None, encoding=None, explicit_start=None, explicit_end=None, version=None, tags=None
23
+ default_style:
24
+ description:
25
+ - Indicates the style of the scalar.
26
+ choices:
27
+ - ''
28
+ - "'"
29
+ - '"'
30
+ - '|'
31
+ - '>'
32
+ type: string
33
+ canonical:
34
+ description:
35
+ - If set to V(True), export tag type to the output.
36
+ type: bool
37
+ width:
38
+ description: Set the preferred line width.
39
+ type: int
40
+ line_break:
41
+ description: Specify the line break.
42
+ type: string
43
+ encoding:
44
+ description: Specify the output encoding.
45
+ type: string
46
+ explicit_start:
47
+ description: If set to V(True), adds an explicit start using "---".
48
+ type: bool
49
+ explicit_end:
50
+ description: If set to V(True), adds an explicit end using "...".
51
+ type: bool
24
52
  notes:
25
53
  - More options may be available, see L(PyYAML documentation, https://pyyaml.org/wiki/PyYAMLDocumentation) for details.
26
- - 'These parameters to C(yaml.dump) will be ignored, as they are overridden internally: I(default_flow_style)'
54
+ - 'These parameters to C(yaml.dump) will be ignored, as they are overridden internally: I(default_flow_style), I(allow_unicode).'
27
55
 
28
56
  EXAMPLES: |
29
57
  # dump variable in a template to create a YAML document
@@ -20,21 +20,38 @@ DOCUMENTATION:
20
20
  description: Affects sorting of dictionary keys.
21
21
  default: True
22
22
  type: bool
23
+ default_style:
24
+ description:
25
+ - Indicates the style of the scalar.
26
+ choices:
27
+ - ''
28
+ - "'"
29
+ - '"'
30
+ - '|'
31
+ - '>'
32
+ type: string
33
+ canonical:
34
+ description:
35
+ - If set to V(True), export tag type to the output.
36
+ type: bool
37
+ width:
38
+ description: Set the preferred line width.
39
+ type: integer
40
+ line_break:
41
+ description: Specify the line break.
42
+ type: string
43
+ encoding:
44
+ description: Specify the output encoding.
45
+ type: string
46
+ explicit_start:
47
+ description: If set to V(True), adds an explicit start using "---".
48
+ type: bool
49
+ explicit_end:
50
+ description: If set to V(True), adds an explicit end using "...".
51
+ type: bool
23
52
  notes:
24
53
  - More options may be available, see L(PyYAML documentation, https://pyyaml.org/wiki/PyYAMLDocumentation) for details.
25
54
 
26
- # TODO: find docs for these
27
- #default_flow_style
28
- #default_style
29
- #canonical=None,
30
- #width=None,
31
- #line_break=None,
32
- #encoding=None,
33
- #explicit_start=None,
34
- #explicit_end=None,
35
- #version=None,
36
- #tags=None
37
-
38
55
  EXAMPLES: |
39
56
  # dump variable in a template to create a YAML document
40
57
  {{ github_workflow | to_yaml }}
@@ -34,7 +34,6 @@ from ansible.parsing.dataloader import DataLoader
34
34
  from ansible.plugins import AnsiblePlugin, _ConfigurablePlugin
35
35
  from ansible.plugins.cache import CachePluginAdjudicator
36
36
  from ansible.module_utils.common.text.converters import to_bytes, to_native
37
- from ansible.module_utils.six import string_types
38
37
  from ansible.utils.display import Display
39
38
  from ansible.utils.vars import combine_vars, load_extra_vars
40
39
 
@@ -439,7 +438,7 @@ class Constructable(_BaseInventoryPlugin):
439
438
  new_raw_group_names = []
440
439
  if use_default:
441
440
  new_raw_group_names.append(default_value_name)
442
- elif isinstance(key, string_types):
441
+ elif isinstance(key, str):
443
442
  new_raw_group_names.append(key)
444
443
  elif isinstance(key, list):
445
444
  for name in key:
@@ -256,9 +256,10 @@ class InventoryModule(BaseInventoryPlugin):
256
256
  group = self.inventory.add_group(group)
257
257
 
258
258
  if not isinstance(data, dict):
259
+ original_type = native_type_name(data)
259
260
  data = {'hosts': data}
260
261
  display.deprecated(
261
- msg=f"Group {group!r} was converted to {native_type_name(dict)!r} from {native_type_name(data)!r}.",
262
+ msg=f"Group {group!r} was converted to {native_type_name(dict)!r} from {original_type!r}.",
262
263
  version='2.23',
263
264
  obj=origin,
264
265
  )
@@ -89,8 +89,6 @@ import tomllib
89
89
  from collections.abc import MutableMapping, MutableSequence
90
90
 
91
91
  from ansible.errors import AnsibleFileNotFound, AnsibleParserError
92
- from ansible.module_utils.common.text.converters import to_bytes, to_native
93
- from ansible.module_utils.six import string_types
94
92
  from ansible.plugins.inventory import BaseFileInventoryPlugin
95
93
  from ansible.utils.display import Display
96
94
 
@@ -147,11 +145,10 @@ class InventoryModule(BaseFileInventoryPlugin):
147
145
  )
148
146
 
149
147
  def _load_file(self, file_name):
150
- if not file_name or not isinstance(file_name, string_types):
151
- raise AnsibleParserError("Invalid filename: '%s'" % to_native(file_name))
148
+ if not file_name or not isinstance(file_name, str):
149
+ raise AnsibleParserError("Invalid filename: '%s'" % file_name)
152
150
 
153
- b_file_name = to_bytes(self.loader.path_dwim(file_name))
154
- if not self.loader.path_exists(b_file_name):
151
+ if not self.loader.path_exists(file_name):
155
152
  raise AnsibleFileNotFound("Unable to retrieve file contents", file_name=file_name)
156
153
 
157
154
  try:
@@ -70,7 +70,6 @@ import os
70
70
  from collections.abc import MutableMapping
71
71
 
72
72
  from ansible.errors import AnsibleError, AnsibleParserError
73
- from ansible.module_utils.six import string_types
74
73
  from ansible.module_utils.common.text.converters import to_native, to_text
75
74
  from ansible.plugins.inventory import BaseFileInventoryPlugin
76
75
 
@@ -136,7 +135,7 @@ class InventoryModule(BaseFileInventoryPlugin):
136
135
  for section in ['vars', 'children', 'hosts']:
137
136
  if section in group_data:
138
137
  # convert strings to dicts as these are allowed
139
- if isinstance(group_data[section], string_types):
138
+ if isinstance(group_data[section], str):
140
139
  group_data[section] = {group_data[section]: None}
141
140
 
142
141
  if not isinstance(group_data[section], (MutableMapping, NoneType)): # type: ignore[misc]
ansible/plugins/list.py CHANGED
@@ -105,18 +105,25 @@ def _list_plugins_from_paths(ptype, dirs, collection, depth=0, docs=False):
105
105
  ]):
106
106
  continue
107
107
 
108
+ resource_dir = to_native(os.path.dirname(full_path))
109
+ resource_name = get_composite_name(collection, plugin, resource_dir, depth)
110
+
108
111
  if ptype in ('test', 'filter'):
112
+ # NOTE: pass the composite resource to ensure any relative
113
+ # imports it contains are interpreted in the correct context
114
+ if collection:
115
+ resource_name = '.'.join(resource_name.split('.')[2:])
109
116
  try:
110
- file_plugins = _list_j2_plugins_from_file(collection, full_path, ptype, plugin)
117
+ file_plugins = _list_j2_plugins_from_file(collection, full_path, ptype, resource_name)
111
118
  except KeyError as e:
112
119
  display.warning('Skipping file %s: %s' % (full_path, to_native(e)))
113
120
  continue
114
121
 
115
122
  for plugin in file_plugins:
116
- plugin_name = get_composite_name(collection, plugin.ansible_name, os.path.dirname(to_native(full_path)), depth)
123
+ plugin_name = get_composite_name(collection, plugin.ansible_name, resource_dir, depth)
117
124
  plugins[plugin_name] = full_path
118
125
  else:
119
- plugin_name = get_composite_name(collection, plugin, os.path.dirname(to_native(full_path)), depth)
126
+ plugin_name = resource_name
120
127
  plugins[plugin_name] = full_path
121
128
  else:
122
129
  display.debug("Skip listing plugins in '{0}' as it is not a directory".format(path))
ansible/plugins/loader.py CHANGED
@@ -27,7 +27,6 @@ from ansible import _internal, constants as C
27
27
  from ansible.errors import AnsibleError, AnsiblePluginCircularRedirect, AnsiblePluginRemovedError, AnsibleCollectionUnsupportedVersionError
28
28
  from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
29
29
  from ansible.module_utils.datatag import deprecator_from_collection_name
30
- from ansible.module_utils.six import string_types
31
30
  from ansible.parsing.yaml.loader import AnsibleLoader
32
31
  from ansible._internal._yaml._loader import AnsibleInstrumentedLoader
33
32
  from ansible.plugins import get_plugin_class, MODULE_CACHE, PATH_CACHE, PLUGIN_PATH_CACHE, AnsibleJinja2Plugin
@@ -36,6 +35,7 @@ from ansible.utils.collection_loader._collection_finder import _AnsibleCollectio
36
35
  from ansible.utils.display import Display
37
36
  from ansible.utils.plugin_docs import add_fragments
38
37
  from ansible._internal._datatag import _tags
38
+ from ansible._internal import _display_utils
39
39
 
40
40
  from . import _AnsiblePluginInfoMixin
41
41
  from .filter import AnsibleJinja2Filter
@@ -96,7 +96,7 @@ def get_shell_plugin(shell_type=None, executable=None):
96
96
 
97
97
  # mostly for backwards compat
98
98
  if executable:
99
- if isinstance(executable, string_types):
99
+ if isinstance(executable, str):
100
100
  shell_filename = os.path.basename(executable)
101
101
  try:
102
102
  shell = shell_loader.get(shell_filename)
@@ -517,7 +517,7 @@ class PluginLoader:
517
517
  # filename, cn = find_plugin_docfile( name, type_name, self, [os.path.dirname(path)], C.YAML_DOC_EXTENSIONS)
518
518
 
519
519
  if dstring:
520
- add_fragments(dstring, path, fragment_loader=fragment_loader, is_module=(type_name == 'module'))
520
+ add_fragments(dstring, path, fragment_loader=fragment_loader, is_module=(type_name == 'module'), section='DOCUMENTATION')
521
521
 
522
522
  if 'options' in dstring and isinstance(dstring['options'], dict):
523
523
  C.config.initialize_plugin_configuration_definitions(type_name, name, dstring['options'])
@@ -606,7 +606,7 @@ class PluginLoader:
606
606
  warning_text = tombstone.get('warning_text') or ''
607
607
  warning_plugin_type = "module" if self.type == "modules" else f'{self.type} plugin'
608
608
  warning_text = f'The {fq_name!r} {warning_plugin_type} has been removed.{" " if warning_text else ""}{warning_text}'
609
- removed_msg = display._get_deprecation_message_with_plugin_info(
609
+ removed_msg = _display_utils.get_deprecation_message_with_plugin_info(
610
610
  msg=warning_text,
611
611
  version=removal_version,
612
612
  date=removal_date,
@@ -1411,7 +1411,7 @@ class Jinja2Loader(PluginLoader):
1411
1411
  removal_version = tombstone_entry.get('removal_version')
1412
1412
  warning_text = f'The {key!r} {self.type} plugin has been removed.{" " if warning_text else ""}{warning_text}'
1413
1413
 
1414
- exc_msg = display._get_deprecation_message_with_plugin_info(
1414
+ exc_msg = _display_utils.get_deprecation_message_with_plugin_info(
1415
1415
  msg=warning_text,
1416
1416
  version=removal_version,
1417
1417
  date=removal_date,
@@ -1674,7 +1674,7 @@ def _configure_collection_loader(prefix_collections_path=None):
1674
1674
 
1675
1675
  # insert the internal ansible._protomatter collection up front
1676
1676
  paths = [os.path.dirname(_internal.__file__)] + list(prefix_collections_path) + C.COLLECTIONS_PATHS
1677
- finder = _AnsibleCollectionFinder(paths, C.COLLECTIONS_SCAN_SYS_PATH)
1677
+ finder = _AnsibleCollectionFinder(paths, C.COLLECTIONS_SCAN_SYS_PATH, internal_collections=paths[0])
1678
1678
  finder._install()
1679
1679
 
1680
1680
  # this should succeed now
@@ -134,7 +134,6 @@ import hashlib
134
134
 
135
135
  from ansible.errors import AnsibleError, AnsibleAssertionError
136
136
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
137
- from ansible.module_utils.six import string_types
138
137
  from ansible.parsing.splitter import parse_kv
139
138
  from ansible.plugins.lookup import LookupBase
140
139
  from ansible.utils.encrypt import BaseHash, do_encrypt, random_password, random_salt
@@ -335,7 +334,7 @@ class LookupModule(LookupBase):
335
334
 
336
335
  # chars still might need more
337
336
  chars = params.get('chars', self.get_option('chars'))
338
- if chars and isinstance(chars, string_types):
337
+ if chars and isinstance(chars, str):
339
338
  tmp_chars = []
340
339
  if u',,' in chars:
341
340
  tmp_chars.append(u',')
@@ -83,7 +83,6 @@ _list:
83
83
  """
84
84
 
85
85
  from ansible.errors import AnsibleError
86
- from ansible.module_utils.six import string_types
87
86
  from ansible.module_utils.parsing.convert_bool import boolean
88
87
  from ansible.plugins.lookup import LookupBase
89
88
 
@@ -104,7 +103,7 @@ class LookupModule(LookupBase):
104
103
  _raise_terms_error()
105
104
 
106
105
  # first term should be a list (or dict), second a string holding the subkey
107
- if not isinstance(terms[0], (list, dict)) or not isinstance(terms[1], string_types):
106
+ if not isinstance(terms[0], (list, dict)) or not isinstance(terms[1], str):
108
107
  _raise_terms_error("first a dict or a list, second a string pointing to the subkey")
109
108
  subelements = terms[1].split(".")
110
109
 
@@ -122,7 +121,7 @@ class LookupModule(LookupBase):
122
121
  flags = {}
123
122
  if len(terms) == 3:
124
123
  flags = terms[2]
125
- if not isinstance(flags, dict) and not all(isinstance(key, string_types) and key in FLAGS for key in flags):
124
+ if not isinstance(flags, dict) and not all(isinstance(key, str) and key in FLAGS for key in flags):
126
125
  _raise_terms_error("the optional third item must be a dict with flags %s" % FLAGS)
127
126
 
128
127
  # build_items
@@ -164,7 +164,7 @@ options:
164
164
  description:
165
165
  - SSL/TLS Ciphers to use for the request
166
166
  - 'When a list is provided, all ciphers are joined in order with C(:)'
167
- - See the L(OpenSSL Cipher List Format,https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html#CIPHER-LIST-FORMAT)
167
+ - See the L(OpenSSL Cipher List Format,https://docs.openssl.org/master/man1/openssl-ciphers/#cipher-list-format)
168
168
  for more details.
169
169
  - The available ciphers is dependent on the Python and OpenSSL/LibreSSL versions
170
170
  type: list
@@ -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
  """
@@ -900,7 +903,7 @@ class StrategyBase:
900
903
  display.warning("%s task does not support when conditional" % task_name)
901
904
 
902
905
  def _execute_meta(self, task: Task, play_context, iterator, target_host: Host):
903
- task.resolved_action = 'ansible.builtin.meta' # _post_validate_args is never called for meta actions, so resolved_action hasn't been set
906
+ task._resolved_action = 'ansible.builtin.meta' # _post_validate_args is never called for meta actions, so resolved_action hasn't been set
904
907
 
905
908
  # meta tasks store their args in the _raw_params field of args,
906
909
  # since they do not use k=v pairs, so get that
@@ -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
@@ -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: