ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b6__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 (225) hide show
  1. ansible/_internal/__init__.py +1 -1
  2. ansible/_internal/_ansiballz/__init__.py +0 -0
  3. ansible/_internal/_ansiballz/_builder.py +101 -0
  4. ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
  5. ansible/_internal/_collection_proxy.py +1 -1
  6. ansible/_internal/_errors/_alarm_timeout.py +66 -0
  7. ansible/_internal/_errors/_captured.py +25 -30
  8. ansible/_internal/_errors/_error_factory.py +89 -0
  9. ansible/_internal/_errors/_error_utils.py +240 -0
  10. ansible/_internal/_errors/_task_timeout.py +28 -0
  11. ansible/_internal/_event_formatting.py +127 -0
  12. ansible/_internal/_json/__init__.py +5 -5
  13. ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
  14. ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
  15. ansible/_internal/_json/_profiles/_legacy.py +3 -11
  16. ansible/_internal/_ssh/__init__.py +0 -0
  17. ansible/_internal/_ssh/_agent_launch.py +91 -0
  18. ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
  19. ansible/_internal/_templating/__init__.py +5 -3
  20. ansible/_internal/_templating/_datatag.py +2 -1
  21. ansible/_internal/_templating/_engine.py +3 -4
  22. ansible/_internal/_templating/_jinja_bits.py +28 -20
  23. ansible/_internal/_templating/_jinja_common.py +18 -27
  24. ansible/_internal/_templating/_jinja_plugins.py +36 -5
  25. ansible/_internal/_templating/_lazy_containers.py +5 -5
  26. ansible/_internal/_templating/_template_vars.py +72 -0
  27. ansible/_internal/_templating/_transform.py +26 -19
  28. ansible/_internal/_templating/_utils.py +1 -1
  29. ansible/_internal/_yaml/_constructor.py +4 -4
  30. ansible/_internal/_yaml/_dumper.py +26 -18
  31. ansible/_internal/_yaml/_errors.py +7 -7
  32. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
  33. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
  34. ansible/cli/__init__.py +11 -93
  35. ansible/cli/arguments/option_helpers.py +3 -4
  36. ansible/cli/console.py +1 -1
  37. ansible/cli/doc.py +86 -30
  38. ansible/cli/inventory.py +5 -7
  39. ansible/compat/importlib_resources.py +9 -12
  40. ansible/config/base.yml +46 -0
  41. ansible/errors/__init__.py +98 -50
  42. ansible/executor/module_common.py +75 -49
  43. ansible/executor/powershell/async_watchdog.ps1 +2 -2
  44. ansible/executor/powershell/async_wrapper.ps1 +3 -3
  45. ansible/executor/powershell/become_wrapper.ps1 +20 -2
  46. ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
  47. ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
  48. ansible/executor/powershell/exec_wrapper.ps1 +219 -6
  49. ansible/executor/powershell/module_manifest.py +52 -0
  50. ansible/executor/powershell/module_wrapper.ps1 +47 -21
  51. ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
  52. ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
  53. ansible/executor/process/worker.py +40 -115
  54. ansible/executor/task_executor.py +26 -61
  55. ansible/executor/task_result.py +2 -4
  56. ansible/galaxy/api.py +1 -4
  57. ansible/galaxy/collection/__init__.py +2 -10
  58. ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
  59. ansible/galaxy/role.py +2 -2
  60. ansible/inventory/manager.py +1 -1
  61. ansible/module_utils/_internal/__init__.py +7 -7
  62. ansible/module_utils/_internal/_ambient_context.py +3 -3
  63. ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
  64. ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
  65. ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
  66. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
  67. ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +13 -39
  68. ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
  69. ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
  70. ansible/module_utils/_internal/_datatag/__init__.py +43 -15
  71. ansible/module_utils/_internal/_datatag/_tags.py +2 -2
  72. ansible/module_utils/_internal/_deprecator.py +67 -55
  73. ansible/module_utils/_internal/_errors.py +88 -17
  74. ansible/module_utils/_internal/_event_utils.py +61 -0
  75. ansible/module_utils/_internal/_json/_profiles/__init__.py +22 -4
  76. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
  77. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
  78. ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
  79. ansible/module_utils/{common/messages.py → _internal/_messages.py} +54 -49
  80. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
  81. ansible/module_utils/_internal/_plugin_info.py +15 -2
  82. ansible/module_utils/_internal/_stack.py +22 -0
  83. ansible/module_utils/_internal/_text_utils.py +6 -0
  84. ansible/module_utils/_internal/_traceback.py +11 -8
  85. ansible/module_utils/ansible_release.py +1 -1
  86. ansible/module_utils/basic.py +95 -71
  87. ansible/module_utils/common/arg_spec.py +2 -2
  88. ansible/module_utils/common/collections.py +6 -0
  89. ansible/module_utils/common/json.py +2 -2
  90. ansible/module_utils/common/respawn.py +4 -41
  91. ansible/module_utils/common/text/converters.py +3 -3
  92. ansible/module_utils/common/validation.py +1 -1
  93. ansible/module_utils/common/warnings.py +80 -23
  94. ansible/module_utils/common/yaml.py +1 -1
  95. ansible/module_utils/connection.py +8 -11
  96. ansible/module_utils/datatag.py +5 -2
  97. ansible/module_utils/facts/hardware/linux.py +1 -1
  98. ansible/module_utils/facts/sysctl.py +4 -6
  99. ansible/module_utils/facts/system/caps.py +2 -2
  100. ansible/module_utils/facts/system/distribution.py +16 -3
  101. ansible/module_utils/facts/system/local.py +1 -1
  102. ansible/module_utils/facts/virtual/linux.py +2 -2
  103. ansible/module_utils/service.py +3 -10
  104. ansible/module_utils/urls.py +4 -4
  105. ansible/modules/apt_repository.py +17 -39
  106. ansible/modules/assemble.py +2 -2
  107. ansible/modules/async_status.py +13 -11
  108. ansible/modules/async_wrapper.py +12 -22
  109. ansible/modules/command.py +3 -3
  110. ansible/modules/copy.py +4 -4
  111. ansible/modules/cron.py +1 -1
  112. ansible/modules/dnf5.py +14 -22
  113. ansible/modules/file.py +16 -17
  114. ansible/modules/find.py +3 -3
  115. ansible/modules/get_url.py +17 -0
  116. ansible/modules/git.py +9 -7
  117. ansible/modules/hostname.py +0 -1
  118. ansible/modules/known_hosts.py +12 -14
  119. ansible/modules/package.py +6 -0
  120. ansible/modules/replace.py +2 -2
  121. ansible/modules/service.py +3 -9
  122. ansible/modules/slurp.py +10 -13
  123. ansible/modules/stat.py +5 -7
  124. ansible/modules/unarchive.py +6 -6
  125. ansible/modules/user.py +1 -1
  126. ansible/modules/wait_for.py +28 -30
  127. ansible/modules/yum_repository.py +4 -3
  128. ansible/parsing/ajson.py +3 -5
  129. ansible/parsing/dataloader.py +6 -6
  130. ansible/parsing/mod_args.py +1 -1
  131. ansible/parsing/plugin_docs.py +2 -2
  132. ansible/parsing/utils/yaml.py +3 -3
  133. ansible/parsing/vault/__init__.py +10 -14
  134. ansible/playbook/base.py +7 -2
  135. ansible/playbook/included_file.py +3 -1
  136. ansible/playbook/play_context.py +2 -0
  137. ansible/playbook/playbook_include.py +1 -1
  138. ansible/playbook/taggable.py +19 -8
  139. ansible/playbook/task.py +2 -0
  140. ansible/plugins/__init__.py +0 -25
  141. ansible/plugins/action/__init__.py +8 -31
  142. ansible/plugins/action/add_host.py +1 -1
  143. ansible/plugins/action/assemble.py +8 -16
  144. ansible/plugins/action/async_status.py +7 -2
  145. ansible/plugins/action/copy.py +8 -7
  146. ansible/plugins/action/fetch.py +3 -3
  147. ansible/plugins/action/gather_facts.py +8 -8
  148. ansible/plugins/action/package.py +5 -8
  149. ansible/plugins/action/script.py +8 -15
  150. ansible/plugins/action/service.py +3 -7
  151. ansible/plugins/action/template.py +11 -10
  152. ansible/plugins/action/unarchive.py +5 -15
  153. ansible/plugins/action/uri.py +9 -20
  154. ansible/plugins/cache/__init__.py +17 -19
  155. ansible/plugins/callback/__init__.py +4 -6
  156. ansible/plugins/callback/junit.py +4 -2
  157. ansible/plugins/callback/tree.py +5 -5
  158. ansible/plugins/connection/local.py +6 -6
  159. ansible/plugins/connection/paramiko_ssh.py +5 -5
  160. ansible/plugins/connection/ssh.py +25 -15
  161. ansible/plugins/connection/winrm.py +6 -3
  162. ansible/plugins/doc_fragments/constructed.py +2 -2
  163. ansible/plugins/filter/core.py +32 -27
  164. ansible/plugins/filter/encryption.py +14 -6
  165. ansible/plugins/inventory/__init__.py +11 -10
  166. ansible/plugins/inventory/script.py +1 -1
  167. ansible/plugins/list.py +73 -19
  168. ansible/plugins/loader.py +7 -7
  169. ansible/plugins/lookup/csvfile.py +16 -71
  170. ansible/plugins/lookup/first_found.py +2 -1
  171. ansible/plugins/lookup/template.py +9 -4
  172. ansible/plugins/shell/__init__.py +56 -2
  173. ansible/plugins/shell/powershell.py +67 -9
  174. ansible/plugins/shell/sh.py +10 -5
  175. ansible/plugins/strategy/__init__.py +3 -3
  176. ansible/plugins/test/core.py +22 -16
  177. ansible/plugins/test/finished.yml +1 -1
  178. ansible/plugins/test/uri.py +2 -5
  179. ansible/release.py +1 -1
  180. ansible/template/__init__.py +38 -54
  181. ansible/utils/collection_loader/_collection_finder.py +3 -3
  182. ansible/utils/display.py +124 -138
  183. ansible/utils/galaxy.py +2 -2
  184. ansible/utils/hashing.py +6 -8
  185. ansible/utils/listify.py +6 -4
  186. ansible/utils/path.py +5 -7
  187. ansible/utils/py3compat.py +2 -1
  188. ansible/utils/ssh_functions.py +3 -2
  189. ansible/utils/unsafe_proxy.py +1 -1
  190. ansible/vars/hostvars.py +1 -1
  191. ansible/vars/plugins.py +3 -3
  192. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
  193. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +224 -204
  194. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +1 -1
  195. ansible_test/_data/completion/docker.txt +3 -3
  196. ansible_test/_data/completion/remote.txt +1 -0
  197. ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
  198. ansible_test/_data/requirements/sanity.changelog.txt +2 -2
  199. ansible_test/_data/requirements/sanity.pep8.txt +1 -1
  200. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  201. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  202. ansible_test/_internal/commands/integration/coverage.py +7 -2
  203. ansible_test/_internal/host_profiles.py +62 -10
  204. ansible_test/_internal/provisioning.py +10 -4
  205. ansible_test/_internal/ssh.py +1 -5
  206. ansible_test/_internal/thread.py +2 -1
  207. ansible_test/_internal/timeout.py +1 -1
  208. ansible_test/_internal/util.py +40 -12
  209. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
  210. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
  211. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
  212. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
  213. ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
  214. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
  215. ansible_test/_util/target/setup/bootstrap.sh +31 -0
  216. ansible_test/_util/target/setup/requirements.py +3 -9
  217. ansible/_internal/_errors/_utils.py +0 -310
  218. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
  219. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
  220. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  221. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  222. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  223. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  224. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  225. {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
@@ -75,7 +75,7 @@ except ImportError:
75
75
  # Python2 & 3 way to get NoneType
76
76
  NoneType = type(None)
77
77
 
78
- from ._internal import _traceback, _errors, _debugging, _deprecator
78
+ from ._internal import _traceback, _errors, _debugging, _deprecator, _messages
79
79
 
80
80
  from .common.text.converters import (
81
81
  to_native,
@@ -161,10 +161,10 @@ from ansible.module_utils.common.validation import (
161
161
  safe_eval,
162
162
  )
163
163
  from ansible.module_utils.common._utils import get_all_subclasses as _get_all_subclasses
164
- from ansible.module_utils.common import messages as _messages
165
164
  from ansible.module_utils.parsing.convert_bool import BOOLEANS, BOOLEANS_FALSE, BOOLEANS_TRUE, boolean
166
165
  from ansible.module_utils.common.warnings import (
167
166
  deprecate,
167
+ error_as_warning,
168
168
  get_deprecations,
169
169
  get_warnings,
170
170
  warn,
@@ -480,9 +480,11 @@ class AnsibleModule(object):
480
480
  if basedir is not None and not os.path.exists(basedir):
481
481
  try:
482
482
  os.makedirs(basedir, mode=0o700)
483
- except (OSError, IOError) as e:
484
- self.warn("Unable to use %s as temporary directory, "
485
- "failing back to system: %s" % (basedir, to_native(e)))
483
+ except OSError as ex:
484
+ self.error_as_warning(
485
+ msg=f"Unable to use {basedir!r} as temporary directory, falling back to system default.",
486
+ exception=ex,
487
+ )
486
488
  basedir = None
487
489
  else:
488
490
  self.warn("Module remote_tmp %s did not exist and was "
@@ -494,20 +496,45 @@ class AnsibleModule(object):
494
496
  basefile = "ansible-moduletmp-%s-" % time.time()
495
497
  try:
496
498
  tmpdir = tempfile.mkdtemp(prefix=basefile, dir=basedir)
497
- except (OSError, IOError) as e:
498
- self.fail_json(
499
- msg="Failed to create remote module tmp path at dir %s "
500
- "with prefix %s: %s" % (basedir, basefile, to_native(e))
501
- )
499
+ except OSError as ex:
500
+ raise Exception(
501
+ f"Failed to create remote module tmp path at dir {basedir!r} "
502
+ f"with prefix {basefile!r}.",
503
+ ) from ex
502
504
  if not self._keep_remote_files:
503
505
  atexit.register(shutil.rmtree, tmpdir)
504
506
  self._tmpdir = tmpdir
505
507
 
506
508
  return self._tmpdir
507
509
 
508
- def warn(self, warning):
509
- warn(warning)
510
- self.log('[WARNING] %s' % warning)
510
+ def warn(
511
+ self,
512
+ warning: str,
513
+ *,
514
+ help_text: str | None = None,
515
+ ) -> None:
516
+ _skip_stackwalk = True
517
+
518
+ warn(
519
+ warning=warning,
520
+ help_text=help_text,
521
+ )
522
+
523
+ def error_as_warning(
524
+ self,
525
+ msg: str | None,
526
+ exception: BaseException,
527
+ *,
528
+ help_text: str | None = None,
529
+ ) -> None:
530
+ """Display an exception as a warning."""
531
+ _skip_stackwalk = True
532
+
533
+ error_as_warning(
534
+ msg=msg,
535
+ exception=exception,
536
+ help_text=help_text,
537
+ )
511
538
 
512
539
  def deprecate(
513
540
  self,
@@ -633,11 +660,8 @@ class AnsibleModule(object):
633
660
  return context
634
661
  try:
635
662
  ret = selinux.lgetfilecon_raw(to_native(path, errors='surrogate_or_strict'))
636
- except OSError as e:
637
- if e.errno == errno.ENOENT:
638
- self.fail_json(path=path, msg='path %s does not exist' % path)
639
- else:
640
- self.fail_json(path=path, msg='failed to retrieve selinux context')
663
+ except OSError as ex:
664
+ self.fail_json(path=path, msg='Failed to retrieve selinux context.', exception=ex)
641
665
  if ret[0] == -1:
642
666
  return context
643
667
  # Limit split to 4 because the selevel, the last in the list,
@@ -777,9 +801,9 @@ class AnsibleModule(object):
777
801
  return True
778
802
  try:
779
803
  os.lchown(b_path, uid, -1)
780
- except (IOError, OSError) as e:
804
+ except OSError as ex:
781
805
  path = to_text(b_path)
782
- self.fail_json(path=path, msg='chown failed: %s' % (to_text(e)))
806
+ self.fail_json(path=path, msg='chown failed', exception=ex)
783
807
  changed = True
784
808
  return changed
785
809
 
@@ -1305,7 +1329,7 @@ class AnsibleModule(object):
1305
1329
  else:
1306
1330
  journal.send(MESSAGE=u"%s %s" % (module, journal_msg),
1307
1331
  **dict(journal_args))
1308
- except IOError:
1332
+ except OSError:
1309
1333
  # fall back to syslog since logging to journal failed
1310
1334
  self._log_to_syslog(journal_msg)
1311
1335
  else:
@@ -1399,7 +1423,7 @@ class AnsibleModule(object):
1399
1423
  # deprecate(
1400
1424
  # msg="The `AnsibleModule.jsonify' method is deprecated.",
1401
1425
  # version="2.27",
1402
- # # help_text="", # DTFIX-RELEASE: fill in this help text
1426
+ # # help_text="", # DTFIX-FUTURE: fill in this help text
1403
1427
  # )
1404
1428
 
1405
1429
  try:
@@ -1528,16 +1552,25 @@ class AnsibleModule(object):
1528
1552
  )
1529
1553
 
1530
1554
  if isinstance(exception, BaseException):
1531
- # Include a `_messages.ErrorDetail` in the result.
1532
- # The `msg` is included in the list of errors to ensure it is not lost when looking only at `exception` from the result.
1533
-
1534
- error_summary = _errors.create_error_summary(exception)
1535
- error_summary = _dataclasses.replace(error_summary, details=(_messages.Detail(msg=msg),) + error_summary.details)
1536
-
1537
- kwargs.update(exception=error_summary)
1555
+ # Include a `_messages.Event` in the result.
1556
+ # The `msg` is included in the chain to ensure it is not lost when looking only at `exception` from the result.
1557
+
1558
+ kwargs.update(
1559
+ exception=_messages.ErrorSummary(
1560
+ event=_messages.Event(
1561
+ msg=msg,
1562
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR),
1563
+ chain=_messages.EventChain(
1564
+ msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
1565
+ traceback_reason="The above exception was the direct cause of the following error:",
1566
+ event=_errors.EventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR)),
1567
+ ),
1568
+ ),
1569
+ ),
1570
+ )
1538
1571
  elif _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR):
