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
ansible/utils/display.py CHANGED
@@ -17,6 +17,7 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
+ import contextlib
20
21
  import dataclasses
21
22
 
22
23
  try:
@@ -51,13 +52,14 @@ from struct import unpack, pack
51
52
  from ansible import constants as C
52
53
  from ansible.constants import config
53
54
  from ansible.errors import AnsibleAssertionError, AnsiblePromptInterrupt, AnsiblePromptNoninteractive, AnsibleError
54
- from ansible._internal._errors import _utils
55
- from ansible.module_utils._internal import _ambient_context, _deprecator
55
+ from ansible._internal._errors import _error_utils, _error_factory
56
+ from ansible._internal import _event_formatting
57
+ from ansible.module_utils._internal import _ambient_context, _deprecator, _messages
56
58
  from ansible.module_utils.common.text.converters import to_bytes, to_text
59
+ from ansible.module_utils.datatag import deprecator_from_collection_name
57
60
  from ansible._internal._datatag._tags import TrustedAsTemplate
58
- from ansible.module_utils.common.messages import ErrorSummary, WarningSummary, DeprecationSummary, Detail, SummaryBase, PluginInfo
59
61
  from ansible.module_utils.six import text_type
60
- from ansible.module_utils._internal import _traceback
62
+ from ansible.module_utils._internal import _traceback, _errors
61
63
  from ansible.utils.color import stringc
62
64
  from ansible.utils.multiprocessing import context as multiprocessing_context
63
65
  from ansible.utils.singleton import Singleton
@@ -90,6 +92,9 @@ def _is_controller_traceback_enabled(event: _traceback.TracebackEvent) -> bool:
90
92
  if 'never' in flag_values:
91
93
  return False
92
94
 
95
+ if _traceback.TracebackEvent.DEPRECATED_VALUE.name.lower() in flag_values:
96
+ flag_values.add(_traceback.TracebackEvent.DEPRECATED.name.lower()) # DEPRECATED_VALUE implies DEPRECATED
97
+
93
98
  return event.name.lower() in flag_values
94
99
 
95
100
 
@@ -212,10 +217,22 @@ b_COW_PATHS = (
212
217
 
213
218
 
214
219
  def _synchronize_textiowrapper(tio: t.TextIO, lock: threading.RLock):
215
- # Ensure that a background thread can't hold the internal buffer lock on a file object
216
- # during a fork, which causes forked children to hang. We're using display's existing lock for
217
- # convenience (and entering the lock before a fork).
220
+ """
221
+ This decorator ensures that the supplied RLock is held before invoking the wrapped methods.
222
+ It is intended to prevent background threads from holding the Python stdout/stderr buffer lock on a file object during a fork.
223
+ Since background threads are abandoned in child forks, locks they hold are orphaned in a locked state.
224
+ Attempts to acquire an orphaned lock in this state will block forever, effectively hanging the child process on stdout/stderr writes.
225
+ The shared lock is permanently disabled immediately after a fork.
226
+ This prevents hangs in early post-fork code (e.g., stdio writes from pydevd, coverage, etc.) before user code has resumed and released the lock.
227
+ """
228
+
218
229
  def _wrap_with_lock(f, lock):
230
+ def disable_lock():
231
+ nonlocal lock
232
+ lock = contextlib.nullcontext()
233
+
234
+ os.register_at_fork(after_in_child=disable_lock)
235
+
219
236
  @wraps(f)
220
237
  def locking_wrapper(*args, **kwargs):
221
238
  with lock:
@@ -433,6 +450,10 @@ class Display(metaclass=Singleton):
433
450
  if not isinstance(msg, str):
434
451
  raise TypeError(f'Display message must be str, not: {msg.__class__.__name__}')
435
452
 
453
+ # Convert Windows newlines to Unix newlines.
454
+ # Some environments, such as Azure Pipelines, render `\r` as an additional `\n`.
455
+ msg = msg.replace('\r\n', '\n')
456
+
436
457
  nocolor = msg
437
458
 
438
459
  if not log_only:
@@ -466,7 +487,7 @@ class Display(metaclass=Singleton):
466
487
  # final flush at shutdown.
467
488
  # try:
468
489
  # fileobj.flush()
469
- # except IOError as e:
490
+ # except OSError as e:
470
491
  # # Ignore EPIPE in case fileobj has been prematurely closed, eg.
471
492
  # # when piping to "head -n1"
472
493
  # if e.errno != errno.EPIPE:
@@ -568,7 +589,7 @@ class Display(metaclass=Singleton):
568
589
  version=version,
569
590
  removed=removed,
570
591
  date=date,
571
- deprecator=PluginInfo._from_collection_name(collection_name),
592
+ deprecator=deprecator_from_collection_name(collection_name),
572
593
  )
