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
ansible/utils/display.py CHANGED
@@ -51,13 +51,14 @@ from struct import unpack, pack
51
51
  from ansible import constants as C
52
52
  from ansible.constants import config
53
53
  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
54
+ from ansible._internal._errors import _error_utils, _error_factory
55
+ from ansible._internal import _event_formatting
56
+ from ansible.module_utils._internal import _ambient_context, _deprecator, _messages
56
57
  from ansible.module_utils.common.text.converters import to_bytes, to_text
58
+ from ansible.module_utils.datatag import deprecator_from_collection_name
57
59
  from ansible._internal._datatag._tags import TrustedAsTemplate
58
- from ansible.module_utils.common.messages import ErrorSummary, WarningSummary, DeprecationSummary, Detail, SummaryBase, PluginInfo
59
60
  from ansible.module_utils.six import text_type
60
- from ansible.module_utils._internal import _traceback
61
+ from ansible.module_utils._internal import _traceback, _errors
61
62
  from ansible.utils.color import stringc
62
63
  from ansible.utils.multiprocessing import context as multiprocessing_context
63
64
  from ansible.utils.singleton import Singleton
@@ -90,6 +91,9 @@ def _is_controller_traceback_enabled(event: _traceback.TracebackEvent) -> bool:
90
91
  if 'never' in flag_values:
91
92
  return False
92
93
 
94
+ if _traceback.TracebackEvent.DEPRECATED_VALUE.name.lower() in flag_values:
95
+ flag_values.add(_traceback.TracebackEvent.DEPRECATED.name.lower()) # DEPRECATED_VALUE implies DEPRECATED
96
+
93
97
  return event.name.lower() in flag_values
94
98
 
95
99
 
@@ -433,6 +437,10 @@ class Display(metaclass=Singleton):
433
437
  if not isinstance(msg, str):
434
438
  raise TypeError(f'Display message must be str, not: {msg.__class__.__name__}')
435
439
 
440
+ # Convert Windows newlines to Unix newlines.
441
+ # Some environments, such as Azure Pipelines, render `\r` as an additional `\n`.
442
+ msg = msg.replace('\r\n', '\n')
443
+
436
444
  nocolor = msg
437
445
 
438
446
  if not log_only:
@@ -568,7 +576,7 @@ class Display(metaclass=Singleton):
568
576
  version=version,
569
577
  removed=removed,
570
578
  date=date,
571
- deprecator=PluginInfo._from_collection_name(collection_name),
579
+ deprecator=deprecator_from_collection_name(collection_name),
572
580
  )
573
581
 
574
582
  if removed:
@@ -585,10 +593,10 @@ class Display(metaclass=Singleton):
585
593
  version: str | None,
586
594
  removed: bool = False,
587
595
  date: str | None,
588
- deprecator: PluginInfo | None,
596
+ deprecator: _messages.PluginInfo | None,
589
597
  ) -> str:
590
598
  """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
599
+ # 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
600
 
593
601
  if removed:
594
602
  removal_fragment = 'This feature was removed'
@@ -598,13 +606,13 @@ class Display(metaclass=Singleton):
598
606
  if not deprecator or deprecator.type == _deprecator.INDETERMINATE_DEPRECATOR.type:
599
607
  collection = None
600
608
  plugin_fragment = ''
601
- elif deprecator.type == _deprecator.PluginInfo._COLLECTION_ONLY_TYPE:
609
+ elif deprecator.type == _deprecator._COLLECTION_ONLY_TYPE:
602
610
  collection = deprecator.resolved_name
603
611
  plugin_fragment = ''
604
612
  else:
605
613
  parts = deprecator.resolved_name.split('.')
606
614
  plugin_name = parts[-1]
607
- # DTFIX-RELEASE: normalize 'modules' -> 'module' before storing it so we can eliminate the normalization here
615
+ # DTFIX1: normalize 'modules' -> 'module' before storing it so we can eliminate the normalization here
608
616
  plugin_type = "module" if deprecator.type in ("module", "modules") else f'{deprecator.type} plugin'
609
617
 
610
618
  collection = '.'.join(parts[:2]) if len(parts) > 2 else None
@@ -668,7 +676,7 @@ class Display(metaclass=Singleton):
668
676
  date: str | None = None,
669
677
  collection_name: str | None = None,
670
678
  *,
671
- deprecator: PluginInfo | None = None,
679
+ deprecator: _messages.PluginInfo | None = None,
672
680
  help_text: str | None = None,
673
681
  obj: t.Any = None,
674
682
  ) -> None:
@@ -678,8 +686,8 @@ class Display(metaclass=Singleton):
678
686
  Specify `version` or `date`, but not both.
679
687
  If `date` is a string, it must be in the form `YYYY-MM-DD`.
680
688
  """
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?
689
+ # DTFIX3: are there any deprecation calls where the feature is switching from enabled to disabled, rather than being removed entirely?
690
+ # DTFIX3: are there deprecated features which should going through deferred deprecation instead?
683
691
 