1539
1572
  # Include only a formatted traceback string in the result.
1540
- # The controller will combine this with `msg` to create an `_messages.ErrorDetail`.
1573
+ # The controller will combine this with `msg` to create an `_messages.ErrorSummary`.
1541
1574
 
1542
1575
  formatted_traceback: str | None
1543
1576
 
@@ -1546,7 +1579,7 @@ class AnsibleModule(object):
1546
1579
  elif exception is _UNSET and (current_exception := t.cast(t.Optional[BaseException], sys.exc_info()[1])):
1547
1580
  formatted_traceback = _traceback.maybe_extract_traceback(current_exception, _traceback.TracebackEvent.ERROR)
1548
1581
  else:
1549
- formatted_traceback = _traceback.maybe_capture_traceback(_traceback.TracebackEvent.ERROR)
1582
+ formatted_traceback = _traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR)
1550
1583
 
1551
1584
  if formatted_traceback:
1552
1585
  kwargs.update(exception=formatted_traceback)
@@ -1626,8 +1659,8 @@ class AnsibleModule(object):
1626
1659
 
1627
1660
  try:
1628
1661
  self.preserved_copy(fn, backupdest)
1629
- except (shutil.Error, IOError) as e:
1630
- self.fail_json(msg='Could not make backup of %s to %s: %s' % (fn, backupdest, to_native(e)))
1662
+ except (shutil.Error, OSError) as ex:
1663
+ raise Exception(f'Could not make backup of {fn!r} to {backupdest!r}.') from ex
1631
1664
 