573
594
 
574
595
  if removed:
@@ -585,30 +606,28 @@ class Display(metaclass=Singleton):
585
606
  version: str | None,
586
607
  removed: bool = False,
587
608
  date: str | None,
588
- deprecator: PluginInfo | None,
609
+ deprecator: _messages.PluginInfo | None,
589
610
  ) -> str:
590
611
  """Internal use only. Return a deprecation message and help text for display."""
591
- # DTFIX-RELEASE: the logic for omitting date/version doesn't apply to the payload, so it shows up in vars in some cases when it should not
612
+ # DTFIX-FUTURE: the logic for omitting date/version doesn't apply to the payload, so it shows up in vars in some cases when it should not
592
613
 
593
614
  if removed:
594
615
  removal_fragment = 'This feature was removed'
595
616
  else:
596
617
  removal_fragment = 'This feature will be removed'
597
618
 
598
- if not deprecator or deprecator.type == _deprecator.INDETERMINATE_DEPRECATOR.type:
599
- collection = None
600
- plugin_fragment = ''
601
- elif deprecator.type == _deprecator.PluginInfo._COLLECTION_ONLY_TYPE:
602
- collection = deprecator.resolved_name
619
+ if not deprecator or not deprecator.type:
620
+ # indeterminate has no resolved_name or type
621
+ # collections have a resolved_name but no type
622
+ collection = deprecator.resolved_name if deprecator else None
603
623
  plugin_fragment = ''
604
624
  else:
605
625
  parts = deprecator.resolved_name.split('.')
606
626
  plugin_name = parts[-1]
607
- # DTFIX-RELEASE: normalize 'modules' -> 'module' before storing it so we can eliminate the normalization here
608
- plugin_type = "module" if deprecator.type in ("module", "modules") else f'{deprecator.type} plugin'
627
+ plugin_type_name = str(deprecator.type) if deprecator.type is _messages.PluginType.MODULE else f'{deprecator.type} plugin'
609
628
 
610
629
  collection = '.'.join(parts[:2]) if len(parts) > 2 else None
611
- plugin_fragment = f'{plugin_type} {plugin_name!r}'
630
+ plugin_fragment = f'{plugin_type_name} {plugin_name!r}'
612
631
 
613
632
  if collection and plugin_fragment:
614
633
  plugin_fragment += ' in'
@@ -668,7 +687,7 @@ class Display(metaclass=Singleton):
668
687
  date: str | None = None,
669
688
  collection_name: str | None = None,
670
689
  *,
671
- deprecator: PluginInfo | None = None,
690
+ deprecator: _messages.PluginInfo | None = None,
672
691
  help_text: str | None = None,
673
692
  obj: t.Any = None,
674
693
  ) -> None:
@@ -678,8 +697,8 @@ class Display(metaclass=Singleton):
678
697
  Specify `version` or `date`, but not both.
679
698
  If `date` is a string, it must be in the form `YYYY-MM-DD`.
