ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. ansible/_internal/__init__.py +1 -1
  2. ansible/_internal/_collection_proxy.py +1 -1
  3. ansible/_internal/_errors/_alarm_timeout.py +66 -0
  4. ansible/_internal/_errors/_captured.py +25 -30
  5. ansible/_internal/_errors/_error_factory.py +89 -0
  6. ansible/_internal/_errors/_error_utils.py +240 -0
  7. ansible/_internal/_errors/_task_timeout.py +28 -0
  8. ansible/_internal/_event_formatting.py +127 -0
  9. ansible/_internal/_json/__init__.py +5 -5
  10. ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
  11. ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
  12. ansible/_internal/_json/_profiles/_legacy.py +3 -11
  13. ansible/_internal/_ssh/__init__.py +0 -0
  14. ansible/_internal/_ssh/_agent_launch.py +91 -0
  15. ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
  16. ansible/_internal/_templating/__init__.py +5 -3
  17. ansible/_internal/_templating/_datatag.py +2 -1
  18. ansible/_internal/_templating/_engine.py +3 -4
  19. ansible/_internal/_templating/_jinja_bits.py +21 -16
  20. ansible/_internal/_templating/_jinja_common.py +18 -27
  21. ansible/_internal/_templating/_jinja_plugins.py +31 -3
  22. ansible/_internal/_templating/_lazy_containers.py +5 -5
  23. ansible/_internal/_templating/_transform.py +20 -19
  24. ansible/_internal/_templating/_utils.py +1 -1
  25. ansible/_internal/_yaml/_dumper.py +1 -1
  26. ansible/_internal/_yaml/_errors.py +7 -7
  27. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
  28. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
  29. ansible/cli/__init__.py +5 -82
  30. ansible/cli/arguments/option_helpers.py +2 -3
  31. ansible/cli/doc.py +84 -28
  32. ansible/cli/inventory.py +1 -1
  33. ansible/compat/importlib_resources.py +9 -12
  34. ansible/config/base.yml +22 -0
  35. ansible/errors/__init__.py +96 -49
  36. ansible/executor/module_common.py +8 -10
  37. ansible/executor/powershell/async_watchdog.ps1 +2 -2
  38. ansible/executor/powershell/async_wrapper.ps1 +3 -3
  39. ansible/executor/powershell/become_wrapper.ps1 +20 -2
  40. ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
  41. ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
  42. ansible/executor/powershell/exec_wrapper.ps1 +219 -6
  43. ansible/executor/powershell/module_manifest.py +52 -0
  44. ansible/executor/powershell/module_wrapper.ps1 +47 -21
  45. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  46. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  47. ansible/executor/process/worker.py +38 -113
  48. ansible/executor/task_executor.py +26 -61
  49. ansible/executor/task_result.py +2 -4
  50. ansible/galaxy/collection/__init__.py +1 -4
  51. ansible/inventory/manager.py +1 -1
  52. ansible/module_utils/_internal/__init__.py +0 -3
  53. ansible/module_utils/_internal/_ambient_context.py +3 -3
  54. ansible/module_utils/_internal/_ansiballz.py +4 -2
  55. ansible/module_utils/_internal/_datatag/__init__.py +20 -14
  56. ansible/module_utils/_internal/_datatag/_tags.py +2 -2
  57. ansible/module_utils/_internal/_deprecator.py +66 -48
  58. ansible/module_utils/_internal/_errors.py +88 -17
  59. ansible/module_utils/_internal/_event_utils.py +61 -0
  60. ansible/module_utils/_internal/_json/_profiles/__init__.py +21 -4
  61. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
  62. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
  63. ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
  64. ansible/module_utils/{common/messages.py → _internal/_messages.py} +28 -47
  65. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
  66. ansible/module_utils/_internal/_plugin_info.py +1 -1
  67. ansible/module_utils/_internal/_stack.py +22 -0
  68. ansible/module_utils/_internal/_text_utils.py +6 -0
  69. ansible/module_utils/_internal/_traceback.py +11 -8
  70. ansible/module_utils/ansible_release.py +1 -1
  71. ansible/module_utils/basic.py +49 -15
  72. ansible/module_utils/common/arg_spec.py +2 -2
  73. ansible/module_utils/common/collections.py +6 -0
  74. ansible/module_utils/common/json.py +2 -2
  75. ansible/module_utils/common/text/converters.py +3 -3
  76. ansible/module_utils/common/validation.py +1 -1
  77. ansible/module_utils/common/warnings.py +80 -23
  78. ansible/module_utils/common/yaml.py +1 -1
  79. ansible/module_utils/datatag.py +5 -2
  80. ansible/module_utils/facts/system/distribution.py +16 -3
  81. ansible/module_utils/facts/virtual/linux.py +1 -1
  82. ansible/module_utils/service.py +2 -9
  83. ansible/modules/apt_repository.py +7 -29
  84. ansible/modules/async_status.py +13 -11
  85. ansible/modules/async_wrapper.py +5 -5
  86. ansible/modules/dnf5.py +14 -22
  87. ansible/modules/hostname.py +0 -1
  88. ansible/modules/service.py +3 -9
  89. ansible/parsing/ajson.py +3 -5
  90. ansible/parsing/dataloader.py +4 -4
  91. ansible/parsing/mod_args.py +1 -1
  92. ansible/parsing/plugin_docs.py +2 -2
  93. ansible/parsing/utils/yaml.py +3 -3
  94. ansible/parsing/vault/__init__.py +4 -4
  95. ansible/playbook/playbook_include.py +1 -1
  96. ansible/playbook/taggable.py +0 -3
  97. ansible/plugins/__init__.py +0 -25
  98. ansible/plugins/action/__init__.py +8 -31
  99. ansible/plugins/action/add_host.py +1 -1
  100. ansible/plugins/action/assemble.py +8 -16
  101. ansible/plugins/action/async_status.py +7 -2
  102. ansible/plugins/action/copy.py +8 -7
  103. ansible/plugins/action/gather_facts.py +8 -8
  104. ansible/plugins/action/package.py +5 -8
  105. ansible/plugins/action/script.py +8 -15
  106. ansible/plugins/action/service.py +3 -7
  107. ansible/plugins/action/template.py +3 -8
  108. ansible/plugins/action/unarchive.py +5 -15
  109. ansible/plugins/action/uri.py +9 -20
  110. ansible/plugins/callback/__init__.py +4 -6
  111. ansible/plugins/callback/junit.py +4 -2
  112. ansible/plugins/connection/local.py +2 -2
  113. ansible/plugins/connection/ssh.py +17 -9
  114. ansible/plugins/connection/winrm.py +5 -2
  115. ansible/plugins/doc_fragments/constructed.py +2 -2
  116. ansible/plugins/filter/core.py +13 -6
  117. ansible/plugins/filter/encryption.py +4 -4
  118. ansible/plugins/inventory/__init__.py +11 -10
  119. ansible/plugins/inventory/script.py +1 -1
  120. ansible/plugins/list.py +69 -16
  121. ansible/plugins/loader.py +7 -7
  122. ansible/plugins/lookup/csvfile.py +16 -71
  123. ansible/plugins/lookup/first_found.py +2 -1
  124. ansible/plugins/shell/__init__.py +56 -2
  125. ansible/plugins/shell/powershell.py +66 -9
  126. ansible/plugins/shell/sh.py +9 -5
  127. ansible/plugins/test/core.py +21 -15
  128. ansible/plugins/test/finished.yml +1 -1
  129. ansible/plugins/test/uri.py +2 -5
  130. ansible/release.py +1 -1
  131. ansible/template/__init__.py +30 -2
  132. ansible/utils/display.py +103 -128
  133. ansible/utils/hashing.py +0 -1
  134. ansible/utils/listify.py +6 -4
  135. ansible/utils/unsafe_proxy.py +1 -1
  136. ansible/vars/hostvars.py +1 -1
  137. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/METADATA +1 -1
  138. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/RECORD +162 -151
  139. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/WHEEL +1 -1
  140. ansible_test/_data/completion/docker.txt +3 -3
  141. ansible_test/_data/completion/remote.txt +1 -0
  142. ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
  143. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  144. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  145. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  146. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  147. ansible_test/_internal/util.py +20 -0
  148. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
  149. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  150. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
  151. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  152. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  153. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
  154. ansible_test/_util/target/setup/bootstrap.sh +31 -0
  155. ansible/_internal/_errors/_utils.py +0 -310
  156. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/entry_points.txt +0 -0
  157. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/COPYING +0 -0
  158. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  159. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  160. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  161. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  162. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  163. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b5.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,7 @@
