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