ansible-core 2.19.0b3__py3-none-any.whl → 2.19.0b5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ansible/_internal/__init__.py +2 -2
- 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 +6 -6
- 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 +21 -16
- ansible/_internal/_templating/_jinja_common.py +18 -27
- ansible/_internal/_templating/_jinja_plugins.py +31 -3
- ansible/_internal/_templating/_lazy_containers.py +5 -5
- ansible/_internal/_templating/_transform.py +20 -19
- ansible/_internal/_templating/_utils.py +1 -1
- ansible/_internal/_testing.py +26 -0
- ansible/_internal/_yaml/_dumper.py +1 -1
- 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 +5 -82
- ansible/cli/arguments/option_helpers.py +8 -5
- ansible/cli/doc.py +84 -28
- ansible/cli/inventory.py +1 -1
- ansible/compat/importlib_resources.py +9 -12
- ansible/config/base.yml +27 -23
- ansible/config/manager.py +142 -101
- ansible/constants.py +1 -1
- ansible/errors/__init__.py +96 -49
- ansible/executor/module_common.py +8 -10
- 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 +38 -113
- ansible/executor/task_executor.py +26 -61
- ansible/executor/task_result.py +2 -4
- ansible/galaxy/collection/__init__.py +1 -4
- ansible/inventory/manager.py +1 -0
- ansible/module_utils/_internal/__init__.py +0 -3
- ansible/module_utils/_internal/_ambient_context.py +3 -3
- ansible/module_utils/_internal/_ansiballz.py +4 -2
- ansible/module_utils/_internal/_datatag/__init__.py +20 -14
- ansible/module_utils/_internal/_datatag/_tags.py +2 -2
- ansible/module_utils/_internal/_deprecator.py +66 -48
- 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 +21 -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} +28 -47
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
- ansible/module_utils/_internal/_plugin_info.py +1 -1
- 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 +49 -15
- 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/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/datatag.py +5 -2
- ansible/module_utils/facts/system/distribution.py +16 -3
- ansible/module_utils/facts/virtual/linux.py +2 -2
- ansible/module_utils/parsing/convert_bool.py +6 -0
- ansible/module_utils/service.py +2 -9
- ansible/modules/apt_repository.py +7 -29
- ansible/modules/assemble.py +4 -4
- ansible/modules/async_status.py +13 -11
- ansible/modules/async_wrapper.py +5 -5
- ansible/modules/cron.py +3 -5
- ansible/modules/dnf5.py +15 -22
- ansible/modules/git.py +1 -6
- ansible/modules/hostname.py +0 -1
- ansible/modules/pip.py +2 -4
- ansible/modules/service.py +3 -9
- ansible/modules/sysvinit.py +3 -3
- ansible/parsing/ajson.py +3 -5
- ansible/parsing/dataloader.py +4 -4
- 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 +4 -4
- ansible/playbook/playbook_include.py +1 -1
- ansible/playbook/taggable.py +0 -3
- ansible/plugins/__init__.py +0 -25
- ansible/plugins/action/__init__.py +9 -32
- 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/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 +6 -8
- ansible/plugins/action/unarchive.py +5 -15
- ansible/plugins/action/uri.py +9 -20
- ansible/plugins/callback/__init__.py +4 -6
- ansible/plugins/callback/junit.py +4 -2
- ansible/plugins/connection/local.py +2 -2
- ansible/plugins/connection/ssh.py +17 -9
- ansible/plugins/connection/winrm.py +5 -2
- ansible/plugins/doc_fragments/constructed.py +2 -2
- ansible/plugins/filter/core.py +13 -6
- ansible/plugins/filter/encryption.py +4 -4
- ansible/plugins/inventory/__init__.py +11 -10
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +69 -16
- ansible/plugins/loader.py +10 -9
- ansible/plugins/lookup/csvfile.py +16 -71
- ansible/plugins/lookup/first_found.py +2 -1
- ansible/plugins/shell/__init__.py +56 -2
- ansible/plugins/shell/powershell.py +66 -9
- ansible/plugins/shell/sh.py +9 -5
- ansible/plugins/test/core.py +21 -15
- ansible/plugins/test/finished.yml +1 -1
- ansible/plugins/test/uri.py +2 -5
- ansible/release.py +1 -1
- ansible/template/__init__.py +30 -2
- ansible/utils/collection_loader/__init__.py +2 -0
- ansible/utils/display.py +107 -128
- ansible/utils/hashing.py +0 -1
- ansible/utils/listify.py +6 -4
- ansible/utils/plugin_docs.py +2 -1
- ansible/utils/unsafe_proxy.py +1 -1
- ansible/vars/hostvars.py +1 -1
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/METADATA +3 -2
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/RECORD +173 -161
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.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/util.py +20 -0
- 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 +73 -8
- ansible_test/_util/target/setup/bootstrap.sh +31 -0
- ansible/_internal/_errors/_utils.py +0 -310
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses}/COPYING +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/Apache-License.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/MIT-license.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/PSF-license.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info/licenses/licenses}/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b3.dist-info → ansible_core-2.19.0b5.dist-info}/top_level.txt +0 -0
@@ -7,13 +7,11 @@ A future release will remove the provisional status.
|
|
7
7
|
|
8
8
|
from __future__ import annotations as _annotations
|
9
9
|
|
10
|
-
import sys as _sys
|
11
10
|
import dataclasses as _dataclasses
|
11
|
+
import sys as _sys
|
12
|
+
import typing as _t
|
12
13
|
|
13
|
-
|
14
|
-
from ..compat import typing as _t
|
15
|
-
|
16
|
-
from ansible.module_utils._internal import _datatag, _validation
|
14
|
+
from ansible.module_utils._internal import _datatag, _dataclass_validation
|
17
15
|
|
18
16
|
if _sys.version_info >= (3, 10):
|
19
17
|
# Using slots for reduced memory usage and improved performance.
|
@@ -29,50 +27,50 @@ class PluginInfo(_datatag.AnsibleSerializableDataclass):
|
|
29
27
|
|
30
28
|
resolved_name: str
|
31
29
|
"""The resolved canonical plugin name; always fully-qualified for collection plugins."""
|
30
|
+
|
32
31
|
type: str
|
33
32
|
"""The plugin type."""
|
34
33
|
|
35
|
-
_COLLECTION_ONLY_TYPE: _t.ClassVar[str] = 'collection'
|
36
|
-
"""This is not a real plugin type. It's a placeholder for use by a `PluginInfo` instance which references a collection without a plugin."""
|
37
34
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
if not collection_name:
|
42
|
-
return None
|
35
|
+
@_dataclasses.dataclass(**_dataclass_kwargs)
|
36
|
+
class EventChain(_datatag.AnsibleSerializableDataclass):
|
37
|
+
"""A chain used to link one event to another."""
|
43
38
|
|
44
|
-
|
39
|
+
_validation_auto_enabled = False
|
45
40
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
41
|
+
def __post_init__(self): ... # required for deferred dataclass validation
|
42
|
+
|
43
|
+
msg_reason: str
|
44
|
+
traceback_reason: str
|
45
|
+
event: Event
|
46
|
+
follow: bool = True
|
50
47
|
|
51
48
|
|
52
49
|
@_dataclasses.dataclass(**_dataclass_kwargs)
|
53
|
-
class
|
54
|
-
"""
|
50
|
+
class Event(_datatag.AnsibleSerializableDataclass):
|
51
|
+
"""Base class for an error/warning/deprecation event with optional chain (from an exception __cause__ chain) and an optional traceback."""
|
52
|
+
|
53
|
+
_validation_auto_enabled = False
|
54
|
+
|
55
|
+
def __post_init__(self): ... # required for deferred dataclass validation
|
55
56
|
|
56
57
|
msg: str
|
57
58
|
formatted_source_context: _t.Optional[str] = None
|
59
|
+
formatted_traceback: _t.Optional[str] = None
|
58
60
|
help_text: _t.Optional[str] = None
|
61
|
+
chain: _t.Optional[EventChain] = None
|
62
|
+
events: _t.Optional[_t.Tuple[Event, ...]] = None
|
63
|
+
|
64
|
+
|
65
|
+
_dataclass_validation.inject_post_init_validation(EventChain, EventChain._validation_allow_subclasses)
|
66
|
+
_dataclass_validation.inject_post_init_validation(Event, Event._validation_allow_subclasses)
|
59
67
|
|
60
68
|
|
61
69
|
@_dataclasses.dataclass(**_dataclass_kwargs)
|
62
70
|
class SummaryBase(_datatag.AnsibleSerializableDataclass):
|
63
71
|
"""Base class for an error/warning/deprecation summary with details (possibly derived from an exception __cause__ chain) and an optional traceback."""
|
64
72
|
|
65
|
-
|
66
|
-
formatted_traceback: _t.Optional[str] = None
|
67
|
-
|
68
|
-
def _format(self) -> str:
|
69
|
-
"""Returns a string representation of the details."""
|
70
|
-
# DTFIX-RELEASE: eliminate this function and use a common message squashing utility such as get_chained_message on instances of this type
|
71
|
-
return ': '.join(detail.msg for detail in self.details)
|
72
|
-
|
73
|
-
def _post_validate(self) -> None:
|
74
|
-
if not self.details:
|
75
|
-
raise ValueError(f'{type(self).__name__}.details cannot be empty')
|
73
|
+
event: Event
|
76
74
|
|
77
75
|
|
78
76
|
@_dataclasses.dataclass(**_dataclass_kwargs)
|
@@ -106,20 +104,3 @@ class DeprecationSummary(WarningSummary):
|
|
106
104
|
Ignored if `deprecator` is not provided.
|
107
105
|
Ignored if `date` is provided.
|
108
106
|
"""
|
109
|
-
|
110
|
-
def _as_simple_dict(self) -> _t.Dict[str, _t.Any]:
|
111
|
-
"""Returns a dictionary representation of the deprecation object in the format exposed to playbooks."""
|
112
|
-
from ansible.module_utils._internal._deprecator import INDETERMINATE_DEPRECATOR # circular import from messages
|
113
|
-
|
114
|
-
if self.deprecator and self.deprecator != INDETERMINATE_DEPRECATOR:
|
115
|
-
collection_name = '.'.join(self.deprecator.resolved_name.split('.')[:2])
|
116
|
-
else:
|
117
|
-
collection_name = None
|
118
|
-
|
119
|
-
result = self._as_dict()
|
120
|
-
result.update(
|
121
|
-
msg=self._format(),
|
122
|
-
collection_name=collection_name,
|
123
|
-
)
|
124
|
-
|
125
|
-
return result
|
@@ -1,7 +1,5 @@
|
|
1
1
|
"""Patches for builtin `dataclasses` module."""
|
2
2
|
|
3
|
-
# deprecated: description='verify ClassVar support in dataclasses has been fixed in Python before removing this patching code', python_version='3.13'
|
4
|
-
|
5
3
|
from __future__ import annotations
|
6
4
|
|
7
5
|
import dataclasses
|
@@ -26,7 +24,7 @@ class DataclassesIsTypePatch(CallablePatch):
|
|
26
24
|
@dataclasses.dataclass
|
27
25
|
class CheckClassVar:
|
28
26
|
# this is the broken case requiring patching: ClassVar dot-referenced from a module that is not `typing` is treated as an instance field
|
29
|
-
# DTFIX-
|
27
|
+
# DTFIX-FUTURE: file/link CPython bug report, deprecate this patch if/when it's fixed in CPython
|
30
28
|
a_classvar: _ts.ClassVar[int] # type: ignore[name-defined]
|
31
29
|
a_field: int
|
32
30
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from __future__ import annotations as _annotations
|
2
|
+
|
3
|
+
import inspect as _inspect
|
4
|
+
import typing as _t
|
5
|
+
|
6
|
+
|
7
|
+
def caller_frame() -> _inspect.FrameInfo | None:
|
8
|
+
"""Return the caller stack frame, skipping any marked with the `_skip_stackwalk` local."""
|
9
|
+
_skip_stackwalk = True
|
10
|
+
|
11
|
+
return next(iter_stack(), None)
|
12
|
+
|
13
|
+
|
14
|
+
def iter_stack() -> _t.Generator[_inspect.FrameInfo]:
|
15
|
+
"""Iterate over stack frames, skipping any marked with the `_skip_stackwalk` local."""
|
16
|
+
_skip_stackwalk = True
|
17
|
+
|
18
|
+
for frame_info in _inspect.stack():
|
19
|
+
if '_skip_stackwalk' in frame_info.frame.f_locals:
|
20
|
+
continue
|
21
|
+
|
22
|
+
yield frame_info
|
@@ -6,9 +6,10 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
import enum
|
9
|
-
import inspect
|
10
9
|
import traceback
|
11
10
|
|
11
|
+
from . import _stack
|
12
|
+
|
12
13
|
|
13
14
|
class TracebackEvent(enum.Enum):
|
14
15
|
"""The events for which tracebacks can be enabled."""
|
@@ -16,6 +17,7 @@ class TracebackEvent(enum.Enum):
|
|
16
17
|
ERROR = enum.auto()
|
17
18
|
WARNING = enum.auto()
|
18
19
|
DEPRECATED = enum.auto()
|
20
|
+
DEPRECATED_VALUE = enum.auto() # implies DEPRECATED
|
19
21
|
|
20
22
|
|
21
23
|
def traceback_for() -> list[str]:
|
@@ -28,24 +30,25 @@ def is_traceback_enabled(event: TracebackEvent) -> bool:
|
|
28
30
|
return _is_traceback_enabled(event)
|
29
31
|
|
30
32
|
|
31
|
-
def maybe_capture_traceback(event: TracebackEvent) -> str | None:
|
33
|
+
def maybe_capture_traceback(msg: str, event: TracebackEvent) -> str | None:
|
32
34
|
"""
|
33
35
|
Optionally capture a traceback for the current call stack, formatted as a string, if the specified traceback event is enabled.
|
34
|
-
|
36
|
+
Frames marked with the `_skip_stackwalk` local are omitted.
|
35
37
|
"""
|
38
|
+
_skip_stackwalk = True
|
39
|
+
|
36
40
|
if not is_traceback_enabled(event):
|
37
41
|
return None
|
38
42
|
|
39
43
|
tb_lines = []
|
40
44
|
|
41
|
-
if
|
45
|
+
if frame_info := _stack.caller_frame():
|
42
46
|
# DTFIX-FUTURE: rewrite target-side tracebacks to point at controller-side paths?
|
43
|
-
frames = inspect.getouterframes(current_frame)
|
44
|
-
ignore_frame_count = 2 # ignore this function and its caller
|
45
47
|
tb_lines.append('Traceback (most recent call last):\n')
|
46
|
-
tb_lines.extend(traceback.format_stack(
|
48
|
+
tb_lines.extend(traceback.format_stack(frame_info.frame))
|
49
|
+
tb_lines.append(f'Message: {msg}\n')
|
47
50
|
else:
|
48
|
-
tb_lines.append('
|
51
|
+
tb_lines.append('(frame not found)\n') # pragma: nocover
|
49
52
|
|
50
53
|
return ''.join(tb_lines)
|
51
54
|
|
ansible/module_utils/basic.py
CHANGED
@@ -75,7 +75,7 @@ except ImportError:
|
|
75
75
|
# Python2 & 3 way to get NoneType
|
76
76
|
NoneType = type(None)
|
77
77
|
|
78
|
-
from ._internal import _traceback, _errors, _debugging, _deprecator
|
78
|
+
from ._internal import _traceback, _errors, _debugging, _deprecator, _messages
|
79
79
|
|
80
80
|
from .common.text.converters import (
|
81
81
|
to_native,
|
@@ -161,10 +161,10 @@ from ansible.module_utils.common.validation import (
|
|
161
161
|
safe_eval,
|
162
162
|
)
|
163
163
|
from ansible.module_utils.common._utils import get_all_subclasses as _get_all_subclasses
|
164
|
-
from ansible.module_utils.common import messages as _messages
|
165
164
|
from ansible.module_utils.parsing.convert_bool import BOOLEANS, BOOLEANS_FALSE, BOOLEANS_TRUE, boolean
|
166
165
|
from ansible.module_utils.common.warnings import (
|
167
166
|
deprecate,
|
167
|
+
error_as_warning,
|
168
168
|
get_deprecations,
|
169
169
|
get_warnings,
|
170
170
|
warn,
|
@@ -505,9 +505,34 @@ class AnsibleModule(object):
|
|
505
505
|
|
506
506
|
return self._tmpdir
|
507
507
|
|
508
|
-
def warn(
|
509
|
-
|
510
|
-
|
508
|
+
def warn(
|
509
|
+
self,
|
510
|
+
warning: str,
|
511
|
+
*,
|
512
|
+
help_text: str | None = None,
|
513
|
+
) -> None:
|
514
|
+
_skip_stackwalk = True
|
515
|
+
|
516
|
+
warn(
|
517
|
+
warning=warning,
|
518
|
+
help_text=help_text,
|
519
|
+
)
|
520
|
+
|
521
|
+
def error_as_warning(
|
522
|
+
self,
|
523
|
+
msg: str | None,
|
524
|
+
exception: BaseException,
|
525
|
+
*,
|
526
|
+
help_text: str | None = None,
|
527
|
+
) -> None:
|
528
|
+
"""Display an exception as a warning."""
|
529
|
+
_skip_stackwalk = True
|
530
|
+
|
531
|
+
error_as_warning(
|
532
|
+
msg=msg,
|
533
|
+
exception=exception,
|
534
|
+
help_text=help_text,
|
535
|
+
)
|
511
536
|
|
512
537
|
def deprecate(
|
513
538
|
self,
|
@@ -1399,7 +1424,7 @@ class AnsibleModule(object):
|
|
1399
1424
|
# deprecate(
|
1400
1425
|
# msg="The `AnsibleModule.jsonify' method is deprecated.",
|
1401
1426
|
# version="2.27",
|
1402
|
-
# # help_text="", # DTFIX-
|
1427
|
+
# # help_text="", # DTFIX-FUTURE: fill in this help text
|
1403
1428
|
# )
|
1404
1429
|
|
1405
1430
|
try:
|
@@ -1528,16 +1553,25 @@ class AnsibleModule(object):
|
|
1528
1553
|
)
|
1529
1554
|
|
1530
1555
|
if isinstance(exception, BaseException):
|
1531
|
-
# Include a `_messages.
|
1532
|
-
# The `msg` is included in the
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1556
|
+
# Include a `_messages.Event` in the result.
|
1557
|
+
# The `msg` is included in the chain to ensure it is not lost when looking only at `exception` from the result.
|
1558
|
+
|
1559
|
+
kwargs.update(
|
1560
|
+
exception=_messages.ErrorSummary(
|
1561
|
+
event=_messages.Event(
|
1562
|
+
msg=msg,
|
1563
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR),
|
1564
|
+
chain=_messages.EventChain(
|
1565
|
+
msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
|
1566
|
+
traceback_reason="The above exception was the direct cause of the following error:",
|
1567
|
+
event=_errors.EventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR)),
|
1568
|
+
),
|
1569
|
+
),
|
1570
|
+
),
|
1571
|
+
)
|
1538
1572
|
elif _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR):
|
1539
1573
|
# Include only a formatted traceback string in the result.
|
1540
|
-
# The controller will combine this with `msg` to create an `_messages.
|
1574
|
+
# The controller will combine this with `msg` to create an `_messages.ErrorSummary`.
|
1541
1575
|
|
1542
1576
|
formatted_traceback: str | None
|
1543
1577
|
|
@@ -1546,7 +1580,7 @@ class AnsibleModule(object):
|
|
1546
1580
|
elif exception is _UNSET and (current_exception := t.cast(t.Optional[BaseException], sys.exc_info()[1])):
|
1547
1581
|
formatted_traceback = _traceback.maybe_extract_traceback(current_exception, _traceback.TracebackEvent.ERROR)
|
1548
1582
|
else:
|
1549
|
-
formatted_traceback = _traceback.maybe_capture_traceback(_traceback.TracebackEvent.ERROR)
|
1583
|
+
formatted_traceback = _traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR)
|
1550
1584
|
|
1551
1585
|
if formatted_traceback:
|
1552
1586
|
kwargs.update(exception=formatted_traceback)
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
6
6
|
|
7
7
|
from copy import deepcopy
|
8
8
|
|
9
|
+
from ansible.module_utils.datatag import deprecator_from_collection_name
|
9
10
|
from ansible.module_utils.common.parameters import (
|
10
11
|
_ADDITIONAL_CHECKS,
|
11
12
|
_get_legal_inputs,
|
@@ -22,7 +23,6 @@ from ansible.module_utils.common.parameters import (
|
|
22
23
|
|
23
24
|
from ansible.module_utils.common.text.converters import to_native
|
24
25
|
from ansible.module_utils.common.warnings import deprecate, warn
|
25
|
-
from ansible.module_utils.common import messages as _messages
|
26
26
|
|
27
27
|
from ansible.module_utils.common.validation import (
|
28
28
|
check_mutually_exclusive,
|
@@ -306,7 +306,7 @@ class ModuleArgumentSpecValidator(ArgumentSpecValidator):
|
|
306
306
|
msg=d['msg'],
|
307
307
|
version=d.get('version'),
|
308
308
|
date=d.get('date'),
|
309
|
-
deprecator=
|
309
|
+
deprecator=deprecator_from_collection_name(d.get('collection_name')),
|
310
310
|
)
|
311
311
|
|
312
312
|
for w in result._warnings:
|
@@ -6,6 +6,7 @@
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
8
|
|
9
|
+
from ansible.module_utils.common import warnings as _warnings
|
9
10
|
from ansible.module_utils.six import binary_type, text_type
|
10
11
|
from ansible.module_utils.six.moves.collections_abc import Hashable, Mapping, MutableMapping, Sequence # pylint: disable=unused-import
|
11
12
|
|
@@ -102,6 +103,11 @@ def count(seq):
|
|
102
103
|
code is run on Python 2.6.* where collections.Counter is not available. It should be
|
103
104
|
deprecated and replaced when support for Python < 2.7 is dropped.
|
104
105
|
"""
|
106
|
+
_warnings.deprecate(
|
107
|
+
msg="The `ansible.module_utils.common.collections.count` function is deprecated.",
|
108
|
+
version="2.23",
|
109
|
+
help_text="Use `collections.Counter` from the Python standard library instead.",
|
110
|
+
)
|
105
111
|
if not is_iterable(seq):
|
106
112
|
raise Exception('Argument provided is not an iterable')
|
107
113
|
counters = dict()
|
@@ -20,7 +20,7 @@ def __getattr__(name: str) -> object:
|
|
20
20
|
# _warnings.deprecate(
|
21
21
|
# msg="The `AnsibleJSONEncoder` type is deprecated.",
|
22
22
|
# version="2.27",
|
23
|
-
# help_text="Use a profile-based encoder instead.", # DTFIX-
|
23
|
+
# help_text="Use a profile-based encoder instead.", # DTFIX-FUTURE: improve this help text
|
24
24
|
# )
|
25
25
|
|
26
26
|
return _get_legacy_encoder()
|
@@ -31,7 +31,7 @@ def __getattr__(name: str) -> object:
|
|
31
31
|
# _warnings.deprecate(
|
32
32
|
# msg="The `AnsibleJSONDecoder` type is deprecated.",
|
33
33
|
# version="2.27",
|
34
|
-
# help_text="Use a profile-based decoder instead.", # DTFIX-
|
34
|
+
# help_text="Use a profile-based decoder instead.", # DTFIX-FUTURE: improve this help text
|
35
35
|
# )
|
36
36
|
|
37
37
|
return _tagless.Decoder
|
@@ -245,7 +245,7 @@ def jsonify(data, **kwargs):
|
|
245
245
|
# deprecate(
|
246
246
|
# msg="The `jsonify` function is deprecated.",
|
247
247
|
# version="2.27",
|
248
|
-
# # help_text="", # DTFIX-
|
248
|
+
# # help_text="", # DTFIX-FUTURE: fill in this help text
|
249
249
|
# )
|
250
250
|
|
251
251
|
return json.dumps(data, cls=_common_json._get_legacy_encoder(), _decode_bytes=True, **kwargs)
|
@@ -257,7 +257,7 @@ def container_to_bytes(d, encoding='utf-8', errors='surrogate_or_strict'):
|
|
257
257
|
Specialized for json return because this only handles, lists, tuples,
|
258
258
|
and dict container types (the containers that the json module returns)
|
259
259
|
"""
|
260
|
-
# DTFIX-
|
260
|
+
# DTFIX-FUTURE: deprecate
|
261
261
|
|
262
262
|
if isinstance(d, text_type):
|
263
263
|
return to_bytes(d, encoding=encoding, errors=errors)
|
@@ -277,7 +277,7 @@ def container_to_text(d, encoding='utf-8', errors='surrogate_or_strict'):
|
|
277
277
|
Specialized for json return because this only handles, lists, tuples,
|
278
278
|
and dict container types (the containers that the json module returns)
|
279
279
|
"""
|
280
|
-
# DTFIX-
|
280
|
+
# DTFIX-FUTURE: deprecate
|
281
281
|
|
282
282
|
if isinstance(d, binary_type):
|
283
283
|
# Warning, can traceback
|
@@ -402,7 +402,7 @@ def check_type_list(value):
|
|
402
402
|
if isinstance(value, list):
|
403
403
|
return value
|
404
404
|
|
405
|
-
# DTFIX-
|
405
|
+
# DTFIX-FUTURE: deprecate legacy comma split functionality, eventually replace with `_check_type_list_strict`
|
406
406
|
if isinstance(value, string_types):
|
407
407
|
return value.split(",")
|
408
408
|
elif isinstance(value, int) or isinstance(value, float):
|
@@ -6,20 +6,77 @@ from __future__ import annotations as _annotations
|
|
6
6
|
|
7
7
|
import typing as _t
|
8
8
|
|
9
|
-
from ansible.module_utils._internal import _traceback, _deprecator
|
10
|
-
from ansible.module_utils.common import messages as _messages
|
9
|
+
from ansible.module_utils._internal import _traceback, _deprecator, _event_utils, _messages, _errors
|
11
10
|
from ansible.module_utils import _internal
|
12
11
|
|
13
12
|
|
14
|
-
def warn(
|
13
|
+
def warn(
|
14
|
+
warning: str,
|
15
|
+
*,
|
16
|
+
help_text: str | None = None,
|
17
|
+
obj: object | None = None,
|
18
|
+
) -> None:
|
15
19
|
"""Record a warning to be returned with the module result."""
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
_skip_stackwalk = True
|
21
|
+
|
22
|
+
if _internal.is_controller:
|
23
|
+
_display = _internal.import_controller_module('ansible.utils.display').Display()
|
24
|
+
_display.warning(
|
25
|
+
msg=warning,
|
26
|
+
help_text=help_text,
|
27
|
+
obj=obj,
|
28
|
+
)
|
29
|
+
|
30
|
+
return
|
31
|
+
|
32
|
+
warning = _messages.WarningSummary(
|
33
|
+
event=_messages.Event(
|
34
|
+
msg=warning,
|
35
|
+
help_text=help_text,
|
36
|
+
formatted_traceback=_traceback.maybe_capture_traceback(warning, _traceback.TracebackEvent.WARNING),
|
37
|
+
),
|
38
|
+
)
|
39
|
+
|
40
|
+
_global_warnings[warning] = None
|
41
|
+
|
42
|
+
|
43
|
+
def error_as_warning(
|
44
|
+
msg: str | None,
|
45
|
+
exception: BaseException,
|
46
|
+
*,
|
47
|
+
help_text: str | None = None,
|
48
|
+
obj: object = None,
|
49
|
+
) -> None:
|
50
|
+
"""Display an exception as a warning."""
|
51
|
+
_skip_stackwalk = True
|
52
|
+
|
53
|
+
if _internal.is_controller:
|
54
|
+
_display = _internal.import_controller_module('ansible.utils.display').Display()
|
55
|
+
_display.error_as_warning(
|
56
|
+
msg=msg,
|
57
|
+
exception=exception,
|
58
|
+
help_text=help_text,
|
59
|
+
obj=obj,
|
60
|
+
)
|
61
|
+
|
62
|
+
return
|
63
|
+
|
64
|
+
event = _errors.EventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.WARNING))
|
65
|
+
|
66
|
+
warning = _messages.WarningSummary(
|
67
|
+
event=_messages.Event(
|
68
|
+
msg=msg,
|
69
|
+
help_text=help_text,
|
70
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.WARNING),
|
71
|
+
chain=_messages.EventChain(
|
72
|
+
msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
|
73
|
+
traceback_reason=_errors.TRACEBACK_REASON_EXCEPTION_DIRECT_WARNING,
|
74
|
+
event=event,
|
75
|
+
),
|
20
76
|
),
|
21
|
-
|
22
|
-
|
77
|
+
)
|
78
|
+
|
79
|
+
_global_warnings[warning] = None
|
23
80
|
|
24
81
|
|
25
82
|
def deprecate(
|
@@ -57,30 +114,30 @@ def deprecate(
|
|
57
114
|
|
58
115
|
return
|
59
116
|
|
60
|
-
|
61
|
-
|
62
|
-
|
117
|
+
warning = _messages.DeprecationSummary(
|
118
|
+
event=_messages.Event(
|
119
|
+
msg=msg,
|
120
|
+
help_text=help_text,
|
121
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.DEPRECATED),
|
63
122
|
),
|
64
|
-
formatted_traceback=_traceback.maybe_capture_traceback(_traceback.TracebackEvent.DEPRECATED),
|
65
123
|
version=version,
|
66
124
|
date=date,
|
67
125
|
deprecator=deprecator,
|
68
|
-
)
|
126
|
+
)
|
127
|
+
|
128
|
+
_global_deprecations[warning] = None
|
69
129
|
|
70
130
|
|
71
131
|
def get_warning_messages() -> tuple[str, ...]:
|
72
132
|
"""Return a tuple of warning messages accumulated over this run."""
|
73
|
-
#
|
74
|
-
return tuple(item.
|
75
|
-
|
76
|
-
|
77
|
-
_DEPRECATION_MESSAGE_KEYS = frozenset({'msg', 'date', 'version', 'collection_name'})
|
133
|
+
# DTFIX7: add future deprecation comment
|
134
|
+
return tuple(_event_utils.format_event_brief_message(item.event) for item in _global_warnings)
|
78
135
|
|
79
136
|
|
80
137
|
def get_deprecation_messages() -> tuple[dict[str, _t.Any], ...]:
|
81
138
|
"""Return a tuple of deprecation warning messages accumulated over this run."""
|
82
|
-
#
|
83
|
-
return tuple(
|
139
|
+
# DTFIX7: add future deprecation comment
|
140
|
+
return tuple(_event_utils.deprecation_as_dict(item) for item in _global_deprecations)
|
84
141
|
|
85
142
|
|
86
143
|
def get_warnings() -> list[_messages.WarningSummary]:
|
@@ -94,7 +151,7 @@ def get_deprecations() -> list[_messages.DeprecationSummary]:
|
|
94
151
|
|
95
152
|
|
96
153
|
_global_warnings: dict[_messages.WarningSummary, object] = {}
|
97
|
-
"""Global, ordered, de-
|
154
|
+
"""Global, ordered, de-duplicated storage of accumulated warnings for the current module run."""
|
98
155
|
|
99
156
|
_global_deprecations: dict[_messages.DeprecationSummary, object] = {}
|
100
|
-
"""Global, ordered, de-
|
157
|
+
"""Global, ordered, de-duplicated storage of accumulated deprecations for the current module run."""
|
@@ -24,7 +24,7 @@ except ImportError:
|
|
24
24
|
else:
|
25
25
|
HAS_YAML = True
|
26
26
|
|
27
|
-
# DTFIX-
|
27
|
+
# DTFIX-FUTURE: refactor this to share the implementation with the controller version
|
28
28
|
# use an abstract base class, with __init_subclass__ for representer registration, and instance methods for overridable representers
|
29
29
|
# then tests can be consolidated intead of having two nearly identical copies
|
30
30
|
|
ansible/module_utils/datatag.py
CHANGED
@@ -3,13 +3,15 @@ from __future__ import annotations as _annotations
|
|
3
3
|
|
4
4
|
import typing as _t
|
5
5
|
|
6
|
-
from ._internal import _datatag, _deprecator
|
6
|
+
from ._internal import _datatag, _deprecator, _traceback, _messages
|
7
7
|
from ._internal._datatag import _tags
|
8
|
-
from .common import messages as _messages
|
9
8
|
|
10
9
|
_T = _t.TypeVar('_T')
|
11
10
|
|
12
11
|
|
12
|
+
deprecator_from_collection_name = _deprecator.deprecator_from_collection_name
|
13
|
+
|
14
|
+
|
13
15
|
def deprecate_value(
|
14
16
|
value: _T,
|
15
17
|
msg: str,
|
@@ -36,6 +38,7 @@ def deprecate_value(
|
|
36
38
|
date=date,
|
37
39
|
version=version,
|
38
40
|
deprecator=_deprecator.get_best_deprecator(deprecator=deprecator, collection_name=collection_name),
|
41
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.DEPRECATED_VALUE),
|
39
42
|
)
|
40
43
|
|
41
44
|
return deprecated.tag(value)
|
@@ -310,9 +310,22 @@ class DistributionFiles:
|
|
310
310
|
suse_facts['distribution_release'] = release.group(1)
|
311
311
|
suse_facts['distribution_version'] = collected_facts['distribution_version'] + '.' + release.group(1)
|
312
312
|
|
313
|
-
#
|
314
|
-
|
315
|
-
|
313
|
+
# Check VARIANT_ID first for SLES4SAP or SL-Micro
|
314
|
+
variant_id_match = re.search(r'^VARIANT_ID="?([^"\n]*)"?', data, re.MULTILINE)
|
315
|
+
if variant_id_match:
|
316
|
+
variant_id = variant_id_match.group(1)
|
317
|
+
if variant_id in ('server-sap', 'sles-sap'):
|
318
|
+
suse_facts['distribution'] = 'SLES_SAP'
|
319
|
+
elif variant_id == 'transactional':
|
320
|
+
suse_facts['distribution'] = 'SL-Micro'
|
321
|
+
else:
|
322
|
+
# Fallback for older SLES 15 using baseproduct symlink
|
323
|
+
if os.path.islink('/etc/products.d/baseproduct'):
|
324
|
+
resolved = os.path.realpath('/etc/products.d/baseproduct')
|
325
|
+
if resolved.endswith('SLES_SAP.prod'):
|
326
|
+
suse_facts['distribution'] = 'SLES_SAP'
|
327
|
+
elif resolved.endswith('SL-Micro.prod'):
|
328
|
+
suse_facts['distribution'] = 'SL-Micro'
|
316
329
|
|
317
330
|
return True, suse_facts
|
318
331
|
|