684
692
  _skip_stackwalk = True
685
693
 
@@ -691,6 +699,7 @@ class Display(metaclass=Singleton):
691
699
  help_text=help_text,
692
700
  obj=obj,
693
701
  deprecator=_deprecator.get_best_deprecator(deprecator=deprecator, collection_name=collection_name),
702
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.DEPRECATED),
694
703
  )
695
704
 
696
705
  def _deprecated_with_plugin_info(
@@ -702,7 +711,8 @@ class Display(metaclass=Singleton):
702
711
  date: str | None,
703
712
  help_text: str | None,
704
713
  obj: t.Any,
705
- deprecator: PluginInfo | None,
714
+ deprecator: _messages.PluginInfo | None,
715
+ formatted_traceback: str | None = None,
706
716
  ) -> None:
707
717
  """
708
718
  This is the internal pre-proxy half of the `deprecated` implementation.
@@ -721,23 +731,21 @@ class Display(metaclass=Singleton):
721
731
 
722
732
  raise AnsibleError(formatted_msg)
723
733
 
724
- if source_context := _utils.SourceContext.from_value(obj):
734
+ if source_context := _error_utils.SourceContext.from_value(obj):
725
735
  formatted_source_context = str(source_context)
726
736
  else:
727
737
  formatted_source_context = None
728
738
 
729
- deprecation = DeprecationSummary(
730
- details=(
731
- Detail(
732
- msg=msg,
733
- formatted_source_context=formatted_source_context,
734
- help_text=help_text,
735
- ),
739
+ deprecation = _messages.DeprecationSummary(
740
+ event=_messages.Event(
741
+ msg=msg,
742
+ formatted_source_context=formatted_source_context,
743
+ help_text=help_text,
744
+ formatted_traceback=formatted_traceback,
736
745
  ),
737
746
  version=version,
738
747
  date=date,
739
748
  deprecator=deprecator,
740
- formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.DEPRECATED),
741
749
  )
742
750
 
743
751
  if warning_ctx := _DeferredWarningContext.current(optional=True):
@@ -747,7 +755,7 @@ class Display(metaclass=Singleton):
747
755
  self._deprecated(deprecation)
748
756
 
749
757
  @_proxy
750
- def _deprecated(self, warning: DeprecationSummary) -> None:
758
+ def _deprecated(self, warning: _messages.DeprecationSummary) -> None:
751
759
  """Internal implementation detail, use `deprecated` instead."""
752
760
 
753
761
  # This is the post-proxy half of the `deprecated` implementation.
@@ -758,10 +766,10 @@ class Display(metaclass=Singleton):
758
766
 
759
767
  self.warning('Deprecation warnings can be disabled by setting `deprecation_warnings=False` in ansible.cfg.')
760
768
 
761
- msg = format_message(warning)
769
+ msg = _format_message(warning, _traceback.is_traceback_enabled(_traceback.TracebackEvent.DEPRECATED))
762
770
  msg = f'[DEPRECATION WARNING]: {msg}'
763
771
 
764
- # DTFIX-RELEASE: what should we do with wrap_message?
772
+ # DTFIX3: what should we do with wrap_message?
765
773
  msg = self._wrap_message(msg=msg, wrap_text=True)
766
774
 
767
775
  if self._deduplicate(msg, self._deprecations):
@@ -778,47 +786,46 @@ class Display(metaclass=Singleton):
778
786
  obj: t.Any = None
779
787
  ) -> None:
780
788
  """Display a warning message."""
789
+ _skip_stackwalk = True
781
790
 
782
791
  # This is the pre-proxy half of the `warning` implementation.
783
792
  # Any logic that must occur on workers needs to be implemented here.
784
793
 
785
- if source_context := _utils.SourceContext.from_value(obj):
794
+ if source_context := _error_utils.SourceContext.from_value(obj):
786
795
  formatted_source_context = str(source_context)
787
796
  else:
788
797
  formatted_source_context = None
789
798
 
790
- warning = WarningSummary(
791
- details=(
792
- Detail(
793
- msg=msg,
794
- help_text=help_text,
795
- formatted_source_context=formatted_source_context,
796
- ),
799
+ warning = _messages.WarningSummary(
800
+ event=_messages.Event(
801
+ msg=msg,
802
+ help_text=help_text,
803
+ formatted_source_context=formatted_source_context,
804
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.WARNING),
797
805
  ),
798
- formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.WARNING),
799
806
  )
800
807
 
801
808
  if warning_ctx := _DeferredWarningContext.current(optional=True):
802
809
  warning_ctx.capture(warning)
803
- # DTFIX-RELEASE: what to do about propagating wrap_text?
810
+ # DTFIX3: what to do about propagating wrap_text?
804
811
  return
805
812
 
806
813
  self._warning(warning, wrap_text=not formatted)
807
814
 
808
815
  @_proxy
809
- def _warning(self, warning: WarningSummary, wrap_text: bool) -> None:
816
+ def _warning(self, warning: _messages.WarningSummary, wrap_text: bool) -> None:
810
817
  """Internal implementation detail, use `warning` instead."""
811
818
 
812
819
  # This is the post-proxy half of the `warning` implementation.
813
820
  # Any logic that must occur in the primary controller process needs to be implemented here.
814
821
 
815
- msg = format_message(warning)
822
+ msg = _format_message(warning, _traceback.is_traceback_enabled(_traceback.TracebackEvent.WARNING))
816
823
  msg = f"[WARNING]: {msg}"
817
824
 
818
825
  if self._deduplicate(msg, self._warns):
819
826
  return
820
827
 
821
- # DTFIX-RELEASE: what should we do with wrap_message?
828
+ # DTFIX3: what should we do with wrap_message?
822
829
  msg = self._wrap_message(msg=msg, wrap_text=wrap_text)
823
830
 
824
831
  self.display(msg, color=C.config.get_config_value('COLOR_WARN'), stderr=True, caplevel=-2)
@@ -870,17 +877,39 @@ class Display(metaclass=Singleton):
870
877
  (out, err) = cmd.communicate()
871
878
  self.display(u"%s\n" % to_text(out), color=color)
872
879
 
873
- def error_as_warning(self, msg: str | None, exception: BaseException) -> None:
880
+ def error_as_warning(
881
+ self,
882
+ msg: str | None,
883
+ exception: BaseException,
884
+ *,
885
+ help_text: str | None = None,
886
+ obj: t.Any = None,
887
+ ) -> None:
874
888
  """Display an exception as a warning."""
889
+ _skip_stackwalk = True
875
890
 
876
- error = _utils._create_error_summary(exception, _traceback.TracebackEvent.WARNING)
891
+ event = _error_factory.ControllerEventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.WARNING))
877
892
 
878
893
  if msg:
879
- error = dataclasses.replace(error, details=(Detail(msg=msg),) + error.details)
894
+ if source_context := _error_utils.SourceContext.from_value(obj):
895
+ formatted_source_context = str(source_context)
896
+ else:
897
+ formatted_source_context = None
898
+
899
+ event = _messages.Event(
900
+ msg=msg,
901
+ help_text=help_text,
902
+ formatted_source_context=formatted_source_context,
903
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.WARNING),
904
+ chain=_messages.EventChain(
905
+ msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
906
+ traceback_reason=_errors.TRACEBACK_REASON_EXCEPTION_DIRECT_WARNING,
907
+ event=event,
908
+ ),
909
+ )
880
910
 
881
- warning = WarningSummary(
882
- details=error.details,
883
- formatted_traceback=error.formatted_traceback,
911
+ warning = _messages.WarningSummary(
912
+ event=event,
884
913
  )
885
914
 
886
915
  if warning_ctx := _DeferredWarningContext.current(optional=True):
@@ -891,32 +920,41 @@ class Display(metaclass=Singleton):
891
920
 
892
921
  def error(self, msg: str | BaseException, wrap_text: bool = True, stderr: bool = True) -> None:
893
922
  """Display an error message."""
923
+ _skip_stackwalk = True
894
924
 
895
925
  # This is the pre-proxy half of the `error` implementation.
896
926
  # Any logic that must occur on workers needs to be implemented here.
897
927
 
898
928
  if isinstance(msg, BaseException):
899
- error = _utils._create_error_summary(msg, _traceback.TracebackEvent.ERROR)
929
+ event = _error_factory.ControllerEventFactory.from_exception(msg, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR))
930
+
900
931
  wrap_text = False
901
932
  else:
902
- error = ErrorSummary(details=(Detail(msg=msg),), formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.ERROR))
933
+ event = _messages.Event(
934
+ msg=msg,
935
+ formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR),
936
+ )
937
+
938
+ error = _messages.ErrorSummary(
939
+ event=event,
940
+ )
903
941
 
904
942
  self._error(error, wrap_text=wrap_text, stderr=stderr)
905
943
 
906
944
  @_proxy
907
- def _error(self, error: ErrorSummary, wrap_text: bool, stderr: bool) -> None:
945
+ def _error(self, error: _messages.ErrorSummary, wrap_text: bool, stderr: bool) -> None:
908
946
  """Internal implementation detail, use `error` instead."""
909
947
 
910
948
  # This is the post-proxy half of the `error` implementation.
911
949
  # Any logic that must occur in the primary controller process needs to be implemented here.
912
950
 
913
- msg = format_message(error)
951
+ msg = _format_message(error, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR))
914
952
  msg = f'[ERROR]: {msg}'
915
953
 
916
954
  if self._deduplicate(msg, self._errors):
917
955
  return
918
956
 
919
- # DTFIX-RELEASE: what should we do with wrap_message?
957
+ # DTFIX3: what should we do with wrap_message?
920
958
  msg = self._wrap_message(msg=msg, wrap_text=wrap_text)
921
959
 
922
960
  self.display(msg, color=C.config.get_config_value('COLOR_ERROR'), stderr=stderr, caplevel=-1)
@@ -1146,9 +1184,9 @@ class _DeferredWarningContext(_ambient_context.AmbientContextBase):
1146
1184
 
1147
1185
  def __init__(self, *, variables: dict[str, object]) -> None:
1148
1186
  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()
1187
+ self._deprecation_warnings: list[_messages.DeprecationSummary] = []
1188
+ self._warnings: list[_messages.WarningSummary] = []
1189
+ self._seen: set[_messages.WarningSummary] = set()
1152
1190
 
1153
1191
  @classmethod
1154
1192
  def deprecation_warnings_enabled(cls) -> bool:
@@ -1161,82 +1199,29 @@ class _DeferredWarningContext(_ambient_context.AmbientContextBase):
1161
1199
 
1162
1200
  return C.config.get_config_value('DEPRECATION_WARNINGS', variables=variables)
1163
1201
 
1164
- def capture(self, warning: WarningSummary) -> None:
1202
+ def capture(self, warning: _messages.WarningSummary) -> None:
1165
1203
  """Add the warning/deprecation to the context if it has not already been seen by this context."""
1166
1204
  if warning in self._seen:
1167
1205
  return
1168
1206
 
1169
1207
  self._seen.add(warning)
1170
1208
 
1171
- if isinstance(warning, DeprecationSummary):
1209
+ if isinstance(warning, _messages.DeprecationSummary):
1172
1210
  self._deprecation_warnings.append(warning)
1173
1211
  else:
1174
1212
  self._warnings.append(warning)
1175
1213
 
1176
- def get_warnings(self) -> list[WarningSummary]:
1214
+ def get_warnings(self) -> list[_messages.WarningSummary]:
1177
1215
  """Return a list of the captured non-deprecation warnings."""
1178
1216
  # DTFIX-FUTURE: return a read-only list proxy instead
1179
1217
  return self._warnings
1180
1218
 
1181
- def get_deprecation_warnings(self) -> list[DeprecationSummary]:
1219
+ def get_deprecation_warnings(self) -> list[_messages.DeprecationSummary]:
1182
1220
  """Return a list of the captured deprecation warnings."""
1183
1221
  # DTFIX-FUTURE: return a read-only list proxy instead
1184
1222
  return self._deprecation_warnings
1185
1223
 
1186
1224
 
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
1225
  def _join_sentences(first: str | None, second: str | None) -> str:
1241
1226
  """Join two sentences together."""
1242
1227
  first = (first or '').strip()
@@ -1257,34 +1242,28 @@ def _join_sentences(first: str | None, second: str | None) -> str:
1257
1242
  return ' '.join((first, second))
1258
1243
 
1259
1244
 
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,
1245
+ def _format_message(summary: _messages.SummaryBase, include_traceback: bool) -> str:
1246
+ if isinstance(summary, _messages.DeprecationSummary):
1247
+ deprecation_message = _display._get_deprecation_message_with_plugin_info(
1248
+ msg=summary.event.msg,
1270
1249
  version=summary.version,
1271
1250
  date=summary.date,
1272
1251
  deprecator=summary.deprecator,
1273
1252
  )
1274
1253
 
1275
- detail_list[0] = dataclasses.replace(
1276
- detail,
1277
- msg=deprecation_msg,
1278
- help_text=detail.help_text,
1279
- )
1280
-
1281
- details = detail_list
1254
+ event = dataclasses.replace(summary.event, msg=deprecation_message)
1255
+ else:
1256
+ event = summary.event
1282
1257
 
1283
- return _format_error_details(details, summary.formatted_traceback)
1258
+ return _event_formatting.format_event(event, include_traceback)
1284
1259
 
1285
1260
 
1286
- def _report_config_warnings(deprecator: PluginInfo) -> None:
1261
+ def _report_config_warnings(deprecator: _messages.PluginInfo) -> None:
1287
1262
  """Called by config to report warnings/deprecations collected during a config parse."""
1263
+ while config._errors:
1264
+ msg, exception = config._errors.pop()
1265
+ _display.error_as_warning(msg=msg, exception=exception)
1266
+
1288
1267
  while config.WARNINGS:
1289
1268
  warn = config.WARNINGS.pop()
1290
1269
  _display.warning(warn)
ansible/utils/hashing.py CHANGED
@@ -59,7 +59,6 @@ def secure_hash(filename, hash_func=sha1):
59
59
  return digest.hexdigest()
60
60
 
61
61
 
62
- # The checksum algorithm must match with the algorithm in ShellModule.checksum() method
63
62
  checksum = secure_hash
64
63
  checksum_s = secure_hash_s
65
64
 
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()
@@ -154,7 +154,8 @@ def add_fragments(doc, filename, fragment_loader, is_module=False):
154
154
  unknown_fragments.append(fragment_slug)
155
155
  continue
156
156
 
157
- fragment_yaml = getattr(fragment_class, fragment_var, None)
157
+ # trust-tagged source propagates to loaded values; expressions and templates in config require trust
158
+ fragment_yaml = _tags.TrustedAsTemplate().tag(getattr(fragment_class, fragment_var, None))
158
159
  if fragment_yaml is None:
159
160
  if fragment_var != 'DOCUMENTATION':
160
161
  # if it's asking for something specific that's missing, that's an error
@@ -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})
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: ansible-core
3
- Version: 2.19.0b3
3
+ Version: 2.19.0b5
4
4
  Summary: Radically simple IT automation
5
5
  Author: Ansible Project
6
6
  Project-URL: Homepage, https://ansible.com/
@@ -38,6 +38,7 @@ Requires-Dist: PyYAML>=5.1
38
38
  Requires-Dist: cryptography
39
39
  Requires-Dist: packaging
40
40
  Requires-Dist: resolvelib<2.0.0,>=0.5.3
41
+ Dynamic: license-file
41
42
 
42
43
  [![PyPI version](https://img.shields.io/pypi/v/ansible-core.svg)](https://pypi.org/project/ansible-core)
43
44
  [![Docs badge](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.ansible.com/ansible/latest/)