680
699
  """
681
- # DTFIX-RELEASE: are there any deprecation calls where the feature is switching from enabled to disabled, rather than being removed entirely?
682
- # DTFIX-RELEASE: are there deprecated features which should going through deferred deprecation instead?
700
+ # DTFIX3: are there any deprecation calls where the feature is switching from enabled to disabled, rather than being removed entirely?
701
+ # DTFIX3: are there deprecated features which should going through deferred deprecation instead?
683
702
 
684
703
  _skip_stackwalk = True
685
704
 
@@ -691,6 +710,7 @@ class Display(metaclass=Singleton):
691
710
  help_text=help_text,
692
711
  obj=obj,
693
712
  deprecator=_deprecator.get_best_deprecator(deprecator=deprecator, collection_name=collection_name),
713
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.DEPRECATED),
694
714
  )
695
715
 
696
716
  def _deprecated_with_plugin_info(
@@ -702,7 +722,8 @@ class Display(metaclass=Singleton):
702
722
  date: str | None,
703
723
  help_text: str | None,
704
724
  obj: t.Any,
705
- deprecator: PluginInfo | None,
725
+ deprecator: _messages.PluginInfo | None,
726
+ formatted_traceback: str | None = None,
706
727
  ) -> None:
707
728
  """
708
729
  This is the internal pre-proxy half of the `deprecated` implementation.
@@ -721,23 +742,21 @@ class Display(metaclass=Singleton):
721
742
 
722
743
  raise AnsibleError(formatted_msg)
723
744
 
724
- if source_context := _utils.SourceContext.from_value(obj):
745
+ if source_context := _error_utils.SourceContext.from_value(obj):
725
746
  formatted_source_context = str(source_context)
726
747
  else:
727
748
  formatted_source_context = None
728
749
 
729
- deprecation = DeprecationSummary(
730
- details=(
731
- Detail(
732
- msg=msg,
733
- formatted_source_context=formatted_source_context,
734
- help_text=help_text,
735
- ),
750
+ deprecation = _messages.DeprecationSummary(
751
+ event=_messages.Event(
752
+ msg=msg,
753
+ formatted_source_context=formatted_source_context,
754
+ help_text=help_text,
755
+ formatted_traceback=formatted_traceback,
736
756
  ),
737
757
  version=version,
738
758
  date=date,
739
759
  deprecator=deprecator,
740
- formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.DEPRECATED),
741
760
  )
742
761
 
743
762
  if warning_ctx := _DeferredWarningContext.current(optional=True):
@@ -747,7 +766,7 @@ class Display(metaclass=Singleton):
747
766
  self._deprecated(deprecation)
748
767
 
749
768
  @_proxy
750
- def _deprecated(self, warning: DeprecationSummary) -> None:
769
+ def _deprecated(self, warning: _messages.DeprecationSummary) -> None:
751
770
  """Internal implementation detail, use `deprecated` instead."""
752
771
 
753
772
  # This is the post-proxy half of the `deprecated` implementation.
@@ -758,10 +777,10 @@ class Display(metaclass=Singleton):
758
777
 
759
778
  self.warning('Deprecation warnings can be disabled by setting `deprecation_warnings=False` in ansible.cfg.')
760
779
 
761
- msg = format_message(warning)
780
+ msg = _format_message(warning, _traceback.is_traceback_enabled(_traceback.TracebackEvent.DEPRECATED))
762
781
  msg = f'[DEPRECATION WARNING]: {msg}'
763
782
 
764
- # DTFIX-RELEASE: what should we do with wrap_message?
783
+ # DTFIX3: what should we do with wrap_message?
765
784
  msg = self._wrap_message(msg=msg, wrap_text=True)
766
785
 
767
786
  if self._deduplicate(msg, self._deprecations):
@@ -778,47 +797,46 @@ class Display(metaclass=Singleton):
778
797
  obj: t.Any = None
779
798
  ) -> None:
780
799
  """Display a warning message."""
800
+ _skip_stackwalk = True
781
801
 
782
802
  # This is the pre-proxy half of the `warning` implementation.
783
803
  # Any logic that must occur on workers needs to be implemented here.
784
804
 
785
- if source_context := _utils.SourceContext.from_value(obj):
805
+ if source_context := _error_utils.SourceContext.from_value(obj):
786
806
  formatted_source_context = str(source_context)