1632
1665
  return backupdest
1633
1666
 
@@ -1701,28 +1734,25 @@ class AnsibleModule(object):
1701
1734
  try:
1702
1735
  # Optimistically try a rename, solves some corner cases and can avoid useless work, throws exception if not atomic.
1703
1736
  os.rename(b_src, b_dest)
1704
- except (IOError, OSError) as e:
1705
- if e.errno not in [errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY, errno.EBUSY]:
1737
+ except OSError as ex:
1738
+ if ex.errno in (errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY, errno.EBUSY):
1706
1739
  # only try workarounds for errno 18 (cross device), 1 (not permitted), 13 (permission denied)
1707
1740
  # and 26 (text file busy) which happens on vagrant synced folders and other 'exotic' non posix file systems
1708
- self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, to_native(e)))
1709
- else:
1710
1741
  # Use bytes here. In the shippable CI, this fails with
1711
1742
  # a UnicodeError with surrogateescape'd strings for an unknown
1712
1743
  # reason (doesn't happen in a local Ubuntu16.04 VM)
1713
1744
  b_dest_dir = os.path.dirname(b_dest)
1714
1745
  b_suffix = os.path.basename(b_dest)
1715
- error_msg = None
1716
1746
  tmp_dest_name = None
1717
1747
  try:
1718
1748
  tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=b'.ansible_tmp', dir=b_dest_dir, suffix=b_suffix)
1719
- except (OSError, IOError) as e:
1720
- error_msg = 'The destination directory (%s) is not writable by the current user. Error was: %s' % (os.path.dirname(dest), to_native(e))
1721
-
1749
+ except OSError as ex:
1722
1750
  if unsafe_writes:
1723
1751
  self._unsafe_writes(b_src, b_dest)
1724
1752
  else:
1725
- self.fail_json(msg=error_msg)
1753
+ raise Exception(
1754
+ f'The destination directory {os.path.dirname(dest)!r} is not writable by the current user.'
1755
+ ) from ex
1726
1756
 
1727
1757
  if tmp_dest_name:
1728
1758
  b_tmp_dest_name = to_bytes(tmp_dest_name, errors='surrogate_or_strict')
@@ -1751,24 +1781,27 @@ class AnsibleModule(object):
1751
1781
  if dest_stat and (tmp_stat.st_uid != dest_stat.st_uid or tmp_stat.st_gid != dest_stat.st_gid):
1752
1782
  os.chown(b_tmp_dest_name, dest_stat.st_uid, dest_stat.st_gid)
1753
1783
  os.utime(b_tmp_dest_name, times=(time.time(), time.time()))
