ansible-core 2.19.0b3__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 (174) hide show
  1. ansible/_internal/__init__.py +2 -2
  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 +6 -6
  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/_testing.py +26 -0
  26. ansible/_internal/_yaml/_dumper.py +1 -1
  27. ansible/_internal/_yaml/_errors.py +7 -7
  28. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
  29. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
  30. ansible/cli/__init__.py +5 -82
  31. ansible/cli/arguments/option_helpers.py +8 -5
  32. ansible/cli/doc.py +84 -28
  33. ansible/cli/inventory.py +1 -1
  34. ansible/compat/importlib_resources.py +9 -12
  35. ansible/config/base.yml +27 -23
  36. ansible/config/manager.py +142 -101
  37. ansible/constants.py +1 -1
  38. ansible/errors/__init__.py +96 -49
  39. ansible/executor/module_common.py +8 -10
  40. ansible/executor/powershell/async_watchdog.ps1 +2 -2
  41. ansible/executor/powershell/async_wrapper.ps1 +3 -3
  42. ansible/executor/powershell/become_wrapper.ps1 +20 -2
  43. ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
  44. ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
  45. ansible/executor/powershell/exec_wrapper.ps1 +219 -6
  46. ansible/executor/powershell/module_manifest.py +52 -0
  47. ansible/executor/powershell/module_wrapper.ps1 +47 -21
  48. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  49. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  50. ansible/executor/process/worker.py +38 -113
  51. ansible/executor/task_executor.py +26 -61
  52. ansible/executor/task_result.py +2 -4
  53. ansible/galaxy/collection/__init__.py +1 -4
  54. ansible/inventory/manager.py +1 -0
  55. ansible/module_utils/_internal/__init__.py +0 -3
  56. ansible/module_utils/_internal/_ambient_context.py +3 -3
  57. ansible/module_utils/_internal/_ansiballz.py +4 -2
  58. ansible/module_utils/_internal/_datatag/__init__.py +20 -14
  59. ansible/module_utils/_internal/_datatag/_tags.py +2 -2
  60. ansible/module_utils/_internal/_deprecator.py +66 -48
  61. ansible/module_utils/_internal/_errors.py +88 -17
  62. ansible/module_utils/_internal/_event_utils.py +61 -0
  63. ansible/module_utils/_internal/_json/_profiles/__init__.py +21 -4
  64. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
  65. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
  66. ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
  67. ansible/module_utils/{common/messages.py → _internal/_messages.py} +28 -47
  68. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
  69. ansible/module_utils/_internal/_plugin_info.py +1 -1
  70. ansible/module_utils/_internal/_stack.py +22 -0
  71. ansible/module_utils/_internal/_text_utils.py +6 -0
  72. ansible/module_utils/_internal/_traceback.py +11 -8
  73. ansible/module_utils/ansible_release.py +1 -1
  74. ansible/module_utils/basic.py +49 -15
  75. ansible/module_utils/common/arg_spec.py +2 -2
  76. ansible/module_utils/common/collections.py +6 -0
  77. ansible/module_utils/common/json.py +2 -2
  78. ansible/module_utils/common/text/converters.py +3 -3
  79. ansible/module_utils/common/validation.py +1 -1
  80. ansible/module_utils/common/warnings.py +80 -23
  81. ansible/module_utils/common/yaml.py +1 -1
  82. ansible/module_utils/datatag.py +5 -2
  83. ansible/module_utils/facts/system/distribution.py +16 -3
  84. ansible/module_utils/facts/virtual/linux.py +2 -2
  85. ansible/module_utils/parsing/convert_bool.py +6 -0
  86. ansible/module_utils/service.py +2 -9
  87. ansible/modules/apt_repository.py +7 -29
  88. ansible/modules/assemble.py +4 -4
  89. ansible/modules/async_status.py +13 -11
  90. ansible/modules/async_wrapper.py +5 -5
  91. ansible/modules/cron.py +3 -5
  92. ansible/modules/dnf5.py +15 -22
  93. ansible/modules/git.py +1 -6
  94. ansible/modules/hostname.py +0 -1
  95. ansible/modules/pip.py +2 -4
  96. ansible/modules/service.py +3 -9
  97. ansible/modules/sysvinit.py +3 -3
  98. ansible/parsing/ajson.py +3 -5
  99. ansible/parsing/dataloader.py +4 -4
  100. ansible/parsing/mod_args.py +1 -1
  101. ansible/parsing/plugin_docs.py +2 -2
  102. ansible/parsing/utils/yaml.py +3 -3
  103. ansible/parsing/vault/__init__.py +4 -4
  104. ansible/playbook/playbook_include.py +1 -1
  105. ansible/playbook/taggable.py +0 -3
  106. ansible/plugins/__init__.py +0 -25
  107. ansible/plugins/action/__init__.py +9 -32
  108. ansible/plugins/action/add_host.py +1 -1
  109. ansible/plugins/action/assemble.py +8 -16
  110. ansible/plugins/action/async_status.py +7 -2
  111. ansible/plugins/action/copy.py +8 -7
  112. ansible/plugins/action/gather_facts.py +8 -8
  113. ansible/plugins/action/package.py +5 -8
  114. ansible/plugins/action/script.py +8 -15
  115. ansible/plugins/action/service.py +3 -7
  116. ansible/plugins/action/template.py +6 -8
  117. ansible/plugins/action/unarchive.py +5 -15
  118. ansible/plugins/action/uri.py +9 -20
  119. ansible/plugins/callback/__init__.py +4 -6
  120. ansible/plugins/callback/junit.py +4 -2
  121. ansible/plugins/connection/local.py +2 -2
  122. ansible/plugins/connection/ssh.py +17 -9
  123. ansible/plugins/connection/winrm.py +5 -2
  124. ansible/plugins/doc_fragments/constructed.py +2 -2
  125. ansible/plugins/filter/core.py +13 -6
  126. ansible/plugins/filter/encryption.py +4 -4
  127. ansible/plugins/inventory/__init__.py +11 -10
  128. ansible/plugins/inventory/script.py +1 -1
  129. ansible/plugins/list.py +69 -16
  130. ansible/plugins/loader.py +10 -9
  131. ansible/plugins/lookup/csvfile.py +16 -71
  132. ansible/plugins/lookup/first_found.py +2 -1
  133. ansible/plugins/shell/__init__.py +56 -2
  134. ansible/plugins/shell/powershell.py +66 -9
  135. ansible/plugins/shell/sh.py +9 -5
  136. ansible/plugins/test/core.py +21 -15
  137. ansible/plugins/test/finished.yml +1 -1
  138. ansible/plugins/test/uri.py +2 -5
  139. ansible/release.py +1 -1
  140. ansible/template/__init__.py +30 -2
  141. ansible/utils/collection_loader/__init__.py +2 -0
  142. ansible/utils/display.py +107 -128
  143. ansible/utils/hashing.py +0 -1
  144. ansible/utils/listify.py +6 -4
  145. ansible/utils/plugin_docs.py +2 -1
  146. ansible/utils/unsafe_proxy.py +1 -1
  147. ansible/vars/hostvars.py +1 -1
  148. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/METADATA +3 -2
  149. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/RECORD +173 -161
  150. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/WHEEL +1 -1
  151. ansible_test/_data/completion/docker.txt +3 -3
  152. ansible_test/_data/completion/remote.txt +1 -0
  153. ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
  154. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  155. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  156. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  157. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  158. ansible_test/_internal/util.py +20 -0
  159. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
  160. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  161. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
  162. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  163. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  164. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +73 -8
  165. ansible_test/_util/target/setup/bootstrap.sh +31 -0
  166. ansible/_internal/_errors/_utils.py +0 -310
  167. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/entry_points.txt +0 -0
  168. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses}/COPYING +0 -0
  169. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/Apache-License.txt +0 -0
  170. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/BSD-3-Clause.txt +0 -0
  171. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/MIT-license.txt +0 -0
  172. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/PSF-license.txt +0 -0
  173. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/simplified_bsd.txt +0 -0
  174. {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/top_level.txt +0 -0
@@ -45,9 +45,6 @@ class Taggable:
45
45
  return ds
46
46
 
47
47
  if isinstance(ds, str):
48
- # DTFIX-RELEASE: this allows each individual tag to be templated, but prevents the use of commas in templates, is that what we want?
49
- # DTFIX-RELEASE: this can return empty tags (including a list of nothing but empty tags), is that correct?
50
- # DTFIX-RELEASE: the original code seemed to attempt to preserve `ds` if there were no commas, but it never ran, what should it actually do?
51
48
  return [AnsibleTagHelper.tag_copy(ds, item.strip()) for item in ds.split(',')]
52
49
 
53
50
  raise AnsibleError('tags must be specified as a list', obj=ds)
@@ -189,28 +189,3 @@ class AnsibleJinja2Plugin(AnsiblePlugin, metaclass=abc.ABCMeta):
189
189
  @property
190
190
  def j2_function(self) -> t.Callable:
191
191
  return self._function
192
-
193
-
194
- _TCallable = t.TypeVar('_TCallable', bound=t.Callable)
195
-
196
-
197
- def accept_args_markers(plugin: _TCallable) -> _TCallable:
198
- """
199
- A decorator to mark a Jinja plugin as capable of handling `Marker` values for its top-level arguments.
200
- Non-decorated plugin invocation is skipped when a top-level argument is a `Marker`, with the first such value substituted as the plugin result.
201
- This ensures that only plugins which understand `Marker` instances for top-level arguments will encounter them.
202
- """
203
- plugin.accept_args_markers = True
204
-
205
- return plugin
206
-
207
-
208
- def accept_lazy_markers(plugin: _TCallable) -> _TCallable:
209
- """
210
- A decorator to mark a Jinja plugin as capable of handling `Marker` values retrieved from lazy containers.
211
- Non-decorated plugins will trigger a `MarkerError` exception when attempting to retrieve a `Marker` from a lazy container.
212
- This ensures that only plugins which understand lazy retrieval of `Marker` instances will encounter them.
213
- """
214
- plugin.accept_lazy_markers = True
215
-
216
- return plugin
@@ -20,9 +20,8 @@ from abc import ABC, abstractmethod
20
20
  from collections.abc import Sequence
21
21
 
22
22
  from ansible import constants as C
23
- from ansible._internal._errors import _captured
23
+ from ansible._internal._errors import _captured, _error_utils
24
24
  from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleActionSkip, AnsibleActionFail, AnsibleAuthenticationFailure
25
- from ansible._internal._errors import _utils
26
25
  from ansible.executor.module_common import modify_module, _BuiltModule
27
26
  from ansible.executor.interpreter_discovery import discover_interpreter, InterpreterDiscoveryRequiredError
28
27
  from ansible.module_utils._internal import _traceback
@@ -41,7 +40,6 @@ from ansible import _internal
41
40
  from ansible._internal._templating import _engine
42
41
 
43
42
  from .. import _AnsiblePluginInfoMixin
44
- from ...module_utils.common.messages import PluginInfo
45
43
 
46
44
  display = Display()
47
45
 
@@ -121,8 +119,7 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
121
119
 
122
120
  * Module parameters. These are stored in self._task.args
123
121
  """
124
-
125
- # does not default to {'changed': False, 'failed': False}, as it breaks async
122
+ # does not default to {'changed': False, 'failed': False}, as it used to break async
126
123
  result = {}
127
124
 
128
125
  if tmp is not None:
@@ -476,8 +473,8 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
476
473
 
477
474
  become_unprivileged = self._is_become_unprivileged()
478
475
  basefile = self._connection._shell._generate_temp_dir_name()
479
- cmd = self._connection._shell.mkdtemp(basefile=basefile, system=become_unprivileged, tmpdir=tmpdir)
480
- result = self._low_level_execute_command(cmd, sudoable=False)
476
+ cmd = self._connection._shell._mkdtemp2(basefile=basefile, system=become_unprivileged, tmpdir=tmpdir)
477
+ result = self._low_level_execute_command(cmd.command, in_data=cmd.input_data, sudoable=False)
481
478
 
482
479
  # error handling on this seems a little aggressive?
483
480
  if result['rc'] != 0:
@@ -908,8 +905,8 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
908
905
  expand_path = '~%s' % (self._get_remote_user() or '')
909
906
 
910
907
  # use shell to construct appropriate command and execute
911
- cmd = self._connection._shell.expand_user(expand_path)
912
- data = self._low_level_execute_command(cmd, sudoable=False)
908
+ cmd = self._connection._shell._expand_user2(expand_path)
909
+ data = self._low_level_execute_command(cmd.command, in_data=cmd.input_data, sudoable=False)
913
910
 
914
911
  try:
915
912
  initial_fragment = data['stdout'].strip().splitlines()[-1]
@@ -1002,7 +999,7 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
1002
999
  # tells the module to ignore options that are not in its argspec.
1003
1000
  module_args['_ansible_ignore_unknown_opts'] = ignore_unknown_opts
1004
1001
 
1005
- # allow user to insert string to add context to remote loggging
1002
+ # allow user to insert string to add context to remote logging
1006
1003
  module_args['_ansible_target_log_info'] = C.config.get_config_value('TARGET_LOG_INFO', variables=task_vars)
1007
1004
 
1008
1005
  module_args['_ansible_tracebacks_for'] = _traceback.traceback_for()
@@ -1115,7 +1112,7 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
1115
1112
  if wrap_async and not self._connection.always_pipeline_modules:
1116
1113
  # configure, upload, and chmod the async_wrapper module
1117
1114
  (async_module_bits, async_module_path) = self._configure_module(module_name='ansible.legacy.async_wrapper', module_args=dict(), task_vars=task_vars)
1118
- (async_module_style, shebang, async_module_data) = (async_module_bits.module_style, async_module_bits.shebang, async_module_bits.b_module_data)
1115
+ (shebang, async_module_data) = (async_module_bits.shebang, async_module_bits.b_module_data)
1119
1116
  async_module_remote_filename = self._connection._shell.get_remote_filename(async_module_path)
1120
1117
  remote_async_module_path = self._connection._shell.join_path(tmpdir, async_module_remote_filename)
1121
1118
  self._transfer_data(remote_async_module_path, async_module_data)
@@ -1255,7 +1252,7 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
1255
1252
  except AnsibleError as ansible_ex:
1256
1253
  sentinel = object()
1257
1254
 
1258
- data = self.result_dict_from_exception(ansible_ex)
1255
+ data = _error_utils.result_dict_from_exception(ansible_ex)
1259
1256
  data.update(
1260
1257
  _ansible_parsed=False,
1261
1258
  module_stdout=res.get('stdout', ''),
@@ -1436,23 +1433,3 @@ class ActionBase(ABC, _AnsiblePluginInfoMixin):
1436
1433
 
1437
1434
  # if missing it will return a file not found exception
1438
1435
  return self._loader.path_dwim_relative_stack(path_stack, dirname, needle)
1439
-
1440
- @staticmethod
1441
- def result_dict_from_exception(exception: BaseException) -> dict[str, t.Any]:
1442
- """Return a failed task result dict from the given exception."""
1443
- if ansible_remoted_error := _captured.AnsibleResultCapturedError.find_first_remoted_error(exception):
1444
- result = ansible_remoted_error._result.copy()
1445
- else:
1446
- result = {}
1447
-
1448
- error_summary = _utils._create_error_summary(exception, _traceback.TracebackEvent.ERROR)
1449
-
1450
- result.update(
1451
- failed=True,
1452
- exception=error_summary,
1453
- )
1454
-
1455
- if 'msg' not in result:
1456
- result.update(msg=_utils._dedupe_and_concat_message_chain([md.msg for md in error_summary.details]))
1457
-
1458
- return result
@@ -77,7 +77,7 @@ class ActionModule(ActionBase):
77
77
  elif isinstance(groups, string_types):
78
78
  group_list = groups.split(",")
79
79
  else:
80
- raise AnsibleActionFail("Groups must be specified as a list.", obj=self._task)
80
+ raise AnsibleActionFail("Groups must be specified as a list.", obj=groups)
81
81
 
82
82
  for group_name in group_list:
83
83
  if group_name not in new_groups:
@@ -25,8 +25,8 @@ import re
25
25
  import tempfile
26
26
 
27
27
  from ansible import constants as C
28
- from ansible.errors import AnsibleError, AnsibleAction, _AnsibleActionDone, AnsibleActionFail
29
- from ansible.module_utils.common.text.converters import to_native, to_text
28
+ from ansible.errors import AnsibleActionFail
29
+ from ansible.module_utils.common.text.converters import to_text
30
30
  from ansible.module_utils.parsing.convert_bool import boolean
31
31
  from ansible.plugins.action import ActionBase
32
32
  from ansible.utils.hashing import checksum_s
@@ -83,7 +83,7 @@ class ActionModule(ActionBase):
83
83
 
84
84
  self._supports_check_mode = False
85
85
 
86
- result = super(ActionModule, self).run(tmp, task_vars)
86
+ super(ActionModule, self).run(tmp, task_vars)
87
87
  del tmp # tmp no longer has any effect
88
88
 
89
89
  if task_vars is None:
@@ -104,13 +104,9 @@ class ActionModule(ActionBase):
104
104
 
105
105
  if boolean(remote_src, strict=False):
106
106
  # call assemble via ansible.legacy to allow library/ overrides of the module without collection search
107
- result.update(self._execute_module(module_name='ansible.legacy.assemble', task_vars=task_vars))
108
- raise _AnsibleActionDone()
109
- else:
110
- try:
111
- src = self._find_needle('files', src)
112
- except AnsibleError as e:
113
- raise AnsibleActionFail(to_native(e))
107
+ return self._execute_module(module_name='ansible.legacy.assemble', task_vars=task_vars)
108
+
109
+ src = self._find_needle('files', src)
114
110
 
115
111
  if not os.path.isdir(src):
116
112
  raise AnsibleActionFail(u"Source (%s) is not a directory" % src)
@@ -153,13 +149,9 @@ class ActionModule(ActionBase):
153
149
  res = self._execute_module(module_name='ansible.legacy.copy', module_args=new_module_args, task_vars=task_vars)
154
150
  if diff:
155
151
  res['diff'] = diff
156
- result.update(res)
152
+ return res
157
153
  else:
158
- result.update(self._execute_module(module_name='ansible.legacy.file', module_args=new_module_args, task_vars=task_vars))
154
+ return self._execute_module(module_name='ansible.legacy.file', module_args=new_module_args, task_vars=task_vars)
159
155
 
160
- except AnsibleAction as e:
161
- result.update(e.result)
162
156
  finally:
163
157
  self._remove_tmp_path(self._connection._shell.tmpdir)
164
-
165
- return result
@@ -28,7 +28,7 @@ class ActionModule(ActionBase):
28
28
  )
29
29
 
30
30
  # initialize response
31
- results['started'] = results['finished'] = 0
31
+ results['started'] = results['finished'] = False
32
32
  results['stdout'] = results['stderr'] = ''
33
33
  results['stdout_lines'] = results['stderr_lines'] = []
34
34
 
@@ -43,9 +43,14 @@ class ActionModule(ActionBase):
43
43
  results['erased'] = log_path
44
44
  else:
45
45
  results['results_file'] = log_path
46
- results['started'] = 1
46
+ results['started'] = True
47
47
 
48
48
  new_module_args['_async_dir'] = async_dir
49
49
  results = merge_hash(results, self._execute_module(module_name='ansible.legacy.async_status', task_vars=task_vars, module_args=new_module_args))
50
50
 
51
+ # Backwards compat shim for when started/finished were ints,
52
+ # mostly to work with ansible.windows.async_status
53
+ for convert in ('started', 'finished'):
54
+ results[convert] = bool(results[convert])
55
+
51
56
  return results
@@ -27,7 +27,7 @@ import tempfile
27
27
  from ansible import constants as C
28
28
  from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleFileNotFound
29
29
  from ansible.module_utils.basic import FILE_COMMON_ARGUMENTS
30
- from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
30
+ from ansible.module_utils.common.text.converters import to_bytes, to_text
31
31
  from ansible.module_utils.parsing.convert_bool import boolean
32
32
  from ansible.plugins.action import ActionBase
33
33
  from ansible.utils.hashing import checksum
@@ -409,6 +409,7 @@ class ActionModule(ActionBase):
409
409
  task_vars = dict()
410
410
 
411
411
  result = super(ActionModule, self).run(tmp, task_vars)
412
+
412
413
  del tmp # tmp no longer has any effect
413
414
 
414
415
  # ensure user is not setting internal parameters
@@ -450,10 +451,10 @@ class ActionModule(ActionBase):
450
451
  else:
451
452
  content_tempfile = self._create_content_tempfile(content)
452
453
  source = content_tempfile
453
- except Exception as err:
454
- result['failed'] = True
455
- result['msg'] = "could not write content temp file: %s" % to_native(err)
456
- return self._ensure_invocation(result)
454
+ except Exception as ex:
455
+ self._ensure_invocation(result)
456
+
457
+ raise AnsibleActionFail(message="could not write content temp file", result=result) from ex
457
458
 
458
459
  # if we have first_available_file in our vars
459
460
  # look up the files and use the first one we find as src
@@ -470,9 +471,9 @@ class ActionModule(ActionBase):
470
471
  # find in expected paths
471
472
  source = self._find_needle('files', source)
472
473
  except AnsibleError as ex:
473
- result.update(self.result_dict_from_exception(ex))
474
+ self._ensure_invocation(result)
474
475
 
475
- return self._ensure_invocation(result)
476
+ raise AnsibleActionFail(result=result) from ex
476
477
 
477
478
  if trailing_slash != source.endswith(os.path.sep):
478
479
  if source[-1] == os.path.sep:
@@ -13,6 +13,7 @@ from ansible.executor.module_common import _apply_action_arg_defaults
13
13
  from ansible.module_utils.parsing.convert_bool import boolean
14
14
  from ansible.plugins.action import ActionBase
15
15
  from ansible.utils.vars import merge_hash
16
+ from ansible._internal._errors import _error_utils
16
17
 
17
18
 
18
19
  class ActionModule(ActionBase):
@@ -127,8 +128,6 @@ class ActionModule(ActionBase):
127
128
  # TODO: use gather_timeout to cut module execution if module itself does not support gather_timeout
128
129
  res = self._execute_module(module_name=fact_module, module_args=mod_args, task_vars=task_vars, wrap_async=False)
129
130
  if res.get('failed', False):
130
- # DTFIX-RELEASE: this trashes the individual failure details and does not work with the new error handling; need to do something to
131
- # invoke per-item error handling- perhaps returning this as a synthetic loop result?
132
131
  failed[fact_module] = res
133
132
  elif res.get('skipped', False):
134
133
  skipped[fact_module] = res
@@ -159,10 +158,8 @@ class ActionModule(ActionBase):
159
158
  for module in jobs:
160
159
  poll_args = {'jid': jobs[module]['ansible_job_id'], '_async_dir': os.path.dirname(jobs[module]['results_file'])}
161
160
  res = self._execute_module(module_name='ansible.legacy.async_status', module_args=poll_args, task_vars=task_vars, wrap_async=False)
162
- if res.get('finished', 0) == 1:
161
+ if res.get('finished', False):
163
162
  if res.get('failed', False):
164
- # DTFIX-RELEASE: this trashes the individual failure details and does not work with the new error handling; need to do something to
165
- # invoke per-item error handling- perhaps returning this as a synthetic loop result?
166
163
  failed[module] = res
167
164
  elif res.get('skipped', False):
168
165
  skipped[module] = res
@@ -180,16 +177,19 @@ class ActionModule(ActionBase):
180
177
  self._task.async_val = async_val
181
178
 
182
179
  if skipped:
183
- result['msg'] = "The following modules were skipped: %s\n" % (', '.join(skipped.keys()))
180
+ result['msg'] = f"The following modules were skipped: {', '.join(skipped.keys())}."
184
181
  result['skipped_modules'] = skipped
185
182
  if len(skipped) == len(modules):
186
183
  result['skipped'] = True
187
184
 
188
185
  if failed:
189
- result['failed'] = True
190
- result['msg'] = "The following modules failed to execute: %s\n" % (', '.join(failed.keys()))
191
186
  result['failed_modules'] = failed
192
187
 
188
+ result.update(_error_utils.result_dict_from_captured_errors(
189
+ msg=f"The following modules failed to execute: {', '.join(failed.keys())}.",
190
+ errors=[r['exception'] for r in failed.values()],
191
+ ))
192
+
193
193
  # tell executor facts were gathered
194
194
  result['ansible_facts']['_ansible_facts_gathered'] = True
195
195
 
@@ -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
@@ -132,6 +132,9 @@ class ActionModule(ActionBase):
132
132
  data_templar = self._templar.copy_with_new_env(searchpath=searchpath, available_variables=temp_vars)
133
133
  resultant = data_templar.template(template_data, escape_backslashes=False, overrides=overrides)
134
134
 
135
+ if resultant is None:
136
+ resultant = ''
137
+
135
138
  new_task = self._task.copy()
136
139
  # mode is either the mode from task.args or the mode of the source file if the task.args
137
140
  # mode == 'preserve'
@@ -164,13 +167,8 @@ class ActionModule(ActionBase):
164
167
  loader=self._loader,
165
168
  templar=self._templar,
166
169
  shared_loader_obj=self._shared_loader_obj)
167
- result.update(copy_action.run(task_vars=task_vars))
170
+ return copy_action.run(task_vars=task_vars)
168
171
  finally:
169
172
  shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict'))
170
-
171
- except AnsibleAction as e:
172
- result.update(e.result)
173
173
  finally:
174
174
  self._remove_tmp_path(self._connection._shell.tmpdir)
175
-
176
- 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