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
ansible/keyword_desc.yml
CHANGED
|
@@ -13,7 +13,7 @@ become_method: Which method of privilege escalation to use (such as sudo or su).
|
|
|
13
13
|
become_user: "User that you 'become' after using privilege escalation. The remote/login user must have permissions to become this user."
|
|
14
14
|
block: List of tasks in a block.
|
|
15
15
|
changed_when: "Conditional expression that overrides the task's normal 'changed' status."
|
|
16
|
-
check_mode: A boolean that controls if a task is
|
|
16
|
+
check_mode: A boolean that controls if a task is run normally or avoids changes to the target and tries to report what it would have done (check mode/dry run). See :ref:`check_mode_dry`.
|
|
17
17
|
collections: |
|
|
18
18
|
List of collection namespaces to search for modules, plugins, and roles. See :ref:`collections_using_playbook`
|
|
19
19
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import collections.abc as c
|
|
4
|
+
|
|
5
|
+
import typing as t
|
|
6
|
+
|
|
7
|
+
if t.TYPE_CHECKING:
|
|
8
|
+
from ansible.module_utils.compat.typing import TypeGuard
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
INTERMEDIATE_MAPPING_TYPES = (c.Mapping,)
|
|
12
|
+
"""
|
|
13
|
+
Mapping types which are supported for recursion and runtime usage, such as in serialization and templating.
|
|
14
|
+
These will be converted to a simple Python `dict` before serialization or storage as a variable.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
INTERMEDIATE_ITERABLE_TYPES = (tuple, set, frozenset, c.Sequence)
|
|
18
|
+
"""
|
|
19
|
+
Iterable types which are supported for recursion and runtime usage, such as in serialization and templating.
|
|
20
|
+
These will be converted to a simple Python `list` before serialization or storage as a variable.
|
|
21
|
+
CAUTION: Scalar types which are sequences should be excluded when using this.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
ITERABLE_SCALARS_NOT_TO_ITERATE = (str, bytes)
|
|
25
|
+
"""Scalars which are also iterable, and should thus be excluded from iterable checks."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_intermediate_mapping(value: object) -> TypeGuard[c.Mapping]:
|
|
29
|
+
"""Returns `True` if `value` is a type supported for projection to a Python `dict`, otherwise returns `False`."""
|
|
30
|
+
return isinstance(value, INTERMEDIATE_MAPPING_TYPES)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_intermediate_iterable(value: object) -> TypeGuard[c.Iterable]:
|
|
34
|
+
"""Returns `True` if `value` is a type supported for projection to a Python `list`, otherwise returns `False`."""
|
|
35
|
+
return isinstance(value, INTERMEDIATE_ITERABLE_TYPES) and not isinstance(value, ITERABLE_SCALARS_NOT_TO_ITERATE)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
is_controller: bool = False
|
|
39
|
+
"""Set to True automatically when this module is imported into an Ansible controller context."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_controller_serialize_map() -> dict[type, t.Callable]:
|
|
43
|
+
"""
|
|
44
|
+
Called to augment serialization maps.
|
|
45
|
+
This implementation is replaced with the one from ansible._internal in controller contexts.
|
|
46
|
+
"""
|
|
47
|
+
return {}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def import_controller_module(_module_name: str, /) -> t.Any:
|
|
51
|
+
"""
|
|
52
|
+
Called to conditionally import the named module in a controller context, otherwise returns `None`.
|
|
53
|
+
This implementation is replaced with the one from ansible._internal in controller contexts.
|
|
54
|
+
"""
|
|
55
|
+
return None
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Copyright (c) 2024 Ansible Project
|
|
2
|
+
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import contextlib
|
|
7
|
+
import contextvars
|
|
8
|
+
|
|
9
|
+
# deprecated: description='typing.Self exists in Python 3.11+' python_version='3.10'
|
|
10
|
+
from ..compat import typing as t
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AmbientContextBase:
|
|
14
|
+
"""
|
|
15
|
+
An abstract base context manager that, once entered, will be accessible via its `current` classmethod to any code in the same
|
|
16
|
+
`contextvars` context (e.g. same thread/coroutine), until it is exited.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
__slots__ = ('_contextvar_token',)
|
|
20
|
+
|
|
21
|
+
# DTFIX-FUTURE: subclasses need to be able to opt-in to blocking nested contexts of the same type (basically optional per-callstack singleton behavior)
|
|
22
|
+
# DTFIX-FUTURE: this class should enforce strict nesting of contexts; overlapping context lifetimes leads to incredibly difficult to
|
|
23
|
+
# debug situations with undefined behavior, so it should fail fast.
|
|
24
|
+
# DTFIX-FUTURE: make frozen=True dataclass subclasses work (fix the mutability of the contextvar instance)
|
|
25
|
+
|
|
26
|
+
_contextvar: t.ClassVar[contextvars.ContextVar] # pylint: disable=declare-non-slot # pylint bug, see https://github.com/pylint-dev/pylint/issues/9950
|
|
27
|
+
_contextvar_token: contextvars.Token
|
|
28
|
+
|
|
29
|
+
def __init_subclass__(cls, **kwargs) -> None:
|
|
30
|
+
cls._contextvar = contextvars.ContextVar(cls.__name__)
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def when(cls, condition: bool, /, *args, **kwargs) -> t.Self | contextlib.nullcontext:
|
|
34
|
+
"""Return an instance of the context if `condition` is `True`, otherwise return a `nullcontext` instance."""
|
|
35
|
+
return cls(*args, **kwargs) if condition else contextlib.nullcontext()
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def current(cls, optional: bool = False) -> t.Self | None:
|
|
39
|
+
"""
|
|
40
|
+
Return the currently active context value for the current thread or coroutine.
|
|
41
|
+
Raises ReferenceError if a context is not active, unless `optional` is `True`.
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
return cls._contextvar.get()
|
|
45
|
+
except LookupError:
|
|
46
|
+
if optional:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
raise ReferenceError(f"A required {cls.__name__} context is not active.") from None
|
|
50
|
+
|
|
51
|
+
def __enter__(self) -> t.Self:
|
|
52
|
+
# DTFIX-FUTURE: actively block multiple entry
|
|
53
|
+
self._contextvar_token = self.__class__._contextvar.set(self)
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
57
|
+
self.__class__._contextvar.reset(self._contextvar_token)
|
|
58
|
+
del self._contextvar_token
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import atexit
|
|
4
|
+
import dataclasses
|
|
5
|
+
import importlib.util
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
import typing as t
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclasses.dataclass(frozen=True)
|
|
13
|
+
class Options:
|
|
14
|
+
"""Code coverage options."""
|
|
15
|
+
|
|
16
|
+
config: str
|
|
17
|
+
output: str | None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def run(args: dict[str, t.Any]) -> None: # pragma: nocover
|
|
21
|
+
"""Bootstrap `coverage` for the current Ansible module invocation."""
|
|
22
|
+
options = Options(**args)
|
|
23
|
+
|
|
24
|
+
if options.output:
|
|
25
|
+
# Enable code coverage analysis of the module.
|
|
26
|
+
# This feature is for internal testing and may change without notice.
|
|
27
|
+
python_version_string = '.'.join(str(v) for v in sys.version_info[:2])
|
|
28
|
+
os.environ['COVERAGE_FILE'] = f'{options.output}=python-{python_version_string}=coverage'
|
|
29
|
+
|
|
30
|
+
import coverage
|
|
31
|
+
|
|
32
|
+
cov = coverage.Coverage(config_file=options.config)
|
|
33
|
+
|
|
34
|
+
def atexit_coverage() -> None:
|
|
35
|
+
cov.stop()
|
|
36
|
+
cov.save()
|
|
37
|
+
|
|
38
|
+
atexit.register(atexit_coverage)
|
|
39
|
+
|
|
40
|
+
cov.start()
|
|
41
|
+
else:
|
|
42
|
+
# Verify coverage is available without importing it.
|
|
43
|
+
# This will detect when a module would fail with coverage enabled with minimal overhead.
|
|
44
|
+
if importlib.util.find_spec('coverage') is None:
|
|
45
|
+
raise RuntimeError('Could not find the `coverage` Python module.')
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Remote debugging support for AnsiballZ modules.
|
|
3
|
+
|
|
4
|
+
To use with PyCharm:
|
|
5
|
+
|
|
6
|
+
1) Choose an available port for PyCharm to listen on (e.g. 5678).
|
|
7
|
+
2) Create a Python Debug Server using that port.
|
|
8
|
+
3) Start the Python Debug Server.
|
|
9
|
+
4) Ensure the correct version of `pydevd-pycharm` is installed for the interpreter(s) which will run the code being debugged.
|
|
10
|
+
5) Configure Ansible with the `_ANSIBALLZ_DEBUGGER_CONFIG` option.
|
|
11
|
+
See `Options` below for the structure of the debugger configuration.
|
|
12
|
+
Example configuration using an environment variable:
|
|
13
|
+
export _ANSIBLE_ANSIBALLZ_DEBUGGER_CONFIG='{"module": "pydevd_pycharm", "settrace": {"host": "localhost", "port": 5678, "suspend": false}}'
|
|
14
|
+
6) Set any desired breakpoints.
|
|
15
|
+
7) Run Ansible commands.
|
|
16
|
+
|
|
17
|
+
A similar process should work for other pydevd based debuggers, such as Visual Studio Code, but they have not been tested.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import dataclasses
|
|
23
|
+
import importlib
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import pathlib
|
|
27
|
+
|
|
28
|
+
import typing as t
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclasses.dataclass(frozen=True)
|
|
32
|
+
class Options:
|
|
33
|
+
"""Debugger options for pydevd and its derivatives."""
|
|
34
|
+
|
|
35
|
+
module: str = 'pydevd'
|
|
36
|
+
"""The Python module which will be imported and which provides the `settrace` method."""
|
|
37
|
+
settrace: dict[str, object] = dataclasses.field(default_factory=dict)
|
|
38
|
+
"""The options to pass to the `{module}.settrace` method."""
|
|
39
|
+
source_mapping: dict[str, str] = dataclasses.field(default_factory=dict)
|
|
40
|
+
"""
|
|
41
|
+
A mapping of source paths to provide to pydevd.
|
|
42
|
+
This setting is used internally by AnsiballZ and is not required unless Ansible CLI commands are run from a different system than your IDE.
|
|
43
|
+
In that scenario, use this setting instead of configuring source mapping in your IDE.
|
|
44
|
+
The key is a path known to the IDE.
|
|
45
|
+
The value is the same path as known to the Ansible CLI.
|
|
46
|
+
Both file paths and directories are supported.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def run(args: dict[str, t.Any]) -> None: # pragma: nocover
|
|
51
|
+
"""Enable remote debugging."""
|
|
52
|
+
|
|
53
|
+
options = Options(**args)
|
|
54
|
+
temp_dir = pathlib.Path(__file__).parent.parent.parent.parent.parent.parent
|
|
55
|
+
path_mapping = [[key, str(temp_dir / value)] for key, value in options.source_mapping.items()]
|
|
56
|
+
|
|
57
|
+
os.environ['PATHS_FROM_ECLIPSE_TO_PYTHON'] = json.dumps(path_mapping)
|
|
58
|
+
|
|
59
|
+
debugging_module = importlib.import_module(options.module)
|
|
60
|
+
debugging_module.settrace(**options.settrace)
|
|
61
|
+
|
|
62
|
+
pass # when suspend is True, execution pauses here -- it's also a convenient place to put a breakpoint
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Copyright (c) 2024 Ansible Project
|
|
2
|
+
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
|
3
|
+
|
|
4
|
+
"""Support code for exclusive use by the AnsiballZ wrapper."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import importlib
|
|
9
|
+
import json
|
|
10
|
+
import runpy
|
|
11
|
+
import sys
|
|
12
|
+
import typing as t
|
|
13
|
+
|
|
14
|
+
from ansible.module_utils import basic
|
|
15
|
+
from ansible.module_utils._internal import _errors, _traceback, _messages, _ansiballz
|
|
16
|
+
from ansible.module_utils.common.json import get_module_encoder, Direction
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def run_module(
|
|
20
|
+
*,
|
|
21
|
+
json_params: bytes,
|
|
22
|
+
profile: str,
|
|
23
|
+
module_fqn: str,
|
|
24
|
+
modlib_path: str,
|
|
25
|
+
extensions: dict[str, dict[str, object]],
|
|
26
|
+
init_globals: dict[str, t.Any] | None = None,
|
|
27
|
+
) -> None: # pragma: nocover
|
|
28
|
+
"""Used internally by the AnsiballZ wrapper to run an Ansible module."""
|
|
29
|
+
try:
|
|
30
|
+
for extension, args in extensions.items():
|
|
31
|
+
# importing _ansiballz instead of _extensions avoids an unnecessary import when extensions are not in use
|
|
32
|
+
extension_module = importlib.import_module(f'{_ansiballz.__name__}._extensions.{extension}')
|
|
33
|
+
extension_module.run(args)
|
|
34
|
+
|
|
35
|
+
_run_module(
|
|
36
|
+
json_params=json_params,
|
|
37
|
+
profile=profile,
|
|
38
|
+
module_fqn=module_fqn,
|
|
39
|
+
modlib_path=modlib_path,
|
|
40
|
+
init_globals=init_globals,
|
|
41
|
+
)
|
|
42
|
+
except Exception as ex: # not BaseException, since modules are expected to raise SystemExit
|
|
43
|
+
_handle_exception(ex, profile)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _run_module(
|
|
47
|
+
*,
|
|
48
|
+
json_params: bytes,
|
|
49
|
+
profile: str,
|
|
50
|
+
module_fqn: str,
|
|
51
|
+
modlib_path: str,
|
|
52
|
+
init_globals: dict[str, t.Any] | None = None,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Used internally by `_run_module` to run an Ansible module after coverage has been enabled (if applicable)."""
|
|
55
|
+
basic._ANSIBLE_ARGS = json_params
|
|
56
|
+
basic._ANSIBLE_PROFILE = profile
|
|
57
|
+
|
|
58
|
+
init_globals = init_globals or {}
|
|
59
|
+
init_globals.update(_module_fqn=module_fqn, _modlib_path=modlib_path)
|
|
60
|
+
|
|
61
|
+
# Run the module. By importing it as '__main__', it executes as a script.
|
|
62
|
+
runpy.run_module(mod_name=module_fqn, init_globals=init_globals, run_name='__main__', alter_sys=True)
|
|
63
|
+
|
|
64
|
+
# An Ansible module must print its own results and exit. If execution reaches this point, that did not happen.
|
|
65
|
+
raise RuntimeError('New-style module did not handle its own exit.')
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _handle_exception(exception: BaseException, profile: str) -> t.NoReturn:
|
|
69
|
+
"""Handle the given exception."""
|
|
70
|
+
result = dict(
|
|
71
|
+
failed=True,
|
|
72
|
+
exception=_messages.ErrorSummary(
|
|
73
|
+
event=_errors.EventFactory.from_exception(exception, _traceback.is_traceback_enabled(_traceback.TracebackEvent.ERROR)),
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
encoder = get_module_encoder(profile, Direction.MODULE_TO_CONTROLLER)
|
|
78
|
+
|
|
79
|
+
print(json.dumps(result, cls=encoder)) # pylint: disable=ansible-bad-function
|
|
80
|
+
|
|
81
|
+
sys.exit(1) # pylint: disable=ansible-bad-function
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from ... import basic
|
|
7
|
+
from . import _respawn_wrapper
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_payload() -> str:
|
|
11
|
+
"""Create and return an AnsiballZ payload for respawning a module."""
|
|
12
|
+
main = sys.modules['__main__']
|
|
13
|
+
code = inspect.getsource(_respawn_wrapper)
|
|
14
|
+
|
|
15
|
+
args = dict(
|
|
16
|
+
module_fqn=main._module_fqn,
|
|
17
|
+
modlib_path=main._modlib_path,
|
|
18
|
+
profile=basic._ANSIBLE_PROFILE,
|
|
19
|
+
json_params=basic._ANSIBLE_ARGS,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
args_string = '\n'.join(f'{key}={value!r},' for key, value in args.items())
|
|
23
|
+
|
|
24
|
+
wrapper = f"""{code}
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
_respawn_main(
|
|
28
|
+
{args_string}
|
|
29
|
+
)
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
return wrapper
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _respawn_main(
|
|
5
|
+
json_params: bytes,
|
|
6
|
+
profile: str,
|
|
7
|
+
module_fqn: str,
|
|
8
|
+
modlib_path: str,
|
|
9
|
+
) -> None:
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
sys.path.insert(0, modlib_path)
|
|
13
|
+
|
|
14
|
+
from ansible.module_utils._internal._ansiballz import _loader
|
|
15
|
+
|
|
16
|
+
_loader.run_module(
|
|
17
|
+
json_params=json_params,
|
|
18
|
+
profile=profile,
|
|
19
|
+
module_fqn=module_fqn,
|
|
20
|
+
modlib_path=modlib_path,
|
|
21
|
+
extensions={},
|
|
22
|
+
init_globals=dict(_respawned=True),
|
|
23
|
+
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Proxy stdlib threading module that only supports non-joinable daemon threads."""
|
|
2
|
+
|
|
2
3
|
# NB: all new local module attrs are _ prefixed to ensure an identical public attribute surface area to the module we're proxying
|
|
3
4
|
|
|
4
5
|
from __future__ import annotations as _annotations
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Copyright (c) 2024 Ansible Project
|
|
2
|
+
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
|
3
|
+
|
|
4
|
+
"""Code generation of __post_init__ methods for efficient dataclass field type checking at runtime."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import atexit
|
|
9
|
+
import functools
|
|
10
|
+
import itertools
|
|
11
|
+
import shutil
|
|
12
|
+
import tempfile
|
|
13
|
+
import types
|
|
14
|
+
import typing as t
|
|
15
|
+
|
|
16
|
+
_write_generated_code_to_disk = False
|
|
17
|
+
|
|
18
|
+
# deprecated: description='types.UnionType is available in Python 3.10' python_version='3.9'
|
|
19
|
+
try:
|
|
20
|
+
_union_type: type | None = types.UnionType # type: ignore[attr-defined]
|
|
21
|
+
_union_types: tuple = (t.Union, types.UnionType) # type: ignore[attr-defined]
|
|
22
|
+
except AttributeError:
|
|
23
|
+
_union_type = None # type: ignore[assignment]
|
|
24
|
+
_union_types = (t.Union,) # type: ignore[assignment]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def inject_post_init_validation(cls: type, allow_subclasses=False) -> None:
|
|
28
|
+
"""Inject a __post_init__ field validation method on the given dataclass. An existing __post_init__ attribute must already exist."""
|
|
29
|
+
# DTFIX-FUTURE: when cls must have a __post_init__, enforcing it as a no-op would be nice, but is tricky on slotted dataclasses due to double-creation
|
|
30
|
+
post_validate_name = '_post_validate'
|
|
31
|
+
method_name = '__post_init__'
|
|
32
|
+
exec_globals: dict[str, t.Any] = {}
|
|
33
|
+
known_types: dict[type, str] = {}
|
|
34
|
+
lines: list[str] = []
|
|
35
|
+
field_type_hints = t.get_type_hints(cls)
|
|
36
|
+
indent = 1
|
|
37
|
+
|
|
38
|
+
def append_line(line: str) -> None:
|
|
39
|
+
"""Append a line to the generated source at the current indentation level."""
|
|
40
|
+
lines.append((' ' * indent * 4) + line)
|
|
41
|
+
|
|
42
|
+
def register_type(target_type: type) -> str:
|
|
43
|
+
"""Register the target type and return the local name."""
|
|
44
|
+
target_name = f'{target_type.__module__.replace(".", "_")}_{target_type.__name__}'
|
|
45
|
+
|
|
46
|
+
known_types[target_type] = target_name
|
|
47
|
+
exec_globals[target_name] = target_type
|
|
48
|
+
|
|
49
|
+
return target_name
|
|
50
|
+
|
|
51
|
+
def validate_value(target_name: str, target_ref: str, target_type: type) -> None:
|
|
52
|
+
"""Generate code to validate the specified value."""
|
|
53
|
+
nonlocal indent
|
|
54
|
+
|
|
55
|
+
origin_type = t.get_origin(target_type)
|
|
56
|
+
|
|
57
|
+
if origin_type is t.ClassVar:
|
|
58
|
+
return # ignore annotations which are not fields, indicated by the t.ClassVar annotation
|
|
59
|
+
|
|
60
|
+
allowed_types = _get_allowed_types(target_type)
|
|
61
|
+
|
|
62
|
+
# check value
|
|
63
|
+
|
|
64
|
+
if origin_type is t.Literal:
|
|
65
|
+
# DTFIX-FUTURE: support optional literals
|
|
66
|
+
|
|
67
|
+
values = t.get_args(target_type)
|
|
68
|
+
|
|
69
|
+
append_line(f"""if {target_ref} not in {values}:""")
|
|
70
|
+
append_line(f""" raise ValueError(rf"{target_name} must be one of {values} instead of {{{target_ref}!r}}")""")
|
|
71
|
+
|
|
72
|
+
allowed_refs = [register_type(allowed_type) for allowed_type in allowed_types]
|
|
73
|
+
allowed_names = [repr(allowed_type) for allowed_type in allowed_types]
|
|
74
|
+
|
|
75
|
+
if allow_subclasses:
|
|
76
|
+
if len(allowed_refs) == 1:
|
|
77
|
+
append_line(f"""if not isinstance({target_ref}, {allowed_refs[0]}):""")
|
|
78
|
+
else:
|
|
79
|
+
append_line(f"""if not isinstance({target_ref}, ({', '.join(allowed_refs)})):""")
|
|
80
|
+
else:
|
|
81
|
+
if len(allowed_refs) == 1:
|
|
82
|
+
append_line(f"""if type({target_ref}) is not {allowed_refs[0]}:""")
|
|
83
|
+
else:
|
|
84
|
+
append_line(f"""if type({target_ref}) not in ({', '.join(allowed_refs)}):""")
|
|
85
|
+
|
|
86
|
+
append_line(f""" raise TypeError(f"{target_name} must be {' or '.join(allowed_names)} instead of {{type({target_ref})}}")""")
|
|
87
|
+
|
|
88
|
+
# check elements (for containers)
|
|
89
|
+
|
|
90
|
+
if target_ref.startswith('self.'):
|
|
91
|
+
local_ref = target_ref[5:]
|
|
92
|
+
else:
|
|
93
|
+
local_ref = target_ref
|
|
94
|
+
|
|
95
|
+
if tuple in allowed_types:
|
|
96
|
+
tuple_type = _extract_type(target_type, tuple)
|
|
97
|
+
|
|
98
|
+
idx_ref = f'{local_ref}_idx'
|
|
99
|
+
item_ref = f'{local_ref}_item'
|
|
100
|
+
item_name = f'{target_name}[{{{idx_ref}!r}}]'
|
|
101
|
+
item_type, _ellipsis = t.get_args(tuple_type)
|
|
102
|
+
|
|
103
|
+
if _ellipsis is not ...:
|
|
104
|
+
raise ValueError(f"{cls} tuple fields must be a tuple of a single element type")
|
|
105
|
+
|
|
106
|
+
append_line(f"""if isinstance({target_ref}, {known_types[tuple]}):""")
|
|
107
|
+
append_line(f""" for {idx_ref}, {item_ref} in enumerate({target_ref}):""")
|
|
108
|
+
|
|
109
|
+
indent += 2
|
|
110
|
+
validate_value(target_name=item_name, target_ref=item_ref, target_type=item_type)
|
|
111
|
+
indent -= 2
|
|
112
|
+
|
|
113
|
+
if list in allowed_types:
|
|
114
|
+
list_type = _extract_type(target_type, list)
|
|
115
|
+
|
|
116
|
+
idx_ref = f'{local_ref}_idx'
|
|
117
|
+
item_ref = f'{local_ref}_item'
|
|
118
|
+
item_name = f'{target_name}[{{{idx_ref}!r}}]'
|
|
119
|
+
(item_type,) = t.get_args(list_type)
|
|
120
|
+
|
|
121
|
+
append_line(f"""if isinstance({target_ref}, {known_types[list]}):""")
|
|
122
|
+
append_line(f""" for {idx_ref}, {item_ref} in enumerate({target_ref}):""")
|
|
123
|
+
|
|
124
|
+
indent += 2
|
|
125
|
+
validate_value(target_name=item_name, target_ref=item_ref, target_type=item_type)
|
|
126
|
+
indent -= 2
|
|
127
|
+
|
|
128
|
+
if dict in allowed_types:
|
|
129
|
+
dict_type = _extract_type(target_type, dict)
|
|
130
|
+
|
|
131
|
+
key_ref, value_ref = f'{local_ref}_key', f'{local_ref}_value'
|
|
132
|
+
key_type, value_type = t.get_args(dict_type)
|
|
133
|
+
key_name, value_name = f'{target_name!r} key {{{key_ref}!r}}', f'{target_name}[{{{key_ref}!r}}]'
|
|
134
|
+
|
|
135
|
+
append_line(f"""if isinstance({target_ref}, {known_types[dict]}):""")
|
|
136
|
+
append_line(f""" for {key_ref}, {value_ref} in {target_ref}.items():""")
|
|
137
|
+
|
|
138
|
+
indent += 2
|
|
139
|
+
validate_value(target_name=key_name, target_ref=key_ref, target_type=key_type)
|
|
140
|
+
validate_value(target_name=value_name, target_ref=value_ref, target_type=value_type)
|
|
141
|
+
indent -= 2
|
|
142
|
+
|
|
143
|
+
for field_name in cls.__annotations__:
|
|
144
|
+
validate_value(target_name=f'{{type(self).__name__}}.{field_name}', target_ref=f'self.{field_name}', target_type=field_type_hints[field_name])
|
|
145
|
+
|
|
146
|
+
if hasattr(cls, post_validate_name):
|
|
147
|
+
append_line(f"self.{post_validate_name}()")
|
|
148
|
+
|
|
149
|
+
if not lines:
|
|
150
|
+
return # nothing to validate (empty dataclass)
|
|
151
|
+
|
|
152
|
+
if '__init__' in cls.__dict__ and not hasattr(cls, method_name):
|
|
153
|
+
raise ValueError(f"{cls} must have a {method_name!r} method to override when invoked after the '__init__' method is created")
|
|
154
|
+
|
|
155
|
+
if any(hasattr(parent, method_name) for parent in cls.__mro__[1:]):
|
|
156
|
+
lines.insert(0, f' super({register_type(cls)}, self).{method_name}()')
|
|
157
|
+
|
|
158
|
+
lines.insert(0, f'def {method_name}(self):')
|
|
159
|
+
|
|
160
|
+
source = '\n'.join(lines) + '\n'
|
|
161
|
+
|
|
162
|
+
if _write_generated_code_to_disk:
|
|
163
|
+
tmp = tempfile.NamedTemporaryFile(mode='w+t', suffix=f'-{cls.__module__}.{cls.__name__}.py', delete=False, dir=_get_temporary_directory())
|
|
164
|
+
|
|
165
|
+
tmp.write(source)
|
|
166
|
+
tmp.flush()
|
|
167
|
+
|
|
168
|
+
filename = tmp.name
|
|
169
|
+
else:
|
|
170
|
+
filename = f'<string> generated for {cls}'
|
|
171
|
+
|
|
172
|
+
code = compile(source, filename, 'exec')
|
|
173
|
+
|
|
174
|
+
exec(code, exec_globals)
|
|
175
|
+
setattr(cls, method_name, exec_globals[method_name])
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@functools.lru_cache(maxsize=1)
|
|
179
|
+
def _get_temporary_directory() -> str:
|
|
180
|
+
"""Create a temporary directory and return its full path. The directory will be deleted when the process exits."""
|
|
181
|
+
temp_dir = tempfile.mkdtemp()
|
|
182
|
+
|
|
183
|
+
atexit.register(lambda: shutil.rmtree(temp_dir))
|
|
184
|
+
|
|
185
|
+
return temp_dir
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _get_allowed_types(target_type: type) -> tuple[type, ...]:
|
|
189
|
+
"""Return a tuple of types usable in instance checks for the given target_type."""
|
|
190
|
+
origin_type = t.get_origin(target_type)
|
|
191
|
+
|
|
192
|
+
if origin_type in _union_types:
|
|
193
|
+
allowed_types = tuple(set(itertools.chain.from_iterable(_get_allowed_types(arg) for arg in t.get_args(target_type))))
|
|
194
|
+
elif origin_type is t.Literal:
|
|
195
|
+
allowed_types = (str,) # DTFIX-FUTURE: support non-str literal types
|
|
196
|
+
elif origin_type:
|
|
197
|
+
allowed_types = (origin_type,)
|
|
198
|
+
else:
|
|
199
|
+
allowed_types = (target_type,)
|
|
200
|
+
|
|
201
|
+
return allowed_types
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _extract_type(target_type: type, of_type: type) -> type:
|
|
205
|
+
"""Return `of_type` from `target_type`, where `target_type` may be a union."""
|
|
206
|
+
origin_type = t.get_origin(target_type)
|
|
207
|
+
|
|
208
|
+
if origin_type is of_type: # pylint: disable=unidiomatic-typecheck
|
|
209
|
+
return target_type
|
|
210
|
+
|
|
211
|
+
if origin_type is t.Union or (_union_type and isinstance(target_type, _union_type)):
|
|
212
|
+
args = t.get_args(target_type)
|
|
213
|
+
extracted_types = [arg for arg in args if type(arg) is of_type or t.get_origin(arg) is of_type] # pylint: disable=unidiomatic-typecheck
|
|
214
|
+
(extracted_type,) = extracted_types
|
|
215
|
+
return extracted_type
|
|
216
|
+
|
|
217
|
+
raise NotImplementedError(f'{target_type} is not supported')
|