ansible-core 2.18.7rc1__py3-none-any.whl → 2.19.0__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.
Potentially problematic release.
This version of ansible-core might be problematic. Click here for more details.
- ansible/_internal/__init__.py +53 -0
- ansible/_internal/_ansiballz/__init__.py +0 -0
- ansible/_internal/_ansiballz/_builder.py +101 -0
- ansible/_internal/_ansiballz/_wrapper.py +262 -0
- ansible/_internal/_collection_proxy.py +47 -0
- ansible/_internal/_datatag/__init__.py +0 -0
- ansible/_internal/_datatag/_tags.py +130 -0
- ansible/_internal/_datatag/_utils.py +19 -0
- ansible/_internal/_datatag/_wrappers.py +33 -0
- ansible/_internal/_errors/__init__.py +0 -0
- ansible/_internal/_errors/_alarm_timeout.py +66 -0
- ansible/_internal/_errors/_captured.py +123 -0
- ansible/_internal/_errors/_error_factory.py +89 -0
- ansible/_internal/_errors/_error_utils.py +240 -0
- ansible/_internal/_errors/_handler.py +91 -0
- ansible/_internal/_errors/_task_timeout.py +28 -0
- ansible/_internal/_event_formatting.py +127 -0
- ansible/_internal/_json/__init__.py +214 -0
- ansible/_internal/_json/_legacy_encoder.py +34 -0
- ansible/_internal/_json/_profiles/__init__.py +0 -0
- ansible/_internal/_json/_profiles/_cache_persistence.py +57 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
- ansible/_internal/_json/_profiles/_legacy.py +189 -0
- ansible/_internal/_locking.py +21 -0
- ansible/_internal/_plugins/__init__.py +0 -0
- ansible/_internal/_plugins/_cache.py +57 -0
- ansible/_internal/_ssh/__init__.py +0 -0
- ansible/_internal/_ssh/_agent_launch.py +91 -0
- ansible/_internal/_ssh/_ssh_agent.py +619 -0
- ansible/_internal/_task.py +78 -0
- ansible/_internal/_templating/__init__.py +12 -0
- ansible/_internal/_templating/_access.py +86 -0
- ansible/_internal/_templating/_chain_templar.py +63 -0
- ansible/_internal/_templating/_datatag.py +95 -0
- ansible/_internal/_templating/_engine.py +592 -0
- ansible/_internal/_templating/_errors.py +28 -0
- ansible/_internal/_templating/_jinja_bits.py +1106 -0
- ansible/_internal/_templating/_jinja_common.py +323 -0
- ansible/_internal/_templating/_jinja_patches.py +44 -0
- ansible/_internal/_templating/_jinja_plugins.py +375 -0
- ansible/_internal/_templating/_lazy_containers.py +633 -0
- ansible/_internal/_templating/_marker_behaviors.py +103 -0
- ansible/_internal/_templating/_template_vars.py +72 -0
- ansible/_internal/_templating/_transform.py +70 -0
- ansible/_internal/_templating/_utils.py +108 -0
- ansible/_internal/_testing.py +26 -0
- ansible/_internal/_wrapt.py +1052 -0
- ansible/_internal/_yaml/__init__.py +0 -0
- ansible/_internal/_yaml/_constructor.py +240 -0
- ansible/_internal/_yaml/_dumper.py +70 -0
- ansible/_internal/_yaml/_errors.py +166 -0
- ansible/_internal/_yaml/_loader.py +66 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/README.md +11 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/action/debug.py +36 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/apply_trust.py +19 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +27 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/finalize.py +16 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/origin.py +18 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.py +24 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.yml +33 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/tag_names.py +16 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +17 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +49 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.py +21 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.yml +2 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.py +15 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.yml +19 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.py +18 -0
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.yml +19 -0
- ansible/cli/__init__.py +93 -104
- ansible/cli/_ssh_askpass.py +54 -0
- ansible/cli/adhoc.py +20 -10
- ansible/cli/arguments/option_helpers.py +163 -10
- ansible/cli/config.py +43 -68
- ansible/cli/console.py +13 -11
- ansible/cli/doc.py +134 -77
- ansible/cli/galaxy.py +27 -20
- ansible/cli/inventory.py +28 -28
- ansible/cli/playbook.py +4 -12
- ansible/cli/pull.py +6 -3
- ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
- ansible/cli/vault.py +12 -11
- ansible/compat/__init__.py +2 -2
- ansible/compat/importlib_resources.py +9 -12
- ansible/config/base.yml +218 -133
- ansible/config/manager.py +220 -159
- ansible/constants.py +2 -65
- ansible/errors/__init__.py +350 -256
- ansible/executor/interpreter_discovery.py +28 -149
- ansible/executor/module_common.py +480 -514
- ansible/executor/play_iterator.py +22 -27
- ansible/executor/playbook_executor.py +11 -11
- ansible/executor/powershell/async_watchdog.ps1 +97 -102
- ansible/executor/powershell/async_wrapper.ps1 +204 -153
- ansible/executor/powershell/become_wrapper.ps1 +107 -144
- ansible/executor/powershell/bootstrap_wrapper.ps1 +46 -9
- ansible/executor/powershell/coverage_wrapper.ps1 +91 -135
- ansible/executor/powershell/exec_wrapper.ps1 +675 -196
- ansible/executor/powershell/module_manifest.py +469 -265
- ansible/executor/powershell/module_wrapper.ps1 +195 -186
- ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
- ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
- ansible/executor/powershell/psrp_fetch_file.ps1 +41 -0
- ansible/executor/powershell/psrp_put_file.ps1 +122 -0
- ansible/executor/powershell/winrm_fetch_file.ps1 +46 -0
- ansible/executor/powershell/winrm_put_file.ps1 +36 -0
- ansible/executor/process/worker.py +139 -149
- ansible/executor/stats.py +5 -5
- ansible/executor/task_executor.py +270 -297
- ansible/executor/task_queue_manager.py +135 -137
- ansible/executor/task_result.py +182 -79
- ansible/galaxy/__init__.py +2 -2
- ansible/galaxy/api.py +26 -25
- ansible/galaxy/collection/__init__.py +6 -14
- ansible/galaxy/collection/concrete_artifact_manager.py +12 -21
- ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
- ansible/galaxy/dependency_resolution/providers.py +4 -4
- ansible/galaxy/dependency_resolution/reporters.py +81 -0
- ansible/galaxy/role.py +6 -10
- ansible/galaxy/token.py +28 -21
- ansible/inventory/data.py +47 -57
- ansible/inventory/group.py +50 -73
- ansible/inventory/helpers.py +9 -0
- ansible/inventory/host.py +37 -54
- ansible/inventory/manager.py +79 -34
- ansible/keyword_desc.yml +1 -1
- ansible/module_utils/_internal/__init__.py +55 -0
- ansible/module_utils/_internal/_ambient_context.py +58 -0
- 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/_loader.py +81 -0
- ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
- ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
- ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
- ansible/module_utils/_internal/_dataclass_validation.py +217 -0
- ansible/module_utils/_internal/_datatag/__init__.py +961 -0
- ansible/module_utils/_internal/_datatag/_tags.py +16 -0
- ansible/module_utils/_internal/_debugging.py +31 -0
- ansible/module_utils/_internal/_deprecator.py +157 -0
- ansible/module_utils/_internal/_errors.py +101 -0
- ansible/module_utils/_internal/_event_utils.py +61 -0
- ansible/module_utils/_internal/_json/__init__.py +63 -0
- ansible/module_utils/_internal/_json/_legacy_encoder.py +26 -0
- ansible/module_utils/_internal/_json/_profiles/__init__.py +428 -0
- ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +33 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +37 -0
- ansible/module_utils/_internal/_json/_profiles/_module_modern_c2m.py +35 -0
- ansible/module_utils/_internal/_json/_profiles/_module_modern_m2c.py +33 -0
- ansible/module_utils/_internal/_json/_profiles/_tagless.py +52 -0
- ansible/module_utils/_internal/_messages.py +130 -0
- ansible/module_utils/_internal/_patches/__init__.py +66 -0
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +53 -0
- ansible/module_utils/_internal/_patches/_socket_patch.py +34 -0
- ansible/module_utils/_internal/_patches/_sys_intern_patch.py +34 -0
- ansible/module_utils/_internal/_plugin_info.py +38 -0
- ansible/module_utils/_internal/_stack.py +22 -0
- ansible/module_utils/_internal/_testing.py +0 -0
- ansible/module_utils/_internal/_text_utils.py +6 -0
- ansible/module_utils/_internal/_traceback.py +92 -0
- ansible/module_utils/_internal/_validation.py +14 -0
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/api.py +1 -2
- ansible/module_utils/basic.py +303 -202
- ansible/module_utils/common/_utils.py +24 -28
- ansible/module_utils/common/arg_spec.py +8 -3
- ansible/module_utils/common/collections.py +7 -2
- ansible/module_utils/common/dict_transformations.py +2 -2
- ansible/module_utils/common/file.py +2 -2
- ansible/module_utils/common/json.py +90 -84
- ansible/module_utils/common/locale.py +2 -2
- ansible/module_utils/common/parameters.py +27 -24
- ansible/module_utils/common/process.py +2 -3
- ansible/module_utils/common/respawn.py +11 -33
- ansible/module_utils/common/sentinel.py +66 -0
- ansible/module_utils/common/sys_info.py +8 -8
- ansible/module_utils/common/text/converters.py +16 -37
- ansible/module_utils/common/validation.py +35 -24
- ansible/module_utils/common/warnings.py +143 -25
- ansible/module_utils/common/yaml.py +29 -3
- ansible/module_utils/compat/datetime.py +33 -21
- ansible/module_utils/compat/paramiko.py +21 -10
- ansible/module_utils/compat/typing.py +6 -5
- ansible/module_utils/connection.py +10 -13
- ansible/module_utils/csharp/Ansible.Basic.cs +15 -12
- ansible/module_utils/csharp/Ansible.Become.cs +1 -0
- ansible/module_utils/csharp/Ansible.Privilege.cs +2 -2
- ansible/module_utils/csharp/Ansible._Async.cs +517 -0
- ansible/module_utils/datatag.py +49 -0
- ansible/module_utils/distro/__init__.py +2 -2
- ansible/module_utils/facts/ansible_collector.py +4 -5
- ansible/module_utils/facts/collector.py +13 -14
- ansible/module_utils/facts/compat.py +4 -4
- ansible/module_utils/facts/default_collectors.py +1 -1
- ansible/module_utils/facts/hardware/aix.py +34 -0
- ansible/module_utils/facts/hardware/base.py +2 -2
- ansible/module_utils/facts/hardware/darwin.py +1 -3
- ansible/module_utils/facts/hardware/freebsd.py +2 -2
- ansible/module_utils/facts/hardware/linux.py +5 -5
- ansible/module_utils/facts/namespace.py +1 -1
- ansible/module_utils/facts/network/base.py +1 -1
- ansible/module_utils/facts/network/fc_wwn.py +1 -2
- ansible/module_utils/facts/network/iscsi.py +1 -2
- ansible/module_utils/facts/network/nvme.py +1 -2
- ansible/module_utils/facts/other/facter.py +2 -3
- ansible/module_utils/facts/other/ohai.py +2 -3
- ansible/module_utils/facts/sysctl.py +4 -6
- ansible/module_utils/facts/system/apparmor.py +1 -2
- ansible/module_utils/facts/system/caps.py +3 -3
- ansible/module_utils/facts/system/chroot.py +1 -2
- ansible/module_utils/facts/system/cmdline.py +1 -2
- ansible/module_utils/facts/system/date_time.py +5 -3
- ansible/module_utils/facts/system/distribution.py +27 -13
- ansible/module_utils/facts/system/dns.py +1 -1
- ansible/module_utils/facts/system/env.py +1 -2
- ansible/module_utils/facts/system/fips.py +7 -20
- ansible/module_utils/facts/system/loadavg.py +1 -2
- ansible/module_utils/facts/system/local.py +2 -3
- ansible/module_utils/facts/system/lsb.py +1 -2
- ansible/module_utils/facts/system/pkg_mgr.py +1 -2
- ansible/module_utils/facts/system/platform.py +1 -2
- ansible/module_utils/facts/system/python.py +1 -2
- ansible/module_utils/facts/system/selinux.py +1 -1
- ansible/module_utils/facts/system/service_mgr.py +1 -2
- ansible/module_utils/facts/system/ssh_pub_keys.py +1 -1
- ansible/module_utils/facts/system/systemd.py +1 -1
- ansible/module_utils/facts/system/user.py +1 -2
- ansible/module_utils/facts/utils.py +3 -3
- ansible/module_utils/facts/virtual/base.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +3 -3
- ansible/module_utils/facts/virtual/sunos.py +3 -15
- ansible/module_utils/facts/virtual/sysctl.py +3 -16
- ansible/module_utils/json_utils.py +2 -2
- ansible/module_utils/parsing/convert_bool.py +7 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.AddType.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CamelConversion.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +1 -1
- ansible/module_utils/powershell/Ansible.ModuleUtils.WebRequest.psm1 +1 -1
- ansible/module_utils/service.py +21 -31
- ansible/module_utils/splitter.py +7 -7
- ansible/module_utils/testing.py +31 -0
- ansible/module_utils/urls.py +64 -35
- ansible/modules/add_host.py +4 -4
- ansible/modules/apt.py +69 -49
- ansible/modules/apt_key.py +19 -12
- ansible/modules/apt_repository.py +32 -51
- ansible/modules/assemble.py +16 -14
- ansible/modules/assert.py +4 -4
- ansible/modules/async_status.py +24 -24
- ansible/modules/async_wrapper.py +20 -25
- ansible/modules/blockinfile.py +6 -7
- ansible/modules/command.py +13 -20
- ansible/modules/copy.py +60 -147
- ansible/modules/cron.py +24 -21
- ansible/modules/deb822_repository.py +8 -9
- ansible/modules/debconf.py +5 -5
- ansible/modules/debug.py +4 -4
- ansible/modules/dnf.py +8 -8
- ansible/modules/dnf5.py +39 -13
- ansible/modules/dpkg_selections.py +4 -4
- ansible/modules/expect.py +13 -15
- ansible/modules/fail.py +4 -4
- ansible/modules/fetch.py +4 -4
- ansible/modules/file.py +184 -144
- ansible/modules/find.py +22 -20
- ansible/modules/gather_facts.py +3 -3
- ansible/modules/get_url.py +77 -54
- ansible/modules/getent.py +7 -9
- ansible/modules/git.py +38 -38
- ansible/modules/group.py +6 -6
- ansible/modules/group_by.py +4 -4
- ansible/modules/hostname.py +15 -32
- ansible/modules/import_playbook.py +6 -6
- ansible/modules/import_role.py +6 -6
- ansible/modules/import_tasks.py +6 -6
- ansible/modules/include_role.py +6 -6
- ansible/modules/include_tasks.py +6 -6
- ansible/modules/include_vars.py +6 -6
- ansible/modules/iptables.py +86 -73
- ansible/modules/known_hosts.py +22 -24
- ansible/modules/lineinfile.py +5 -5
- ansible/modules/meta.py +4 -4
- ansible/modules/mount_facts.py +2 -2
- ansible/modules/package.py +10 -4
- ansible/modules/package_facts.py +22 -10
- ansible/modules/pause.py +6 -6
- ansible/modules/ping.py +6 -6
- ansible/modules/pip.py +21 -26
- ansible/modules/raw.py +6 -6
- ansible/modules/reboot.py +6 -6
- ansible/modules/replace.py +10 -14
- ansible/modules/rpm_key.py +7 -8
- ansible/modules/script.py +4 -4
- ansible/modules/service.py +10 -17
- ansible/modules/service_facts.py +87 -10
- ansible/modules/set_fact.py +5 -5
- ansible/modules/set_stats.py +4 -4
- ansible/modules/setup.py +2 -2
- ansible/modules/shell.py +6 -6
- ansible/modules/slurp.py +16 -19
- ansible/modules/stat.py +15 -31
- ansible/modules/subversion.py +15 -15
- ansible/modules/systemd.py +7 -7
- ansible/modules/systemd_service.py +7 -7
- ansible/modules/sysvinit.py +9 -9
- ansible/modules/tempfile.py +5 -6
- ansible/modules/template.py +6 -6
- ansible/modules/unarchive.py +38 -17
- ansible/modules/uri.py +33 -26
- ansible/modules/user.py +45 -32
- ansible/modules/validate_argument_spec.py +10 -7
- ansible/modules/wait_for.py +70 -60
- ansible/modules/wait_for_connection.py +6 -6
- ansible/modules/yum_repository.py +10 -9
- ansible/parsing/ajson.py +17 -37
- ansible/parsing/dataloader.py +99 -54
- ansible/parsing/mod_args.py +62 -60
- ansible/parsing/plugin_docs.py +21 -86
- ansible/parsing/quoting.py +1 -1
- ansible/parsing/splitter.py +27 -12
- ansible/parsing/utils/addresses.py +24 -24
- ansible/parsing/utils/jsonify.py +5 -1
- ansible/parsing/utils/yaml.py +32 -61
- ansible/parsing/vault/__init__.py +327 -99
- ansible/parsing/yaml/__init__.py +0 -18
- ansible/parsing/yaml/dumper.py +6 -120
- ansible/parsing/yaml/loader.py +6 -39
- ansible/parsing/yaml/objects.py +43 -335
- ansible/playbook/__init__.py +1 -1
- ansible/playbook/attribute.py +8 -3
- ansible/playbook/base.py +187 -134
- ansible/playbook/block.py +26 -24
- ansible/playbook/collectionsearch.py +1 -15
- ansible/playbook/conditional.py +3 -77
- ansible/playbook/handler.py +8 -2
- ansible/playbook/helpers.py +41 -53
- ansible/playbook/included_file.py +32 -26
- ansible/playbook/loop_control.py +2 -2
- ansible/playbook/play.py +85 -44
- ansible/playbook/play_context.py +14 -17
- ansible/playbook/playbook_include.py +27 -62
- ansible/playbook/role/__init__.py +64 -49
- ansible/playbook/role/definition.py +15 -17
- ansible/playbook/role/include.py +2 -4
- ansible/playbook/role/metadata.py +10 -11
- ansible/playbook/role_include.py +3 -3
- ansible/playbook/taggable.py +28 -12
- ansible/playbook/task.py +192 -121
- ansible/playbook/task_include.py +5 -5
- ansible/plugins/__init__.py +58 -26
- ansible/plugins/action/__init__.py +188 -186
- ansible/plugins/action/add_host.py +2 -2
- ansible/plugins/action/assemble.py +11 -18
- ansible/plugins/action/assert.py +55 -67
- ansible/plugins/action/async_status.py +7 -2
- ansible/plugins/action/copy.py +14 -17
- ansible/plugins/action/debug.py +37 -31
- ansible/plugins/action/dnf.py +3 -4
- ansible/plugins/action/fail.py +1 -1
- ansible/plugins/action/fetch.py +7 -8
- ansible/plugins/action/gather_facts.py +13 -14
- ansible/plugins/action/group_by.py +1 -1
- ansible/plugins/action/include_vars.py +10 -11
- ansible/plugins/action/package.py +8 -14
- ansible/plugins/action/pause.py +2 -2
- ansible/plugins/action/script.py +27 -38
- ansible/plugins/action/service.py +9 -18
- ansible/plugins/action/set_fact.py +3 -12
- ansible/plugins/action/set_stats.py +3 -8
- ansible/plugins/action/template.py +47 -67
- ansible/plugins/action/unarchive.py +6 -16
- ansible/plugins/action/uri.py +9 -20
- ansible/plugins/action/validate_argument_spec.py +5 -5
- ansible/plugins/action/wait_for_connection.py +1 -1
- ansible/plugins/become/__init__.py +31 -8
- ansible/plugins/become/runas.py +71 -0
- ansible/plugins/become/su.py +13 -8
- ansible/plugins/become/sudo.py +19 -0
- ansible/plugins/cache/__init__.py +52 -63
- ansible/plugins/cache/base.py +8 -0
- ansible/plugins/cache/jsonfile.py +10 -16
- ansible/plugins/cache/memory.py +6 -12
- ansible/plugins/callback/__init__.py +294 -201
- ansible/plugins/callback/default.py +99 -95
- ansible/plugins/callback/junit.py +44 -43
- ansible/plugins/callback/minimal.py +28 -25
- ansible/plugins/callback/oneline.py +34 -21
- ansible/plugins/callback/tree.py +27 -16
- ansible/plugins/connection/__init__.py +47 -34
- ansible/plugins/connection/local.py +156 -60
- ansible/plugins/connection/paramiko_ssh.py +34 -24
- ansible/plugins/connection/psrp.py +76 -165
- ansible/plugins/connection/ssh.py +326 -86
- ansible/plugins/connection/winrm.py +62 -141
- ansible/plugins/doc_fragments/action_common_attributes.py +14 -14
- ansible/plugins/doc_fragments/action_core.py +6 -6
- ansible/plugins/doc_fragments/backup.py +2 -2
- ansible/plugins/doc_fragments/checksum_common.py +27 -0
- ansible/plugins/doc_fragments/constructed.py +8 -4
- ansible/plugins/doc_fragments/decrypt.py +2 -2
- ansible/plugins/doc_fragments/default_callback.py +2 -2
- ansible/plugins/doc_fragments/files.py +2 -2
- ansible/plugins/doc_fragments/inventory_cache.py +2 -2
- ansible/plugins/doc_fragments/result_format_callback.py +2 -2
- ansible/plugins/doc_fragments/return_common.py +2 -2
- ansible/plugins/doc_fragments/template_common.py +4 -4
- ansible/plugins/doc_fragments/url.py +17 -1
- ansible/plugins/doc_fragments/url_windows.py +2 -2
- ansible/plugins/doc_fragments/validate.py +2 -2
- ansible/plugins/doc_fragments/vars_plugin_staging.py +2 -2
- ansible/plugins/filter/__init__.py +6 -2
- ansible/plugins/filter/b64decode.yml +22 -0
- ansible/plugins/filter/b64encode.yml +22 -0
- ansible/plugins/filter/bool.yml +11 -4
- ansible/plugins/filter/core.py +245 -120
- ansible/plugins/filter/encryption.py +42 -34
- ansible/plugins/filter/flatten.yml +3 -2
- ansible/plugins/filter/human_to_bytes.yml +1 -1
- ansible/plugins/filter/mathstuff.py +30 -37
- ansible/plugins/filter/password_hash.yml +8 -0
- ansible/plugins/filter/pow.yml +1 -1
- ansible/plugins/filter/regex_search.yml +1 -4
- ansible/plugins/filter/root.yml +1 -1
- ansible/plugins/filter/split.yml +1 -1
- ansible/plugins/filter/strftime.yml +3 -3
- ansible/plugins/filter/to_nice_yaml.yml +0 -4
- ansible/plugins/filter/to_uuid.yml +1 -1
- ansible/plugins/filter/to_yaml.yml +0 -4
- ansible/plugins/filter/unvault.yml +1 -1
- ansible/plugins/filter/urls.py +1 -1
- ansible/plugins/filter/urlsplit.py +8 -9
- ansible/plugins/filter/vault.yml +14 -9
- ansible/plugins/filter/win_basename.yml +6 -1
- ansible/plugins/filter/win_dirname.yml +5 -0
- ansible/plugins/inventory/__init__.py +107 -86
- ansible/plugins/inventory/advanced_host_list.py +7 -5
- ansible/plugins/inventory/auto.py +11 -4
- ansible/plugins/inventory/constructed.py +21 -24
- ansible/plugins/inventory/generator.py +16 -11
- ansible/plugins/inventory/host_list.py +7 -5
- ansible/plugins/inventory/ini.py +78 -44
- ansible/plugins/inventory/script.py +190 -120
- ansible/plugins/inventory/toml.py +16 -126
- ansible/plugins/inventory/yaml.py +10 -8
- ansible/plugins/list.py +72 -19
- ansible/plugins/loader.py +383 -198
- ansible/plugins/lookup/__init__.py +21 -4
- ansible/plugins/lookup/config.py +21 -35
- ansible/plugins/lookup/csvfile.py +19 -73
- ansible/plugins/lookup/dict.py +1 -6
- ansible/plugins/lookup/env.py +12 -9
- ansible/plugins/lookup/file.py +5 -8
- ansible/plugins/lookup/first_found.py +87 -55
- ansible/plugins/lookup/indexed_items.py +1 -10
- ansible/plugins/lookup/ini.py +14 -13
- ansible/plugins/lookup/items.py +1 -1
- ansible/plugins/lookup/lines.py +8 -1
- ansible/plugins/lookup/list.py +1 -1
- ansible/plugins/lookup/nested.py +2 -18
- ansible/plugins/lookup/password.py +5 -5
- ansible/plugins/lookup/pipe.py +5 -7
- ansible/plugins/lookup/sequence.py +18 -8
- ansible/plugins/lookup/subelements.py +1 -4
- ansible/plugins/lookup/template.py +47 -36
- ansible/plugins/lookup/together.py +0 -12
- ansible/plugins/lookup/unvault.py +1 -5
- ansible/plugins/lookup/url.py +4 -10
- ansible/plugins/lookup/vars.py +16 -24
- ansible/plugins/shell/__init__.py +58 -4
- ansible/plugins/shell/cmd.py +2 -2
- ansible/plugins/shell/powershell.py +106 -31
- ansible/plugins/shell/sh.py +13 -7
- ansible/plugins/strategy/__init__.py +168 -193
- ansible/plugins/strategy/debug.py +2 -2
- ansible/plugins/strategy/free.py +16 -31
- ansible/plugins/strategy/host_pinned.py +2 -2
- ansible/plugins/strategy/linear.py +41 -41
- ansible/plugins/terminal/__init__.py +4 -4
- ansible/plugins/test/__init__.py +7 -2
- ansible/plugins/test/core.py +75 -35
- ansible/plugins/test/files.py +1 -1
- ansible/plugins/test/finished.yml +1 -1
- ansible/plugins/test/mathstuff.py +3 -3
- ansible/plugins/test/uri.py +5 -8
- ansible/plugins/vars/host_group_vars.py +7 -14
- ansible/release.py +2 -2
- ansible/template/__init__.py +353 -943
- ansible/utils/__init__.py +0 -18
- ansible/utils/collection_loader/__init__.py +54 -5
- ansible/utils/collection_loader/_collection_config.py +5 -6
- ansible/utils/collection_loader/_collection_finder.py +82 -96
- ansible/utils/collection_loader/_collection_meta.py +15 -8
- ansible/utils/display.py +485 -73
- ansible/utils/encrypt.py +27 -19
- ansible/utils/fqcn.py +2 -2
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +8 -10
- ansible/utils/helpers.py +2 -2
- ansible/utils/listify.py +10 -8
- ansible/utils/lock.py +2 -2
- ansible/utils/path.py +10 -12
- ansible/utils/plugin_docs.py +16 -14
- ansible/utils/py3compat.py +2 -7
- ansible/utils/sentinel.py +4 -62
- ansible/utils/singleton.py +2 -0
- ansible/utils/ssh_functions.py +6 -2
- ansible/utils/unsafe_proxy.py +23 -332
- ansible/utils/vars.py +55 -8
- ansible/utils/version.py +2 -2
- ansible/vars/clean.py +5 -5
- ansible/vars/hostvars.py +60 -90
- ansible/vars/manager.py +220 -285
- ansible/vars/plugins.py +4 -4
- ansible/vars/reserved.py +13 -12
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/METADATA +4 -3
- ansible_core-2.19.0.dist-info/RECORD +1097 -0
- ansible_core-2.19.0.dist-info/licenses/licenses/BSD-3-Clause.txt +28 -0
- ansible_test/_data/completion/docker.txt +7 -7
- ansible_test/_data/completion/remote.txt +6 -6
- ansible_test/_data/completion/windows.txt +1 -0
- ansible_test/_data/requirements/ansible.txt +2 -2
- ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
- ansible_test/_data/requirements/sanity.changelog.txt +2 -2
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +5 -5
- ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_data/requirements/units.txt +1 -0
- ansible_test/_internal/__init__.py +6 -0
- ansible_test/_internal/ansible_util.py +3 -1
- ansible_test/_internal/become.py +1 -0
- ansible_test/_internal/bootstrap.py +1 -0
- ansible_test/_internal/cache.py +1 -0
- ansible_test/_internal/cgroup.py +1 -0
- ansible_test/_internal/ci/__init__.py +1 -0
- ansible_test/_internal/ci/azp.py +1 -0
- ansible_test/_internal/ci/local.py +1 -0
- ansible_test/_internal/classification/__init__.py +1 -0
- ansible_test/_internal/classification/common.py +1 -0
- ansible_test/_internal/classification/csharp.py +1 -0
- ansible_test/_internal/classification/powershell.py +1 -0
- ansible_test/_internal/classification/python.py +1 -0
- ansible_test/_internal/cli/__init__.py +1 -0
- ansible_test/_internal/cli/actions.py +1 -0
- ansible_test/_internal/cli/argparsing/__init__.py +1 -0
- ansible_test/_internal/cli/argparsing/actions.py +1 -0
- ansible_test/_internal/cli/argparsing/argcompletion.py +1 -0
- ansible_test/_internal/cli/argparsing/parsers.py +1 -0
- ansible_test/_internal/cli/commands/__init__.py +11 -5
- ansible_test/_internal/cli/commands/coverage/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py +1 -0
- ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py +1 -0
- ansible_test/_internal/cli/commands/coverage/combine.py +1 -0
- ansible_test/_internal/cli/commands/coverage/erase.py +1 -0
- ansible_test/_internal/cli/commands/coverage/html.py +1 -0
- ansible_test/_internal/cli/commands/coverage/report.py +1 -0
- ansible_test/_internal/cli/commands/coverage/xml.py +1 -0
- ansible_test/_internal/cli/commands/env.py +1 -0
- ansible_test/_internal/cli/commands/integration/__init__.py +1 -0
- ansible_test/_internal/cli/commands/integration/network.py +1 -0
- ansible_test/_internal/cli/commands/integration/posix.py +1 -0
- ansible_test/_internal/cli/commands/integration/windows.py +1 -0
- ansible_test/_internal/cli/commands/sanity.py +9 -0
- ansible_test/_internal/cli/commands/shell.py +1 -0
- ansible_test/_internal/cli/commands/units.py +1 -0
- ansible_test/_internal/cli/compat.py +1 -0
- ansible_test/_internal/cli/completers.py +1 -0
- ansible_test/_internal/cli/converters.py +1 -0
- ansible_test/_internal/cli/environments.py +52 -5
- ansible_test/_internal/cli/epilog.py +1 -0
- ansible_test/_internal/cli/parsers/__init__.py +1 -0
- ansible_test/_internal/cli/parsers/base_argument_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/helpers.py +1 -0
- ansible_test/_internal/cli/parsers/host_config_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/key_value_parsers.py +1 -0
- ansible_test/_internal/cli/parsers/value_parsers.py +1 -0
- ansible_test/_internal/commands/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/__init__.py +3 -2
- ansible_test/_internal/commands/coverage/analyze/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/combine.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/expand.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/filter.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/generate.py +1 -0
- ansible_test/_internal/commands/coverage/analyze/targets/missing.py +1 -0
- ansible_test/_internal/commands/coverage/combine.py +2 -1
- ansible_test/_internal/commands/coverage/erase.py +1 -0
- ansible_test/_internal/commands/coverage/html.py +1 -0
- ansible_test/_internal/commands/coverage/report.py +1 -0
- ansible_test/_internal/commands/coverage/xml.py +1 -0
- ansible_test/_internal/commands/env/__init__.py +2 -0
- ansible_test/_internal/commands/integration/__init__.py +22 -5
- ansible_test/_internal/commands/integration/cloud/__init__.py +1 -0
- ansible_test/_internal/commands/integration/cloud/acme.py +2 -1
- ansible_test/_internal/commands/integration/cloud/aws.py +1 -0
- ansible_test/_internal/commands/integration/cloud/azure.py +1 -0
- ansible_test/_internal/commands/integration/cloud/cs.py +1 -0
- ansible_test/_internal/commands/integration/cloud/digitalocean.py +1 -0
- ansible_test/_internal/commands/integration/cloud/galaxy.py +3 -2
- ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -0
- ansible_test/_internal/commands/integration/cloud/httptester.py +3 -2
- ansible_test/_internal/commands/integration/cloud/nios.py +2 -1
- ansible_test/_internal/commands/integration/cloud/opennebula.py +1 -0
- ansible_test/_internal/commands/integration/cloud/openshift.py +1 -0
- ansible_test/_internal/commands/integration/cloud/scaleway.py +1 -0
- ansible_test/_internal/commands/integration/cloud/vcenter.py +1 -0
- ansible_test/_internal/commands/integration/cloud/vultr.py +1 -0
- ansible_test/_internal/commands/integration/coverage.py +8 -2
- ansible_test/_internal/commands/integration/filters.py +1 -0
- ansible_test/_internal/commands/integration/network.py +1 -0
- ansible_test/_internal/commands/integration/posix.py +1 -0
- ansible_test/_internal/commands/integration/windows.py +1 -0
- ansible_test/_internal/commands/sanity/__init__.py +19 -2
- ansible_test/_internal/commands/sanity/ansible_doc.py +1 -0
- ansible_test/_internal/commands/sanity/bin_symlinks.py +1 -0
- ansible_test/_internal/commands/sanity/compile.py +1 -0
- ansible_test/_internal/commands/sanity/ignores.py +1 -0
- ansible_test/_internal/commands/sanity/import.py +1 -0
- ansible_test/_internal/commands/sanity/integration_aliases.py +12 -0
- ansible_test/_internal/commands/sanity/pep8.py +1 -0
- ansible_test/_internal/commands/sanity/pslint.py +1 -0
- ansible_test/_internal/commands/sanity/pylint.py +25 -26
- ansible_test/_internal/commands/sanity/shellcheck.py +1 -0
- ansible_test/_internal/commands/sanity/validate_modules.py +1 -0
- ansible_test/_internal/commands/sanity/yamllint.py +1 -0
- ansible_test/_internal/commands/shell/__init__.py +44 -4
- ansible_test/_internal/commands/units/__init__.py +5 -1
- ansible_test/_internal/compat/__init__.py +1 -0
- ansible_test/_internal/compat/packaging.py +1 -0
- ansible_test/_internal/compat/yaml.py +1 -0
- ansible_test/_internal/completion.py +1 -0
- ansible_test/_internal/config.py +23 -13
- ansible_test/_internal/connections.py +1 -0
- ansible_test/_internal/constants.py +1 -0
- ansible_test/_internal/containers.py +1 -0
- ansible_test/_internal/content_config.py +1 -0
- ansible_test/_internal/core_ci.py +1 -0
- ansible_test/_internal/coverage_util.py +11 -10
- ansible_test/_internal/data.py +1 -0
- ansible_test/_internal/debugging.py +166 -0
- ansible_test/_internal/delegation.py +22 -13
- ansible_test/_internal/dev/__init__.py +1 -0
- ansible_test/_internal/dev/container_probe.py +1 -0
- ansible_test/_internal/diff.py +3 -2
- ansible_test/_internal/docker_util.py +2 -1
- ansible_test/_internal/encoding.py +1 -0
- ansible_test/_internal/executor.py +1 -0
- ansible_test/_internal/git.py +1 -0
- ansible_test/_internal/host_configs.py +1 -0
- ansible_test/_internal/host_profiles.py +260 -16
- ansible_test/_internal/http.py +1 -0
- ansible_test/_internal/init.py +1 -0
- ansible_test/_internal/inventory.py +39 -3
- ansible_test/_internal/io.py +1 -0
- ansible_test/_internal/metadata.py +95 -4
- ansible_test/_internal/payload.py +1 -0
- ansible_test/_internal/processes.py +80 -0
- ansible_test/_internal/provider/__init__.py +1 -0
- ansible_test/_internal/provider/layout/__init__.py +1 -0
- ansible_test/_internal/provider/layout/ansible.py +1 -0
- ansible_test/_internal/provider/layout/collection.py +1 -0
- ansible_test/_internal/provider/layout/unsupported.py +1 -0
- ansible_test/_internal/provider/source/__init__.py +1 -0
- ansible_test/_internal/provider/source/git.py +1 -0
- ansible_test/_internal/provider/source/installed.py +1 -0
- ansible_test/_internal/provider/source/unsupported.py +1 -0
- ansible_test/_internal/provider/source/unversioned.py +1 -0
- ansible_test/_internal/provisioning.py +11 -4
- ansible_test/_internal/pypi_proxy.py +6 -5
- ansible_test/_internal/python_requirements.py +28 -0
- ansible_test/_internal/ssh.py +2 -5
- ansible_test/_internal/target.py +9 -0
- ansible_test/_internal/test.py +3 -2
- ansible_test/_internal/thread.py +3 -1
- ansible_test/_internal/timeout.py +2 -1
- ansible_test/_internal/util.py +41 -12
- ansible_test/_internal/util_common.py +18 -5
- ansible_test/_internal/venv.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/changelog.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/empty-init.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/line-endings.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-assert.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-get-exception.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-illegal-filenames.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/no-smart-quotes.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/replace-urlopen.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +28 -1
- ansible_test/_util/controller/sanity/code-smell/shebang.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/symlinks.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/use-argspec-type-path.py +1 -0
- ansible_test/_util/controller/sanity/code-smell/use-compat-six.py +1 -0
- ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py +2 -1
- ansible_test/_util/controller/sanity/pep8/current-ignore.txt +4 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +8 -5
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +8 -5
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +8 -5
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +4 -5
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +8 -7
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +541 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_comment.py +137 -0
- ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +1 -8
- ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +1 -8
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +55 -28
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +12 -5
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +13 -2
- ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -0
- ansible_test/_util/controller/sanity/yamllint/yamllinter.py +35 -17
- ansible_test/_util/controller/tools/collection_detail.py +1 -0
- ansible_test/_util/controller/tools/yaml_to_json.py +2 -1
- ansible_test/_util/target/injector/python.py +8 -0
- ansible_test/_util/target/pytest/plugins/ansible_forked.py +6 -1
- ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +2 -1
- ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -0
- ansible_test/_util/target/sanity/compile/compile.py +1 -0
- ansible_test/_util/target/sanity/import/importer.py +15 -16
- ansible_test/_util/target/setup/bootstrap.sh +9 -20
- ansible_test/_util/target/setup/probe_cgroups.py +1 -0
- ansible_test/_util/target/setup/quiet_pip.py +1 -0
- ansible_test/_util/target/setup/requirements.py +38 -36
- ansible_test/_util/target/tools/virtualenvcheck.py +2 -1
- ansible_test/_util/target/tools/yamlcheck.py +2 -1
- ansible/compat/selectors.py +0 -32
- ansible/errors/yaml_strings.py +0 -138
- ansible/executor/action_write_locks.py +0 -44
- ansible/executor/discovery/python_target.py +0 -47
- ansible/executor/powershell/module_powershell_wrapper.ps1 +0 -86
- ansible/executor/powershell/module_script_wrapper.ps1 +0 -22
- ansible/module_utils/compat/importlib.py +0 -26
- ansible/module_utils/compat/selectors.py +0 -32
- ansible/module_utils/pycompat24.py +0 -73
- ansible/parsing/yaml/constructor.py +0 -178
- ansible/template/native_helpers.py +0 -251
- ansible/template/template.py +0 -43
- ansible/template/vars.py +0 -77
- ansible/utils/native_jinja.py +0 -11
- ansible/vars/fact_cache.py +0 -71
- ansible_core-2.18.7rc1.dist-info/RECORD +0 -992
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +0 -411
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/WHEEL +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.18.7rc1.dist-info → ansible_core-2.19.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import signal
|
|
5
|
+
import types
|
|
6
|
+
import typing as _t
|
|
7
|
+
|
|
8
|
+
from ansible.module_utils import datatag
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AnsibleTimeoutError(BaseException):
|
|
12
|
+
"""A general purpose timeout."""
|
|
13
|
+
|
|
14
|
+
_MAX_TIMEOUT = 100_000_000
|
|
15
|
+
"""
|
|
16
|
+
The maximum supported timeout value.
|
|
17
|
+
This value comes from BSD's alarm limit, which is due to that function using setitimer.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, timeout: int) -> None:
|
|
21
|
+
self.timeout = timeout
|
|
22
|
+
|
|
23
|
+
super().__init__(f"Timed out after {timeout} second(s).")
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
@contextlib.contextmanager
|
|
27
|
+
def alarm_timeout(cls, timeout: int | None) -> _t.Iterator[None]:
|
|
28
|
+
"""
|
|
29
|
+
Context for running code under an optional timeout.
|
|
30
|
+
Raises an instance of this class if the timeout occurs.
|
|
31
|
+
|
|
32
|
+
New usages of this timeout mechanism are discouraged.
|
|
33
|
+
"""
|
|
34
|
+
if timeout is not None:
|
|
35
|
+
if not isinstance(timeout, int):
|
|
36
|
+
raise TypeError(f"Timeout requires 'int' argument, not {datatag.native_type_name(timeout)!r}.")
|
|
37
|
+
|
|
38
|
+
if timeout < 0 or timeout > cls._MAX_TIMEOUT:
|
|
39
|
+
# On BSD based systems, alarm is implemented using setitimer.
|
|
40
|
+
# If out-of-bounds values are passed to alarm, they will return -1, which would be interpreted as an existing timer being set.
|
|
41
|
+
# To avoid that, bounds checking is performed in advance.
|
|
42
|
+
raise ValueError(f'Timeout {timeout} is invalid, it must be between 0 and {cls._MAX_TIMEOUT}.')
|
|
43
|
+
|
|
44
|
+
if not timeout:
|
|
45
|
+
yield # execute the context manager's body
|
|
46
|
+
return # no timeout to deal with, exit immediately
|
|
47
|
+
|
|
48
|
+
def on_alarm(_signal: int, _frame: types.FrameType) -> None:
|
|
49
|
+
raise cls(timeout)
|
|
50
|
+
|
|
51
|
+
if signal.signal(signal.SIGALRM, on_alarm):
|
|
52
|
+
raise RuntimeError("An existing alarm handler was present.")
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
try:
|
|
56
|
+
if signal.alarm(timeout):
|
|
57
|
+
raise RuntimeError("An existing alarm was set.")
|
|
58
|
+
|
|
59
|
+
yield # execute the context manager's body
|
|
60
|
+
finally:
|
|
61
|
+
# Disable the alarm.
|
|
62
|
+
# If the alarm fires inside this finally block, the alarm is still disabled.
|
|
63
|
+
# This guarantees the cleanup code in the outer finally block runs without risk of encountering the `TaskTimeoutError` from the alarm.
|
|
64
|
+
signal.alarm(0)
|
|
65
|
+
finally:
|
|
66
|
+
signal.signal(signal.SIGALRM, signal.SIG_DFL)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import collections.abc as _c
|
|
4
|
+
import dataclasses
|
|
5
|
+
import typing as t
|
|
6
|
+
|
|
7
|
+
from ansible._internal._errors import _error_utils
|
|
8
|
+
from ansible.errors import AnsibleRuntimeError
|
|
9
|
+
from ansible.module_utils._internal import _messages
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AnsibleCapturedError(AnsibleRuntimeError):
|
|
13
|
+
"""An exception representing error detail captured in another context where the error detail must be serialized to be preserved."""
|
|
14
|
+
|
|
15
|
+
context: t.ClassVar[str]
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
*,
|
|
20
|
+
obj: t.Any = None,
|
|
21
|
+
event: _messages.Event,
|
|
22
|
+
) -> None:
|
|
23
|
+
super().__init__(
|
|
24
|
+
obj=obj,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
self._event = event
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AnsibleResultCapturedError(AnsibleCapturedError, _error_utils.ContributesToTaskResult):
|
|
31
|
+
"""
|
|
32
|
+
An exception representing error detail captured in a foreign context where an action/module result dictionary is involved.
|
|
33
|
+
|
|
34
|
+
This exception provides a result dictionary via the ContributesToTaskResult mixin.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, event: _messages.Event, result: dict[str, t.Any]) -> None:
|
|
38
|
+
super().__init__(event=event)
|
|
39
|
+
|
|
40
|
+
self._result = result
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def result_contribution(self) -> _c.Mapping[str, object]:
|
|
44
|
+
return self._result
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def maybe_raise_on_result(cls, result: dict[str, t.Any]) -> None:
|
|
48
|
+
"""Normalize the result and raise an exception if the result indicated failure."""
|
|
49
|
+
if error_summary := cls.normalize_result_exception(result):
|
|
50
|
+
raise error_summary.error_type(error_summary.event, result)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def normalize_result_exception(cls, result: dict[str, t.Any]) -> CapturedErrorSummary | None:
|
|
54
|
+
"""
|
|
55
|
+
Normalize the result `exception`, if any, to be a `CapturedErrorSummary` instance.
|
|
56
|
+
If a new `CapturedErrorSummary` was created, the `error_type` will be `cls`.
|
|
57
|
+
The `exception` key will be removed if falsey.
|
|
58
|
+
A `CapturedErrorSummary` instance will be returned if `failed` is truthy.
|
|
59
|
+
"""
|
|
60
|
+
if type(cls) is AnsibleResultCapturedError: # pylint: disable=unidiomatic-typecheck
|
|
61
|
+
raise TypeError('The normalize_result_exception method cannot be called on the AnsibleCapturedError base type, use a derived type.')
|
|
62
|
+
|
|
63
|
+
if not isinstance(result, dict):
|
|
64
|
+
raise TypeError(f'Malformed result. Received {type(result)} instead of {dict}.')
|
|
65
|
+
|
|
66
|
+
failed = result.get('failed') # DTFIX-FUTURE: warn if failed is present and not a bool, or exception is present without failed being True
|
|
67
|
+
exception = result.pop('exception', None)
|
|
68
|
+
|
|
69
|
+
if not failed and not exception:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
if isinstance(exception, CapturedErrorSummary):
|
|
73
|
+
error_summary = exception
|
|
74
|
+
elif isinstance(exception, _messages.ErrorSummary):
|
|
75
|
+
error_summary = CapturedErrorSummary(
|
|
76
|
+
event=exception.event,
|
|
77
|
+
error_type=cls,
|
|
78
|
+
)
|
|
79
|
+
else:
|
|
80
|
+
# translate non-ErrorDetail errors
|
|
81
|
+
error_summary = CapturedErrorSummary(
|
|
82
|
+
event=_messages.Event(
|
|
83
|
+
msg=str(result.get('msg', 'Unknown error.')),
|
|
84
|
+
formatted_traceback=cls._normalize_traceback(exception),
|
|
85
|
+
),
|
|
86
|
+
error_type=cls,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
result.update(exception=error_summary)
|
|
90
|
+
|
|
91
|
+
return error_summary if failed else None # even though error detail was normalized, only return it if the result indicated failure
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
def _normalize_traceback(cls, value: object | None) -> str | None:
|
|
95
|
+
"""Normalize the provided traceback value, returning None if it is falsey."""
|
|
96
|
+
if not value:
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
value = str(value).rstrip()
|
|
100
|
+
|
|
101
|
+
if not value:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
return value + '\n'
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class AnsibleActionCapturedError(AnsibleResultCapturedError):
|
|
108
|
+
"""An exception representing error detail sourced directly by an action in its result dictionary."""
|
|
109
|
+
|
|
110
|
+
_default_message = 'Action failed.'
|
|
111
|
+
context = 'action'
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class AnsibleModuleCapturedError(AnsibleResultCapturedError):
|
|
115
|
+
"""An exception representing error detail captured in a module context and returned from an action's result dictionary."""
|
|
116
|
+
|
|
117
|
+
_default_message = 'Module failed.'
|
|
118
|
+
context = 'target'
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclasses.dataclass(**_messages._dataclass_kwargs)
|
|
122
|
+
class CapturedErrorSummary(_messages.ErrorSummary):
|
|
123
|
+
error_type: type[AnsibleResultCapturedError] | None = None
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from __future__ import annotations as _annotations
|
|
2
|
+
|
|
3
|
+
from ansible.module_utils._internal import _errors, _messages
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ControllerEventFactory(_errors.EventFactory):
|
|
7
|
+
"""Factory for creating `Event` instances from `BaseException` instances on the controller."""
|
|
8
|
+
|
|
9
|
+
def _get_msg(self, exception: BaseException) -> str | None:
|
|
10
|
+
from ansible.errors import AnsibleError
|
|
11
|
+
|
|
12
|
+
if not isinstance(exception, AnsibleError):
|
|
13
|
+
return super()._get_msg(exception)
|
|
14
|
+
|
|
15
|
+
return exception._original_message.strip()
|
|
16
|
+
|
|
17
|
+
def _get_formatted_source_context(self, exception: BaseException) -> str | None:
|
|
18
|
+
from ansible.errors import AnsibleError
|
|
19
|
+
|
|
20
|
+
if not isinstance(exception, AnsibleError):
|
|
21
|
+
return super()._get_formatted_source_context(exception)
|
|
22
|
+
|
|
23
|
+
return exception._formatted_source_context
|
|
24
|
+
|
|
25
|
+
def _get_help_text(self, exception: BaseException) -> str | None:
|
|
26
|
+
from ansible.errors import AnsibleError
|
|
27
|
+
|
|
28
|
+
if not isinstance(exception, AnsibleError):
|
|
29
|
+
return super()._get_help_text(exception)
|
|
30
|
+
|
|
31
|
+
return exception._help_text
|
|
32
|
+
|
|
33
|
+
def _get_chain(self, exception: BaseException) -> _messages.EventChain | None:
|
|
34
|
+
from ansible._internal._errors import _captured # avoid circular import due to AnsibleError import
|
|
35
|
+
|
|
36
|
+
if isinstance(exception, _captured.AnsibleCapturedError):
|
|
37
|
+
# a captured error provides its own cause event, it never has a normal __cause__
|
|
38
|
+
return _messages.EventChain(
|
|
39
|
+
msg_reason=_errors.MSG_REASON_DIRECT_CAUSE,
|
|
40
|
+
traceback_reason=f'The above {exception.context} exception was the direct cause of the following controller exception:',
|
|
41
|
+
event=exception._event,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return super()._get_chain(exception)
|
|
45
|
+
|
|
46
|
+
def _follow_cause(self, exception: BaseException) -> bool:
|
|
47
|
+
from ansible.errors import AnsibleError
|
|
48
|
+
|
|
49
|
+
return not isinstance(exception, AnsibleError) or exception._include_cause_message
|
|
50
|
+
|
|
51
|
+
def _get_cause(self, exception: BaseException) -> BaseException | None:
|
|
52
|
+
# deprecated: description='remove support for orig_exc (deprecated in 2.23)' core_version='2.27'
|
|
53
|
+
|
|
54
|
+
cause = super()._get_cause(exception)
|
|
55
|
+
|
|
56
|
+
from ansible.errors import AnsibleError
|
|
57
|
+
|
|
58
|
+
if not isinstance(exception, AnsibleError):
|
|
59
|
+
return cause
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
from ansible.utils.display import _display
|
|
63
|
+
except Exception: # pylint: disable=broad-except # if config is broken, this can raise things other than ImportError
|
|
64
|
+
_display = None
|
|
65
|
+
|
|
66
|
+
if cause:
|
|
67
|
+
if exception.orig_exc and exception.orig_exc is not cause and _display:
|
|
68
|
+
_display.warning(
|
|
69
|
+
msg=f"The `orig_exc` argument to `{type(exception).__name__}` was given, but differed from the cause given by `raise ... from`.",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return cause
|
|
73
|
+
|
|
74
|
+
if exception.orig_exc:
|
|
75
|
+
if _display:
|
|
76
|
+
# encourage the use of `raise ... from` before deprecating `orig_exc`
|
|
77
|
+
_display.warning(
|
|
78
|
+
msg=f"The `orig_exc` argument to `{type(exception).__name__}` was given without using `raise ... from orig_exc`.",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return exception.orig_exc
|
|
82
|
+
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
def _get_events(self, exception: BaseException) -> tuple[_messages.Event, ...] | None:
|
|
86
|
+
if isinstance(exception, BaseExceptionGroup):
|
|
87
|
+
return tuple(self._convert_exception(ex) for ex in exception.exceptions)
|
|
88
|
+
|
|
89
|
+
return None
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import collections.abc as _c
|
|
5
|
+
import dataclasses
|
|
6
|
+
import itertools
|
|
7
|
+
import pathlib
|
|
8
|
+
import textwrap
|
|
9
|
+
import typing as t
|
|
10
|
+
|
|
11
|
+
from ansible._internal._datatag._tags import Origin
|
|
12
|
+
from ansible._internal._errors import _error_factory
|
|
13
|
+
from ansible.module_utils._internal import _ambient_context, _event_utils, _messages, _traceback
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ContributesToTaskResult(metaclass=abc.ABCMeta):
|
|
17
|
+
"""Exceptions may include this mixin to contribute task result dictionary data directly to the final result."""
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
@abc.abstractmethod
|
|
21
|
+
def result_contribution(self) -> _c.Mapping[str, object]:
|
|
22
|
+
"""Mapping of results to apply to the task result."""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def omit_exception_key(self) -> bool:
|
|
26
|
+
"""Non-error exceptions (e.g., `AnsibleActionSkip`) must return `True` to ensure omission of the `exception` key."""
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def omit_failed_key(self) -> bool:
|
|
31
|
+
"""Exceptions representing non-failure scenarios (e.g., `skipped`, `unreachable`) must return `True` to ensure omisson of the `failed` key."""
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class RedactAnnotatedSourceContext(_ambient_context.AmbientContextBase):
|
|
36
|
+
"""When active, this context will redact annotated source lines, showing only the origin."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclasses.dataclass(kw_only=True, frozen=True)
|
|
40
|
+
class SourceContext:
|
|
41
|
+
origin: Origin
|
|
42
|
+
annotated_source_lines: list[str]
|
|
43
|
+
target_line: str | None
|
|
44
|
+
|
|
45
|
+
def __str__(self) -> str:
|
|
46
|
+
msg_lines = [f'Origin: {self.origin}']
|
|
47
|
+
|
|
48
|
+
if self.annotated_source_lines:
|
|
49
|
+
msg_lines.append('')
|
|
50
|
+
msg_lines.extend(self.annotated_source_lines)
|
|
51
|
+
|
|
52
|
+
return '\n'.join(msg_lines)
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_value(cls, value: t.Any) -> SourceContext | None:
|
|
56
|
+
"""Attempt to retrieve source and render a contextual indicator from the value's origin (if any)."""
|
|
57
|
+
if value is None:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
if isinstance(value, Origin):
|
|
61
|
+
origin = value
|
|
62
|
+
value = None
|
|
63
|
+
else:
|
|
64
|
+
origin = Origin.get_tag(value)
|
|
65
|
+
|
|
66
|
+
if RedactAnnotatedSourceContext.current(optional=True):
|
|
67
|
+
return cls.error('content redacted')
|
|
68
|
+
|
|
69
|
+
if origin and origin.path:
|
|
70
|
+
return cls.from_origin(origin)
|
|
71
|
+
|
|
72
|
+
if value is None:
|
|
73
|
+
truncated_value = None
|
|
74
|
+
annotated_source_lines = []
|
|
75
|
+
else:
|
|
76
|
+
# DTFIX-FUTURE: cleanup/share width
|
|
77
|
+
try:
|
|
78
|
+
value = str(value)
|
|
79
|
+
except Exception as ex:
|
|
80
|
+
value = f'<< context unavailable: {ex} >>'
|
|
81
|
+
|
|
82
|
+
truncated_value = textwrap.shorten(value, width=120)
|
|
83
|
+
annotated_source_lines = [truncated_value]
|
|
84
|
+
|
|
85
|
+
return SourceContext(
|
|
86
|
+
origin=origin or Origin.UNKNOWN,
|
|
87
|
+
annotated_source_lines=annotated_source_lines,
|
|
88
|
+
target_line=truncated_value,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def error(message: str | None, origin: Origin | None = None) -> SourceContext:
|
|
93
|
+
return SourceContext(
|
|
94
|
+
origin=origin,
|
|
95
|
+
annotated_source_lines=[f'(source not shown: {message})'] if message else [],
|
|
96
|
+
target_line=None,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def from_origin(cls, origin: Origin) -> SourceContext:
|
|
101
|
+
"""Attempt to retrieve source and render a contextual indicator of an error location."""
|
|
102
|
+
from ansible.parsing.vault import is_encrypted # avoid circular import
|
|
103
|
+
|
|
104
|
+
# DTFIX-FUTURE: support referencing the column after the end of the target line, so we can indicate where a missing character (quote) needs to be added
|
|
105
|
+
# this is also useful for cases like end-of-stream reported by the YAML parser
|
|
106
|
+
|
|
107
|
+
# DTFIX-FUTURE: Implement line wrapping and match annotated line width to the terminal display width.
|
|
108
|
+
|
|
109
|
+
context_line_count: t.Final = 2
|
|
110
|
+
max_annotated_line_width: t.Final = 120
|
|
111
|
+
truncation_marker: t.Final = '...'
|
|
112
|
+
|
|
113
|
+
target_line_num = origin.line_num
|
|
114
|
+
|
|
115
|
+
if RedactAnnotatedSourceContext.current(optional=True):
|
|
116
|
+
return cls.error('content redacted', origin)
|
|
117
|
+
|
|
118
|
+
if not target_line_num or target_line_num < 1:
|
|
119
|
+
return cls.error(None, origin) # message omitted since lack of line number is obvious from pos
|
|
120
|
+
|
|
121
|
+
start_line_idx = max(0, (target_line_num - 1) - context_line_count) # if near start of file
|
|
122
|
+
target_col_num = origin.col_num
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
with pathlib.Path(origin.path).open() as src:
|
|
126
|
+
first_line = src.readline()
|
|
127
|
+
lines = list(itertools.islice(itertools.chain((first_line,), src), start_line_idx, target_line_num))
|
|
128
|
+
except Exception as ex:
|
|
129
|
+
return cls.error(type(ex).__name__, origin)
|
|
130
|
+
|
|
131
|
+
if is_encrypted(first_line):
|
|
132
|
+
return cls.error('content encrypted', origin)
|
|
133
|
+
|
|
134
|
+
if len(lines) != target_line_num - start_line_idx:
|
|
135
|
+
return cls.error('file truncated', origin)
|
|
136
|
+
|
|
137
|
+
annotated_source_lines = []
|
|
138
|
+
|
|
139
|
+
line_label_width = len(str(target_line_num))
|
|
140
|
+
max_src_line_len = max_annotated_line_width - line_label_width - 1
|
|
141
|
+
|
|
142
|
+
usable_line_len = max_src_line_len
|
|
143
|
+
|
|
144
|
+
for line_num, line in enumerate(lines, start_line_idx + 1):
|
|
145
|
+
line = line.rstrip('\n') # universal newline default mode on `open` ensures we'll never see anything but \n
|
|
146
|
+
line = line.replace('\t', ' ') # mixed tab/space handling is intentionally disabled since we're both format and display config agnostic
|
|
147
|
+
|
|
148
|
+
if len(line) > max_src_line_len:
|
|
149
|
+
line = line[: max_src_line_len - len(truncation_marker)] + truncation_marker
|
|
150
|
+
usable_line_len = max_src_line_len - len(truncation_marker)
|
|
151
|
+
|
|
152
|
+
annotated_source_lines.append(f'{str(line_num).rjust(line_label_width)}{" " if line else ""}{line}')
|
|
153
|
+
|
|
154
|
+
if target_col_num and usable_line_len >= target_col_num >= 1:
|
|
155
|
+
column_marker = f'column {target_col_num}'
|
|
156
|
+
|
|
157
|
+
target_col_idx = target_col_num - 1
|
|
158
|
+
|
|
159
|
+
if target_col_idx + 2 + len(column_marker) > max_src_line_len:
|
|
160
|
+
column_marker = f'{" " * (target_col_idx - len(column_marker) - 1)}{column_marker} ^'
|
|
161
|
+
else:
|
|
162
|
+
column_marker = f'{" " * target_col_idx}^ {column_marker}'
|
|
163
|
+
|
|
164
|
+
column_marker = f'{" " * line_label_width} {column_marker}'
|
|
165
|
+
|
|
166
|
+
annotated_source_lines.append(column_marker)
|
|
167
|
+
elif target_col_num is None:
|
|
168
|
+
underline_length = len(annotated_source_lines[-1]) - line_label_width - 1
|
|
169
|
+
annotated_source_lines.append(f'{" " * line_label_width} {"^" * underline_length}')
|
|
170
|
+
|
|
171
|
+
return SourceContext(
|
|
172
|
+
origin=origin,
|
|
173
|
+
annotated_source_lines=annotated_source_lines,
|
|
174
|
+
target_line=lines[-1].rstrip('\n'), # universal newline default mode on `open` ensures we'll never see anything but \n
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def format_exception_message(exception: BaseException) -> str:
|
|
179
|
+
"""Return the full chain of exception messages by concatenating the cause(s) until all are exhausted."""
|
|
180
|
+
return _event_utils.format_event_brief_message(_error_factory.ControllerEventFactory.from_exception(exception, False))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def result_dict_from_exception(exception: BaseException, accept_result_contribution: bool = False) -> dict[str, object]:
|
|
184
|
+
"""Return a failed task result dict from the given exception."""
|
|
185
|
+
event = _error_factory.ControllerEventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR))
|
|
186
|
+
|
|
187
|
+
result: dict[str, object] = {}
|
|
188
|
+
omit_failed_key = False
|
|
189
|
+
omit_exception_key = False
|
|
190
|
+
|
|
191
|
+
if accept_result_contribution:
|
|
192
|
+
while exception:
|
|
193
|
+
if isinstance(exception, ContributesToTaskResult):
|
|
194
|
+
result = dict(exception.result_contribution)
|
|
195
|
+
omit_failed_key = exception.omit_failed_key
|
|
196
|
+
omit_exception_key = exception.omit_exception_key
|
|
197
|
+
break
|
|
198
|
+
|
|
199
|
+
exception = exception.__cause__
|
|
200
|
+
|
|
201
|
+
if omit_failed_key:
|
|
202
|
+
result.pop('failed', None)
|
|
203
|
+
else:
|
|
204
|
+
result.update(failed=True)
|
|
205
|
+
|
|
206
|
+
if omit_exception_key:
|
|
207
|
+
result.pop('exception', None)
|
|
208
|
+
else:
|
|
209
|
+
result.update(exception=_messages.ErrorSummary(event=event))
|
|
210
|
+
|
|
211
|
+
if 'msg' not in result:
|
|
212
|
+
# if nothing contributed `msg`, generate one from the exception messages
|
|
213
|
+
result.update(msg=_event_utils.format_event_brief_message(event))
|
|
214
|
+
|
|
215
|
+
return result
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def result_dict_from_captured_errors(
|
|
219
|
+
msg: str,
|
|
220
|
+
*,
|
|
221
|
+
errors: list[_messages.ErrorSummary] | None = None,
|
|
222
|
+
) -> dict[str, object]:
|
|
223
|
+
"""Return a failed task result dict from the given error message and captured errors."""
|
|
224
|
+
_skip_stackwalk = True
|
|
225
|
+
|
|
226
|
+
event = _messages.Event(
|
|
227
|
+
msg=msg,
|
|
228
|
+
formatted_traceback=_traceback.maybe_capture_traceback(msg, _traceback.TracebackEvent.ERROR),
|
|
229
|
+
events=tuple(error.event for error in errors) if errors else None,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
result = dict(
|
|
233
|
+
failed=True,
|
|
234
|
+
exception=_messages.ErrorSummary(
|
|
235
|
+
event=event,
|
|
236
|
+
),
|
|
237
|
+
msg=_event_utils.format_event_brief_message(event),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
return result
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import enum
|
|
5
|
+
import typing as t
|
|
6
|
+
|
|
7
|
+
from ansible.utils.display import Display
|
|
8
|
+
from ansible.constants import config
|
|
9
|
+
|
|
10
|
+
display = Display()
|
|
11
|
+
|
|
12
|
+
# FUTURE: add sanity test to detect use of skip_on_ignore without Skippable (and vice versa)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ErrorAction(enum.Enum):
|
|
16
|
+
"""Action to take when an error is encountered."""
|
|
17
|
+
|
|
18
|
+
IGNORE = enum.auto()
|
|
19
|
+
WARNING = enum.auto()
|
|
20
|
+
ERROR = enum.auto()
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def from_config(cls, setting: str, variables: dict[str, t.Any] | None = None) -> t.Self:
|
|
24
|
+
"""Return an `ErrorAction` enum from the specified Ansible config setting."""
|
|
25
|
+
return cls[config.get_config_value(setting, variables=variables).upper()]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _SkipException(BaseException):
|
|
29
|
+
"""Internal flow control exception for skipping code blocks within a `Skippable` context manager."""
|
|
30
|
+
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
super().__init__('Skipping ignored action due to use of `skip_on_ignore`. It is a bug to encounter this message outside of debugging.')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _SkippableContextManager:
|
|
36
|
+
"""Internal context manager to support flow control for skipping code blocks."""
|
|
37
|
+
|
|
38
|
+
def __enter__(self) -> None:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def __exit__(self, exc_type, _exc_val, _exc_tb) -> bool:
|
|
42
|
+
if exc_type is None:
|
|
43
|
+
raise RuntimeError('A `Skippable` context manager was entered, but a `skip_on_ignore` handler was never invoked.')
|
|
44
|
+
|
|
45
|
+
return exc_type is _SkipException # only mask a _SkipException, allow all others to raise
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Skippable = _SkippableContextManager()
|
|
49
|
+
"""Context manager singleton required to enclose `ErrorHandler.handle` invocations when `skip_on_ignore` is `True`."""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ErrorHandler:
|
|
53
|
+
"""
|
|
54
|
+
Provides a configurable error handler context manager for a specific list of exception types.
|
|
55
|
+
Unhandled errors leaving the context manager can be ignored, treated as warnings, or allowed to raise by setting `ErrorAction`.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, action: ErrorAction) -> None:
|
|
59
|
+
self.action = action
|
|
60
|
+
|
|
61
|
+
@contextlib.contextmanager
|
|
62
|
+
def handle(self, *args: type[BaseException], skip_on_ignore: bool = False) -> t.Iterator[None]:
|
|
63
|
+
"""
|
|
64
|
+
Handle the specified exception(s) using the defined error action.
|
|
65
|
+
If `skip_on_ignore` is `True`, the body of the context manager will be skipped for `ErrorAction.IGNORE`.
|
|
66
|
+
Use of `skip_on_ignore` requires enclosure within the `Skippable` context manager.
|
|
67
|
+
"""
|
|
68
|
+
if not args:
|
|
69
|
+
raise ValueError('At least one exception type is required.')
|
|
70
|
+
|
|
71
|
+
if skip_on_ignore and self.action == ErrorAction.IGNORE:
|
|
72
|
+
raise _SkipException() # skipping ignored action
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
yield
|
|
76
|
+
except args as ex:
|
|
77
|
+
match self.action:
|
|
78
|
+
case ErrorAction.WARNING:
|
|
79
|
+
display.error_as_warning(msg=None, exception=ex)
|
|
80
|
+
case ErrorAction.ERROR:
|
|
81
|
+
raise
|
|
82
|
+
case _: # ErrorAction.IGNORE
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
if skip_on_ignore:
|
|
86
|
+
raise _SkipException() # completed skippable action, ensures the `Skippable` context was used
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def from_config(cls, setting: str, variables: dict[str, t.Any] | None = None) -> t.Self:
|
|
90
|
+
"""Return an `ErrorHandler` instance configured using the specified Ansible config setting."""
|
|
91
|
+
return cls(ErrorAction.from_config(setting, variables=variables))
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections import abc as _c
|
|
4
|
+
|
|
5
|
+
from ansible._internal._errors._alarm_timeout import AnsibleTimeoutError
|
|
6
|
+
from ansible._internal._errors._error_utils import ContributesToTaskResult
|
|
7
|
+
from ansible.module_utils.datatag import deprecate_value
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TaskTimeoutError(AnsibleTimeoutError, ContributesToTaskResult):
|
|
11
|
+
"""
|
|
12
|
+
A task-specific timeout.
|
|
13
|
+
|
|
14
|
+
This exception provides a result dictionary via the ContributesToTaskResult mixin.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def result_contribution(self) -> _c.Mapping[str, object]:
|
|
19
|
+
help_text = "Configure `DISPLAY_TRACEBACK` to see a traceback on timeout errors."
|
|
20
|
+
|
|
21
|
+
frame = deprecate_value(
|
|
22
|
+
value=help_text,
|
|
23
|
+
msg="The `timedout.frame` task result key is deprecated.",
|
|
24
|
+
help_text=help_text,
|
|
25
|
+
version="2.23",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
return dict(timedout=dict(frame=frame, period=self.timeout))
|