16
16
  # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
17
17
  from __future__ import annotations
18
18
 
19
- from ansible.errors import AnsibleAction, AnsibleActionFail
19
+ from ansible.errors import AnsibleActionFail
20
20
  from ansible.executor.module_common import _apply_action_arg_defaults
21
21
  from ansible.module_utils.facts.system.pkg_mgr import PKG_MGRS
22
22
  from ansible.plugins.action import ActionBase
@@ -38,7 +38,7 @@ class ActionModule(ActionBase):
38
38
  self._supports_check_mode = True
39
39
  self._supports_async = True
40
40
 
41
- result = super(ActionModule, self).run(tmp, task_vars)
41
+ super(ActionModule, self).run(tmp, task_vars)
42
42
 
43
43
  module = self._task.args.get('use', 'auto')
44
44
 
@@ -99,11 +99,8 @@ class ActionModule(ActionBase):
99
99
  module = 'ansible.legacy.' + module
100
100
 
101
101
  display.vvvv("Running %s" % module)
102
- result.update(self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val))
102
+ return self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)
103
103
  else:
104
104
  raise AnsibleActionFail('Could not detect which package manager to use. Try gathering facts or setting the "use" option.')
105
-
106
- except AnsibleAction as e:
107
- result.update(e.result)
108
-
109
- return result
105
+ finally:
106
+ pass # avoid de-dent all on refactor
@@ -21,7 +21,7 @@ import pathlib
21
21
  import re
