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
@@ -19,7 +19,9 @@ from ansible.errors import (
19
19
  AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleTaskError,
20
20
  AnsibleValueOmittedError,
21
21
  )
22
- from ansible.executor.task_result import _RawTaskResult
22
+
23
+ from ansible._internal import _display_utils
24
+ from ansible.executor.task_result import _RawTaskResult, _SUB_PRESERVE
23
25
  from ansible._internal._datatag import _utils
24
26
  from ansible.module_utils._internal import _messages
25
27
  from ansible.module_utils.datatag import native_type_name, deprecator_from_collection_name
@@ -27,7 +29,6 @@ from ansible._internal._datatag._tags import TrustedAsTemplate
27
29
  from ansible.module_utils.parsing.convert_bool import boolean
28
30
  from ansible.module_utils.common.text.converters import to_text, to_native
29
31
  from ansible.module_utils.connection import write_to_stream
30
- from ansible.module_utils.six import string_types
31
32
  from ansible.playbook.task import Task
32
33
  from ansible.plugins import get_plugin_class
33
34
  from ansible.plugins.loader import become_loader, cliconf_loader, connection_loader, httpapi_loader, netconf_loader, terminal_loader
@@ -35,7 +36,7 @@ from ansible._internal._templating._jinja_plugins import _invoke_lookup, _Direct
35
36
  from ansible._internal._templating._engine import TemplateEngine
36
37
  from ansible.template import Templar
37
38
  from ansible.utils.collection_loader import AnsibleCollectionConfig
38
- from ansible.utils.display import Display, _DeferredWarningContext
39
+ from ansible.utils.display import Display
39
40
  from ansible.utils.vars import combine_vars
40
41
  from ansible.vars.clean import namespace_facts, clean_facts
41
42
  from ansible.vars.manager import _deprecate_top_level_fact
@@ -48,6 +49,7 @@ display = Display()
48
49
 
49
50
 
50
51
  RETURN_VARS = [x for x in C.MAGIC_VARIABLE_MAPPING.items() if 'become' not in x and '_pass' not in x]
52
+ _INJECT_FACTS, _INJECT_FACTS_ORIGIN = C.config.get_config_value_and_origin('INJECT_FACTS_AS_VARS')
51
53
 
52
54
  __all__ = ['TaskExecutor']
53
55
 
@@ -340,7 +342,7 @@ class TaskExecutor:
340
342
  })
341
343
 
342
344
  # if plugin is loaded, get resolved name, otherwise leave original task connection
343
- if self._connection and not isinstance(self._connection, string_types):
345
+ if self._connection and not isinstance(self._connection, str):
344
346
  task_fields['connection'] = getattr(self._connection, 'ansible_name')
345
347
 