787
807
  else:
788
808
  formatted_source_context = None
789
809
 
790
- warning = WarningSummary(
791
- details=(
792
- Detail(
793
- msg=msg,
794
- help_text=help_text,
795
- formatted_source_context=formatted_source_context,
796
- ),
810
+ warning = _messages.WarningSummary(
811
+ event=_messages.Event(
812
+ msg=msg,
813
+ help_text=help_text,
814
+ formatted_source_context=formatted_source_context,
815
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.WARNING),
797
816
  ),
798
- formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.WARNING),
799
817
  )
800
818
 
801
819
  if warning_ctx := _DeferredWarningContext.current(optional=True):
802
820
  warning_ctx.capture(warning)
803
- # DTFIX-RELEASE: what to do about propagating wrap_text?
821
+ # DTFIX3: what to do about propagating wrap_text?
804
822
  return
805
823
 
806
824
  self._warning(warning, wrap_text=not formatted)
807
825
 
808
826
  @_proxy
809
- def _warning(self, warning: WarningSummary, wrap_text: bool) -> None:
827
+ def _warning(self, warning: _messages.WarningSummary, wrap_text: bool) -> None:
810
828
  """Internal implementation detail, use `warning` instead."""
811
829
 
812
830
  # This is the post-proxy half of the `warning` implementation.
813
831
  # Any logic that must occur in the primary controller process needs to be implemented here.
814
832
 
815
- msg = format_message(warning)
833
+ msg = _format_message(warning, _traceback.is_traceback_enabled(_traceback.TracebackEvent.WARNING))
816
834
  msg = f"[WARNING]: {msg}"
817
835
 
818
836
  if self._deduplicate(msg, self._warns):
819
837
  return
820
838
 
821
- # DTFIX-RELEASE: what should we do with wrap_message?
839
+ # DTFIX3: what should we do with wrap_message?
822
840
  msg = self._wrap_message(msg=msg, wrap_text=wrap_text)
823
841
 
824
842
  self.display(msg, color=C.config.get_config_value('COLOR_WARN'), stderr=True, caplevel=-2)
@@ -870,17 +888,39 @@ class Display(metaclass=Singleton):
870
888
  (out, err) = cmd.communicate()
871
889
  self.display(u"%s\n" % to_text(out), color=color)
872
890
 
873
- def error_as_warning(self, msg: str | None, exception: BaseException) -> None:
891
+ def error_as_warning(
892
+ self,
893
+ msg: str | None,
894
+ exception: BaseException,
895
+ *,
896
+ help_text: str | None = None,
897
+ obj: t.Any = None,
898
+ ) -> None:
874
899
  """Display an exception as a warning."""
900
+ _skip_stackwalk = True
875
901
 
876
- error = _utils._create_error_summary(exception, _traceback.TracebackEvent.WARNING)
902
+ event = _error_factory.ControllerEventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.WARNING))
877
903
 
878
904
  if msg:
879
- error = dataclasses.replace(error, details=(Detail(msg=msg),) + error.details)
905
+ if source_context := _error_utils.SourceContext.from_value(obj):
906
+ formatted_source_context = str(source_context)
907
+ else:
908
+ formatted_source_context = None
880
909
 
881
- warning = WarningSummary(
882
- details=error.details,
883
- formatted_traceback=error.formatted_traceback,
910
+ event = _messages.Event(
911
+ msg=msg,
912
+ help_text=help_text,
913
+ formatted_source_context=formatted_source_context,
914
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.WARNING),
915
+ chain=_messages.EventChain(
916
+ msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
917
+ traceback_reason=_errors.TRACEBACK_REASON_EXCEPTION_DIRECT_WARNING,
918
+ event=event,
919
+ ),
920
+ )
921
+
922
+ warning = _messages.WarningSummary(
923
+ event=event,
884
924
  )
885
925
 
886
926
  if warning_ctx := _DeferredWarningContext.current(optional=True):