22
22
  import shlex
23
23
 
24
- from ansible.errors import AnsibleError, AnsibleAction, _AnsibleActionDone, AnsibleActionFail, AnsibleActionSkip
24
+ from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleActionSkip
25
25
  from ansible.executor.powershell import module_manifest as ps_manifest
26
26
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
27
27
  from ansible.plugins.action import ActionBase
@@ -53,7 +53,7 @@ class ActionModule(ActionBase):
53
53
  mutually_exclusive=[['_raw_params', 'cmd']],
54
54
  )
55
55
 
56
- result = super(ActionModule, self).run(tmp, task_vars)
56
+ super(ActionModule, self).run(tmp, task_vars)
57
57
  del tmp # tmp no longer has any effect
58
58
 
59
59
  try:
@@ -105,16 +105,11 @@ class ActionModule(ActionBase):
105
105
  # check mode is supported if 'creates' or 'removes' are provided
106
106
  # the task has already been skipped if a change would not occur
107
107
  if new_module_args['creates'] or new_module_args['removes']:
108
- result['changed'] = True
109
- raise _AnsibleActionDone(result=result)
108
+ return dict(changed=True)
110
109
  # If the script doesn't return changed in the result, it defaults to True,
111
110
  # but since the script may override 'changed', just skip instead of guessing.
112
111
  else:
113
- result['changed'] = False
114
- raise AnsibleActionSkip('Check mode is not supported for this task.', result=result)
115
-
116
- # now we execute script, always assume changed.
117
- result['changed'] = True
112
+ raise AnsibleActionSkip('Check mode is not supported for this task.', result=dict(changed=False))
118
113
 
119
114
  # transfer the file to a remote tmp location
120
115
  tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir,
@@ -168,14 +163,12 @@ class ActionModule(ActionBase):
168
163
  # full manual exec of KEEP_REMOTE_FILES
169
164
  script_cmd = self._connection._shell.build_module_command(env_string='', shebang='#!powershell', cmd='')
170
165
 
171
- result.update(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir))
166
+ # now we execute script, always assume changed.
167
+ result = dict(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir), changed=True)
172
168
 
173
169
  if 'rc' in result and result['rc'] != 0:
174
- raise AnsibleActionFail('non-zero return code')
170
+ raise AnsibleActionFail('non-zero return code', result=result)
175
171
 
176
- except AnsibleAction as e:
177
- result.update(e.result)
172
+ return result
178
173
  finally:
179
174
  self._remove_tmp_path(self._connection._shell.tmpdir)
180
-
181
- return result
@@ -16,7 +16,7 @@
16
16
  # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
17
17
  from __future__ import annotations
18
18
 
19
- from ansible.errors import AnsibleAction, AnsibleActionFail
19
+ from ansible.errors import AnsibleActionFail
20
20
  from ansible.executor.module_common import _apply_action_arg_defaults
21
21
  from ansible.plugins.action import ActionBase
22
22
 
@@ -39,7 +39,7 @@ class ActionModule(ActionBase):
39
39
  self._supports_check_mode = True
40
40
  self._supports_async = True
41
41
 
42
- result = super(ActionModule, self).run(tmp, task_vars)
42
+ super(ActionModule, self).run(tmp, task_vars)
43
43
  del tmp # tmp no longer has any effect
44
44
 
45
45
  module = self._task.args.get('use', 'auto').lower()
@@ -84,14 +84,10 @@ class ActionModule(ActionBase):
84
84
  module = 'ansible.legacy.' + module
85
85
 
86
86
  self._display.vvvv("Running %s" % module)
87
- result.update(self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val))
87
+ return self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)
88
88
  else:
89
89
  raise AnsibleActionFail('Could not detect which service manager to use. Try gathering facts or setting the "use" option.')
90
90
 
91
- except AnsibleAction as e:
92
- result.update(e.result)
93
91
  finally:
94
92
  if not self._task.async_val:
95
93
  self._remove_tmp_path(self._connection._shell.tmpdir)
96
-
97
- return result
@@ -20,7 +20,7 @@ from jinja2.defaults import (
20
20
 
21
21
  from ansible import constants as C
22
22
  from ansible.config.manager import ensure_type
23
- from ansible.errors import AnsibleError, AnsibleAction, AnsibleActionFail
23
+ from ansible.errors import AnsibleError, AnsibleActionFail
24
24
  from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
25
25
  from ansible.module_utils.parsing.convert_bool import boolean
26
26
  from ansible.module_utils.six import string_types
@@ -39,7 +39,7 @@ class ActionModule(ActionBase):
39
39
  if task_vars is None:
40
40
  task_vars = dict()
41
41
 
42
- result = super(ActionModule, self).run(tmp, task_vars)
42
+ super(ActionModule, self).run(tmp, task_vars)
43
43
  del tmp # tmp no longer has any effect
44
44
 
45
45
  # Options type validation
@@ -167,13 +167,8 @@ class ActionModule(ActionBase):
167
167
  loader=self._loader,
168
168
  templar=self._templar,
169
169
  shared_loader_obj=self._shared_loader_obj)