346
348
  tr = _RawTaskResult(
@@ -416,7 +418,7 @@ class TaskExecutor:
416
418
  def _execute(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
417
419
  result: dict[str, t.Any]
418
420
 
419
- with _DeferredWarningContext(variables=variables) as warning_ctx:
421
+ with _display_utils.DeferredWarningContext(variables=variables) as warning_ctx:
420
422
  try:
421
423
  # DTFIX-FUTURE: improve error handling to prioritize the earliest exception, turning the remaining ones into warnings
422
424
  result = self._execute_internal(templar, variables)
@@ -431,7 +433,7 @@ class TaskExecutor:
431
433
 
432
434
  self._task.update_result_no_log(templar, result)
433
435
 
434
- # The warnings/deprecations in the result have already been captured in the _DeferredWarningContext by _apply_task_result_compat.
436
+ # The warnings/deprecations in the result have already been captured in the DeferredWarningContext by _apply_task_result_compat.
435
437
  # The captured warnings/deprecations are a superset of the ones from the result, and may have been converted from a dict to a dataclass.
436
438
  # These are then used to supersede the entries in the result.
437
439
 
@@ -664,8 +666,11 @@ class TaskExecutor:
664
666
  # TODO: cleaning of facts should eventually become part of taskresults instead of vars
665
667
  af = result['ansible_facts']
666
668
  vars_copy['ansible_facts'] = combine_vars(vars_copy.get('ansible_facts', {}), namespace_facts(af))
667
- if C.INJECT_FACTS_AS_VARS:
668
- cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
669
+ if _INJECT_FACTS:
670
+ if _INJECT_FACTS_ORIGIN == 'default':
671
+ cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
672
+ else:
673
+ cleaned_toplevel = clean_facts(af)
669
674
  vars_copy.update(cleaned_toplevel)
670
675
 
671
676
  # set the failed property if it was missing.
@@ -759,9 +764,13 @@ class TaskExecutor:
759
764
  # TODO: cleaning of facts should eventually become part of taskresults instead of vars
760
765
  af = result['ansible_facts']
761
766
  variables['ansible_facts'] = combine_vars(variables.get('ansible_facts', {}), namespace_facts(af))
762
- if C.INJECT_FACTS_AS_VARS:
763
- # DTFIX-FUTURE: why is this happening twice, esp since we're post-fork and these will be discarded?
764
- cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
767
+ if _INJECT_FACTS:
768
+ if _INJECT_FACTS_ORIGIN == 'default':
769
+ # This happens x2 due to loops and being able to use values in subsequent iterations
770
+ # these copies are later discared in favor of 'total/final' one on loop end.
771
+ cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
772
+ else:
773
+ cleaned_toplevel = clean_facts(af)
765
774
  variables.update(cleaned_toplevel)
766
775
 
767
776
  # save the notification target in the result, if it was specified, as
@@ -774,21 +783,25 @@ class TaskExecutor:
774
783
  # on the results side without having to do any further templating
775
784
  # also now add connection vars results when delegating
776
785
  if self._task.delegate_to:
777
- result["_ansible_delegated_vars"] = {'ansible_delegated_host': self._task.delegate_to}
778
- for k in plugin_vars:
779
- result["_ansible_delegated_vars"][k] = cvars.get(k)
786
+ result["_ansible_delegated_vars"] = {
787
+ "ansible_delegated_host": self._task.delegate_to,
788
+ "ansible_connection": current_connection,
789
+ }
780
790
 
781
791
  # note: here for callbacks that rely on this info to display delegation
782
- for requireshed in ('ansible_host', 'ansible_port', 'ansible_user', 'ansible_connection'):
783
- if requireshed not in result["_ansible_delegated_vars"] and requireshed in cvars:
784
- result["_ansible_delegated_vars"][requireshed] = cvars.get(requireshed)
792
+ for k in plugin_vars:
793
+ if k not in _SUB_PRESERVE["_ansible_delegated_vars"]:
794
+ continue
795
+
796
+ for o in C.config.get_plugin_options_from_var("connection", current_connection, k):
797
+ result["_ansible_delegated_vars"][k] = self._connection.get_option(o)
785
798
 
786
799
  # and return
787
800
  display.debug("attempt loop complete, returning result")
788
801
  return result
789
802
 
790
803
  @staticmethod
791
- def _apply_task_result_compat(result: dict[str, t.Any], warning_ctx: _DeferredWarningContext) -> None:
804
+ def _apply_task_result_compat(result: dict[str, t.Any], warning_ctx: _display_utils.DeferredWarningContext) -> None:
792
805
  """Apply backward-compatibility mutations to the supplied task result."""
793
806
  if warnings := result.get('warnings'):
794
807
  if isinstance(warnings, list):
@@ -956,9 +969,6 @@ class TaskExecutor:
956
969
 
957
970
  self._play_context.connection = current_connection
958
971
 
959
- # TODO: play context has logic to update the connection for 'smart'
960
- # (default value, will chose between ssh and paramiko) and 'persistent'
961
- # (really paramiko), eventually this should move to task object itself.
962
972
  conn_type = self._play_context.connection
963
973
 
964
974
  connection, plugin_load_context = self._shared_loader_obj.connection_loader.get_with_context(
@@ -1213,7 +1223,7 @@ def start_connection(play_context, options, task_uuid):
1213
1223
  )
1214
1224
 
1215
1225
  write_to_stream(p.stdin, options)
1216
- write_to_stream(p.stdin, play_context.serialize())
1226
+ write_to_stream(p.stdin, play_context.dump_attrs())
1217
1227
 
1218
1228
  (stdout, stderr) = p.communicate()
1219
1229
 
@@ -58,8 +58,6 @@ STDERR_FILENO = 2
58
58
 
59
59
  display = Display()
60
60
 
61
- _T = t.TypeVar('_T')
62
-
63
61
 
64
62
  @dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
65
63
  class CallbackSend:
@@ -413,7 +411,7 @@ class TaskQueueManager:
413
411
  return defunct
414
412
 
415
413
  @staticmethod
416
- def _first_arg_of_type(value_type: t.Type[_T], args: t.Sequence) -> _T | None:
414
+ def _first_arg_of_type[T](value_type: t.Type[T], args: t.Sequence) -> T | None:
417
415
  return next((arg for arg in args if isinstance(arg, value_type)), None)
418
416
 
419
417
  @lock_decorator(attr='_callback_lock')
ansible/galaxy/api.py CHANGED
@@ -25,7 +25,6 @@ from ansible.errors import AnsibleError
25
25
  from ansible.galaxy.user_agent import user_agent
26
26
  from ansible.module_utils.api import retry_with_delays_and_condition
27
27
  from ansible.module_utils.api import generate_jittered_backoff
28
- from ansible.module_utils.six import string_types
29
28
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
30
29
  from ansible.module_utils.urls import open_url, prepare_multipart
31
30
  from ansible.utils.display import Display
@@ -114,13 +113,7 @@ def g_connect(versions):
114
113
  # url + '/api/' appended.
115
114
  self.api_server = n_url
116
115
 
117
- # Default to only supporting v1, if only v1 is returned we also assume that v2 is available even though
118
- # it isn't returned in the available_versions dict.
119
- available_versions = data.get('available_versions', {u'v1': u'v1/'})
120
- if list(available_versions.keys()) == [u'v1']:
121
- available_versions[u'v2'] = u'v2/'
122
-
123
- self._available_api_versions = available_versions
116
+ self._available_api_versions = available_versions = data['available_versions']
124
117
  display.vvvv("Found API version '%s' with Galaxy server %s (%s)"
125
118
  % (', '.join(available_versions.keys()), self.name, self.api_server))
126
119
 
@@ -132,15 +125,6 @@ def g_connect(versions):
132
125
  % (method.__name__, ", ".join(versions), ", ".join(available_versions),
133
126
  self.name, self.api_server))
134
127
 
135
- # Warn only when we know we are talking to a collections API
136
- if common_versions == {'v2'}:
137
- display.deprecated(
138
- 'The v2 Ansible Galaxy API is deprecated and no longer supported. '
139
- 'Ensure that you have configured the ansible-galaxy CLI to utilize an '
140
- 'updated and supported version of Ansible Galaxy.',
141
- version='2.20',
142
- )
143
-
144
128
  return method(self, *args, **kwargs)
145
129
  return wrapped
146
130
  return decorator
@@ -214,11 +198,7 @@ class GalaxyError(AnsibleError):
214
198
  err_info = {}
215
199
 
216
200
  url_split = self.url.split('/')
217
- if 'v2' in url_split:
218
- galaxy_msg = err_info.get('message', http_error.reason)
219
- code = err_info.get('code', 'Unknown')
220
- full_error_msg = u"%s (HTTP Code: %d, Message: %s Code: %s)" % (message, self.http_code, galaxy_msg, code)
221
- elif 'v3' in url_split:
201
+ if 'v3' in url_split:
222
202
  errors = err_info.get('errors', [])
223
203
  if not errors:
224
204
  errors = [{}] # Defaults are set below, we just need to make sure 1 error is present.
@@ -340,7 +320,7 @@ class GalaxyAPI:
340
320
  return self._priority > other_galaxy_api._priority
341
321
 
342
322
  @property # type: ignore[misc] # https://github.com/python/mypy/issues/1362
343
- @g_connect(['v1', 'v2', 'v3'])
323
+ @g_connect(['v1', 'v3'])
344
324
  def available_api_versions(self):
345
325
  # Calling g_connect will populate self._available_api_versions
346
326
  return self._available_api_versions
@@ -595,11 +575,11 @@ class GalaxyAPI:
595
575
  page_size = kwargs.get('page_size', None)
596
576
  author = kwargs.get('author', None)
597
577
 
598
- if tags and isinstance(tags, string_types):
578
+ if tags and isinstance(tags, str):
599
579
  tags = tags.split(',')
600
580
  search_url += '&tags_autocomplete=' + '+'.join(tags)
601
581
 
602
- if platforms and isinstance(platforms, string_types):
582
+ if platforms and isinstance(platforms, str):
603
583
  platforms = platforms.split(',')
604
584
  search_url += '&platforms_autocomplete=' + '+'.join(platforms)
605
585
 
@@ -645,7 +625,7 @@ class GalaxyAPI:
645
625
 
646
626
  # Collection APIs #
647
627
 
648
- @g_connect(['v2', 'v3'])
628
+ @g_connect(['v3'])
649
629
  def publish_collection(self, collection_path):
650
630
  """
651
631
  Publishes a collection to a Galaxy server and returns the import task URI.
@@ -680,19 +660,15 @@ class GalaxyAPI:
680
660
  'Content-length': len(b_form_data),
681
661
  }
682
662
 
683
- if 'v3' in self.available_api_versions:
684
- n_url = _urljoin(self.api_server, self.available_api_versions['v3'], 'artifacts', 'collections') + '/'
685
- else:
686
- n_url = _urljoin(self.api_server, self.available_api_versions['v2'], 'collections') + '/'
687
-
663
+ n_url = _urljoin(self.api_server, self.available_api_versions['v3'], 'artifacts', 'collections') + '/'
688
664
  resp = self._call_galaxy(n_url, args=b_form_data, headers=headers, method='POST', auth_required=True,
689
665
  error_context_msg='Error when publishing collection to %s (%s)'
690
666
  % (self.name, self.api_server))
691
667
 
692
- return resp['task']
668
+ return urljoin(self.api_server, resp['task'])
693
669
 
694
- @g_connect(['v2', 'v3'])
695
- def wait_import_task(self, task_id, timeout=0):
670
+ @g_connect(['v3'])
671
+ def wait_import_task(self, task_url, timeout=0):
696
672
  """
697
673
  Waits until the import process on the Galaxy server has completed or the timeout is reached.
698
674
 
@@ -703,22 +679,14 @@ class GalaxyAPI:
703
679
  state = 'waiting'
704
680
  data = None
705
681
 
706
- # Construct the appropriate URL per version
707
- if 'v3' in self.available_api_versions:
708
- full_url = _urljoin(self.api_server, self.available_api_versions['v3'],
709
- 'imports/collections', task_id, '/')
710
- else:
711
- full_url = _urljoin(self.api_server, self.available_api_versions['v2'],
712
- 'collection-imports', task_id, '/')
713
-
714
- display.display("Waiting until Galaxy import task %s has completed" % full_url)
682
+ display.display("Waiting until Galaxy import task %s has completed" % task_url)
715
683
  start = time.time()
716
684
  wait = C.GALAXY_COLLECTION_IMPORT_POLL_INTERVAL
717
685
 
718
686
  while timeout == 0 or (time.time() - start) < timeout:
719
687
  try:
720
- data = self._call_galaxy(full_url, method='GET', auth_required=True,
721
- error_context_msg='Error when getting import task results at %s' % full_url)
688
+ data = self._call_galaxy(task_url, method='GET', auth_required=True,
689
+ error_context_msg='Error when getting import task results at %s' % task_url)
722
690
  except GalaxyError as e:
723
691
  if e.http_code != 404:
724
692
  raise
@@ -740,7 +708,7 @@ class GalaxyAPI:
740
708
  wait = min(30, wait * C.GALAXY_COLLECTION_IMPORT_POLL_FACTOR)
741
709
  if state == 'waiting':
742
710
  raise AnsibleError("Timeout while waiting for the Galaxy import process to finish, check progress at '%s'"
743
- % to_native(full_url))
711
+ % to_native(task_url))
744
712
 