1754
- except OSError as e:
1755
- if e.errno != errno.EPERM:
1784
+ except OSError as ex:
1785
+ if ex.errno != errno.EPERM:
1756
1786
  raise
1757
1787
  try:
1758
1788
  os.rename(b_tmp_dest_name, b_dest)
1759
- except (shutil.Error, OSError, IOError) as e:
1760
- if unsafe_writes and e.errno == errno.EBUSY:
1789
+ except (shutil.Error, OSError) as ex:
1790
+ if unsafe_writes and ex.errno == errno.EBUSY:
1761
1791
  self._unsafe_writes(b_tmp_dest_name, b_dest)
1762
1792
  else:
1763
- self.fail_json(msg='Unable to make %s into to %s, failed final rename from %s: %s' %
1764
- (src, dest, b_tmp_dest_name, to_native(e)))
1765
- except (shutil.Error, OSError, IOError) as e:
1793
+ raise Exception(
1794
+ f'Unable to make {src!r} into to {dest!r}, failed final rename from {to_text(b_tmp_dest_name)!r}.'
1795
+ ) from ex
1796
+ except (shutil.Error, OSError) as ex:
1766
1797
  if unsafe_writes:
1767
1798
  self._unsafe_writes(b_src, b_dest)
1768
1799
  else:
1769
- self.fail_json(msg='Failed to replace file: %s to %s: %s' % (src, dest, to_native(e)))
1800
+ raise Exception(f'Failed to replace {dest!r} with {src!r}.') from ex
1770
1801
  finally:
1771
1802
  self.cleanup(b_tmp_dest_name)
1803
+ else:
1804
+ raise Exception(f'Could not replace {dest!r} with {src!r}.') from ex
1772
1805
 
1773
1806
  if creating:
1774
1807
  # make sure the file has the correct permissions
@@ -1795,18 +1828,11 @@ class AnsibleModule(object):
1795
1828
  # sadly there are some situations where we cannot ensure atomicity, but only if
1796
1829
  # the user insists and we get the appropriate error we update the file unsafely
1797
1830
  try:
1798
- out_dest = in_src = None
1799
- try:
1800
- out_dest = open(dest, 'wb')
1801
- in_src = open(src, 'rb')
1802
- shutil.copyfileobj(in_src, out_dest)
1803
- finally: # assuring closed files in 2.4 compatible way
1804
- if out_dest:
1805
- out_dest.close()
1806
- if in_src:
1807
- in_src.close()
1808
- except (shutil.Error, OSError, IOError) as e:
1809
- self.fail_json(msg='Could not write data to file (%s) from (%s): %s' % (dest, src, to_native(e)))
1831
+ with open(dest, 'wb') as out_dest:
1832
+ with open(src, 'rb') as in_src:
1833
+ shutil.copyfileobj(in_src, out_dest)
1834
+ except (shutil.Error, OSError) as ex:
1835
+ raise Exception(f'Could not write data to file {dest!r} from {src!r}.') from ex
1810
1836
 
1811
1837
  def _clean_args(self, args):
1812
1838
 
@@ -2092,18 +2118,16 @@ class AnsibleModule(object):
2092
2118
  selector.close()
2093
2119
 
2094
2120
  rc = cmd.returncode
2095
- except (OSError, IOError) as e:
2096
- self.log("Error Executing CMD:%s Exception:%s" % (self._clean_args(args), to_native(e)))
2121
+ except OSError as ex:
2097
2122
  if handle_exceptions:
2098
- self.fail_json(rc=e.errno, stdout=b'', stderr=b'', msg=to_native(e), cmd=self._clean_args(args))
2123
+ self.fail_json(rc=ex.errno, stdout='', stderr='', msg="Error executing command.", cmd=self._clean_args(args), exception=ex)
2099
2124
  else:
2100
- raise e
2101
- except Exception as e:
2102
- self.log("Error Executing CMD:%s Exception:%s" % (self._clean_args(args), to_native(traceback.format_exc())))
2125
+ raise
2126
+ except Exception as ex:
2103
2127
  if handle_exceptions:
2104
- self.fail_json(rc=257, stdout=b'', stderr=b'', msg=to_native(e), cmd=self._clean_args(args))
2128
+ self.fail_json(rc=257, stdout='', stderr='', msg="Error executing command.", cmd=self._clean_args(args), exception=ex)
2105
2129
  else:
2106
- raise e
2130
+ raise
2107
2131
 
2108
2132
  if rc != 0 and check_rc:
2109
2133
  msg = heuristic_log_sanitize(stderr.rstrip(), self.no_log_values)
@@ -2174,7 +2198,7 @@ def __getattr__(importable_name):
2174
2198
  importable = repeat
2175
2199
  elif importable_name in {
2176
2200
  'PY2', 'PY3', 'b', 'binary_type', 'integer_types',
2177
- 'iteritems', 'string_types', 'test_type'
2201
+ 'iteritems', 'string_types', 'text_type',
2178
2202
  }:
2179
2203
  import importlib
