ansible-core 2.19.0b5__py3-none-any.whl → 2.19.0b7__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 (184) hide show
  1. ansible/_internal/_ansiballz/__init__.py +0 -0
  2. ansible/_internal/_ansiballz/_builder.py +101 -0
  3. ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
  4. ansible/_internal/_templating/_jinja_bits.py +22 -4
  5. ansible/_internal/_templating/_jinja_common.py +1 -1
  6. ansible/_internal/_templating/_jinja_plugins.py +5 -2
  7. ansible/_internal/_templating/_template_vars.py +72 -0
  8. ansible/_internal/_templating/_transform.py +6 -0
  9. ansible/_internal/_yaml/_constructor.py +4 -4
  10. ansible/_internal/_yaml/_dumper.py +26 -18
  11. ansible/cli/__init__.py +9 -14
  12. ansible/cli/adhoc.py +6 -3
  13. ansible/cli/arguments/option_helpers.py +1 -1
  14. ansible/cli/console.py +2 -2
  15. ansible/cli/doc.py +4 -4
  16. ansible/cli/inventory.py +5 -7
  17. ansible/config/base.yml +33 -6
  18. ansible/errors/__init__.py +2 -1
  19. ansible/executor/module_common.py +75 -44
  20. ansible/executor/powershell/psrp_put_file.ps1 +1 -1
  21. ansible/executor/process/worker.py +2 -2
  22. ansible/executor/task_executor.py +2 -2
  23. ansible/executor/task_queue_manager.py +34 -70
  24. ansible/executor/task_result.py +1 -1
  25. ansible/galaxy/api.py +3 -6
  26. ansible/galaxy/collection/__init__.py +1 -6
  27. ansible/galaxy/collection/concrete_artifact_manager.py +4 -10
  28. ansible/galaxy/dependency_resolution/providers.py +3 -3
  29. ansible/galaxy/role.py +2 -2
  30. ansible/inventory/group.py +6 -1
  31. ansible/inventory/host.py +6 -1
  32. ansible/module_utils/_internal/__init__.py +7 -4
  33. ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
  34. ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
  35. ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
  36. ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
  37. ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +10 -38
  38. ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
  39. ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
  40. ansible/module_utils/_internal/_datatag/__init__.py +23 -1
  41. ansible/module_utils/_internal/_deprecator.py +39 -34
  42. ansible/module_utils/_internal/_json/_profiles/__init__.py +1 -0
  43. ansible/module_utils/_internal/_messages.py +26 -2
  44. ansible/module_utils/_internal/_plugin_info.py +14 -1
  45. ansible/module_utils/ansible_release.py +1 -1
  46. ansible/module_utils/basic.py +58 -70
  47. ansible/module_utils/common/respawn.py +4 -41
  48. ansible/module_utils/common/yaml.py +1 -1
  49. ansible/module_utils/connection.py +8 -11
  50. ansible/module_utils/csharp/Ansible.Basic.cs +1 -1
  51. ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
  52. ansible/module_utils/facts/hardware/base.py +1 -1
  53. ansible/module_utils/facts/hardware/linux.py +1 -1
  54. ansible/module_utils/facts/other/facter.py +1 -1
  55. ansible/module_utils/facts/sysctl.py +4 -6
  56. ansible/module_utils/facts/system/caps.py +2 -2
  57. ansible/module_utils/facts/system/distribution.py +2 -2
  58. ansible/module_utils/facts/system/local.py +1 -1
  59. ansible/module_utils/facts/virtual/linux.py +1 -1
  60. ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
  61. ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
  62. ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
  63. ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
  64. ansible/module_utils/service.py +1 -1
  65. ansible/module_utils/urls.py +5 -5
  66. ansible/modules/apt.py +9 -3
  67. ansible/modules/apt_repository.py +10 -10
  68. ansible/modules/assemble.py +7 -5
  69. ansible/modules/async_wrapper.py +7 -17
  70. ansible/modules/command.py +3 -3
  71. ansible/modules/copy.py +4 -4
  72. ansible/modules/cron.py +1 -1
  73. ansible/modules/expect.py +5 -5
  74. ansible/modules/file.py +16 -17
  75. ansible/modules/find.py +3 -3
  76. ansible/modules/get_url.py +17 -0
  77. ansible/modules/git.py +9 -7
  78. ansible/modules/hostname.py +2 -2
  79. ansible/modules/known_hosts.py +12 -14
  80. ansible/modules/package.py +6 -0
  81. ansible/modules/pip.py +9 -11
  82. ansible/modules/raw.py +2 -2
  83. ansible/modules/replace.py +2 -2
  84. ansible/modules/slurp.py +10 -13
  85. ansible/modules/stat.py +6 -8
  86. ansible/modules/unarchive.py +6 -6
  87. ansible/modules/user.py +1 -1
  88. ansible/modules/wait_for.py +38 -33
  89. ansible/modules/yum_repository.py +4 -3
  90. ansible/parsing/dataloader.py +2 -2
  91. ansible/parsing/mod_args.py +38 -20
  92. ansible/parsing/vault/__init__.py +9 -13
  93. ansible/playbook/base.py +7 -4
  94. ansible/playbook/helpers.py +1 -1
  95. ansible/playbook/included_file.py +3 -1
  96. ansible/playbook/play_context.py +2 -0
  97. ansible/playbook/playbook_include.py +23 -56
  98. ansible/playbook/role/__init__.py +38 -21
  99. ansible/playbook/taggable.py +19 -5
  100. ansible/playbook/task.py +2 -0
  101. ansible/plugins/action/__init__.py +2 -2
  102. ansible/plugins/action/assemble.py +2 -1
  103. ansible/plugins/action/assert.py +2 -2
  104. ansible/plugins/action/fetch.py +3 -3
  105. ansible/plugins/action/script.py +5 -4
  106. ansible/plugins/action/template.py +9 -3
  107. ansible/plugins/cache/__init__.py +17 -19
  108. ansible/plugins/callback/__init__.py +77 -87
  109. ansible/plugins/callback/default.py +0 -3
  110. ansible/plugins/callback/junit.py +0 -6
  111. ansible/plugins/callback/tree.py +5 -5
  112. ansible/plugins/connection/local.py +4 -4
  113. ansible/plugins/connection/paramiko_ssh.py +5 -5
  114. ansible/plugins/connection/ssh.py +9 -7
  115. ansible/plugins/connection/winrm.py +1 -1
  116. ansible/plugins/filter/core.py +19 -21
  117. ansible/plugins/filter/encryption.py +10 -2
  118. ansible/plugins/filter/pow.yml +1 -1
  119. ansible/plugins/filter/root.yml +1 -1
  120. ansible/plugins/filter/strftime.yml +3 -3
  121. ansible/plugins/filter/to_uuid.yml +1 -1
  122. ansible/plugins/inventory/script.py +1 -1
  123. ansible/plugins/list.py +5 -4
  124. ansible/plugins/loader.py +5 -0
  125. ansible/plugins/lookup/password.py +4 -6
  126. ansible/plugins/lookup/template.py +9 -4
  127. ansible/plugins/shell/powershell.py +3 -2
  128. ansible/plugins/shell/sh.py +3 -2
  129. ansible/plugins/strategy/__init__.py +3 -3
  130. ansible/plugins/test/core.py +2 -2
  131. ansible/release.py +1 -1
  132. ansible/template/__init__.py +9 -53
  133. ansible/utils/collection_loader/_collection_finder.py +3 -3
  134. ansible/utils/display.py +38 -37
  135. ansible/utils/galaxy.py +2 -2
  136. ansible/utils/hashing.py +6 -7
  137. ansible/utils/path.py +6 -8
  138. ansible/utils/py3compat.py +2 -1
  139. ansible/utils/ssh_functions.py +3 -2
  140. ansible/utils/vars.py +4 -1
  141. ansible/vars/manager.py +6 -3
  142. ansible/vars/plugins.py +3 -3
  143. ansible/vars/reserved.py +6 -4
  144. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/METADATA +1 -1
  145. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/RECORD +184 -173
  146. ansible_test/_internal/__init__.py +5 -0
  147. ansible_test/_internal/ansible_util.py +1 -1
  148. ansible_test/_internal/classification/python.py +6 -0
  149. ansible_test/_internal/cli/commands/__init__.py +0 -5
  150. ansible_test/_internal/cli/environments.py +51 -5
  151. ansible_test/_internal/commands/coverage/__init__.py +1 -1
  152. ansible_test/_internal/commands/integration/__init__.py +18 -5
  153. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  154. ansible_test/_internal/commands/integration/coverage.py +7 -2
  155. ansible_test/_internal/commands/sanity/__init__.py +3 -1
  156. ansible_test/_internal/commands/sanity/integration_aliases.py +11 -0
  157. ansible_test/_internal/commands/shell/__init__.py +43 -4
  158. ansible_test/_internal/commands/units/__init__.py +4 -1
  159. ansible_test/_internal/config.py +21 -13
  160. ansible_test/_internal/debugging.py +166 -0
  161. ansible_test/_internal/delegation.py +21 -13
  162. ansible_test/_internal/host_profiles.py +259 -16
  163. ansible_test/_internal/inventory.py +4 -0
  164. ansible_test/_internal/metadata.py +94 -4
  165. ansible_test/_internal/processes.py +80 -0
  166. ansible_test/_internal/provisioning.py +10 -4
  167. ansible_test/_internal/python_requirements.py +27 -0
  168. ansible_test/_internal/ssh.py +1 -5
  169. ansible_test/_internal/target.py +8 -0
  170. ansible_test/_internal/thread.py +2 -1
  171. ansible_test/_internal/timeout.py +1 -1
  172. ansible_test/_internal/util.py +20 -12
  173. ansible_test/_internal/util_common.py +13 -3
  174. ansible_test/_util/target/injector/python.py +8 -0
  175. ansible_test/_util/target/setup/requirements.py +3 -9
  176. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/WHEEL +0 -0
  177. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/entry_points.txt +0 -0
  178. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/COPYING +0 -0
  179. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  180. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  181. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  182. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  183. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  184. {ansible_core-2.19.0b5.dist-info → ansible_core-2.19.0b7.dist-info}/top_level.txt +0 -0