@@ -891,32 +931,41 @@ class Display(metaclass=Singleton):
891
931
 
892
932
  def error(self, msg: str | BaseException, wrap_text: bool = True, stderr: bool = True) -> None:
893
933
  """Display an error message."""
934
+ _skip_stackwalk = True
894
935
 
895
936
  # This is the pre-proxy half of the `error` implementation.
896
937
  # Any logic that must occur on workers needs to be implemented here.
897
938
 
898
939
  if isinstance(msg, BaseException):
899
- error = _utils._create_error_summary(msg, _traceback.TracebackEvent.ERROR)
940
+ event = _error_factory.ControllerEventFactory.from_exception(msg, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR))
941
+
900
942
  wrap_text = False
901
943
  else:
902
- error = ErrorSummary(details=(Detail(msg=msg),), formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.ERROR))
944
+ event = _messages.Event(
945
+ msg=msg,
946
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR),
947
+ )
948
+
949
+ error = _messages.ErrorSummary(
950
+ event=event,
951
+ )
903
952
 
904
953
  self._error(error, wrap_text=wrap_text, stderr=stderr)
905
954
 
906
955
  @_proxy
907
- def _error(self, error: ErrorSummary, wrap_text: bool, stderr: bool) -> None:
956
+ def _error(self, error: _messages.ErrorSummary, wrap_text: bool, stderr: bool) -> None:
908
957
  """Internal implementation detail, use `error` instead."""
909
958
 
910
959
  # This is the post-proxy half of the `error` implementation.
911
960
  # Any logic that must occur in the primary controller process needs to be implemented here.
912
961
 
913
- msg = format_message(error)
962
+ msg = _format_message(error, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR))
914
963
  msg = f'[ERROR]: {msg}'
915
964
 
916
965
  if self._deduplicate(msg, self._errors):
917
966
  return
918
967
 
919
- # DTFIX-RELEASE: what should we do with wrap_message?
968
+ # DTFIX3: what should we do with wrap_message?
920
969
  msg = self._wrap_message(msg=msg, wrap_text=wrap_text)
921
970
 
922
971
  self.display(msg, color=C.config.get_config_value('COLOR_ERROR'), stderr=stderr, caplevel=-1)
@@ -1146,9 +1195,9 @@ class _DeferredWarningContext(_ambient_context.AmbientContextBase):
1146
1195
 
1147
1196
  def __init__(self, *, variables: dict[str, object]) -> None:
1148
1197
  self._variables = variables # DTFIX-FUTURE: move this to an AmbientContext-derived TaskContext (once it exists)
1149
- self._deprecation_warnings: list[DeprecationSummary] = []
1150
- self._warnings: list[WarningSummary] = []
1151
- self._seen: set[WarningSummary] = set()
1198
+ self._deprecation_warnings: list[_messages.DeprecationSummary] = []
1199
+ self._warnings: list[_messages.WarningSummary] = []
1200
+ self._seen: set[_messages.WarningSummary] = set()
1152
1201
 
1153
1202
  @classmethod
1154
1203
  def deprecation_warnings_enabled(cls) -> bool:
@@ -1161,82 +1210,29 @@ class _DeferredWarningContext(_ambient_context.AmbientContextBase):
1161
1210
 
1162
1211
  return C.config.get_config_value('DEPRECATION_WARNINGS', variables=variables)
1163
1212
 
1164
- def capture(self, warning: WarningSummary) -> None:
1213
+ def capture(self, warning: _messages.WarningSummary) -> None:
1165
1214
  """Add the warning/deprecation to the context if it has not already been seen by this context."""
1166
1215
  if warning in self._seen:
1167
1216
  return
1168
1217
 
1169
1218
  self._seen.add(warning)
1170
1219
 
1171
- if isinstance(warning, DeprecationSummary):
1220
+ if isinstance(warning, _messages.DeprecationSummary):
1172
1221
  self._deprecation_warnings.append(warning)
1173
1222
  else:
1174
1223
  self._warnings.append(warning)
1175
1224
 