2180
2204
  importable = getattr(
@@ -6,6 +6,7 @@ from __future__ import annotations
6
6
 
7
7
  from copy import deepcopy
8
8
 
9
+ from ansible.module_utils.datatag import deprecator_from_collection_name
9
10
  from ansible.module_utils.common.parameters import (
10
11
  _ADDITIONAL_CHECKS,
11
12
  _get_legal_inputs,
@@ -22,7 +23,6 @@ from ansible.module_utils.common.parameters import (
22
23
 
23
24
  from ansible.module_utils.common.text.converters import to_native
24
25
  from ansible.module_utils.common.warnings import deprecate, warn
25
- from ansible.module_utils.common import messages as _messages
26
26
 
27
27
  from ansible.module_utils.common.validation import (
28
28
  check_mutually_exclusive,
@@ -306,7 +306,7 @@ class ModuleArgumentSpecValidator(ArgumentSpecValidator):
306
306
  msg=d['msg'],
307
307
  version=d.get('version'),
308
308
  date=d.get('date'),
309
- deprecator=_messages.PluginInfo._from_collection_name(d.get('collection_name')),
309
+ deprecator=deprecator_from_collection_name(d.get('collection_name')),
310
310
  )
311
311
 
312
312
  for w in result._warnings:
@@ -6,6 +6,7 @@
6
6
  from __future__ import annotations
7
7
 
8
8
 
9
+ from ansible.module_utils.common import warnings as _warnings
9
10
  from ansible.module_utils.six import binary_type, text_type
10
11
  from ansible.module_utils.six.moves.collections_abc import Hashable, Mapping, MutableMapping, Sequence # pylint: disable=unused-import
11
12
 
@@ -102,6 +103,11 @@ def count(seq):
102
103
  code is run on Python 2.6.* where collections.Counter is not available. It should be
103
104
  deprecated and replaced when support for Python < 2.7 is dropped.
104
105
  """
106
+ _warnings.deprecate(
107
+ msg="The `ansible.module_utils.common.collections.count` function is deprecated.",
108
+ version="2.23",
109
+ help_text="Use `collections.Counter` from the Python standard library instead.",
110
+ )
105
111
  if not is_iterable(seq):
106
112
  raise Exception('Argument provided is not an iterable')
107
113
  counters = dict()
@@ -20,7 +20,7 @@ def __getattr__(name: str) -> object:
20
20
  # _warnings.deprecate(
21
21
  # msg="The `AnsibleJSONEncoder` type is deprecated.",
22
22
  # version="2.27",
23
- # help_text="Use a profile-based encoder instead.", # DTFIX-RELEASE: improve this help text
23
+ # help_text="Use a profile-based encoder instead.", # DTFIX-FUTURE: improve this help text
24
24
  # )
25
25
 
26
26
  return _get_legacy_encoder()
@@ -31,7 +31,7 @@ def __getattr__(name: str) -> object:
31
31
  # _warnings.deprecate(
32
32
  # msg="The `AnsibleJSONDecoder` type is deprecated.",
33
33
  # version="2.27",
34
- # help_text="Use a profile-based decoder instead.", # DTFIX-RELEASE: improve this help text
34
+ # help_text="Use a profile-based decoder instead.", # DTFIX-FUTURE: improve this help text
35
35
  # )
36
36
 
37
37
  return _tagless.Decoder
@@ -10,6 +10,7 @@ import sys
10
10
  import typing as t
11
11
 
12
12
  from ansible.module_utils.common.text.converters import to_bytes
13
+ from ansible.module_utils._internal._ansiballz import _respawn
13
14
 
14
15
  _ANSIBLE_PARENT_PATH = pathlib.Path(__file__).parents[3]
15
16
 
@@ -39,7 +40,7 @@ def respawn_module(interpreter_path) -> t.NoReturn:
39
40
  raise Exception('module has already been respawned')
40
41
 
41
42
  # FUTURE: we need a safe way to log that a respawn has occurred for forensic/debug purposes
42
- payload = _create_payload()
43
+ payload = _respawn.create_payload()
43
44
  stdin_read, stdin_write = os.pipe()
44
45
  os.write(stdin_write, to_bytes(payload))
45
46
  os.close(stdin_write)
@@ -59,10 +60,12 @@ def probe_interpreters_for_module(interpreter_paths, module_name):
59
60
  :arg module_name: fully-qualified Python module name to probe for (for example, ``selinux``)
60
61
  """
61
62
  PYTHONPATH = os.getenv('PYTHONPATH', '')
63
+
62
64
  env = os.environ.copy()
63
65
  env.update({
64
66
  'PYTHONPATH': f'{_ANSIBLE_PARENT_PATH}:{PYTHONPATH}'.rstrip(': ')
65
67
  })
68
+
66
69
  for interpreter_path in interpreter_paths:
67
70
  if not os.path.exists(interpreter_path):
68
71
  continue
@@ -81,43 +84,3 @@ def probe_interpreters_for_module(interpreter_paths, module_name):
81
84
  continue
82
85
 
83
86
  return None
84
-
85
-
86
- def _create_payload():
87
- # FIXME: move this into _ansiballz and skip the template
88
- from ansible.module_utils import basic
89
-
90
- module_fqn = sys.modules['__main__']._module_fqn
91
- modlib_path = sys.modules['__main__']._modlib_path
92
-
93
- respawn_code_template = """
94
- if __name__ == '__main__':
95
- import runpy
96
- import sys
97
-
98
- json_params = {json_params!r}
99
- profile = {profile!r}
100
- module_fqn = {module_fqn!r}
101
- modlib_path = {modlib_path!r}
102
-
103
- sys.path.insert(0, modlib_path)
104
-
105
- from ansible.module_utils._internal import _ansiballz
106
-
107
- _ansiballz.run_module(
108
- json_params=json_params,
109
- profile=profile,
110
- module_fqn=module_fqn,
111
- modlib_path=modlib_path,
112
- init_globals=dict(_respawned=True),
113
- )
114
- """
115
-
116
- respawn_code = respawn_code_template.format(
117
- json_params=basic._ANSIBLE_ARGS,
118
- profile=basic._ANSIBLE_PROFILE,
119
- module_fqn=module_fqn,
120
- modlib_path=modlib_path,
121
- )
122
-
123
- return respawn_code
@@ -245,7 +245,7 @@ def jsonify(data, **kwargs):
245
245
  # deprecate(
246
246
  # msg="The `jsonify` function is deprecated.",
247
247
  # version="2.27",
248
- # # help_text="", # DTFIX-RELEASE: fill in this help text
248
+ # # help_text="", # DTFIX-FUTURE: fill in this help text
249
249
  # )
250
250
 
251
251
  return json.dumps(data, cls=_common_json._get_legacy_encoder(), _decode_bytes=True, **kwargs)
@@ -257,7 +257,7 @@ def container_to_bytes(d, encoding='utf-8', errors='surrogate_or_strict'):
257
257
  Specialized for json return because this only handles, lists, tuples,
258
258
  and dict container types (the containers that the json module returns)
259
259
  """
260
- # DTFIX-RELEASE: deprecate
260
+ # DTFIX-FUTURE: deprecate
261
261
 
262
262
  if isinstance(d, text_type):
263
263
  return to_bytes(d, encoding=encoding, errors=errors)
@@ -277,7 +277,7 @@ def container_to_text(d, encoding='utf-8', errors='surrogate_or_strict'):
277
277
  Specialized for json return because this only handles, lists, tuples,
278
278
  and dict container types (the containers that the json module returns)
279
279
  """
280
- # DTFIX-RELEASE: deprecate
280
+ # DTFIX-FUTURE: deprecate
281
281
 
282
282
  if isinstance(d, binary_type):
283
283
  # Warning, can traceback
@@ -402,7 +402,7 @@ def check_type_list(value):
402
402
  if isinstance(value, list):
403
403
  return value
404
404
 
405
- # DTFIX-RELEASE: deprecate legacy comma split functionality, eventually replace with `_check_type_list_strict`
405
+ # DTFIX-FUTURE: deprecate legacy comma split functionality, eventually replace with `_check_type_list_strict`
406
406
  if isinstance(value, string_types):
407
407
  return value.split(",")
408
408
  elif isinstance(value, int) or isinstance(value, float):
@@ -6,20 +6,77 @@ from __future__ import annotations as _annotations
6
6
 
7
7
  import typing as _t
8
8
 
9
- from ansible.module_utils._internal import _traceback, _deprecator
10
- from ansible.module_utils.common import messages as _messages
9
+ from ansible.module_utils._internal import _traceback, _deprecator, _event_utils, _messages, _errors
11
10
  from ansible.module_utils import _internal
12
11
 
13
12
 
14
- def warn(warning: str) -> None:
13
+ def warn(
14
+ warning: str,
15
+ *,
16
+ help_text: str | None = None,
17
+ obj: object | None = None,
18
+ ) -> None:
15
19
  """Record a warning to be returned with the module result."""
16
- # DTFIX-RELEASE: shim to controller display warning like `deprecate`
17
- _global_warnings[_messages.WarningSummary(
18
- details=(
19
- _messages.Detail(msg=warning),
20
+ _skip_stackwalk = True
21
+
22
+ if _internal.is_controller:
23
+ _display = _internal.import_controller_module('ansible.utils.display').Display()
24
+ _display.warning(
25
+ msg=warning,
26
+ help_text=help_text,
27
+ obj=obj,
28
+ )
29
+
30
+ return
31
+
32
+ warning = _messages.WarningSummary(
33
+ event=_messages.Event(
34
+ msg=warning,
35
+ help_text=help_text,
36
+ formatted_traceback=_traceback.maybe_capture_traceback(warning, _traceback.TracebackEvent.WARNING),
37
+ ),
38
+ )
39
+
40
+ _global_warnings[warning] = None
41
+
42
+
43
+ def error_as_warning(
44
+ msg: str | None,
45
+ exception: BaseException,
46
+ *,
47
+ help_text: str | None = None,
48
+ obj: object = None,
49
+ ) -> None:
50
+ """Display an exception as a warning."""
51
+ _skip_stackwalk = True
52
+
53
+ if _internal.is_controller:
54
+ _display = _internal.import_controller_module('ansible.utils.display').Display()
55
+ _display.error_as_warning(
56
+ msg=msg,
57
+ exception=exception,
58
+ help_text=help_text,
59
+ obj=obj,
60
+ )
61
+
62
+ return
63
+
64
+ event = _errors.EventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.WARNING))
65
+
66
+ warning = _messages.WarningSummary(
67
+ event=_messages.Event(
68
+ msg=msg,
69
+ help_text=help_text,
70
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.WARNING),
71
+ chain=_messages.EventChain(
72
+ msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
73
+ traceback_reason=_errors.TRACEBACK_REASON_EXCEPTION_DIRECT_WARNING,
74
+ event=event,
75
+ ),
20
76
  ),
21
- formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.WARNING),
22
- )] = None
77
+ )
78
+
79
+ _global_warnings[warning] = None
23
80
 