@@ -19,6 +19,7 @@ from __future__ import annotations
19
19
 
20
20
  import difflib
21
21
  import functools
22
+ import inspect
22
23
  import json
23
24
  import re
24
25
  import sys
@@ -39,6 +40,7 @@ from ansible.utils.display import Display
39
40
  from ansible.vars.clean import strip_internal_keys, module_response_deepcopy
40
41
  from ansible.module_utils._internal._json._profiles import _fallback_to_str
41
42
  from ansible._internal._templating import _engine
43
+ from ansible.module_utils._internal import _deprecator
42
44
 
43
45
  import yaml
44
46
 
@@ -61,16 +63,6 @@ _SPACE_BREAK_RE = re.compile(fr' +([{_YAML_BREAK_CHARS}])')
61
63
  _T_callable = t.TypeVar("_T_callable", bound=t.Callable)
62
64
 
63
65
 
64
- def _callback_base_impl(wrapped: _T_callable) -> _T_callable:
65
- """
66
- Decorator for the no-op methods on the `CallbackBase` base class.
67
- Used to avoid unnecessary dispatch overhead to no-op base callback methods.
68
- """
69
- wrapped._base_impl = True
70
-
71
- return wrapped
72
-
73
-
74
66
  class _AnsibleCallbackDumper(_dumper.AnsibleDumper):