1176
- def get_warnings(self) -> list[WarningSummary]:
1225
+ def get_warnings(self) -> list[_messages.WarningSummary]:
1177
1226
  """Return a list of the captured non-deprecation warnings."""
1178
1227
  # DTFIX-FUTURE: return a read-only list proxy instead
1179
1228
  return self._warnings
1180
1229
 
1181
- def get_deprecation_warnings(self) -> list[DeprecationSummary]:
1230
+ def get_deprecation_warnings(self) -> list[_messages.DeprecationSummary]:
1182
1231
  """Return a list of the captured deprecation warnings."""
1183
1232
  # DTFIX-FUTURE: return a read-only list proxy instead
1184
1233
  return self._deprecation_warnings
1185
1234
 
1186
1235
 
1187
- def _format_error_details(details: t.Sequence[Detail], formatted_tb: str | None = None) -> str:
1188
- details = _utils._collapse_error_details(details)
1189
-
1190
- message_lines: list[str] = []
1191
-
1192
- if len(details) > 1:
1193
- message_lines.append(_utils._dedupe_and_concat_message_chain([md.msg for md in details]))
1194
- message_lines.append('')
1195
-
1196
- for idx, edc in enumerate(details):
1197
- if idx:
1198
- message_lines.extend((
1199
- '',
1200
- '<<< caused by >>>',
1201
- '',
1202
- ))
1203
-
1204
- message_lines.extend(_get_message_lines(edc.msg, edc.help_text, edc.formatted_source_context))
1205
-
1206
- message_lines = [f'{line}\n' for line in message_lines]
1207
-
1208
- if formatted_tb:
1209
- message_lines.append('\n')
1210
- message_lines.append(formatted_tb)
1211
-
1212
- msg = "".join(message_lines).strip()
1213
-
1214
- if '\n' in msg:
1215
- msg += '\n\n'
1216
- else:
1217
- msg += '\n'
1218
-
1219
- return msg
1220
-
1221
-
1222
- def _get_message_lines(message: str, help_text: str | None, formatted_source_context: str | None) -> list[str]:
1223
- """Return a list of error/warning message lines constructed from the given message, help text and source context."""
1224
-
1225
- if help_text and not formatted_source_context and '\n' not in message and '\n' not in help_text:
1226
- return [f'{message} {help_text}'] # prefer a single-line message with help text when there is no source context
1227
-
1228
- message_lines = [message]
1229
-
1230
- if formatted_source_context:
1231
- message_lines.append(formatted_source_context)
1232
-
1233
- if help_text:
1234
- message_lines.append('')
1235
- message_lines.append(help_text)
1236
-
1237
- return message_lines
1238
-
1239
-
1240
1236
  def _join_sentences(first: str | None, second: str | None) -> str:
1241
1237
  """Join two sentences together."""
1242
1238
  first = (first or '').strip()
@@ -1257,33 +1253,23 @@ def _join_sentences(first: str | None, second: str | None) -> str:
1257
1253
  return ' '.join((first, second))
1258
1254
 
1259
1255
 