24
81
 
25
82
  def deprecate(
@@ -57,30 +114,30 @@ def deprecate(
57
114
 
58
115
  return
59
116
 
60
- _global_deprecations[_messages.DeprecationSummary(
61
- details=(
62
- _messages.Detail(msg=msg, help_text=help_text),
117
+ warning = _messages.DeprecationSummary(
118
+ event=_messages.Event(
119
+ msg=msg,
120
+ help_text=help_text,
121
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.DEPRECATED),
63
122
  ),
64
- formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.DEPRECATED),
65
123
  version=version,
66
124
  date=date,
67
125
  deprecator=deprecator,
68
- )] = None
126
+ )
127
+
128
+ _global_deprecations[warning] = None
69
129
 
70
130
 
71
131
  def get_warning_messages() -> tuple[str, ...]:
72
132
  """Return a tuple of warning messages accumulated over this run."""
73
- # DTFIX-RELEASE: add future deprecation comment
74
- return tuple(item._format() for item in _global_warnings)
75
-
76
-
77
- _DEPRECATION_MESSAGE_KEYS = frozenset({'msg', 'date', 'version', 'collection_name'})
133
+ # DTFIX7: add future deprecation comment
134
+ return tuple(_event_utils.format_event_brief_message(item.event) for item in _global_warnings)
78
135
 
79
136
 
80
137
  def get_deprecation_messages() -> tuple[dict[str, _t.Any], ...]:
81
138
  """Return a tuple of deprecation warning messages accumulated over this run."""
82
- # DTFIX-RELEASE: add future deprecation comment
83
- return tuple({key: value for key, value in item._as_simple_dict().items() if key in _DEPRECATION_MESSAGE_KEYS} for item in _global_deprecations)
139
+ # DTFIX7: add future deprecation comment
140
+ return tuple(_event_utils.deprecation_as_dict(item) for item in _global_deprecations)
84
141
 
85
142
 
86
143
  def get_warnings() -> list[_messages.WarningSummary]:
@@ -94,7 +151,7 @@ def get_deprecations() -> list[_messages.DeprecationSummary]:
94
151
 
95
152
 
96
153
  _global_warnings: dict[_messages.WarningSummary, object] = {}
97
- """Global, ordered, de-deplicated storage of acculumated warnings for the current module run."""
154
+ """Global, ordered, de-duplicated storage of accumulated warnings for the current module run."""
98
155
 
99
156
  _global_deprecations: dict[_messages.DeprecationSummary, object] = {}
100
- """Global, ordered, de-deplicated storage of acculumated deprecations for the current module run."""
157
+ """Global, ordered, de-duplicated storage of accumulated deprecations for the current module run."""
@@ -24,7 +24,7 @@ except ImportError:
24
24
  else:
25
25
  HAS_YAML = True
26
26
 
27
- # DTFIX-RELEASE: refactor this to share the implementation with the controller version
27
+ # DTFIX-FUTURE: refactor this to share the implementation with the controller version
28
28
  # use an abstract base class, with __init_subclass__ for representer registration, and instance methods for overridable representers
29
29
  # then tests can be consolidated intead of having two nearly identical copies
30
30