75
67
  def __init__(self, *args, lossy: bool = False, **kwargs):
76
68
  super().__init__(*args, **kwargs)
@@ -154,6 +146,9 @@ class CallbackBase(AnsiblePlugin):
154
146
  custom actions.
155
147
  """
156
148
 
149
+ _implemented_callback_methods: frozenset[str] = frozenset()
150
+ """Set of callback methods overridden by each subclass; used by TQM to bypass callback dispatch on no-op methods."""
151
+
157
152
  def __init__(self, display: Display | None = None, options: dict[str, t.Any] | None = None) -> None:
158
153
  super().__init__()
159
154
 
@@ -189,6 +184,48 @@ class CallbackBase(AnsiblePlugin):
189
184
  # helper for callbacks, so they don't all have to include deepcopy
190
185
  _copy_result = deepcopy
191
186
 
187
+ def _init_callback_methods(self) -> None:
188
+ """Record analysis of callback methods on each callback instance for dispatch optimization and deprecation warnings."""
189
+ implemented_callback_methods: set[str] = set()
190
+ deprecated_v1_method_overrides: set[str] = set()
191
+ plugin_file = sys.modules[type(self).__module__].__file__
192
+
193
+ if plugin_info := _deprecator._path_as_plugininfo(plugin_file):
194
+ plugin_name = plugin_info.resolved_name
195
+ else:
196
+ plugin_name = plugin_file
197
+
198
+ for base_v2_method, base_v1_method in CallbackBase._v2_v1_method_map.items():
199
+ method_name = None
200
+
201
+ if not inspect.ismethod(method := getattr(self, (v2_method_name := base_v2_method.__name__))) or method.__func__ is not base_v2_method:
202
+ implemented_callback_methods.add(v2_method_name) # v2 method directly implemented by subclass
203
+ method_name = v2_method_name
204
+ elif base_v1_method is None:
205
+ pass # no corresponding v1 method
206
+ elif not inspect.ismethod(method := getattr(self, (v1_method_name := base_v1_method.__name__))) or method.__func__ is not base_v1_method:
207
+ implemented_callback_methods.add(v2_method_name) # v1 method directly implemented by subclass
208
+ deprecated_v1_method_overrides.add(v1_method_name)
209
+ method_name = v1_method_name
210
+
211
+ if method_name and v2_method_name == 'v2_on_any':
212
+ deprecated_v1_method_overrides.discard(method_name) # avoid including v1 on_any in the v1 deprecation below
213
+
214
+ global_display.deprecated(
215
+ msg=f'The {plugin_name!r} callback plugin implements deprecated method {method_name!r}.',
216
+ version='2.23',
217
+ help_text='Use event-specific callback methods instead.',
218
+ )
219
+
220
+ self._implemented_callback_methods = frozenset(implemented_callback_methods)
221
+
222
+ if deprecated_v1_method_overrides:
223
+ global_display.deprecated(
224
+ msg=f'The {plugin_name!r} callback plugin implements the following deprecated method(s): {", ".join(sorted(deprecated_v1_method_overrides))}',
225
+ version='2.23',
226
+ help_text='Implement the `v2_*` equivalent callback method(s) instead.',
227
+ )
228
+
192
229
  def set_option(self, k, v):
193
230
  self._plugin_options[k] = C.config.get_config_value(k, plugin_type=self.plugin_type, plugin_name=self._load_name, direct={k: v})
194
231
 
@@ -316,8 +353,7 @@ class CallbackBase(AnsiblePlugin):
316
353
  if res.pop('warnings', None) and self._current_task_result and (warnings := self._current_task_result.warnings):
317
354
  # display warnings from the current task result if `warnings` was not removed from `result` (or made falsey)
318
355
  for warning in warnings:
319
- # DTFIX3: what to do about propagating wrap_text from the original display.warning call?
320
- self._display._warning(warning, wrap_text=False)
356
+ self._display._warning(warning)
321
357
 
322
358
  if res.pop('deprecations', None) and self._current_task_result and (deprecations := self._current_task_result.deprecations):
323
359
  # display deprecations from the current task result if `deprecations` was not removed from `result` (or made falsey)
@@ -327,7 +363,7 @@ class CallbackBase(AnsiblePlugin):
327
363
  def _handle_exception(self, result: _c.MutableMapping[str, t.Any], use_stderr: bool = False) -> None:
328
364
  if result.pop('exception', None) and self._current_task_result and (exception := self._current_task_result.exception):
329
365
  # display exception from the current task result if `exception` was not removed from `result` (or made falsey)
330
- self._display._error(exception, wrap_text=False, stderr=use_stderr)
366
+ self._display._error(exception, stderr=use_stderr)
331
367
 
332
368
  def _handle_warnings_and_exception(self, result: CallbackTaskResult) -> None:
333
369
  """Standardized handling of warnings/deprecations and exceptions from a task/item result."""
@@ -471,96 +507,61 @@ class CallbackBase(AnsiblePlugin):
471
507
  def set_play_context(self, play_context):
472
508
  pass
473
509
 
474
- @_callback_base_impl
475
510
  def on_any(self, *args, **kwargs):
476
511
  pass
477
512
 
478
- @_callback_base_impl
479
513
  def runner_on_failed(self, host, res, ignore_errors=False):
480
514
  pass
481
515
 
482
- @_callback_base_impl
483
516
  def runner_on_ok(self, host, res):
484
517
  pass
485
518
 
486
- @_callback_base_impl
487
519
  def runner_on_skipped(self, host, item=None):
488
520
  pass
489
521
 
490
- @_callback_base_impl
491
522
  def runner_on_unreachable(self, host, res):
492
523
  pass
493
524
 
494
- @_callback_base_impl
495
- def runner_on_no_hosts(self):
496
- pass
497
-
498
- @_callback_base_impl
499
525
  def runner_on_async_poll(self, host, res, jid, clock):
500
526
  pass
501
527
 
502
- @_callback_base_impl
503
528
  def runner_on_async_ok(self, host, res, jid):
504
529
  pass
505
530
 
506
- @_callback_base_impl
507
531
  def runner_on_async_failed(self, host, res, jid):
508
532
  pass
509
533
 
510
- @_callback_base_impl
511
534
  def playbook_on_start(self):
512
535
  pass
513
536
 
514
- @_callback_base_impl
515
537
  def playbook_on_notify(self, host, handler):
516
538
  pass
517
539
 
518
- @_callback_base_impl
519
540
  def playbook_on_no_hosts_matched(self):
520
541
  pass
521
542
 
522
- @_callback_base_impl
523
543
  def playbook_on_no_hosts_remaining(self):
524
544
  pass
525
545
 
526
- @_callback_base_impl
527
546
  def playbook_on_task_start(self, name, is_conditional):
528
547
  pass
529
548
 
530
- @_callback_base_impl
531
549
  def playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None, unsafe=None):
532
550
  pass
533
551
 
534
- @_callback_base_impl
535
- def playbook_on_setup(self):
536
- pass
537
-
538
- @_callback_base_impl
539
- def playbook_on_import_for_host(self, host, imported_file):
540
- pass
541
-
542
- @_callback_base_impl
543
- def playbook_on_not_import_for_host(self, host, missing_file):
544
- pass
545
-
546
- @_callback_base_impl
547
552
  def playbook_on_play_start(self, name):
548
553
  pass
549
554
 
550
- @_callback_base_impl
551
555
  def playbook_on_stats(self, stats):
552
556
  pass
553
557
 
554
- @_callback_base_impl
555
558
  def on_file_diff(self, host, diff):
556
559
  pass
557
560
 
558
561
  # V2 METHODS, by default they call v1 counterparts if possible
559
- @_callback_base_impl
560
562
  def v2_on_any(self, *args, **kwargs):
561
563
  self.on_any(args, kwargs)
562
564
 
563
- @_callback_base_impl
564
565
  def v2_runner_on_failed(self, result: CallbackTaskResult, ignore_errors: bool = False) -> None:
565
566
  """Process results of a failed task.