745
713
  for message in data.get('messages', []):
746
714
  level = message['level']
@@ -754,10 +722,10 @@ class GalaxyAPI:
754
722
  if state == 'failed':
755
723
  code = to_native(data['error'].get('code', 'UNKNOWN'))
756
724
  description = to_native(
757
- data['error'].get('description', "Unknown error, see %s for more details" % full_url))
725
+ data['error'].get('description', "Unknown error, see %s for more details" % task_url))
758
726
  raise AnsibleError("Galaxy import process failed: %s (Code: %s)" % (description, code))
759
727
 
760
- @g_connect(['v2', 'v3'])
728
+ @g_connect(['v3'])
761
729
  def get_collection_metadata(self, namespace, name):
762
730
  """
763
731
  Gets the collection information from the Galaxy server about a specific Collection.
@@ -766,18 +734,11 @@ class GalaxyAPI:
766
734
  :param name: The collection name.
767
735
  return: CollectionMetadata about the collection.
768
736
  """
769
- if 'v3' in self.available_api_versions:
770
- api_path = self.available_api_versions['v3']
771
- field_map = [
772
- ('created_str', 'created_at'),
773
- ('modified_str', 'updated_at'),
774
- ]
775
- else:
776
- api_path = self.available_api_versions['v2']
777
- field_map = [
778
- ('created_str', 'created'),
779
- ('modified_str', 'modified'),
780
- ]
737
+ api_path = self.available_api_versions['v3']
738
+ field_map = [
739
+ ('created_str', 'created_at'),
740
+ ('modified_str', 'updated_at'),
741
+ ]
781
742
 