1260
- def format_message(summary: SummaryBase) -> str:
1261
- details: c.Sequence[Detail] = summary.details
1262
-
1263
- if isinstance(summary, DeprecationSummary) and details:
1264
- # augment the first detail element for deprecations to include additional diagnostic info and help text
1265
- detail_list = list(details)
1266
- detail = detail_list[0]
1267
-
1268
- deprecation_msg = _display._get_deprecation_message_with_plugin_info(
1269
- msg=detail.msg,
1256
+ def _format_message(summary: _messages.SummaryBase, include_traceback: bool) -> str:
1257
+ if isinstance(summary, _messages.DeprecationSummary):
1258
+ deprecation_message = _display._get_deprecation_message_with_plugin_info(
1259
+ msg=summary.event.msg,
1270
1260
  version=summary.version,
1271
1261
  date=summary.date,
1272
1262
  deprecator=summary.deprecator,
1273
1263
  )
1274
1264
 
1275
- detail_list[0] = dataclasses.replace(
1276
- detail,
1277
- msg=deprecation_msg,
1278
- help_text=detail.help_text,
1279
- )
1280
-
1281
- details = detail_list
1265
+ event = dataclasses.replace(summary.event, msg=deprecation_message)
1266
+ else:
1267
+ event = summary.event
1282
1268
 
1283
- return _format_error_details(details, summary.formatted_traceback)
1269
+ return _event_formatting.format_event(event, include_traceback)
1284
1270
 
1285
1271
 
1286
- def _report_config_warnings(deprecator: PluginInfo) -> None:
1272
+ def _report_config_warnings(deprecator: _messages.PluginInfo) -> None:
1287
1273
  """Called by config to report warnings/deprecations collected during a config parse."""
1288
1274
  while config._errors:
1289
1275
  msg, exception = config._errors.pop()
ansible/utils/galaxy.py CHANGED
@@ -57,8 +57,8 @@ def scm_archive_resource(src, scm='git', name=None, version='HEAD', keep_scm_met
57
57
 
58
58
  try:
59
59
  scm_path = get_bin_path(scm)
60
- except (ValueError, OSError, IOError):
61
- raise AnsibleError("could not find/use %s, it is required to continue with installing %s" % (scm, src))
60
+ except (ValueError, OSError) as ex:
61
+ raise AnsibleError(f"Could not find/use {scm!r}, it is required to continue with installing {src!r}.") from ex
62
62
 
63
63
  tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP)
64
64
  clone_cmd = [scm_path, 'clone']
ansible/utils/hashing.py CHANGED
@@ -48,18 +48,16 @@ def secure_hash(filename, hash_func=sha1):
48
48
  digest = hash_func()
49
49
  blocksize = 64 * 1024
50
50
  try:
51
- infile = open(to_bytes(filename, errors='surrogate_or_strict'), 'rb')
52
- block = infile.read(blocksize)
53
- while block:
54
- digest.update(block)
51
+ with open(filename, 'rb') as infile:
55
52
  block = infile.read(blocksize)
56
- infile.close()
57
- except IOError as e:
58
- raise AnsibleError("error while accessing the file %s, error was: %s" % (filename, e))
53
+ while block:
54
+ digest.update(block)
55
+ block = infile.read(blocksize)
56
+ except OSError as ex:
57
+ raise AnsibleError(f"Error while accessing the file {filename!r}.") from ex
59
58
  return digest.hexdigest()
60
59
 
61
60
 
62
- # The checksum algorithm must match with the algorithm in ShellModule.checksum() method
63
61
  checksum = secure_hash
64
62
  checksum_s = secure_hash_s
65
63
 
ansible/utils/listify.py CHANGED
@@ -26,10 +26,12 @@ __all__ = ['listify_lookup_plugin_terms']
26
26
 
27
27
 
28
28
  def listify_lookup_plugin_terms(terms, templar=None, fail_on_undefined=True):
29
- display.deprecated(
30
- msg='"listify_lookup_plugin_terms" is obsolete and in most cases unnecessary',
31
- version='2.23',
32
- )
29
+ # deprecated: description="Calling listify_lookup_plugin_terms function is not necessary; the function should be deprecated." core_version="2.23"
30
+ # display.deprecated(
31
+ # msg='The "listify_lookup_plugin_terms" function is not required for lookup terms to be templated.',
32
+ # version='2.27',
33
+ # help_text='If needed, implement custom `strip` or list-wrapping in the caller.',
34
+ # )
33
35
 
34
36
  if isinstance(terms, str):
35
37
  terms = terms.strip()
ansible/utils/path.py CHANGED
@@ -19,9 +19,8 @@ from __future__ import annotations
19
19
  import os
20
20
  import shutil
21
21
 
22
- from errno import EEXIST
23
22
  from ansible.errors import AnsibleError
24
- from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
23
+ from ansible.module_utils.common.text.converters import to_bytes, to_text
25
24
 
26
25
 
27
26
  __all__ = ['unfrackpath', 'makedirs_safe']
@@ -84,12 +83,11 @@ def makedirs_safe(path, mode=None):
84
83
  if not os.path.exists(b_rpath):
85
84
  try:
86
85
  if mode:
87
- os.makedirs(b_rpath, mode)
86
+ os.makedirs(b_rpath, mode, exist_ok=True)
88
87
  else:
89
- os.makedirs(b_rpath)
90
- except OSError as e:
91
- if e.errno != EEXIST:
92
- raise AnsibleError("Unable to create local directories(%s): %s" % (to_native(rpath), to_native(e)))
88
+ os.makedirs(b_rpath, exist_ok=True)
89
+ except OSError as ex:
90
+ raise AnsibleError(f"Unable to create local directories {rpath!r}.") from ex
93
91
 
94
92
 
95
93
  def basedir(source):
@@ -19,8 +19,9 @@ def __getattr__(name):
19
19
  raise AttributeError(name)
20
20
 
21
21
  display.deprecated(
22
- msg='ansible.utils.py3compat.environ is deprecated in favor of os.environ.',
22
+ msg='`ansible.utils.py3compat.environ` is deprecated.',
23
23
  version='2.20',
24
+ help_text='Use `os.environ` from the Python standard library instead.',
24
25
  )
25
26
 
26
27
  return os.environ
@@ -57,8 +57,9 @@ def set_default_transport():
57
57
  # deal with 'smart' connection .. one time ..
58
58
  if C.DEFAULT_TRANSPORT == 'smart':
59
59
  display.deprecated(
60
- msg="The 'smart' option for connections is deprecated. Set the connection plugin directly instead.",
61
- version='2.20',
60
+ msg="The `smart` option for connections is deprecated.",
61
+ version="2.20",
62
+ help_text="Set the connection plugin directly instead.",
62
63
  )
63
64
 
64
65
  # see if SSH can support ControlPersist if not use paramiko
@@ -2,7 +2,7 @@
2
2
  # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
 
4
4
  # deprecated: description="deprecate unsafe_proxy module" core_version="2.23"
5
- # DTFIX-RELEASE: add full unit test coverage
5
+ # DTFIX5: add full unit test coverage
6
6
  from __future__ import annotations
7
7
 
8
8
  from collections.abc import Mapping, Set
ansible/vars/hostvars.py CHANGED
@@ -113,5 +113,5 @@ class HostVarsVars(c.Mapping):
113
113
  return self
114
114
 
115
115
 
116
- # DTFIX-RELEASE: is there a better way to add this to the ignorable types in the module_utils code
116
+ # DTFIX-FUTURE: is there a better way to add this to the ignorable types in the module_utils code
117
117
  _datatag._untaggable_types.update({HostVars, HostVarsVars})
ansible/vars/plugins.py CHANGED
@@ -35,10 +35,10 @@ def get_plugin_vars(loader, plugin, path, entities):
35
35
  if hasattr(plugin, 'get_host_vars') or hasattr(plugin, 'get_group_vars'):
36
36
  display.deprecated(
37
37
  msg=f"The vars plugin {plugin.ansible_name} from {plugin._original_path} is relying "
38
- "on the deprecated entrypoints 'get_host_vars' and 'get_group_vars'. "
39
- "This plugin should be updated to inherit from BaseVarsPlugin and define "
40
- "a 'get_vars' method as the main entrypoint instead.",
38
+ "on the deprecated entrypoints `get_host_vars` and `get_group_vars`.",
41
39
  version="2.20",
40
+ help_text="This plugin should be updated to inherit from `BaseVarsPlugin` and define "
41
+ "a `get_vars` method as the main entrypoint instead.",
42
42
  )
43
43
  try:
44
44
  for entity in entities:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ansible-core
3
- Version: 2.19.0b4
3
+ Version: 2.19.0b6
4
4
  Summary: Radically simple IT automation
5
5
  Author: Ansible Project
6
6
  Project-URL: Homepage, https://ansible.com/