566
567
 
@@ -583,7 +584,6 @@ class CallbackBase(AnsiblePlugin):
583
584
  host = result.host.get_name()
584
585
  self.runner_on_failed(host, result.result, ignore_errors)
585
586
 
586
- @_callback_base_impl
587
587
  def v2_runner_on_ok(self, result: CallbackTaskResult) -> None:
588
588
  """Process results of a successful task.
589
589
 
@@ -596,7 +596,6 @@ class CallbackBase(AnsiblePlugin):
596
596
  host = result.host.get_name()
597
597
  self.runner_on_ok(host, result.result)
598
598
 
599
- @_callback_base_impl
600
599
  def v2_runner_on_skipped(self, result: CallbackTaskResult) -> None:
601
600
  """Process results of a skipped task.
602
601
 
@@ -610,7 +609,6 @@ class CallbackBase(AnsiblePlugin):
610
609
  host = result.host.get_name()
611
610
  self.runner_on_skipped(host, self._get_item_label(getattr(result.result, 'results', {})))
612
611
 
613
- @_callback_base_impl
614
612
  def v2_runner_on_unreachable(self, result: CallbackTaskResult) -> None:
615
613
  """Process results of a task if a target node is unreachable.
616
614
 
@@ -623,7 +621,6 @@ class CallbackBase(AnsiblePlugin):
623
621
  host = result.host.get_name()
624
622
  self.runner_on_unreachable(host, result.result)
625
623
 
626
- @_callback_base_impl
627
624
  def v2_runner_on_async_poll(self, result: CallbackTaskResult) -> None:
628
625
  """Get details about an unfinished task running in async mode.
629
626
 
@@ -642,7 +639,6 @@ class CallbackBase(AnsiblePlugin):
642
639
  clock = 0
643
640
  self.runner_on_async_poll(host, result.result, jid, clock)
644
641
 
645
- @_callback_base_impl
646
642
  def v2_runner_on_async_ok(self, result: CallbackTaskResult) -> None:
647
643
  """Process results of a successful task that ran in async mode.
648
644
 
@@ -656,7 +652,6 @@ class CallbackBase(AnsiblePlugin):
656
652
  jid = result.result.get('ansible_job_id')
657
653
  self.runner_on_async_ok(host, result.result, jid)
658
654
 
659
- @_callback_base_impl
660
655
  def v2_runner_on_async_failed(self, result: CallbackTaskResult) -> None:
661
656
  host = result.host.get_name()
662
657
  # Attempt to get the async job ID. If the job does not finish before the
@@ -666,89 +661,84 @@ class CallbackBase(AnsiblePlugin):
666
661
  jid = result.result['async_result'].get('ansible_job_id')
667
662
  self.runner_on_async_failed(host, result.result, jid)
668
663
 
669
- @_callback_base_impl
670
664
  def v2_playbook_on_start(self, playbook):
671
665
  self.playbook_on_start()
672
666
 
673
- @_callback_base_impl
674
667
  def v2_playbook_on_notify(self, handler, host):
675
668
  self.playbook_on_notify(host, handler)
676
669
 
677
- @_callback_base_impl
678
670
  def v2_playbook_on_no_hosts_matched(self):
679
671
  self.playbook_on_no_hosts_matched()
680
672
 
681
- @_callback_base_impl
682
673
  def v2_playbook_on_no_hosts_remaining(self):
683
674
  self.playbook_on_no_hosts_remaining()
684
675
 
685
- @_callback_base_impl
686
676
  def v2_playbook_on_task_start(self, task, is_conditional):
687
677
  self.playbook_on_task_start(task.name, is_conditional)
688
678
 
689
- # FIXME: not called
690
- @_callback_base_impl
691
- def v2_playbook_on_cleanup_task_start(self, task):
692
- pass # no v1 correspondence
693
-
694
- @_callback_base_impl
695
679
  def v2_playbook_on_handler_task_start(self, task):
696
680
  pass # no v1 correspondence
697
681
 
698
- @_callback_base_impl
699
682
  def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None, unsafe=None):
700
683
  self.playbook_on_vars_prompt(varname, private, prompt, encrypt, confirm, salt_size, salt, default, unsafe)
701
684
 
702
- # FIXME: not called
703
- @_callback_base_impl
704
- def v2_playbook_on_import_for_host(self, result: CallbackTaskResult, imported_file) -> None:
705
- host = result.host.get_name()
706
- self.playbook_on_import_for_host(host, imported_file)
707
-
708
- # FIXME: not called
709
- @_callback_base_impl
710
- def v2_playbook_on_not_import_for_host(self, result: CallbackTaskResult, missing_file) -> None:
711
- host = result.host.get_name()
712
- self.playbook_on_not_import_for_host(host, missing_file)
713
-
714
- @_callback_base_impl
715
685
  def v2_playbook_on_play_start(self, play):
716
686
  self.playbook_on_play_start(play.name)
717
687
 
718
- @_callback_base_impl
719
688
  def v2_playbook_on_stats(self, stats):
720
689
  self.playbook_on_stats(stats)
721
690
 
722
- @_callback_base_impl
723
691
  def v2_on_file_diff(self, result: CallbackTaskResult) -> None:
724
692
  if 'diff' in result.result:
725
693
  host = result.host.get_name()
726
694
  self.on_file_diff(host, result.result['diff'])
727
695
 
728
- @_callback_base_impl
729
696
  def v2_playbook_on_include(self, included_file):
730
697
  pass # no v1 correspondence
731
698
 
732
- @_callback_base_impl
733
699
  def v2_runner_item_on_ok(self, result: CallbackTaskResult) -> None:
734
700
  pass
735
701
 
736
- @_callback_base_impl
737
702
  def v2_runner_item_on_failed(self, result: CallbackTaskResult) -> None:
738
703
  pass
739
704
 
740
- @_callback_base_impl
741
705
  def v2_runner_item_on_skipped(self, result: CallbackTaskResult) -> None:
742
706
  pass
743
707
 
744
- @_callback_base_impl
745
708
  def v2_runner_retry(self, result: CallbackTaskResult) -> None:
746
709
  pass
747
710
 
748
- @_callback_base_impl
749
711
  def v2_runner_on_start(self, host, task):
750
712
  """Event used when host begins execution of a task
751
713
 
752
714
  .. versionadded:: 2.8
753
715
  """
754
716
  pass
717
+
718
+ _v2_v1_method_map = {
719
+ v2_on_any: on_any,
720
+ v2_on_file_diff: on_file_diff,
721
+ v2_playbook_on_handler_task_start: None,
722
+ v2_playbook_on_include: None,
723
+ v2_playbook_on_no_hosts_matched: playbook_on_no_hosts_matched,
724
+ v2_playbook_on_no_hosts_remaining: playbook_on_no_hosts_remaining,
725
+ v2_playbook_on_notify: playbook_on_notify,
726
+ v2_playbook_on_play_start: playbook_on_play_start,
727
+ v2_playbook_on_start: playbook_on_start,
728
+ v2_playbook_on_stats: playbook_on_stats,
729
+ v2_playbook_on_task_start: playbook_on_task_start,
730
+ v2_playbook_on_vars_prompt: playbook_on_vars_prompt,
731
+ v2_runner_item_on_failed: None,
732
+ v2_runner_item_on_ok: None,
733
+ v2_runner_item_on_skipped: None,
734
+ v2_runner_on_async_failed: runner_on_async_failed,
735
+ v2_runner_on_async_ok: runner_on_async_ok,
736
+ v2_runner_on_async_poll: runner_on_async_poll,
737
+ v2_runner_on_failed: runner_on_failed,
738
+ v2_runner_on_ok: runner_on_ok,
739
+ v2_runner_on_skipped: runner_on_skipped,
740
+ v2_runner_on_start: None,
741
+ v2_runner_on_unreachable: runner_on_unreachable,
742
+ v2_runner_retry: None,
743
+ }
744
+ """Internal mapping of v2 callback methods with v1 counterparts; populated after type init for deprecation warnings and bypass calculation."""
@@ -197,9 +197,6 @@ class CallbackModule(CallbackBase):
197
197
 
198
198
  self._last_task_banner = task._uuid
199
199
 
200
- def v2_playbook_on_cleanup_task_start(self, task):
201
- self._task_start(task, prefix='CLEANUP TASK')
202
-
203
200
  def v2_playbook_on_handler_task_start(self, task):
204
201
  self._task_start(task, prefix='RUNNING HANDLER')
205
202
 
@@ -300,15 +300,9 @@ class CallbackModule(CallbackBase):
300
300
  def v2_playbook_on_play_start(self, play):
301
301
  self._play_name = play.get_name()
302
302
 
303
- def v2_runner_on_no_hosts(self, task: Task) -> None:
304
- self._start_task(task)
305
-
306
303
  def v2_playbook_on_task_start(self, task: Task, is_conditional: bool) -> None:
307
304
  self._start_task(task)
308
305
 
309
- def v2_playbook_on_cleanup_task_start(self, task: Task) -> None:
310
- self._start_task(task)
311
-
312
306
  def v2_playbook_on_handler_task_start(self, task: Task) -> None:
313
307
  self._start_task(task)
314
308
 
@@ -31,7 +31,7 @@ import os
31
31
 
32
32
  from ansible.constants import TREE_DIR
33
33
  from ansible.executor.task_result import CallbackTaskResult
34
- from ansible.module_utils.common.text.converters import to_bytes, to_text
34
+ from ansible.module_utils.common.text.converters import to_bytes
35
35
  from ansible.plugins.callback import CallbackBase
36
36
  from ansible.utils.path import makedirs_safe, unfrackpath
37
37
  from ansible.module_utils._internal import _deprecator
@@ -73,15 +73,15 @@ class CallbackModule(CallbackBase):
73
73
  buf = to_bytes(buf)
74
74
  try:
75
75
  makedirs_safe(self.tree)
76
- except (OSError, IOError) as e:
77
- self._display.warning(u"Unable to access or create the configured directory (%s): %s" % (to_text(self.tree), to_text(e)))
76
+ except OSError as ex:
77
+ self._display.error_as_warning(f"Unable to access or create the configured directory {self.tree!r}.", exception=ex)
78
78
 
79
79
  try:
80
80
  path = to_bytes(os.path.join(self.tree, hostname))
81
81
  with open(path, 'wb+') as fd:
82
82
  fd.write(buf)
83
- except (OSError, IOError) as e:
84
- self._display.warning(u"Unable to write to %s's file: %s" % (hostname, to_text(e)))
83
+ except OSError as ex:
84
+ self._display.error_as_warning(f"Unable to write to {hostname!r}'s file.", exception=ex)
85
85
 
86
86
  def result_to_tree(self, result: CallbackTaskResult) -> None:
87
87
  self.write_tree_file(result.host.get_name(), self._dump_results(result.result))
@@ -114,8 +114,8 @@ class Connection(ConnectionBase):
114
114
  # privileges or the command otherwise needs a pty.
115
115
  try:
116
116
  pty_primary, stdin = pty.openpty()
117
- except (IOError, OSError) as e:
118
- display.debug("Unable to open pty: %s" % to_native(e))
117
+ except OSError as ex:
118
+ display.debug(f"Unable to open pty: {ex}")
119
119
 
120
120
  p = subprocess.Popen(
121
121
  cmd,
@@ -271,8 +271,8 @@ class Connection(ConnectionBase):
271
271
  shutil.copyfile(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
272
272
  except shutil.Error:
273
273
  raise AnsibleError("failed to copy: {0} and {1} are the same".format(to_native(in_path), to_native(out_path)))
274
- except IOError as e:
275
- raise AnsibleError("failed to transfer file to {0}: {1}".format(to_native(out_path), to_native(e)))
274
+ except OSError as ex:
275
+ raise AnsibleError(f"Failed to transfer file to {out_path!r}.") from ex
276
276
 
277
277
  def fetch_file(self, in_path: str, out_path: str) -> None:
278
278
  """ fetch a file from local to local -- for compatibility """
@@ -413,7 +413,7 @@ class Connection(ConnectionBase):
413
413
  # TODO: check if we need to look at several possible locations, possible for loop
414
414
  ssh.load_system_host_keys(ssh_known_hosts)
415
415
  break
416
- except IOError:
416
+ except OSError:
417
417
  pass # file was not found, but not required to function
418
418
  ssh.load_system_host_keys()
419
419
 
@@ -567,8 +567,8 @@ class Connection(ConnectionBase):
567
567
 
568
568
  try:
569
569
  self.sftp.put(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
570
- except IOError:
571
- raise AnsibleError("failed to transfer file to %s" % out_path)
570
+ except OSError as ex:
571
+ raise AnsibleError(f"Failed to transfer file to {out_path!r}.") from ex
572
572
 
573
573
  def _connect_sftp(self) -> paramiko.sftp_client.SFTPClient:
574
574
 
@@ -593,8 +593,8 @@ class Connection(ConnectionBase):
593
593
 
594
594
  try:
595
595
  self.sftp.get(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
596
- except IOError:
597
- raise AnsibleError("failed to transfer file from %s" % in_path)
596
+ except OSError as ex:
597
+ raise AnsibleError(f"Failed to transfer file from {in_path!r}.") from ex
598
598
 
599
599
  def _any_keys_added(self) -> bool:
600
600
 
@@ -332,7 +332,9 @@ DOCUMENTATION = """
332
332
  version_added: '2.7'
333
333
  sftp_batch_mode:
334
334
  default: true
335
- description: 'TODO: write it'
335
+ description:
336
+ - When set to C(True), sftp will be run in batch mode, allowing detection of transfer errors.
337
+ - When set to C(False), sftp will not be run in batch mode, preventing detection of transfer errors.
336
338
  env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
337
339
  ini:
338
340
  - {key: sftp_batch_mode, section: ssh_connection}
@@ -977,7 +979,7 @@ class Connection(ConnectionBase):
977
979
  try:
978
980
  fh.write(to_bytes(in_data))
979
981
  fh.close()
980
- except (OSError, IOError) as ex:
982
+ except OSError as ex:
981
983
  # The ssh connection may have already terminated at this point, with a more useful error
982
984
  # Only raise AnsibleConnectionFailure if the ssh process is still alive
983
985
  time.sleep(0.001)
@@ -993,7 +995,7 @@ class Connection(ConnectionBase):
993
995
  """ Terminate a process, ignoring errors """
994
996
  try:
995
997
  p.terminate()
996
- except (OSError, IOError):
998
+ except OSError:
997
999
  pass
998
1000
 
999
1001
  # This is separate from _run() because we need to do the same thing for stdout
@@ -1134,7 +1136,7 @@ class Connection(ConnectionBase):
1134
1136
  p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **popen_kwargs)
1135
1137
  stdin = os.fdopen(master, 'wb', 0)
1136
1138
  os.close(slave)
1137
- except (OSError, IOError):
1139
+ except OSError:
1138
1140
  p = None
1139
1141
 
1140
1142
  if not p:
@@ -1142,8 +1144,8 @@ class Connection(ConnectionBase):
1142
1144
  p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
1143
1145
  stderr=subprocess.PIPE, **popen_kwargs)
1144
1146
  stdin = p.stdin # type: ignore[assignment] # stdin will be set and not None due to the calls above
1145
- except (OSError, IOError) as e:
1146
- raise AnsibleError('Unable to execute ssh command line on a controller due to: %s' % to_native(e))
1147
+ except OSError as ex:
1148
+ raise AnsibleError('Unable to execute ssh command line on a controller.') from ex
1147
1149
 
1148
1150
  if password_mechanism == 'sshpass' and conn_password:
1149
1151
  os.close(self.sshpass_pipe[0])
@@ -1169,7 +1171,7 @@ class Connection(ConnectionBase):
1169
1171
 
1170
1172
  # Are we requesting privilege escalation? Right now, we may be invoked
1171
1173
  # to execute sftp/scp with sudoable=True, but we can request escalation
1172
- # only when using ssh. Otherwise we can send initial data straightaway.
1174
+ # only when using ssh. Otherwise, we can send initial data straight away.
1173
1175
 
1174
1176
  state = states.index('ready_to_send')
1175
1177
  if to_bytes(self.get_option('ssh_executable')) in cmd and sudoable:
@@ -828,7 +828,7 @@ class Connection(ConnectionBase):
828
828
  stderr = to_text(b_stderr)
829
829
 
830
830
  if status_code != 0:
831
- raise IOError(stderr)
831
+ raise OSError(stderr)
832
832
  if stdout.strip() == '[DIR]':
833
833
  data = None
834
834
  else:
@@ -32,10 +32,10 @@ from ansible.module_utils.common.json import get_encoder, get_decoder
32
32
  from ansible.module_utils.six import string_types, integer_types, text_type
33
33
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
34
34
  from ansible.module_utils.common.collections import is_sequence
35
- from ansible.module_utils.common.yaml import yaml_load, yaml_load_all
36
35
  from ansible.parsing.yaml.dumper import AnsibleDumper
37
36
  from ansible.template import accept_args_markers, accept_lazy_markers
38
37
  from ansible._internal._templating._jinja_common import MarkerError, UndefinedMarker, validate_arg_type
38
+ from ansible._internal._yaml import _loader as _yaml_loader
39
39
  from ansible.utils.display import Display
40
40
  from ansible.utils.encrypt import do_encrypt, PASSLIB_AVAILABLE
41
41
  from ansible.utils.hashing import md5s, checksum_s
@@ -47,13 +47,13 @@ display = Display()
47
47
  UUID_NAMESPACE_ANSIBLE = uuid.UUID('361E6D51-FAEC-444A-9079-341386DA8E2E')
48
48
 
49
49
 
50
- def to_yaml(a, *_args, default_flow_style: bool | None = None, dump_vault_tags: bool | None = None, **kwargs) -> str:
50
+ @accept_lazy_markers
51
+ def to_yaml(a, *_args, default_flow_style: bool | None = None, **kwargs) -> str:
51
52
  """Serialize input as terse flow-style YAML."""
