ansible-core 2.19.0b6__py3-none-any.whl → 2.19.0rc1__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 (111) hide show
  1. ansible/_internal/_json/__init__.py +31 -20
  2. ansible/_internal/_json/_profiles/_legacy.py +1 -1
  3. ansible/_internal/_templating/_jinja_bits.py +46 -14
  4. ansible/_internal/_templating/_jinja_common.py +1 -1
  5. ansible/_internal/_templating/_jinja_plugins.py +5 -2
  6. ansible/_internal/_templating/_utils.py +2 -1
  7. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +9 -0
  8. ansible/cli/__init__.py +2 -2
  9. ansible/cli/_ssh_askpass.py +37 -30
  10. ansible/cli/adhoc.py +6 -3
  11. ansible/cli/console.py +2 -2
  12. ansible/cli/doc.py +2 -2
  13. ansible/config/base.yml +9 -6
  14. ansible/executor/module_common.py +9 -6
  15. ansible/executor/powershell/psrp_put_file.ps1 +1 -1
  16. ansible/executor/task_executor.py +2 -2
  17. ansible/executor/task_queue_manager.py +34 -70
  18. ansible/executor/task_result.py +1 -1
  19. ansible/galaxy/api.py +2 -2
  20. ansible/galaxy/collection/concrete_artifact_manager.py +2 -2
  21. ansible/galaxy/dependency_resolution/providers.py +3 -3
  22. ansible/inventory/group.py +6 -1
  23. ansible/inventory/host.py +6 -1
  24. ansible/module_utils/_internal/_datatag/__init__.py +6 -1
  25. ansible/module_utils/_internal/_deprecator.py +12 -1
  26. ansible/module_utils/ansible_release.py +1 -1
  27. ansible/module_utils/basic.py +14 -16
  28. ansible/module_utils/common/yaml.py +1 -1
  29. ansible/module_utils/csharp/Ansible.Basic.cs +1 -1
  30. ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
  31. ansible/module_utils/facts/hardware/base.py +1 -1
  32. ansible/module_utils/facts/other/facter.py +1 -1
  33. ansible/module_utils/facts/system/distribution.py +2 -2
  34. ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
  35. ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
  36. ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
  37. ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
  38. ansible/module_utils/urls.py +1 -1
  39. ansible/modules/apt.py +9 -3
  40. ansible/modules/assemble.py +5 -3
  41. ansible/modules/expect.py +5 -5
  42. ansible/modules/hostname.py +2 -2
  43. ansible/modules/pip.py +9 -11
  44. ansible/modules/raw.py +2 -2
  45. ansible/modules/stat.py +1 -1
  46. ansible/modules/systemd.py +1 -1
  47. ansible/modules/systemd_service.py +1 -1
  48. ansible/modules/wait_for.py +10 -3
  49. ansible/parsing/mod_args.py +38 -20
  50. ansible/parsing/vault/__init__.py +3 -3
  51. ansible/playbook/base.py +0 -2
  52. ansible/playbook/helpers.py +1 -1
  53. ansible/playbook/playbook_include.py +23 -57
  54. ansible/playbook/role/__init__.py +40 -23
  55. ansible/plugins/action/__init__.py +2 -2
  56. ansible/plugins/action/assemble.py +2 -1
  57. ansible/plugins/action/assert.py +2 -2
  58. ansible/plugins/action/script.py +5 -4
  59. ansible/plugins/action/template.py +1 -1
  60. ansible/plugins/callback/__init__.py +77 -87
  61. ansible/plugins/callback/default.py +0 -3
  62. ansible/plugins/callback/junit.py +0 -6
  63. ansible/plugins/connection/ssh.py +13 -6
  64. ansible/plugins/filter/pow.yml +1 -1
  65. ansible/plugins/filter/root.yml +1 -1
  66. ansible/plugins/filter/strftime.yml +3 -3
  67. ansible/plugins/filter/to_uuid.yml +1 -1
  68. ansible/plugins/inventory/script.py +1 -1
  69. ansible/plugins/loader.py +5 -0
  70. ansible/plugins/lookup/password.py +4 -6
  71. ansible/release.py +1 -1
  72. ansible/utils/display.py +16 -26
  73. ansible/utils/path.py +1 -1
  74. ansible/utils/vars.py +6 -2
  75. ansible/vars/manager.py +6 -3
  76. ansible/vars/reserved.py +6 -4
  77. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/METADATA +1 -1
  78. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/RECORD +111 -109
  79. ansible_test/_internal/__init__.py +5 -0
  80. ansible_test/_internal/ansible_util.py +1 -1
  81. ansible_test/_internal/classification/python.py +6 -0
  82. ansible_test/_internal/cli/commands/__init__.py +0 -5
  83. ansible_test/_internal/cli/environments.py +51 -5
  84. ansible_test/_internal/commands/coverage/__init__.py +1 -1
  85. ansible_test/_internal/commands/integration/__init__.py +18 -5
  86. ansible_test/_internal/commands/integration/cloud/httptester.py +1 -1
  87. ansible_test/_internal/commands/sanity/__init__.py +3 -1
  88. ansible_test/_internal/commands/sanity/integration_aliases.py +11 -0
  89. ansible_test/_internal/commands/shell/__init__.py +43 -4
  90. ansible_test/_internal/commands/units/__init__.py +4 -1
  91. ansible_test/_internal/config.py +21 -13
  92. ansible_test/_internal/debugging.py +166 -0
  93. ansible_test/_internal/delegation.py +21 -13
  94. ansible_test/_internal/host_profiles.py +197 -6
  95. ansible_test/_internal/inventory.py +4 -0
  96. ansible_test/_internal/metadata.py +94 -4
  97. ansible_test/_internal/processes.py +80 -0
  98. ansible_test/_internal/python_requirements.py +27 -0
  99. ansible_test/_internal/target.py +8 -0
  100. ansible_test/_internal/util_common.py +13 -3
  101. ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +2 -1
  102. ansible_test/_util/target/injector/python.py +8 -0
  103. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/WHEEL +0 -0
  104. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/entry_points.txt +0 -0
  105. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/licenses/COPYING +0 -0
  106. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/licenses/licenses/Apache-License.txt +0 -0
  107. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
  108. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/licenses/licenses/MIT-license.txt +0 -0
  109. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/licenses/licenses/PSF-license.txt +0 -0
  110. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
  111. {ansible_core-2.19.0b6.dist-info → ansible_core-2.19.0rc1.dist-info}/top_level.txt +0 -0