170
- result.update(copy_action.run(task_vars=task_vars))
170
+ return copy_action.run(task_vars=task_vars)
171
171
  finally:
172
172
  shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict'))
173
-
174
- except AnsibleAction as e:
175
- result.update(e.result)
176
173
  finally:
177
174
  self._remove_tmp_path(self._connection._shell.tmpdir)
178
-
179
- return result
@@ -19,8 +19,7 @@ from __future__ import annotations
19
19
 
20
20
  import os
21
21
 
22
- from ansible.errors import AnsibleError, AnsibleAction, AnsibleActionFail, AnsibleActionSkip
23
- from ansible.module_utils.common.text.converters import to_text
22
+ from ansible.errors import AnsibleActionFail, AnsibleActionSkip
24
23
  from ansible.module_utils.parsing.convert_bool import boolean
25
24
  from ansible.plugins.action import ActionBase
26
25
 
@@ -34,7 +33,7 @@ class ActionModule(ActionBase):
34
33
  if task_vars is None:
35
34
  task_vars = dict()
36
35
 
37
- result = super(ActionModule, self).run(tmp, task_vars)
36
+ super(ActionModule, self).run(tmp, task_vars)
38
37
  del tmp # tmp no longer has any effect
39
38
 
40
39
  source = self._task.args.get('src', None)
@@ -68,15 +67,9 @@ class ActionModule(ActionBase):
68
67
  source = os.path.expanduser(source)
69
68
 
70
69
  if not remote_src:
71
- try:
72
- source = self._loader.get_real_file(self._find_needle('files', source), decrypt=decrypt)
73
- except AnsibleError as e:
74
- raise AnsibleActionFail(to_text(e))
70
+ source = self._loader.get_real_file(self._find_needle('files', source), decrypt=decrypt)
75
71
 
76
- try:
77
- remote_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=True)
78
- except AnsibleError as e:
79
- raise AnsibleActionFail(to_text(e))
72
+ remote_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=True)
80
73
 
81
74
  if not remote_stat['exists'] or not remote_stat['isdir']:
82
75
  raise AnsibleActionFail("dest '%s' must be an existing dir" % dest)
@@ -102,9 +95,6 @@ class ActionModule(ActionBase):
102
95
 
103
96
  # execute the unarchive module now, with the updated args (using ansible.legacy prefix to eliminate collections
104
97
  # collisions with local override
105
- result.update(self._execute_module(module_name='ansible.legacy.unarchive', module_args=new_module_args, task_vars=task_vars))
106
- except AnsibleAction as e:
107
- result.update(e.result)
98
+ return self._execute_module(module_name='ansible.legacy.unarchive', module_args=new_module_args, task_vars=task_vars)
108
99
  finally:
109
100
  self._remove_tmp_path(self._connection._shell.tmpdir)
110
- return result
@@ -5,11 +5,10 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ import collections.abc as _c
8
9
  import os
9
10
 
10
- from ansible.errors import AnsibleError, AnsibleAction, _AnsibleActionDone, AnsibleActionFail
11
- from ansible.module_utils.common.text.converters import to_native
12
- from ansible.module_utils.common.collections import Mapping, MutableMapping
11
+ from ansible.errors import AnsibleActionFail
13
12
  from ansible.module_utils.parsing.convert_bool import boolean
14
13
  from ansible.plugins.action import ActionBase
15
14
 
@@ -25,7 +24,7 @@ class ActionModule(ActionBase):
25
24
  if task_vars is None:
26
25
  task_vars = dict()
27
26
 
28
- result = super(ActionModule, self).run(tmp, task_vars)
27
+ super(ActionModule, self).run(tmp, task_vars)
29
28
  del tmp # tmp no longer has any effect
30
29
 
31
30
  body_format = self._task.args.get('body_format', 'raw')
@@ -38,38 +37,31 @@ class ActionModule(ActionBase):
38
37
  # everything is remote, so we just execute the module
39
38
  # without changing any of the module arguments
40
39
  # call with ansible.legacy prefix to prevent collections collisions while allowing local override
41
- raise _AnsibleActionDone(result=self._execute_module(module_name='ansible.legacy.uri',
42
- task_vars=task_vars, wrap_async=self._task.async_val))
40
+ return self._execute_module(module_name='ansible.legacy.uri', task_vars=task_vars, wrap_async=self._task.async_val)
43
41
 
44
42
  kwargs = {}
45
43
 
46
44
  if src:
47
- try:
48
- src = self._find_needle('files', src)
49
- except AnsibleError as e:
50
- raise AnsibleActionFail(to_native(e))
45
+ src = self._find_needle('files', src)
51
46
 
52
47
  tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir, os.path.basename(src))
53
48
  kwargs['src'] = tmp_src
54
49
  self._transfer_file(src, tmp_src)