52
- dumper = partial(AnsibleDumper, dump_vault_tags=dump_vault_tags)
53
-
54
- return yaml.dump(a, Dumper=dumper, allow_unicode=True, default_flow_style=default_flow_style, **kwargs)
53
+ return yaml.dump(a, Dumper=AnsibleDumper, allow_unicode=True, default_flow_style=default_flow_style, **kwargs)
55
54
 
56
55
 
56
+ @accept_lazy_markers
57
57
  def to_nice_yaml(a, indent=4, *_args, default_flow_style=False, **kwargs) -> str:
58
58
  """Serialize input as verbose multi-line YAML."""
59
59
  return to_yaml(a, indent=indent, default_flow_style=default_flow_style, **kwargs)
@@ -98,6 +98,7 @@ _valid_bool_false = {'no', 'off', 'false', '0'}
98
98
  def to_bool(value: object) -> bool:
99
99
  """Convert well-known input values to a boolean value."""
100
100
  value_to_check: object
101
+
101
102
  if isinstance(value, str):
102
103
  value_to_check = value.lower() # accept mixed case variants
103
104
  elif isinstance(value, int): # bool is also an int
@@ -105,14 +106,17 @@ def to_bool(value: object) -> bool:
105
106
  else:
106
107
  value_to_check = value
107
108
 
108
- if value_to_check in _valid_bool_true:
109
- return True
109
+ try:
110
+ if value_to_check in _valid_bool_true:
111
+ return True
110
112
 
111
- if value_to_check in _valid_bool_false:
112
- return False
113
+ if value_to_check in _valid_bool_false:
114
+ return False
113
115
 
114
- # if we're still here, the value is unsupported- always fire a deprecation warning
115
- result = value_to_check == 1 # backwards compatibility with the old code which checked: value in ('yes', 'on', '1', 'true', 1)
116
+ # if we're still here, the value is unsupported- always fire a deprecation warning
117
+ result = value_to_check == 1 # backwards compatibility with the old code which checked: value in ('yes', 'on', '1', 'true', 1)
118
+ except TypeError:
119
+ result = False
116
120
 
117
121
  # NB: update the doc string to reflect reality once this fallback is removed
118
122
  display.deprecated(
@@ -250,11 +254,8 @@ def from_yaml(data):
250
254
  if data is None:
251
255
  return None
252
256
 
253
- if isinstance(data, string_types):
254
- # The ``text_type`` call here strips any custom
255
- # string wrapper class, so that CSafeLoader can
256
- # read the data
257
- return yaml_load(text_type(to_text(data, errors='surrogate_or_strict')))
257
+ if isinstance(data, str):
258
+ return yaml.load(data, Loader=_yaml_loader.AnsibleInstrumentedLoader) # type: ignore[arg-type]
258
259
 
259
260
  display.deprecated(f"The from_yaml filter ignored non-string input of type {native_type_name(data)!r}.", version='2.23', obj=data)
260
261
  return data
@@ -264,11 +265,8 @@ def from_yaml_all(data):
264
265
  if data is None:
265
266
  return [] # backward compatibility; ensure consistent result between classic/native Jinja for None/empty string input
266
267
 
267
- if isinstance(data, string_types):
268
- # The ``text_type`` call here strips any custom
269
- # string wrapper class, so that CSafeLoader can
270
- # read the data
271
- return yaml_load_all(text_type(to_text(data, errors='surrogate_or_strict')))
268
+ if isinstance(data, str):
269
+ return yaml.load_all(data, Loader=_yaml_loader.AnsibleInstrumentedLoader) # type: ignore[arg-type]
272
270
 
273
271
  display.deprecated(f"The from_yaml_all filter ignored non-string input of type {native_type_name(data)!r}.", version='2.23', obj=data)
274
272
  return data
@@ -21,7 +21,11 @@ def do_vault(data, secret, salt=None, vault_id='filter_default', wrap_object=Fal
21
21
  raise TypeError(f"Can only vault strings, instead we got {type(data)}.")
22
22
 
23
23
  if vaultid is not None:
24
- display.deprecated("Use of undocumented 'vaultid', use 'vault_id' instead", version='2.20')
24
+ display.deprecated(
25
+ msg="Use of undocumented `vaultid`.",
26
+ version="2.20",
27
+ help_text="Use `vault_id` instead.",
28
+ )
25
29
 
26
30
  if vault_id == 'filter_default':
27
31
  vault_id = vaultid
@@ -58,7 +62,11 @@ def do_unvault(vault, secret, vault_id='filter_default', vaultid=None):
58
62
  raise TypeError(f"Vault should be in the form of a string, instead we got {type(vault)}.")
59
63
 
60
64
  if vaultid is not None:
61
- display.deprecated("Use of undocumented 'vaultid', use 'vault_id' instead", version='2.20')
65
+ display.deprecated(
66
+ msg="Use of undocumented `vaultid`.",
67
+ version="2.20",
68
+ help_text="Use `vault_id` instead.",
69
+ )
62
70
 
63
71
  if vault_id == 'filter_default':
64
72
  vault_id = vaultid
@@ -3,7 +3,7 @@ DOCUMENTATION:
3
3
  version_added: "1.9"
4
4
  short_description: power of (math operation)
5
5
  description:
6
- - Math operation that returns the Nth power of inputed number, C(X ^ N).
6
+ - Math operation that returns the Nth power of inputted number, C(X ^ N).
7
7
  notes:
8
8
  - This is a passthrough to Python's C(math.pow).
9
9
  positional: _input, _power