782
743
  info_url = _urljoin(self.api_server, api_path, 'collections', namespace, name, '/')
783
744
  error_context_msg = 'Error when getting the collection info for %s.%s from %s (%s)' \
@@ -790,7 +751,7 @@ class GalaxyAPI:
790
751
 
791
752
  return CollectionMetadata(namespace, name, **metadata)
792
753
 
793
- @g_connect(['v2', 'v3'])
754
+ @g_connect(['v3'])
794
755
  def get_collection_version_metadata(self, namespace, name, version):
795
756
  """
796
757
  Gets the collection information from the Galaxy server about a specific Collection version.
@@ -800,7 +761,7 @@ class GalaxyAPI:
800
761
  :param version: Version of the collection to get the information for.
801
762
  :return: CollectionVersionMetadata about the collection at the version requested.
802
763
  """
803
- api_path = self.available_api_versions.get('v3', self.available_api_versions.get('v2'))
764
+ api_path = self.available_api_versions['v3']
804
765
  url_paths = [self.api_server, api_path, 'collections', namespace, name, 'versions', version, '/']
805
766
 
806
767
  n_collection_url = _urljoin(*url_paths)
@@ -824,7 +785,7 @@ class GalaxyAPI:
824
785
  download_url, data['artifact']['sha256'],
825
786
  data['metadata']['dependencies'], data['href'], signatures)
826
787
 
827
- @g_connect(['v2', 'v3'])
788
+ @g_connect(['v3'])
828
789
  def get_collection_versions(self, namespace, name):
829
790
  """
830
791
  Gets a list of available versions for a collection on a Galaxy server.
@@ -833,17 +794,10 @@ class GalaxyAPI:
833
794
  :param name: The collection name.
834
795
  :return: A list of versions that are available.
835
796
  """
836
- relative_link = False
837
- if 'v3' in self.available_api_versions:
838
- api_path = self.available_api_versions['v3']
839
- pagination_path = ['links', 'next']
840
- relative_link = True # AH pagination results are relative an not an absolute URI.
841
- else:
842
- api_path = self.available_api_versions['v2']
843
- pagination_path = ['next']
797
+ api_path = self.available_api_versions['v3']
798
+ pagination_path = ['links', 'next']
844
799
 
845
- page_size_name = 'limit' if 'v3' in self.available_api_versions else 'page_size'
846
- versions_url = _urljoin(self.api_server, api_path, 'collections', namespace, name, 'versions', '/?%s=%d' % (page_size_name, COLLECTION_PAGE_SIZE))
800
+ versions_url = _urljoin(self.api_server, api_path, 'collections', namespace, name, 'versions', '/?limit=%d' % COLLECTION_PAGE_SIZE)
847
801
  versions_url_info = urlparse(versions_url)
848
802
  cache_key = versions_url_info.path
849
803
 
@@ -898,11 +852,10 @@ class GalaxyAPI:
898
852
 
899
853
  if not next_link:
900
854
  break
901
- elif relative_link:
902
- next_link_info = urlparse(next_link)
903
- if not next_link_info.scheme and not next_link_info.path.startswith('/'):
904
- raise AnsibleError(f'Invalid non absolute pagination link: {next_link}')
905
- next_link = urljoin(self.api_server, next_link)
855
+ next_link_info = urlparse(next_link)
856
+ if not next_link_info.scheme and not next_link_info.path.startswith('/'):
857
+ raise AnsibleError(f'Invalid non absolute pagination link: {next_link}')
858
+ next_link = urljoin(self.api_server, next_link)
906
859
 
907
860
  data = self._call_galaxy(to_native(next_link, errors='surrogate_or_strict'),
908
861
  error_context_msg=error_context_msg, cache=True, cache_key=cache_key)
@@ -910,7 +863,7 @@ class GalaxyAPI:
910
863
 
911
864
  return versions
912
865
 
913
- @g_connect(['v2', 'v3'])
866
+ @g_connect(['v3'])
914
867
  def get_collection_signatures(self, namespace, name, version):
915
868
  """
916
869
  Gets the collection signatures from the Galaxy server about a specific Collection version.
@@ -920,7 +873,7 @@ class GalaxyAPI:
920
873
  :param version: Version of the collection to get the information for.
921
874
  :return: A list of signature strings.
922
875
  """
923
- api_path = self.available_api_versions.get('v3', self.available_api_versions.get('v2'))
876
+ api_path = self.available_api_versions['v3']
924
877
  url_paths = [self.api_server, api_path, 'collections', namespace, name, 'versions', version, '/']
925
878
 
926
879
  n_collection_url = _urljoin(*url_paths)
@@ -339,12 +339,12 @@ def verify_local_collection(local_collection, remote_collection, artifacts_manag
339
339
  ]
340
340
 
341
341
  # Find any paths not in the FILES.json
342
- for root, dirs, files in os.walk(b_collection_path):
343
- for name in files:
342
+ for root, dirs, filenames in os.walk(b_collection_path):
343
+ for name in filenames:
344
344
  full_path = os.path.join(root, name)
345
345
  path = to_text(full_path[len(b_collection_path) + 1::], errors='surrogate_or_strict')
346
346
  if any(fnmatch.fnmatch(full_path, b_pattern) for b_pattern in b_ignore_patterns):
347
- display.v("Ignoring verification for %s" % full_path)
347
+ display.v("Ignoring verification for %s" % to_text(full_path))
348
348
  continue
349
349
 
350
350
  if full_path not in collection_files:
@@ -623,24 +623,11 @@ def publish_collection(collection_path, api, wait, timeout):
623
623
  import_uri = api.publish_collection(collection_path)
624
624
 
625
625
  if wait:
626
- # Galaxy returns a url fragment which differs between v2 and v3. The second to last entry is
627
- # always the task_id, though.
628
- # v2: {"task": "https://galaxy-dev.ansible.com/api/v2/collection-imports/35573/"}
629
- # v3: {"task": "/api/automation-hub/v3/imports/collections/838d1308-a8f4-402c-95cb-7823f3806cd8/"}
630
- task_id = None
631
- for path_segment in reversed(import_uri.split('/')):
632
- if path_segment:
633
- task_id = path_segment
634
- break
635
-
636
- if not task_id:
637
- raise AnsibleError("Publishing the collection did not return valid task info. Cannot wait for task status. Returned task info: '%s'" % import_uri)
638
-
639
626
  with _display_progress(
640
627
  "Collection has been published to the Galaxy server "
641
628
  "{api.name!s} {api.api_server!s}".format(api=api),
642
629
  ):
643
- api.wait_import_task(task_id, timeout)
630
+ api.wait_import_task(import_uri, timeout)
644
631
  display.display("Collection has been successfully published and imported to the Galaxy server %s %s"
645
632
  % (api.name, api.api_server))
646
633
  else:
@@ -26,9 +26,6 @@ if t.TYPE_CHECKING:
26
26
  '_ComputedReqKindsMixin',
27
27
  )
28
28
 
29
- import ansible
30
- import ansible.release
31
-
32
29
  from ansible.errors import AnsibleError, AnsibleAssertionError
33
30
  from ansible.galaxy.api import GalaxyAPI
34
31
  from ansible.galaxy.collection import HAS_PACKAGING, PkgReq
@@ -42,7 +39,6 @@ _ALLOW_CONCRETE_POINTER_IN_SOURCE = False # NOTE: This is a feature flag
42
39
  _GALAXY_YAML = b'galaxy.yml'
43
40
  _MANIFEST_JSON = b'MANIFEST.json'
44
41
  _SOURCE_METADATA_FILE = b'GALAXY.yml'
45
- _ANSIBLE_PACKAGE_PATH = pathlib.Path(ansible.__file__).parent
46
42
 
47
43
  display = Display()
48
44
 
@@ -229,12 +225,6 @@ class _ComputedReqKindsMixin:
229
225
  dir_path = dir_path.rstrip(to_bytes(os.path.sep))
230
226
  if not _is_collection_dir(dir_path):
231
227
  dir_pathlib = pathlib.Path(to_text(dir_path))
232
-
233
- # special handling for bundled collections without manifests, e.g., ansible._protomatter
234
- if dir_pathlib.is_relative_to(_ANSIBLE_PACKAGE_PATH):
235
- req_name = f'{dir_pathlib.parent.name}.{dir_pathlib.name}'
236
- return cls(req_name, ansible.release.__version__, dir_path, 'dir', None)
237
-
238
228
  display.warning(
239
229
  u"Collection at '{path!s}' does not have a {manifest_json!s} "
240
230
  u'file, nor has it {galaxy_yml!s}: cannot detect version.'.
@@ -24,7 +24,6 @@ from ansible.galaxy.dependency_resolution.versioning import (
24
24
  is_pre_release,
25
25
  meets_requirements,
26
26
  )
27
- from ansible.module_utils.six import string_types
28
27
  from ansible.utils.version import SemanticVersion, LooseVersion
29
28
 
30
29
  try:
@@ -278,7 +277,7 @@ class CollectionDependencyProviderBase(AbstractProvider):
278
277
  # NOTE: Another known mistake is setting a minor part of the SemVer notation
279
278
  # NOTE: skipping the "patch" bit like "1.0" which is assumed non-compliant even
280
279
  # NOTE: after the conversion to string.
281
- if not isinstance(version, string_types):
280
+ if not isinstance(version, str):
282
281
  raise ValueError(version_err)
283
282
  elif version != '*':
284
283
  try:
ansible/galaxy/role.py CHANGED
@@ -23,7 +23,6 @@ from __future__ import annotations
23
23
 
24
24
  import errno
25
25
  import datetime
26
- import functools
27
26
  import os
28
27
  import tarfile
29
28
  import tempfile
@@ -46,32 +45,6 @@ from ansible.utils.path import is_subpath, unfrackpath
46
45
  display = Display()
47
46
 
48
47
 
49
- @functools.cache
50
- def _check_working_data_filter() -> bool:
51
- """
52
- Check if tarfile.data_filter implementation is working
53
- for the current Python version or not
54
- """
55
-
56
- # Implemented the following code to circumvent broken implementation of data_filter
57
- # in tarfile. See for more information - https://github.com/python/cpython/issues/107845
58
- # deprecated: description='probing broken data filter implementation' python_version='3.11'
59
- ret = False
60
- if hasattr(tarfile, 'data_filter'):
61
- # We explicitly check if tarfile.data_filter is broken or not
62
- ti = tarfile.TarInfo('docs/README.md')
63
- ti.type = tarfile.SYMTYPE
64
- ti.linkname = '../README.md'
65
-
66
- try:
67
- tarfile.data_filter(ti, '/foo')
68
- except tarfile.LinkOutsideDestinationError:
69
- pass
70
- else:
71
- ret = True
72
- return ret
73
-
74
-
75
48
  class GalaxyRole(object):
76
49
 
77
50
  SUPPORTED_SCMS = set(['git', 'hg'])
@@ -418,12 +391,7 @@ class GalaxyRole(object):
418
391
  relative_path = os.path.join(*full_path.replace(relative_path_dir, "", 1).split(os.sep))
419
392
  setattr(member, attr, relative_path)
420
393
 
421
- if _check_working_data_filter():
422
- # deprecated: description='extract fallback without filter' python_version='3.11'
423
- role_tar_file.extract(member, to_native(self.path), filter='data') # type: ignore[call-arg]
424
- else:
425
- # Remove along with manual path filter once Python 3.12 is minimum supported version
426
- role_tar_file.extract(member, to_native(self.path))
394
+ role_tar_file.extract(member, to_native(self.path), filter='data')
427
395
 
428
396
  # write out the install info file for later use
429
397
  self._write_galaxy_install_info()
@@ -33,7 +33,6 @@ from ansible._internal import _json, _wrapt
33
33
  from ansible._internal._json import EncryptedStringBehavior
34
34
  from ansible.errors import AnsibleError, AnsibleOptionsError
35
35
  from ansible.inventory.data import InventoryData
36
- from ansible.module_utils.six import string_types
37
36
  from ansible.module_utils.common.text.converters import to_bytes, to_text
38
37
  from ansible.parsing.utils.addresses import parse_address
39
38
  from ansible.plugins.loader import inventory_loader
@@ -112,7 +111,7 @@ def split_host_pattern(pattern):
112
111
  results = (split_host_pattern(p) for p in pattern)
113
112
  # flatten the results
114
113
  return list(itertools.chain.from_iterable(results))
115
- elif not isinstance(pattern, string_types):
114
+ elif not isinstance(pattern, str):
116
115
  pattern = to_text(pattern, errors='surrogate_or_strict')
117
116
 
118
117
  # If it's got commas in it, we'll treat it as a straightforward
@@ -162,7 +161,7 @@ class InventoryManager(object):
162
161
  # the inventory dirs, files, script paths or lists of hosts
163
162
  if sources is None:
164
163
  self._sources = []
165
- elif isinstance(sources, string_types):
164
+ elif isinstance(sources, str):
166
165
  self._sources = [sources]
167
166
  else:
168
167
  self._sources = sources
ansible/keyword_desc.yml CHANGED
@@ -1,6 +1,3 @@
1
- accelerate: "*DEPRECATED*, set to True to use accelerate connection plugin."
2
- accelerate_ipv6: "*DEPRECATED*, set to True to force accelerate plugin to use ipv6 for its connection."
3
- accelerate_port: "*DEPRECATED*, set to override default port use for accelerate connection."
4
1
  action: "The 'action' to execute for a task, it normally translates into a C(module) or action plugin."
5
2
  args: "A secondary way to add arguments into a task. Takes a dictionary in which keys map to options and values."
6
3
  always: List of tasks, in a block, that execute no matter if there is an error in the block or not.
@@ -500,17 +500,9 @@ class AnsibleDatatagBase(AnsibleSerializableDataclass, metaclass=abc.ABCMeta):
500
500
  _known_tag_type_map: t.Dict[str, t.Type[AnsibleDatatagBase]] = {}
501
501
  _known_tag_types: t.Set[t.Type[AnsibleDatatagBase]] = set()
502
502
 
503
- if sys.version_info >= (3, 9):
504
- # Include the key and value types in the type hints on Python 3.9 and later.
505
- # Earlier versions do not support subscriptable dict.
506
- # deprecated: description='always use subscriptable dict' python_version='3.8'
507
- class _AnsibleTagsMapping(dict[type[_TAnsibleDatatagBase], _TAnsibleDatatagBase]):
508
- __slots__ = _NO_INSTANCE_STORAGE
509
503
 
510
- else:
511
-
512
- class _AnsibleTagsMapping(dict):
513
- __slots__ = _NO_INSTANCE_STORAGE
504
+ class _AnsibleTagsMapping(dict[type[_TAnsibleDatatagBase], _TAnsibleDatatagBase]):
505
+ __slots__ = _NO_INSTANCE_STORAGE
514
506
 
515
507
 
516
508
  class _EmptyROInternalTagsMapping(dict):