55
50
  self._fixup_perms2((self._connection._shell.tmpdir, tmp_src))
56
51
  elif body_format == 'form-multipart':
57
- if not isinstance(body, Mapping):
52
+ if not isinstance(body, _c.Mapping):
58
53
  raise AnsibleActionFail(
59
54
  'body must be mapping, cannot be type %s' % body.__class__.__name__
60
55
  )
61
56
  for field, value in body.items():
62
- if not isinstance(value, MutableMapping):
57
+ if not isinstance(value, _c.MutableMapping):
63
58
  continue
64
59
  content = value.get('content')
65
60
  filename = value.get('filename')
66
61
  if not filename or content:
67
62
  continue
68
63
 
69
- try:
70
- filename = self._find_needle('files', filename)
71
- except AnsibleError as e:
72
- raise AnsibleActionFail(to_native(e))
64
+ filename = self._find_needle('files', filename)
73
65
 
74
66
  tmp_src = self._connection._shell.join_path(
75
67
  self._connection._shell.tmpdir,
@@ -83,10 +75,7 @@ class ActionModule(ActionBase):
83
75
  new_module_args = self._task.args | kwargs
84
76
 
85
77
  # call with ansible.legacy prefix to prevent collections collisions while allowing local override
86
- result.update(self._execute_module('ansible.legacy.uri', module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val))
87
- except AnsibleAction as e:
88
- result.update(e.result)
78
+ return self._execute_module('ansible.legacy.uri', module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)
89
79
  finally:
90
80
  if not self._task.async_val:
91
81
  self._remove_tmp_path(self._connection._shell.tmpdir)
92
- return result
@@ -280,8 +280,6 @@ class CallbackBase(AnsiblePlugin):
280
280
  # that want to further modify the result, or use custom serialization
281
281
  return abridged_result
282
282
 
283
- # DTFIX-RELEASE: Switch to stock json/yaml serializers here? We should always have a transformed plain-types result.
284
-
285
283
  if result_format == 'json':
286
284
  return json.dumps(abridged_result, cls=_fallback_to_str.Encoder, indent=indent, ensure_ascii=False, sort_keys=sort_keys)
287
285
 
@@ -310,7 +308,7 @@ class CallbackBase(AnsiblePlugin):
310
308
  ' ' * (indent or 4)
311
309
  )
312
310
 
313
- # DTFIX-RELEASE: add test to exercise this case
311
+ # DTFIX5: add test to exercise this case
314
312
  raise ValueError(f'Unsupported result_format {result_format!r}.')
315
313
 
316
314
  def _handle_warnings(self, res: _c.MutableMapping[str, t.Any]) -> None:
@@ -318,7 +316,7 @@ class CallbackBase(AnsiblePlugin):
318
316
  if res.pop('warnings', None) and self._current_task_result and (warnings := self._current_task_result.warnings):
319
317
  # display warnings from the current task result if `warnings` was not removed from `result` (or made falsey)
320
318
  for warning in warnings:
321
- # DTFIX-RELEASE: what to do about propagating wrap_text from the original display.warning call?
319
+ # DTFIX3: what to do about propagating wrap_text from the original display.warning call?
322
320
  self._display._warning(warning, wrap_text=False)
323
321
 
324
322
  if res.pop('deprecations', None) and self._current_task_result and (deprecations := self._current_task_result.deprecations):
@@ -333,7 +331,7 @@ class CallbackBase(AnsiblePlugin):
333
331
 
334
332
  def _handle_warnings_and_exception(self, result: CallbackTaskResult) -> None:
335
333
  """Standardized handling of warnings/deprecations and exceptions from a task/item result."""
336
- # DTFIX-RELEASE: make/doc/porting-guide a public version of this method?
334
+ # DTFIX5: make/doc/porting-guide a public version of this method?
337
335
  try:
338
336
  use_stderr = self.get_option('display_failed_stderr')
339
337
  except KeyError:
@@ -374,7 +372,7 @@ class CallbackBase(AnsiblePlugin):
374
372
  ' '
375
373
  )
376
374
 
377
- # DTFIX-RELEASE: add test to exercise this case
375
+ # DTFIX5: add test to exercise this case
378
376
  raise ValueError(f'Unsupported result_format {result_format!r}.')
379
377
 
380
378
  def _get_diff(self, difflist):
@@ -90,6 +90,8 @@ import typing as t
90
90
 
91
91
  from ansible import constants
92
92
  from ansible.module_utils.common.text.converters import to_bytes, to_text
93
+ from ansible.module_utils._internal import _event_utils
94
+ from ansible._internal import _event_formatting
93
95
  from ansible.playbook.task import Task
94
96
  from ansible.plugins.callback import CallbackBase
95
97
  from ansible.executor.task_result import CallbackTaskResult
@@ -248,8 +250,8 @@ class CallbackModule(CallbackBase):
248
250
 
249
251
  if host_data.status == 'failed':
250
252
  if error_summary := task_result.exception:
251
- message = error_summary._format()
252
- output = error_summary.formatted_traceback
253
+ message = _event_utils.format_event_brief_message(error_summary.event)
254
+ output = _event_formatting.format_event_traceback(error_summary.event)
253
255
  test_case.errors.append(TestError(message=message, output=output))
254
256
  elif 'msg' in res:
255
257
  message = res['msg']
@@ -101,9 +101,9 @@ class Connection(ConnectionBase):
101
101
  display.debug("opening command with Popen()")
102
102
 
103
103
  if isinstance(cmd, (text_type, binary_type)):
104
- cmd = to_bytes(cmd)
104
+ cmd = to_text(cmd)
105
105
  else:
106
- cmd = map(to_bytes, cmd)
106
+ cmd = map(to_text, cmd)
107
107
 
108
108
  pty_primary = None
109
109
  stdin = subprocess.PIPE
@@ -398,6 +398,17 @@ DOCUMENTATION = """
398
398
  - {key: pkcs11_provider, section: ssh_connection}
399
399
  vars:
400
400
  - name: ansible_ssh_pkcs11_provider
401
+ verbosity:
402
+ version_added: '2.19'
403
+ default: 0
404
+ type: int
405
+ description:
406
+ - Requested verbosity level for the SSH CLI.
407
+ env: [{name: ANSIBLE_SSH_VERBOSITY}]
408
+ ini:
409
+ - {key: verbosity, section: ssh_connection}
410
+ vars:
411
+ - name: ansible_ssh_verbosity
401
412
  """
402
413
 
403
414
  import collections.abc as c
@@ -436,7 +447,7 @@ from ansible.plugins.connection import ConnectionBase, BUFSIZE
436
447
  from ansible.plugins.shell.powershell import _replace_stderr_clixml
437
448
  from ansible.utils.display import Display
438
449
  from ansible.utils.path import unfrackpath, makedirs_safe
439
- from ansible.utils._ssh_agent import SshAgentClient, _key_data_into_crypto_objects
450
+ from ansible._internal._ssh import _ssh_agent
440
451
 
441
452
  try:
442
453
  from cryptography.hazmat.primitives import serialization
@@ -755,12 +766,12 @@ class Connection(ConnectionBase):
755
766
  key_data = self.get_option('private_key')
756
767
  passphrase = self.get_option('private_key_passphrase')
757
768
 