@@ -81,9 +81,10 @@ class ActionModule(ActionBase):
81
81
 
82
82
  def run(self, tmp=None, task_vars=None):
83
83
 
84
- self._supports_check_mode = False
84
+ self._supports_check_mode = True
85
85
 
86
86
  super(ActionModule, self).run(tmp, task_vars)
87
+
87
88
  del tmp # tmp no longer has any effect
88
89
 
89
90
  if task_vars is None:
@@ -72,12 +72,12 @@ class ActionModule(ActionBase):
72
72
  fail_msg = new_module_args['fail_msg']
73
73
  success_msg = new_module_args['success_msg']
74
74
  quiet = new_module_args['quiet']
75
- thats = new_module_args['that']
75
+ that_list = new_module_args['that']
76
76
 
77
77
  if not quiet:
78
78
  result['_ansible_verbose_always'] = True
79
79
 
80
- for that in thats:
80
+ for that in that_list:
81
81
  test_result = self._templar.evaluate_conditional(conditional=that)
82
82
  if not test_result:
83
83
  result['failed'] = True
@@ -20,6 +20,7 @@ import os
20
20
  import pathlib
21
21
  import re
22
22
  import shlex
23
+ import typing as _t
23
24
 
24
25
  from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleActionSkip
25
26
  from ansible.executor.powershell import module_manifest as ps_manifest
@@ -35,7 +36,7 @@ class ActionModule(ActionBase):
35
36
  # after chopping off a potential drive letter.
36
37
  windows_absolute_path_detection = re.compile(r'^(?:[a-zA-Z]\:)?(\\|\/)')
37
38
 
38
- def run(self, tmp=None, task_vars=None):
39
+ def run(self, tmp: str | None = None, task_vars: dict[str, _t.Any] | None = None) -> dict[str, _t.Any]:
39
40
  """ handler for file transfer operations """
40
41
  if task_vars is None:
41
42
  task_vars = dict()
@@ -130,7 +131,7 @@ class ActionModule(ActionBase):
130
131
  self._fixup_perms2((self._connection._shell.tmpdir, tmp_src), execute=True)
131
132
 
132
133
  # add preparation steps to one ssh roundtrip executing the script
133
- env_dict = dict()
134
+ env_dict: dict[str, _t.Any] = {}
134
135
  env_string = self._compute_environment_string(env_dict)
135
136
 
136
137
  if executable:
@@ -164,10 +165,10 @@ class ActionModule(ActionBase):
164
165
  script_cmd = self._connection._shell.build_module_command(env_string='', shebang='#!powershell', cmd='')
165
166
 
166
167
  # now we execute script, always assume changed.
167
- result = dict(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir), changed=True)
168
+ result: dict[str, object] = dict(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir), changed=True)
168
169
 
169
170
  if 'rc' in result and result['rc'] != 0:
170
- raise AnsibleActionFail('non-zero return code', result=result)
171
+ result.update(msg='non-zero return code', failed=True)
171
172
 
172
173
  return result
173
174
  finally:
@@ -44,7 +44,7 @@ class ActionModule(ActionBase):
44
44
  del tmp # tmp no longer has any effect
45
45
 
46
46
  # Options type validation
47
- # stings
47
+ # strings
48
48
  for s_type in ('src', 'dest', 'state', 'newline_sequence', 'variable_start_string', 'variable_end_string', 'block_start_string',
49
49
  'block_end_string', 'comment_start_string', 'comment_end_string'):
50
50
  if s_type in self._task.args:
@@ -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
 
@@ -640,11 +640,11 @@ def _clean_shm(func):
640
640
  self.shm.close()
641
641
  with contextlib.suppress(FileNotFoundError):
642
642
  self.shm.unlink()
643
- if not _HAS_RESOURCE_TRACK:
644
- # deprecated: description='unneeded due to track argument for SharedMemory' python_version='3.12'
645
- # There is a resource tracking issue where the resource is deleted, but tracking still has a record
646
- # This will effectively overwrite the record and remove it
647
- SharedMemory(name=self.shm.name, create=True, size=1).unlink()
643
+ if not _HAS_RESOURCE_TRACK:
644
+ # deprecated: description='unneeded due to track argument for SharedMemory' python_version='3.12'
645
+ # There is a resource tracking issue where the resource is deleted, but tracking still has a record
646
+ # This will effectively overwrite the record and remove it
647
+ SharedMemory(name=self.shm.name, create=True, size=1).unlink()
648
648
  return ret
649
649
  return inner
650
650
 
@@ -961,6 +961,13 @@ class Connection(ConnectionBase):
961
961
  b_args = (b"-o", b'ControlPath="%s"' % to_bytes(self.control_path % dict(directory=cpdir), errors='surrogate_or_strict'))
962
962
  self._add_args(b_command, b_args, u"found only ControlPersist; added ControlPath")
963
963
 
964
+ if password_mechanism == "ssh_askpass":
965
+ self._add_args(
966
+ b_command,
967
+ (b"-o", b"NumberOfPasswordPrompts=1"),
968
+ "Restrict number of password prompts in case incorrect password is provided.",
969
+ )
970
+
964
971
  # Finally, we add any caller-supplied extras.
965
972
  if other_args:
966
973
  b_command += [to_bytes(a) for a in other_args]
@@ -1171,7 +1178,7 @@ class Connection(ConnectionBase):
1171
1178
 
1172
1179
  # Are we requesting privilege escalation? Right now, we may be invoked
1173
1180
  # to execute sftp/scp with sudoable=True, but we can request escalation
1174
- # only when using ssh. Otherwise we can send initial data straightaway.
1181
+ # only when using ssh. Otherwise, we can send initial data straight away.
1175
1182
 
1176
1183
  state = states.index('ready_to_send')
1177
1184
  if to_bytes(self.get_option('ssh_executable')) in cmd and sudoable:
@@ -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
@@ -3,7 +3,7 @@ DOCUMENTATION:
3
3
  version_added: "1.9"
4
4
  short_description: root of (math operation)
5
5
  description:
6
- - Math operation that returns the Nth root of inputed number C(X ^^ N).
6
+ - Math operation that returns the Nth root of inputted number C(X ^^ N).
7
7
  positional: _input, base
8
8
  options:
9
9
  _input:
@@ -1,16 +1,16 @@
1
1
  DOCUMENTATION:
2
2
  name: strftime
3
3
  version_added: "2.4"
4
- short_description: date formating
4
+ short_description: date formatting
5
5
  description:
6
- - Using Python's C(strftime) function, take a data formating string and a date/time to create a formatted date.
6
+ - Using Python's C(strftime) function, take a data formatting string and a date/time to create a formatted date.
7
7
  notes:
8
8
  - This is a passthrough to Python's C(stftime), for a complete set of formatting options go to https://strftime.org/.
9
9
  positional: _input, second, utc
10
10
  options:
11
11
  _input:
12
12
  description:
13
- - A formating string following C(stftime) conventions.
13
+ - A formatting string following C(stftime) conventions.
14
14
  - See L(the Python documentation, https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior) for a reference.
15
15
  type: str
16
16
  required: true
@@ -7,7 +7,7 @@ DOCUMENTATION:
7
7
  positional: _input, namespace
8
8
  options:
9
9
  _input:
10
- description: String to use as base fo the UUID.
10
+ description: String to use as base of the UUID.
11
11
  type: str
12
12
  required: true
13
13
  namespace:
@@ -98,7 +98,7 @@ EXAMPLES = r'''# fmt: code
98
98
  def get_api_data(namespace: str, pretty=False) -> str:
99
99
  """
100
100
  :param namespace: parameter for our custom api
101
- :param pretty: Human redable JSON vs machine readable
101
+ :param pretty: Human readable JSON vs machine readable
102
102
  :return: JSON string
103
103
  """
104
104
  found_data = list(MyInventoryAPI(namespace))
ansible/plugins/loader.py CHANGED
@@ -989,6 +989,9 @@ class PluginLoader:
989
989
  def get_with_context(self, name, *args, **kwargs) -> get_with_context_result:
990
990
  """ instantiates a plugin of the given name using arguments """
991
991
 
992
+ if not name:
993
+ raise ValueError('A non-empty plugin name is required.')
994
+
992
995
  found_in_cache = True
993
996
  class_only = kwargs.pop('class_only', False)
994
997
  collection_list = kwargs.pop('collection_list', None)
@@ -1034,6 +1037,7 @@ class PluginLoader:
1034
1037
  except AttributeError:
1035
1038
  return get_with_context_result(None, plugin_load_context)
1036
1039
  if not issubclass(obj, plugin_class):
1040
+ display.warning(f"Ignoring {self.type} plugin {resolved_type_name!r} due to missing base class {self.base_class!r}.")
1037
1041
  return get_with_context_result(None, plugin_load_context)
1038
1042
 
1039
1043
  # FIXME: update this to use the load context
@@ -1721,6 +1725,7 @@ callback_loader = PluginLoader(
1721
1725
  'ansible.plugins.callback',
1722
1726
  C.DEFAULT_CALLBACK_PLUGIN_PATH,
1723
1727
  'callback_plugins',
1728
+ required_base_class='CallbackBase',
1724
1729
  )
1725
1730
 
1726
1731
  connection_loader = PluginLoader(
@@ -126,6 +126,7 @@ _raw:
126
126
  elements: str
127
127
  """
128
128
 
129
+ import contextlib
129
130
  import os
130
131
  import string
131
132
  import time
@@ -269,15 +270,12 @@ def _get_lock(b_path):
269
270
  b_pathdir = os.path.dirname(b_path)
270
271
  lockfile_name = to_bytes("%s.ansible_lockfile" % hashlib.sha1(b_path).hexdigest())
271
272
  lockfile = os.path.join(b_pathdir, lockfile_name)
272
- if not os.path.exists(lockfile) and b_path != to_bytes('/dev/null'):
273
- try:
274
- makedirs_safe(b_pathdir, mode=0o700)
273
+ if b_path != b'/dev/null':
274
+ makedirs_safe(b_pathdir, mode=0o700)
275
+ with contextlib.suppress(FileExistsError):
275
276
  fd = os.open(lockfile, os.O_CREAT | os.O_EXCL)
276
277
  os.close(fd)
277
278
  first_process = True
278
- except OSError as e:
279
- if e.strerror != 'File exists':
280
- raise
281
279
 
282
280
  counter = 0
283
281
  # if the lock is got by other process, wait until it's released
ansible/release.py CHANGED
@@ -17,6 +17,6 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- __version__ = '2.19.0b6'
20
+ __version__ = '2.19.0rc1'
21
21
  __author__ = 'Ansible, Inc.'
22
22
  __codename__ = "What Is and What Should Never Be"