758
- private_key, public_key, fingerprint = _key_data_into_crypto_objects(
769
+ private_key, public_key, fingerprint = _ssh_agent.key_data_into_crypto_objects(
759
770
  to_bytes(key_data),
760
771
  to_bytes(passphrase) if passphrase else None,
761
772
  )
762
773
 
763
- with SshAgentClient(auth_sock) as client:
774
+ with _ssh_agent.SshAgentClient(auth_sock) as client:
764
775
  if public_key not in client:
765
776
  display.vvv(f'SSH: SSH_AGENT adding {fingerprint} to agent', host=self.host)
766
777
  client.add(
@@ -855,8 +866,8 @@ class Connection(ConnectionBase):
855
866
  self._add_args(b_command, b_args, u'disable batch mode for password auth')
856
867
  b_command += [b'-b', b'-']
857
868
 
858
- if display.verbosity:
859
- b_command.append(b'-' + (b'v' * display.verbosity))
869
+ if (verbosity := self.get_option('verbosity')) > 0:
870
+ b_command.append(b'-' + (b'v' * verbosity))
860
871
 
861
872
  # Next, we add ssh_args
862
873
  ssh_args = self.get_option('ssh_args')
@@ -879,10 +890,7 @@ class Connection(ConnectionBase):
879
890
  try:
880
891
  key = self._populate_agent()
881
892
  except Exception as e:
882
- raise AnsibleAuthenticationFailure(
883
- 'Failed to add configured private key into ssh-agent',
884
- orig_exc=e,
885
- )
893
+ raise AnsibleAuthenticationFailure('Failed to add configured private key into ssh-agent.') from e
886
894
  b_args = (b'-o', b'IdentitiesOnly=yes', b'-o', to_bytes(f'IdentityFile="{key}"', errors='surrogate_or_strict'))
887
895
  self._add_args(b_command, b_args, "ANSIBLE_PRIVATE_KEY/private_key set")
888
896
  elif key := self.get_option('private_key_file'):
@@ -723,8 +723,11 @@ class Connection(ConnectionBase):
723
723
  super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
724
724
 
725
725
  encoded_prefix = self._shell._encode_script('', as_list=False, strict_mode=False, preserve_rc=False)
726
- if cmd.startswith(encoded_prefix):
727
- # Avoid double encoding the script
726
+ if cmd.startswith(encoded_prefix) or cmd.startswith("type "):
727
+ # Avoid double encoding the script, the first means we are already
728
+ # running the standard PowerShell command, the latter is used for
729
+ # the no pipeline case where it uses type to pipe the script into
730
+ # powershell which is known to work without re-encoding as pwsh.
728
731
  cmd_parts = cmd.split(" ")
729
732
  else:
730
733
  cmd_parts = self._shell._encode_script(cmd, as_list=True, strict_mode=False, preserve_rc=False)
@@ -47,13 +47,13 @@ options:
47
47
  - The key from input dictionary used to generate groups.
48
48
  default_value:
49
49
  description:
50
- - The default value when the host variable's value is an empty string.
50
+ - The default value when the host variable's value is V(None) or an empty string.
51
51
  - This option is mutually exclusive with O(keyed_groups[].trailing_separator).
52
52
  type: str
53
53
  version_added: '2.12'
54
54
  trailing_separator:
55
55
  description:
56
- - Set this option to V(false) to omit the O(keyed_groups[].separator) after the host variable when the value is an empty string.
56
+ - Set this option to V(false) to omit the O(keyed_groups[].separator) after the host variable when the value is V(None) or an empty string.
57
57
  - This option is mutually exclusive with O(keyed_groups[].default_value).
58
58
  type: bool
59
59
  default: true
@@ -34,7 +34,7 @@ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_
34
34
  from ansible.module_utils.common.collections import is_sequence
35
35
  from ansible.module_utils.common.yaml import yaml_load, yaml_load_all
36
36
  from ansible.parsing.yaml.dumper import AnsibleDumper
37
- from ansible.plugins import accept_args_markers, accept_lazy_markers
37
+ from ansible.template import accept_args_markers, accept_lazy_markers
38
38
  from ansible._internal._templating._jinja_common import MarkerError, UndefinedMarker, validate_arg_type
39
39
  from ansible.utils.display import Display
40
40
  from ansible.utils.encrypt import do_encrypt, PASSLIB_AVAILABLE
@@ -247,20 +247,30 @@ def regex_escape(string, re_type='python'):
247
247
 
248
248
 
249
249
  def from_yaml(data):
250
+ if data is None:
251
+ return None
252
+
250
253
  if isinstance(data, string_types):
251
254
  # The ``text_type`` call here strips any custom
252
255
  # string wrapper class, so that CSafeLoader can
253
256
  # read the data
254
257
  return yaml_load(text_type(to_text(data, errors='surrogate_or_strict')))
258
+
259
+ display.deprecated(f"The from_yaml filter ignored non-string input of type {native_type_name(data)!r}.", version='2.23', obj=data)
255
260
  return data
256
261
 
257
262
 
258
263
  def from_yaml_all(data):
264
+ if data is None:
265
+ return [] # backward compatibility; ensure consistent result between classic/native Jinja for None/empty string input
266
+
259
267
  if isinstance(data, string_types):
260
268
  # The ``text_type`` call here strips any custom
261
269
  # string wrapper class, so that CSafeLoader can
262
270
  # read the data
263
271
  return yaml_load_all(text_type(to_text(data, errors='surrogate_or_strict')))
272
+
273
+ display.deprecated(f"The from_yaml_all filter ignored non-string input of type {native_type_name(data)!r}.", version='2.23', obj=data)
264
274
  return data
265
275
 
266
276
 
@@ -338,7 +348,7 @@ def to_uuid(string, namespace=UUID_NAMESPACE_ANSIBLE):
338
348
  @accept_args_markers
339
349
  def mandatory(a: object, msg: str | None = None) -> object:
340
350
  """Make a variable mandatory."""
341
- # DTFIX-RELEASE: deprecate this filter; there are much better ways via undef, etc...
351
+ # DTFIX-FUTURE: deprecate this filter; there are much better ways via undef, etc...
342
352
  # also remember to remove unit test checking for _undefined_name
343
353
  if isinstance(a, UndefinedMarker):
344
354
  if msg is not None:
@@ -654,7 +664,7 @@ def _cleansed_groupby(*args, **kwargs):
654
664
 
655
665
  return res
656
666
 
657
- # DTFIX-RELEASE: make these dumb wrappers more dynamic
667
+ # DTFIX-FUTURE: make these dumb wrappers more dynamic
658
668
 
659
669
 
660
670
  @accept_args_markers
@@ -806,7 +816,6 @@ class FilterModule(object):
806
816
  'groupby': _cleansed_groupby,
807
817
 
808
818
  # Jinja builtins that need special arg handling
809
- # DTFIX-RELEASE: document these now that they're overridden, or hide them so they don't show up as undocumented
810
819
  'd': ansible_default, # replaces the implementation instead of wrapping it
811
820
  'default': ansible_default, # replaces the implementation instead of wrapping it
812
821
  'map': wrapped_map,
@@ -815,5 +824,3 @@ class FilterModule(object):
815
824
  'reject': wrapped_reject,
816
825
  'rejectattr': wrapped_rejectattr,
817
826
  }
818
-
819
- # DTFIX-RELEASE: document protomatter plugins, or hide them from ansible-doc/galaxy (not related to this code, but needed some place to put this comment)
@@ -4,10 +4,10 @@ from __future__ import annotations
4
4
 
5
5
  from ansible.errors import AnsibleError
6
6
  from ansible.module_utils.common.text.converters import to_native, to_bytes
7
- from ansible.plugins import accept_args_markers
8
- from ansible._internal._templating._jinja_common import get_first_marker_arg, VaultExceptionMarker
7
+ from ansible._internal._templating._jinja_common import VaultExceptionMarker
9
8
  from ansible._internal._datatag._tags import VaultedValue
10
9
  from ansible.parsing.vault import is_encrypted, VaultSecret, VaultLib, VaultHelper
10
+ from ansible import template as _template
11
11
  from ansible.utils.display import Display
12
12
 
13
13
  display = Display()
@@ -43,12 +43,12 @@ def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=Fal
43
43
  return vault
44
44
 
45
45
 
46
- @accept_args_markers
46
+ @_template.accept_args_markers
47
47
  def do_unvault(vault, secret, vault_id='filter_default', vaultid=None):
48
48
  if isinstance(vault, VaultExceptionMarker):
49
49
  vault = vault._disarm()
50
50
 
51
- if (first_marker := get_first_marker_arg((vault, secret, vault_id, vaultid), {})) is not None:
51
+ if (first_marker := _template.get_first_marker_arg((vault, secret, vault_id, vaultid), {})) is not None:
52
52
  return first_marker
53
53
 
54
54
  if not isinstance(secret, (str, bytes)):
@@ -334,7 +334,6 @@ class Cacheable(_plugin_info.HasPluginInfo, _ConfigurablePlugin):
334
334
 
335
335
  def _get_cache_prefix(self, path: str) -> str:
336
336
  """Return a predictable unique key based on the given path."""
337
- # DTFIX-RELEASE: choose a better hashing approach
338
337
  return 'k' + hashlib.sha256(f'{self.ansible_name}{path}'.encode(), usedforsecurity=False).hexdigest()[:6]
339
338
 
340
339
  def clear_cache(self) -> None:
@@ -402,6 +401,8 @@ class Constructable(_BaseInventoryPlugin):
402
401
 
403
402
  def _add_host_to_keyed_groups(self, keys, variables, host, strict=False, fetch_hostvars=True):
404
403
  """ helper to create groups for plugins based on variable values and add the corresponding hosts to it"""
404
+ should_default_value = (None, '')
405
+
405
406
  if keys and isinstance(keys, list):
406
407
  for keyed in keys:
407
408
  if keyed and isinstance(keyed, dict):
@@ -418,7 +419,9 @@ class Constructable(_BaseInventoryPlugin):
418
419
  trailing_separator = keyed.get('trailing_separator')
419
420
  if trailing_separator is not None and default_value_name is not None:
420
421
  raise AnsibleParserError("parameters are mutually exclusive for keyed groups: default_value|trailing_separator")
421
- if key or (key == '' and default_value_name is not None):
422
+
423
+ use_default = key in should_default_value and default_value_name is not None
424
+ if key or use_default:
422
425
  prefix = keyed.get('prefix', '')
423
426
  sep = keyed.get('separator', '_')
424
427
  raw_parent_name = keyed.get('parent_group', None)
@@ -434,23 +437,21 @@ class Constructable(_BaseInventoryPlugin):
434
437
  continue
435
438
 
436
439
  new_raw_group_names = []
437
- if isinstance(key, string_types):
438
- # if key is empty, 'default_value' will be used as group name
439
- if key == '' and default_value_name is not None:
440
- new_raw_group_names.append(default_value_name)
441
- else:
442
- new_raw_group_names.append(key)
440
+ if use_default:
441
+ new_raw_group_names.append(default_value_name)
442
+ elif isinstance(key, string_types):
443
+ new_raw_group_names.append(key)
443
444
  elif isinstance(key, list):
444
445
  for name in key:
445
446
  # if list item is empty, 'default_value' will be used as group name
446
- if name == '' and default_value_name is not None:
447
+ if name in should_default_value and default_value_name is not None:
447
448
  new_raw_group_names.append(default_value_name)
448
449
  else:
449
450
  new_raw_group_names.append(name)
450
451
  elif isinstance(key, Mapping):
451
452
  for (gname, gval) in key.items():
452
453
  bare_name = '%s%s%s' % (gname, sep, gval)
453
- if gval == '':
454
+ if gval in should_default_value:
454
455
  # key's value is empty
455
456
  if default_value_name is not None:
456
457
  bare_name = '%s%s%s' % (gname, sep, default_value_name)
@@ -367,7 +367,7 @@ def run_command(path: str, options: list[str], origin: Origin) -> tuple[str, str
367
367
  if stderr and not stderr.endswith('\n'):
368
368
  stderr += '\n'
369
369
 
370
- # DTFIX-RELEASE: another use case for the "not quite help text, definitely not message" diagnostic output on errors
370
+ # DTFIX-FUTURE: another use case for the "not quite help text, definitely not message" diagnostic output on errors
371
371
  stderr_help_text = f'Standard error from inventory script:\n{stderr}' if stderr.strip() else None
372
372
 
373
373
  if sp.returncode != 0: