ansible-core 2.18.5rc1__py3-none-any.whl → 2.19.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ansible/_internal/__init__.py +53 -0
- ansible/_internal/_ansiballz.py +265 -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/_captured.py +128 -0
- ansible/_internal/_errors/_handler.py +91 -0
- ansible/_internal/_errors/_utils.py +310 -0
- ansible/_internal/_json/__init__.py +160 -0
- ansible/_internal/_json/_legacy_encoder.py +34 -0
- ansible/_internal/_json/_profiles/__init__.py +0 -0
- ansible/_internal/_json/_profiles/_cache_persistence.py +55 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
- ansible/_internal/_json/_profiles/_legacy.py +198 -0
- ansible/_internal/_locking.py +21 -0
- ansible/_internal/_plugins/__init__.py +0 -0
- ansible/_internal/_plugins/_cache.py +57 -0
- ansible/_internal/_task.py +78 -0
- ansible/_internal/_templating/__init__.py +10 -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 +588 -0
- ansible/_internal/_templating/_errors.py +28 -0
- ansible/_internal/_templating/_jinja_bits.py +1066 -0
- ansible/_internal/_templating/_jinja_common.py +332 -0
- ansible/_internal/_templating/_jinja_patches.py +44 -0
- ansible/_internal/_templating/_jinja_plugins.py +351 -0
- ansible/_internal/_templating/_lazy_containers.py +633 -0
- ansible/_internal/_templating/_marker_behaviors.py +103 -0
- ansible/_internal/_templating/_transform.py +63 -0
- ansible/_internal/_templating/_utils.py +107 -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 +62 -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 +18 -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 +153 -89
- ansible/cli/_ssh_askpass.py +47 -0
- ansible/cli/adhoc.py +14 -7
- ansible/cli/arguments/option_helpers.py +154 -7
- ansible/cli/config.py +43 -68
- ansible/cli/console.py +10 -8
- ansible/cli/doc.py +48 -46
- ansible/cli/galaxy.py +27 -20
- ansible/cli/inventory.py +28 -26
- ansible/cli/playbook.py +4 -12
- ansible/cli/pull.py +51 -11
- ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
- ansible/cli/vault.py +12 -11
- ansible/compat/__init__.py +2 -2
- ansible/config/base.yml +165 -108
- ansible/config/manager.py +52 -49
- ansible/constants.py +3 -4
- ansible/errors/__init__.py +277 -235
- ansible/executor/interpreter_discovery.py +28 -149
- ansible/executor/module_common.py +426 -493
- 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 +202 -151
- ansible/executor/powershell/become_wrapper.ps1 +89 -144
- ansible/executor/powershell/bootstrap_wrapper.ps1 +24 -9
- ansible/executor/powershell/coverage_wrapper.ps1 +82 -135
- ansible/executor/powershell/exec_wrapper.ps1 +462 -196
- ansible/executor/powershell/module_manifest.py +417 -265
- ansible/executor/powershell/module_wrapper.ps1 +169 -186
- 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 +136 -76
- ansible/executor/stats.py +5 -5
- ansible/executor/task_executor.py +237 -236
- ansible/executor/task_queue_manager.py +62 -38
- ansible/executor/task_result.py +21 -12
- ansible/galaxy/__init__.py +2 -2
- ansible/galaxy/api.py +22 -18
- ansible/galaxy/collection/__init__.py +1 -1
- ansible/galaxy/collection/concrete_artifact_manager.py +8 -11
- ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
- ansible/galaxy/dependency_resolution/providers.py +1 -1
- ansible/galaxy/dependency_resolution/reporters.py +81 -0
- ansible/galaxy/role.py +4 -8
- ansible/galaxy/token.py +28 -21
- ansible/inventory/data.py +47 -57
- ansible/inventory/group.py +44 -72
- ansible/inventory/helpers.py +9 -0
- ansible/inventory/host.py +32 -54
- ansible/inventory/manager.py +77 -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.py +133 -0
- ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
- ansible/module_utils/_internal/_dataclass_annotation_patch.py +64 -0
- ansible/module_utils/_internal/_dataclass_validation.py +217 -0
- ansible/module_utils/_internal/_datatag/__init__.py +928 -0
- ansible/module_utils/_internal/_datatag/_tags.py +38 -0
- ansible/module_utils/_internal/_debugging.py +31 -0
- ansible/module_utils/_internal/_errors.py +30 -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 +410 -0
- ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +31 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +35 -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 +50 -0
- ansible/module_utils/_internal/_patches/__init__.py +66 -0
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +55 -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_exec_context.py +49 -0
- ansible/module_utils/_internal/_testing.py +0 -0
- ansible/module_utils/_internal/_traceback.py +89 -0
- ansible/module_utils/ansible_release.py +2 -2
- ansible/module_utils/api.py +1 -2
- ansible/module_utils/basic.py +154 -120
- ansible/module_utils/common/_utils.py +24 -28
- ansible/module_utils/common/collections.py +1 -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/messages.py +108 -0
- ansible/module_utils/common/parameters.py +27 -24
- ansible/module_utils/common/process.py +2 -2
- ansible/module_utils/common/respawn.py +41 -19
- 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 +86 -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 +2 -2
- ansible/module_utils/csharp/Ansible.Basic.cs +14 -11
- ansible/module_utils/csharp/Ansible.Become.cs +1 -0
- ansible/module_utils/csharp/Ansible._Async.cs +517 -0
- ansible/module_utils/datatag.py +46 -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 +1 -1
- 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 +4 -4
- 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 +1 -2
- ansible/module_utils/facts/other/ohai.py +2 -3
- ansible/module_utils/facts/system/apparmor.py +1 -2
- ansible/module_utils/facts/system/caps.py +1 -1
- 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 +9 -8
- 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 +1 -2
- 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/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 +1 -1
- ansible/module_utils/service.py +18 -21
- ansible/module_utils/splitter.py +7 -7
- ansible/module_utils/testing.py +31 -0
- ansible/module_utils/urls.py +60 -31
- ansible/modules/add_host.py +4 -4
- ansible/modules/apt.py +60 -46
- ansible/modules/apt_key.py +19 -12
- ansible/modules/apt_repository.py +19 -16
- ansible/modules/assemble.py +6 -6
- ansible/modules/assert.py +4 -4
- ansible/modules/async_status.py +10 -12
- ansible/modules/async_wrapper.py +8 -3
- ansible/modules/blockinfile.py +6 -7
- ansible/modules/command.py +10 -17
- ansible/modules/copy.py +57 -144
- ansible/modules/cron.py +20 -15
- 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 +8 -10
- ansible/modules/fail.py +4 -4
- ansible/modules/fetch.py +4 -4
- ansible/modules/file.py +174 -133
- ansible/modules/find.py +19 -17
- ansible/modules/gather_facts.py +3 -3
- ansible/modules/get_url.py +59 -53
- ansible/modules/getent.py +7 -9
- ansible/modules/git.py +28 -25
- ansible/modules/group.py +6 -6
- ansible/modules/group_by.py +4 -4
- ansible/modules/hostname.py +13 -29
- 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 +10 -10
- ansible/modules/lineinfile.py +5 -5
- ansible/modules/meta.py +4 -4
- ansible/modules/mount_facts.py +2 -2
- ansible/modules/package.py +4 -4
- ansible/modules/package_facts.py +22 -10
- ansible/modules/pause.py +6 -6
- ansible/modules/ping.py +6 -6
- ansible/modules/pip.py +10 -11
- ansible/modules/raw.py +4 -4
- ansible/modules/reboot.py +6 -6
- ansible/modules/replace.py +9 -13
- ansible/modules/rpm_key.py +7 -8
- ansible/modules/script.py +4 -4
- ansible/modules/service.py +7 -8
- 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 +6 -6
- ansible/modules/stat.py +9 -23
- ansible/modules/subversion.py +15 -15
- ansible/modules/systemd.py +6 -6
- ansible/modules/systemd_service.py +6 -6
- ansible/modules/sysvinit.py +6 -6
- ansible/modules/tempfile.py +5 -6
- ansible/modules/template.py +6 -6
- ansible/modules/unarchive.py +32 -11
- ansible/modules/uri.py +33 -26
- ansible/modules/user.py +53 -34
- ansible/modules/validate_argument_spec.py +10 -7
- ansible/modules/wait_for.py +32 -27
- ansible/modules/wait_for_connection.py +6 -6
- ansible/modules/yum_repository.py +6 -6
- ansible/parsing/ajson.py +14 -32
- ansible/parsing/dataloader.py +99 -54
- ansible/parsing/mod_args.py +28 -44
- 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/yaml.py +32 -61
- ansible/parsing/vault/__init__.py +319 -87
- 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 +36 -339
- ansible/playbook/__init__.py +1 -1
- ansible/playbook/attribute.py +8 -3
- ansible/playbook/base.py +182 -132
- 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 +6 -15
- ansible/playbook/loop_control.py +2 -2
- ansible/playbook/play.py +85 -44
- ansible/playbook/play_context.py +12 -17
- ansible/playbook/playbook_include.py +14 -15
- ansible/playbook/role/__init__.py +24 -26
- 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 +13 -8
- ansible/playbook/task.py +188 -118
- ansible/playbook/task_include.py +5 -5
- ansible/plugins/__init__.py +68 -21
- ansible/plugins/action/__init__.py +209 -176
- ansible/plugins/action/add_host.py +1 -1
- ansible/plugins/action/assemble.py +1 -1
- ansible/plugins/action/assert.py +54 -66
- ansible/plugins/action/copy.py +7 -11
- 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 +4 -5
- ansible/plugins/action/gather_facts.py +7 -6
- ansible/plugins/action/group_by.py +1 -1
- ansible/plugins/action/include_vars.py +10 -11
- ansible/plugins/action/package.py +3 -6
- ansible/plugins/action/pause.py +2 -2
- ansible/plugins/action/script.py +15 -8
- ansible/plugins/action/service.py +6 -11
- ansible/plugins/action/set_fact.py +3 -12
- ansible/plugins/action/set_stats.py +3 -8
- ansible/plugins/action/template.py +35 -59
- ansible/plugins/action/unarchive.py +1 -1
- 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 +35 -44
- 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 +141 -123
- ansible/plugins/callback/default.py +30 -23
- ansible/plugins/callback/junit.py +28 -24
- ansible/plugins/callback/minimal.py +17 -14
- ansible/plugins/callback/oneline.py +13 -7
- ansible/plugins/callback/tree.py +10 -6
- ansible/plugins/connection/__init__.py +47 -34
- ansible/plugins/connection/local.py +150 -54
- ansible/plugins/connection/paramiko_ssh.py +21 -18
- ansible/plugins/connection/psrp.py +76 -165
- ansible/plugins/connection/ssh.py +301 -78
- ansible/plugins/connection/winrm.py +58 -140
- 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 +6 -2
- 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 +218 -108
- ansible/plugins/filter/encryption.py +32 -32
- 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/regex_search.yml +1 -4
- ansible/plugins/filter/split.yml +1 -1
- ansible/plugins/filter/to_nice_yaml.yml +0 -4
- 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 +97 -77
- 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 +189 -119
- ansible/plugins/inventory/toml.py +16 -126
- ansible/plugins/inventory/yaml.py +10 -8
- ansible/plugins/list.py +3 -3
- ansible/plugins/loader.py +197 -82
- ansible/plugins/lookup/__init__.py +21 -4
- ansible/plugins/lookup/config.py +21 -35
- ansible/plugins/lookup/csvfile.py +3 -2
- 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 +86 -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 +42 -36
- ansible/plugins/lookup/together.py +0 -12
- ansible/plugins/lookup/unvault.py +1 -5
- ansible/plugins/lookup/url.py +2 -8
- ansible/plugins/lookup/vars.py +16 -24
- ansible/plugins/shell/__init__.py +2 -2
- ansible/plugins/shell/cmd.py +2 -2
- ansible/plugins/shell/powershell.py +39 -22
- ansible/plugins/shell/sh.py +3 -2
- ansible/plugins/strategy/__init__.py +94 -113
- ansible/plugins/strategy/debug.py +2 -2
- ansible/plugins/strategy/free.py +13 -28
- ansible/plugins/strategy/host_pinned.py +2 -2
- ansible/plugins/strategy/linear.py +31 -33
- ansible/plugins/terminal/__init__.py +4 -4
- ansible/plugins/test/__init__.py +7 -2
- ansible/plugins/test/core.py +54 -20
- ansible/plugins/test/files.py +1 -1
- ansible/plugins/test/mathstuff.py +3 -3
- ansible/plugins/test/uri.py +3 -3
- ansible/plugins/vars/host_group_vars.py +7 -14
- ansible/release.py +2 -2
- ansible/template/__init__.py +368 -944
- ansible/utils/__init__.py +0 -18
- ansible/utils/_ssh_agent.py +657 -0
- ansible/utils/collection_loader/__init__.py +52 -5
- ansible/utils/collection_loader/_collection_config.py +5 -6
- ansible/utils/collection_loader/_collection_finder.py +79 -93
- ansible/utils/collection_loader/_collection_meta.py +13 -8
- ansible/utils/display.py +428 -58
- ansible/utils/encrypt.py +27 -19
- ansible/utils/fqcn.py +2 -2
- ansible/utils/hashing.py +2 -2
- ansible/utils/helpers.py +2 -2
- ansible/utils/listify.py +8 -8
- ansible/utils/lock.py +2 -2
- ansible/utils/path.py +4 -4
- ansible/utils/plugin_docs.py +14 -13
- ansible/utils/sentinel.py +4 -62
- ansible/utils/singleton.py +2 -0
- ansible/utils/ssh_functions.py +1 -1
- ansible/utils/unsafe_proxy.py +23 -332
- ansible/utils/vars.py +28 -8
- ansible/utils/version.py +2 -2
- ansible/vars/clean.py +4 -4
- ansible/vars/hostvars.py +60 -90
- ansible/vars/manager.py +205 -264
- ansible/vars/reserved.py +8 -9
- ansible_core-2.19.0b1.dist-info/BSD-3-Clause.txt +28 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/METADATA +5 -4
- ansible_core-2.19.0b1.dist-info/RECORD +1070 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/WHEEL +1 -1
- 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 +1 -1
- ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- 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 +1 -0
- ansible_test/_internal/ansible_util.py +2 -0
- 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 -0
- 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 +1 -0
- 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 +2 -1
- 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 +4 -0
- 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 +2 -1
- 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 +1 -0
- 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 +16 -1
- 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 +1 -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 +24 -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 +1 -0
- ansible_test/_internal/commands/units/__init__.py +1 -0
- 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 +2 -0
- 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/delegation.py +1 -0
- 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 +1 -0
- ansible_test/_internal/http.py +1 -0
- ansible_test/_internal/init.py +1 -0
- ansible_test/_internal/inventory.py +35 -3
- ansible_test/_internal/io.py +1 -0
- ansible_test/_internal/metadata.py +1 -0
- ansible_test/_internal/payload.py +1 -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 +1 -0
- ansible_test/_internal/pypi_proxy.py +6 -5
- ansible_test/_internal/python_requirements.py +1 -0
- ansible_test/_internal/ssh.py +1 -0
- ansible_test/_internal/target.py +1 -0
- ansible_test/_internal/test.py +3 -2
- ansible_test/_internal/thread.py +1 -0
- ansible_test/_internal/timeout.py +1 -0
- ansible_test/_internal/util.py +1 -0
- ansible_test/_internal/util_common.py +5 -2
- 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 +7 -5
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +7 -5
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +7 -5
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +3 -5
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +7 -7
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -13
- 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/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 +35 -27
- 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/utils/jsonify.py +0 -36
- 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.5rc1.dist-info/RECORD +0 -992
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/Apache-License.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/COPYING +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/MIT-license.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/PSF-license.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/simplified_bsd.txt +0 -0
- {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,633 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import copy
|
4
|
+
import dataclasses
|
5
|
+
import functools
|
6
|
+
import types
|
7
|
+
import typing as t
|
8
|
+
|
9
|
+
from jinja2.environment import TemplateModule
|
10
|
+
|
11
|
+
from ansible.module_utils._internal._datatag import (
|
12
|
+
AnsibleTagHelper,
|
13
|
+
AnsibleTaggedObject,
|
14
|
+
_AnsibleTaggedDict,
|
15
|
+
_AnsibleTaggedList,
|
16
|
+
_AnsibleTaggedTuple,
|
17
|
+
_NO_INSTANCE_STORAGE,
|
18
|
+
_try_get_internal_tags_mapping,
|
19
|
+
)
|
20
|
+
|
21
|
+
from ansible.utils.sentinel import Sentinel
|
22
|
+
from ansible.errors import AnsibleVariableTypeError
|
23
|
+
from ansible._internal._errors._handler import Skippable
|
24
|
+
from ansible.vars.hostvars import HostVarsVars, HostVars
|
25
|
+
|
26
|
+
from ._access import AnsibleAccessContext
|
27
|
+
from ._jinja_common import Marker, _TemplateConfig
|
28
|
+
from ._utils import TemplateContext, PASS_THROUGH_SCALAR_VAR_TYPES, LazyOptions
|
29
|
+
|
30
|
+
if t.TYPE_CHECKING:
|
31
|
+
from ._engine import TemplateEngine
|
32
|
+
|
33
|
+
_KNOWN_TYPES: t.Final[set[type]] = (
|
34
|
+
{
|
35
|
+
HostVars, # example: hostvars
|
36
|
+
HostVarsVars, # example: hostvars.localhost | select
|
37
|
+
type, # example: range(20) | list # triggered on retrieval of `range` type from globals
|
38
|
+
range, # example: range(20) | list # triggered when returning a `range` instance from a call
|
39
|
+
types.FunctionType, # example: undef() | default("blah")
|
40
|
+
types.MethodType, # example: ansible_facts.get | type_debug
|
41
|
+
functools.partial,
|
42
|
+
type(''.startswith), # example: inventory_hostname.upper | type_debug # using `startswith` to resolve `builtin_function_or_method`
|
43
|
+
TemplateModule, # example: '{% import "importme.j2" as im %}{{ im | type_debug }}'
|
44
|
+
}
|
45
|
+
| set(PASS_THROUGH_SCALAR_VAR_TYPES)
|
46
|
+
| set(Marker.concrete_subclasses)
|
47
|
+
)
|
48
|
+
"""
|
49
|
+
These types are known to the templating system.
|
50
|
+
In addition to the statically defined types, additional types will be added at runtime.
|
51
|
+
When enabled in config, this set will be used to determine if an encountered type should trigger a warning or error.
|
52
|
+
"""
|
53
|
+
|
54
|
+
|
55
|
+
def register_known_types(*args: type) -> None:
|
56
|
+
"""Register a type with the template engine so it will not trigger warnings or errors when encountered."""
|
57
|
+
_KNOWN_TYPES.update(args)
|
58
|
+
|
59
|
+
|
60
|
+
class UnsupportedConstructionMethodError(RuntimeError):
|
61
|
+
"""Error raised when attempting to construct a lazy container with unsupported arguments."""
|
62
|
+
|
63
|
+
def __init__(self):
|
64
|
+
super().__init__("Direct construction of lazy containers is not supported.")
|
65
|
+
|
66
|
+
|
67
|
+
@t.final
|
68
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
69
|
+
class _LazyValue:
|
70
|
+
"""Wrapper around values to indicate lazy behavior has not yet been applied."""
|
71
|
+
|
72
|
+
value: t.Any
|
73
|
+
|
74
|
+
|
75
|
+
@t.final
|
76
|
+
@dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
|
77
|
+
class _LazyValueSource:
|
78
|
+
"""Intermediate value source for lazy-eligible collection copy operations."""
|
79
|
+
|
80
|
+
source: t.Iterable
|
81
|
+
templar: TemplateEngine
|
82
|
+
lazy_options: LazyOptions
|
83
|
+
|
84
|
+
|
85
|
+
@t.final
|
86
|
+
class _NoKeySentinel(Sentinel):
|
87
|
+
"""Sentinel used to indicate a requested key was not found."""
|
88
|
+
|
89
|
+
|
90
|
+
# There are several operations performed by lazy containers, with some variation between types.
|
91
|
+
#
|
92
|
+
# Columns: D=dict, L=list, T=tuple
|
93
|
+
# Cells: l=lazy (upon access), n=non-lazy (__init__/__new__)
|
94
|
+
#
|
95
|
+
# D L T Feature Description
|
96
|
+
# - - - ----------- ---------------------------------------------------------------
|
97
|
+
# l l n propagation when container items which are containers become lazy instances
|
98
|
+
# l l n transform when transforms are applied to container items
|
99
|
+
# l l n templating when templating is performed on container items
|
100
|
+
# l l l access when access calls are performed on container items
|
101
|
+
|
102
|
+
|
103
|
+
class _AnsibleLazyTemplateMixin:
|
104
|
+
__slots__ = _NO_INSTANCE_STORAGE
|
105
|
+
|
106
|
+
_dispatch_types: t.ClassVar[dict[type, type[_AnsibleLazyTemplateMixin]]] = {} # populated by __init_subclass__
|
107
|
+
_container_types: t.ClassVar[set[type]] = set() # populated by __init_subclass__
|
108
|
+
|
109
|
+
_native_type: t.ClassVar[type] # from AnsibleTaggedObject
|
110
|
+
|
111
|
+
_SLOTS: t.Final = (
|
112
|
+
'_templar',
|
113
|
+
'_lazy_options',
|
114
|
+
)
|
115
|
+
|
116
|
+
_templar: TemplateEngine
|
117
|
+
_lazy_options: LazyOptions
|
118
|
+
|
119
|
+
def __init_subclass__(cls, **kwargs) -> None:
|
120
|
+
tagged_type = cls.__mro__[1]
|
121
|
+
native_type = tagged_type.__mro__[1]
|
122
|
+
|
123
|
+
for check_type in (tagged_type, native_type):
|
124
|
+
if conflicting_type := cls._dispatch_types.get(check_type):
|
125
|
+
raise TypeError(f"Lazy mixin {cls.__name__!r} type {check_type.__name__!r} conflicts with {conflicting_type.__name__!r}.")
|
126
|
+
|
127
|
+
cls._dispatch_types[native_type] = cls
|
128
|
+
cls._dispatch_types[tagged_type] = cls
|
129
|
+
cls._container_types.add(native_type)
|
130
|
+
cls._empty_tags_as_native = False # never revert to the native type when no tags remain
|
131
|
+
|
132
|
+
register_known_types(cls)
|
133
|
+
|
134
|
+
def __init__(self, contents: t.Iterable | _LazyValueSource) -> None:
|
135
|
+
if isinstance(contents, _LazyValueSource):
|
136
|
+
self._templar = contents.templar
|
137
|
+
self._lazy_options = contents.lazy_options
|
138
|
+
elif isinstance(contents, _AnsibleLazyTemplateMixin):
|
139
|
+
self._templar = contents._templar
|
140
|
+
self._lazy_options = contents._lazy_options
|
141
|
+
else:
|
142
|
+
raise UnsupportedConstructionMethodError()
|
143
|
+
|
144
|
+
def __reduce_ex__(self, protocol):
|
145
|
+
raise NotImplementedError("Pickling of Ansible lazy objects is not permitted.")
|
146
|
+
|
147
|
+
@staticmethod
|
148
|
+
def _try_create(item: t.Any, lazy_options: LazyOptions = LazyOptions.DEFAULT) -> t.Any:
|
149
|
+
"""
|
150
|
+
If `item` is a container type which supports lazy access and/or templating, return a lazy wrapped version -- otherwise return it as-is.
|
151
|
+
When returning as-is, a warning or error may be generated for unknown types.
|
152
|
+
The `lazy_options.skip_templates` argument should be set to `True` when `item` is sourced from a plugin instead of Ansible variable storage.
|
153
|
+
This provides backwards compatibility and reduces lazy overhead, as plugins do not normally introduce templates.
|
154
|
+
If a plugin needs to introduce templates, the plugin is responsible for invoking the templar and returning the result.
|
155
|
+
"""
|
156
|
+
item_type = type(item)
|
157
|
+
|
158
|
+
# Try to use exact type match first to determine which wrapper (if any) to apply; isinstance checks
|
159
|
+
# are extremely expensive, so try to avoid them for our commonly-supported types.
|
160
|
+
if (dispatcher := _AnsibleLazyTemplateMixin._dispatch_types.get(item_type)) is not None:
|
161
|
+
# Create a generator that yields the elements of `item` wrapped in a `_LazyValue` wrapper.
|
162
|
+
# The wrapper is used to signal to the lazy container that the value must be processed before being returned.
|
163
|
+
# Values added to the lazy container later through other means will be returned as-is, without any special processing.
|
164
|
+
lazy_values = dispatcher._lazy_values(item, lazy_options)
|
165
|
+
tags_mapping = _try_get_internal_tags_mapping(item)
|
166
|
+
value = t.cast(AnsibleTaggedObject, dispatcher)._instance_factory(lazy_values, tags_mapping)
|
167
|
+
|
168
|
+
return value
|
169
|
+
|
170
|
+
with Skippable, _TemplateConfig.unknown_type_encountered_handler.handle(AnsibleVariableTypeError, skip_on_ignore=True):
|
171
|
+
if item_type not in _KNOWN_TYPES:
|
172
|
+
raise AnsibleVariableTypeError(
|
173
|
+
message=f"Encountered unknown type {item_type.__name__!r} during template operation.",
|
174
|
+
help_text="Use supported types to avoid unexpected behavior.",
|
175
|
+
obj=TemplateContext.current().template_value,
|
176
|
+
)
|
177
|
+
|
178
|
+
return item
|
179
|
+
|
180
|
+
def _is_not_lazy_combine_candidate(self, other: object) -> bool:
|
181
|
+
"""Returns `True` if `other` cannot be lazily combined with the current instance due to differing templar/options, otherwise returns `False`."""
|
182
|
+
return isinstance(other, _AnsibleLazyTemplateMixin) and (self._templar is not other._templar or self._lazy_options != other._lazy_options)
|
183
|
+
|
184
|
+
def _non_lazy_copy(self) -> t.Collection:
|
185
|
+
"""
|
186
|
+
Return a non-lazy copy of this collection.
|
187
|
+
Any remaining lazy wrapped values will be unwrapped without further processing.
|
188
|
+
Tags on this instance will be preserved on the returned copy.
|
189
|
+
"""
|
190
|
+
raise NotImplementedError() # pragma: nocover
|
191
|
+
|
192
|
+
@staticmethod
|
193
|
+
def _lazy_values(values: t.Any, lazy_options: LazyOptions) -> _LazyValueSource:
|
194
|
+
"""
|
195
|
+
Return an iterable that wraps each of the given elements in a lazy wrapper.
|
196
|
+
Only elements wrapped this way will receive lazy processing when retrieved from the collection.
|
197
|
+
"""
|
198
|
+
# DTFIX-RELEASE: check relative performance of method-local vs stored generator expressions on implementations of this method
|
199
|
+
raise NotImplementedError() # pragma: nocover
|
200
|
+
|
201
|
+
def _proxy_or_render_lazy_value(self, key: t.Any, value: t.Any) -> t.Any:
|
202
|
+
"""
|
203
|
+
Ensure that the value is lazy-proxied or rendered, and if a key is provided, replace the original value with the result.
|
204
|
+
"""
|
205
|
+
if type(value) is not _LazyValue: # pylint: disable=unidiomatic-typecheck
|
206
|
+
if self._lazy_options.access:
|
207
|
+
AnsibleAccessContext.current().access(value)
|
208
|
+
|
209
|
+
return value
|
210
|
+
|
211
|
+
original_value = value.value
|
212
|
+
|
213
|
+
if self._lazy_options.access:
|
214
|
+
AnsibleAccessContext.current().access(original_value)
|
215
|
+
|
216
|
+
new_value = self._templar.template(original_value, lazy_options=self._lazy_options)
|
217
|
+
|
218
|
+
if new_value is not original_value and self._lazy_options.access:
|
219
|
+
AnsibleAccessContext.current().access(new_value)
|
220
|
+
|
221
|
+
if key is not _NoKeySentinel:
|
222
|
+
self._native_type.__setitem__(self, key, new_value) # type: ignore # pylint: disable=unnecessary-dunder-call
|
223
|
+
|
224
|
+
return new_value
|
225
|
+
|
226
|
+
|
227
|
+
@t.final # consumers of lazy collections rely heavily on the concrete types being final
|
228
|
+
class _AnsibleLazyTemplateDict(_AnsibleTaggedDict, _AnsibleLazyTemplateMixin):
|
229
|
+
__slots__ = _AnsibleLazyTemplateMixin._SLOTS
|
230
|
+
|
231
|
+
def __init__(self, contents: t.Iterable | _LazyValueSource, /, **kwargs) -> None:
|
232
|
+
_AnsibleLazyTemplateMixin.__init__(self, contents)
|
233
|
+
|
234
|
+
if isinstance(contents, _AnsibleLazyTemplateDict):
|
235
|
+
super().__init__(dict.items(contents), **kwargs)
|
236
|
+
elif isinstance(contents, _LazyValueSource):
|
237
|
+
super().__init__(contents.source, **kwargs)
|
238
|
+
else:
|
239
|
+
raise UnsupportedConstructionMethodError()
|
240
|
+
|
241
|
+
def get(self, key: t.Any, default: t.Any = None) -> t.Any:
|
242
|
+
if (value := super().get(key, _NoKeySentinel)) is _NoKeySentinel:
|
243
|
+
return default
|
244
|
+
|
245
|
+
return self._proxy_or_render_lazy_value(key, value)
|
246
|
+
|
247
|
+
def __getitem__(self, key: t.Any, /) -> t.Any:
|
248
|
+
return self._proxy_or_render_lazy_value(key, super().__getitem__(key))
|
249
|
+
|
250
|
+
def __str__(self):
|
251
|
+
return str(self.copy()._native_copy()) # inefficient, but avoids mutating the current instance (to make debugging practical)
|
252
|
+
|
253
|
+
def __repr__(self):
|
254
|
+
return repr(self.copy()._native_copy()) # inefficient, but avoids mutating the current instance (to make debugging practical)
|
255
|
+
|
256
|
+
def __iter__(self):
|
257
|
+
# We're using the base implementation, but must override `__iter__` to skip `dict` fast-path copy, which would bypass lazy behavior.
|
258
|
+
# See: https://github.com/python/cpython/blob/ffcc450a9b8b6927549b501eff7ac14abc238448/Objects/dictobject.c#L3861-L3864
|
259
|
+
return super().__iter__()
|
260
|
+
|
261
|
+
def setdefault(self, key, default=None, /) -> t.Any:
|
262
|
+
if (value := self.get(key, _NoKeySentinel)) is not _NoKeySentinel:
|
263
|
+
return value
|
264
|
+
|
265
|
+
super().__setitem__(key, default)
|
266
|
+
|
267
|
+
return default
|
268
|
+
|
269
|
+
def items(self):
|
270
|
+
for key, value in super().items():
|
271
|
+
yield key, self._proxy_or_render_lazy_value(key, value)
|
272
|
+
|
273
|
+
def values(self):
|
274
|
+
for _key, value in self.items():
|
275
|
+
yield value
|
276
|
+
|
277
|
+
def pop(self, key, default=_NoKeySentinel, /) -> t.Any:
|
278
|
+
if (value := super().get(key, _NoKeySentinel)) is _NoKeySentinel:
|
279
|
+
if default is _NoKeySentinel:
|
280
|
+
raise KeyError(key)
|
281
|
+
|
282
|
+
return default
|
283
|
+
|
284
|
+
value = self._proxy_or_render_lazy_value(_NoKeySentinel, value)
|
285
|
+
|
286
|
+
del self[key]
|
287
|
+
|
288
|
+
return value
|
289
|
+
|
290
|
+
def popitem(self) -> t.Any:
|
291
|
+
try:
|
292
|
+
key = next(reversed(self))
|
293
|
+
except StopIteration:
|
294
|
+
raise KeyError("popitem(): dictionary is empty")
|
295
|
+
|
296
|
+
value = self._proxy_or_render_lazy_value(_NoKeySentinel, self[key])
|
297
|
+
|
298
|
+
del self[key]
|
299
|
+
|
300
|
+
return key, value
|
301
|
+
|
302
|
+
def _native_copy(self) -> dict:
|
303
|
+
return dict(self.items())
|
304
|
+
|
305
|
+
@staticmethod
|
306
|
+
def _item_source(value: dict) -> dict | _LazyValueSource:
|
307
|
+
if isinstance(value, _AnsibleLazyTemplateDict):
|
308
|
+
return _LazyValueSource(source=dict.items(value), templar=value._templar, lazy_options=value._lazy_options)
|
309
|
+
|
310
|
+
return value
|
311
|
+
|
312
|
+
def _yield_non_lazy_dict_items(self) -> t.Iterator[tuple[str, t.Any]]:
|
313
|
+
"""
|
314
|
+
Delegate to the base collection items iterator to yield the raw contents.
|
315
|
+
As of Python 3.13, generator functions are significantly faster than inline generator expressions.
|
316
|
+
"""
|
317
|
+
for k, v in dict.items(self):
|
318
|
+
yield k, v.value if type(v) is _LazyValue else v # pylint: disable=unidiomatic-typecheck
|
319
|
+
|
320
|
+
def _non_lazy_copy(self) -> dict:
|
321
|
+
return AnsibleTagHelper.tag_copy(self, self._yield_non_lazy_dict_items(), value_type=dict)
|
322
|
+
|
323
|
+
@staticmethod
|
324
|
+
def _lazy_values(values: dict, lazy_options: LazyOptions) -> _LazyValueSource:
|
325
|
+
return _LazyValueSource(source=((k, _LazyValue(v)) for k, v in values.items()), templar=TemplateContext.current().templar, lazy_options=lazy_options)
|
326
|
+
|
327
|
+
@staticmethod
|
328
|
+
def _proxy_or_render_other(other: t.Any | None) -> None:
|
329
|
+
"""Call `_proxy_or_render_lazy_values` if `other` is a lazy dict. Used internally by comparison methods."""
|
330
|
+
if type(other) is _AnsibleLazyTemplateDict: # pylint: disable=unidiomatic-typecheck
|
331
|
+
other._proxy_or_render_lazy_values()
|
332
|
+
|
333
|
+
def _proxy_or_render_lazy_values(self) -> None:
|
334
|
+
"""Ensure all `_LazyValue` wrapped values have been processed."""
|
335
|
+
for _unused in self.values():
|
336
|
+
pass
|
337
|
+
|
338
|
+
def __eq__(self, other):
|
339
|
+
self._proxy_or_render_lazy_values()
|
340
|
+
self._proxy_or_render_other(other)
|
341
|
+
return super().__eq__(other)
|
342
|
+
|
343
|
+
def __ne__(self, other):
|
344
|
+
self._proxy_or_render_lazy_values()
|
345
|
+
self._proxy_or_render_other(other)
|
346
|
+
return super().__ne__(other)
|
347
|
+
|
348
|
+
def __or__(self, other):
|
349
|
+
# DTFIX-RELEASE: support preservation of laziness when possible like we do for list
|
350
|
+
# Both sides end up going through _proxy_or_render_lazy_value, so there's no Templar preservation needed.
|
351
|
+
# In the future this could be made more lazy when both Templar instances are the same, or if per-value Templar tracking was used.
|
352
|
+
return super().__or__(other)
|
353
|
+
|
354
|
+
def __ror__(self, other):
|
355
|
+
# DTFIX-RELEASE: support preservation of laziness when possible like we do for list
|
356
|
+
# Both sides end up going through _proxy_or_render_lazy_value, so there's no Templar preservation needed.
|
357
|
+
# In the future this could be made more lazy when both Templar instances are the same, or if per-value Templar tracking was used.
|
358
|
+
return super().__ror__(other)
|
359
|
+
|
360
|
+
def __deepcopy__(self, memo):
|
361
|
+
return _AnsibleLazyTemplateDict(
|
362
|
+
_LazyValueSource(
|
363
|
+
source=((copy.deepcopy(k), copy.deepcopy(v)) for k, v in super().items()),
|
364
|
+
templar=copy.deepcopy(self._templar),
|
365
|
+
lazy_options=copy.deepcopy(self._lazy_options),
|
366
|
+
)
|
367
|
+
)
|
368
|
+
|
369
|
+
|
370
|
+
@t.final # consumers of lazy collections rely heavily on the concrete types being final
|
371
|
+
class _AnsibleLazyTemplateList(_AnsibleTaggedList, _AnsibleLazyTemplateMixin):
|
372
|
+
__slots__ = _AnsibleLazyTemplateMixin._SLOTS
|
373
|
+
|
374
|
+
def __init__(self, contents: t.Iterable | _LazyValueSource, /) -> None:
|
375
|
+
_AnsibleLazyTemplateMixin.__init__(self, contents)
|
376
|
+
|
377
|
+
if isinstance(contents, _AnsibleLazyTemplateList):
|
378
|
+
super().__init__(list.__iter__(contents))
|
379
|
+
elif isinstance(contents, _LazyValueSource):
|
380
|
+
super().__init__(contents.source)
|
381
|
+
else:
|
382
|
+
raise UnsupportedConstructionMethodError()
|
383
|
+
|
384
|
+
def __getitem__(self, key: t.SupportsIndex | slice, /) -> t.Any:
|
385
|
+
if type(key) is slice: # pylint: disable=unidiomatic-typecheck
|
386
|
+
return _AnsibleLazyTemplateList(_LazyValueSource(source=super().__getitem__(key), templar=self._templar, lazy_options=self._lazy_options))
|
387
|
+
|
388
|
+
return self._proxy_or_render_lazy_value(key, super().__getitem__(key))
|
389
|
+
|
390
|
+
def __iter__(self):
|
391
|
+
for key, value in enumerate(super().__iter__()):
|
392
|
+
yield self._proxy_or_render_lazy_value(key, value)
|
393
|
+
|
394
|
+
def pop(self, idx: t.SupportsIndex = -1, /) -> t.Any:
|
395
|
+
if not self:
|
396
|
+
raise IndexError('pop from empty list')
|
397
|
+
|
398
|
+
try:
|
399
|
+
value = self[idx]
|
400
|
+
except IndexError:
|
401
|
+
raise IndexError('pop index out of range')
|
402
|
+
|
403
|
+
value = self._proxy_or_render_lazy_value(_NoKeySentinel, value)
|
404
|
+
|
405
|
+
del self[idx]
|
406
|
+
|
407
|
+
return value
|
408
|
+
|
409
|
+
def __str__(self):
|
410
|
+
return str(self.copy()._native_copy()) # inefficient, but avoids mutating the current instance (to make debugging practical)
|
411
|
+
|
412
|
+
def __repr__(self):
|
413
|
+
return repr(self.copy()._native_copy()) # inefficient, but avoids mutating the current instance (to make debugging practical)
|
414
|
+
|
415
|
+
@staticmethod
|
416
|
+
def _item_source(value: list) -> list | _LazyValueSource:
|
417
|
+
if isinstance(value, _AnsibleLazyTemplateList):
|
418
|
+
return _LazyValueSource(source=list.__iter__(value), templar=value._templar, lazy_options=value._lazy_options)
|
419
|
+
|
420
|
+
return value
|
421
|
+
|
422
|
+
def _yield_non_lazy_list_items(self):
|
423
|
+
"""
|
424
|
+
Delegate to the base collection iterator to yield the raw contents.
|
425
|
+
As of Python 3.13, generator functions are significantly faster than inline generator expressions.
|
426
|
+
"""
|
427
|
+
for v in list.__iter__(self):
|
428
|
+
yield v.value if type(v) is _LazyValue else v # pylint: disable=unidiomatic-typecheck
|
429
|
+
|
430
|
+
def _non_lazy_copy(self) -> list:
|
431
|
+
return AnsibleTagHelper.tag_copy(self, self._yield_non_lazy_list_items(), value_type=list)
|
432
|
+
|
433
|
+
@staticmethod
|
434
|
+
def _lazy_values(values: list, lazy_options: LazyOptions) -> _LazyValueSource:
|
435
|
+
return _LazyValueSource(source=(_LazyValue(v) for v in values), templar=TemplateContext.current().templar, lazy_options=lazy_options)
|
436
|
+
|
437
|
+
@staticmethod
|
438
|
+
def _proxy_or_render_other(other: t.Any | None) -> None:
|
439
|
+
"""Call `_proxy_or_render_lazy_values` if `other` is a lazy list. Used internally by comparison methods."""
|
440
|
+
if type(other) is _AnsibleLazyTemplateList: # pylint: disable=unidiomatic-typecheck
|
441
|
+
other._proxy_or_render_lazy_values()
|
442
|
+
|
443
|
+
def _proxy_or_render_lazy_values(self) -> None:
|
444
|
+
"""Ensure all `_LazyValue` wrapped values have been processed."""
|
445
|
+
for _unused in self:
|
446
|
+
pass
|
447
|
+
|
448
|
+
def __eq__(self, other):
|
449
|
+
self._proxy_or_render_lazy_values()
|
450
|
+
self._proxy_or_render_other(other)
|
451
|
+
return super().__eq__(other)
|
452
|
+
|
453
|
+
def __ne__(self, other):
|
454
|
+
self._proxy_or_render_lazy_values()
|
455
|
+
self._proxy_or_render_other(other)
|
456
|
+
return super().__ne__(other)
|
457
|
+
|
458
|
+
def __gt__(self, other):
|
459
|
+
self._proxy_or_render_lazy_values()
|
460
|
+
self._proxy_or_render_other(other)
|
461
|
+
return super().__gt__(other)
|
462
|
+
|
463
|
+
def __ge__(self, other):
|
464
|
+
self._proxy_or_render_lazy_values()
|
465
|
+
self._proxy_or_render_other(other)
|
466
|
+
return super().__ge__(other)
|
467
|
+
|
468
|
+
def __lt__(self, other):
|
469
|
+
self._proxy_or_render_lazy_values()
|
470
|
+
self._proxy_or_render_other(other)
|
471
|
+
return super().__lt__(other)
|
472
|
+
|
473
|
+
def __le__(self, other):
|
474
|
+
self._proxy_or_render_lazy_values()
|
475
|
+
self._proxy_or_render_other(other)
|
476
|
+
return super().__le__(other)
|
477
|
+
|
478
|
+
def __contains__(self, item):
|
479
|
+
self._proxy_or_render_lazy_values()
|
480
|
+
return super().__contains__(item)
|
481
|
+
|
482
|
+
def __reversed__(self):
|
483
|
+
for idx in range(self.__len__() - 1, -1, -1):
|
484
|
+
yield self[idx]
|
485
|
+
|
486
|
+
def __add__(self, other):
|
487
|
+
if self._is_not_lazy_combine_candidate(other):
|
488
|
+
# When other is lazy with a different templar/options, it cannot be lazily combined with self and a plain list must be returned.
|
489
|
+
# If other is a list, de-lazify both, otherwise just let the operation fail.
|
490
|
+
|
491
|
+
if isinstance(other, _AnsibleLazyTemplateList):
|
492
|
+
self._proxy_or_render_lazy_values()
|
493
|
+
other._proxy_or_render_lazy_values()
|
494
|
+
|
495
|
+
return super().__add__(other)
|
496
|
+
|
497
|
+
# For all other cases, the new list inherits our templar and all values stay lazy.
|
498
|
+
# We use list.__add__ to avoid implementing all its error behavior.
|
499
|
+
return _AnsibleLazyTemplateList(_LazyValueSource(source=super().__add__(other), templar=self._templar, lazy_options=self._lazy_options))
|
500
|
+
|
501
|
+
def __radd__(self, other):
|
502
|
+
if not (other_add := getattr(other, '__add__', None)):
|
503
|
+
raise TypeError(f'unsupported operand type(s) for +: {type(other).__name__!r} and {type(self).__name__!r}') from None
|
504
|
+
|
505
|
+
return _AnsibleLazyTemplateList(_LazyValueSource(source=other_add(self), templar=self._templar, lazy_options=self._lazy_options))
|
506
|
+
|
507
|
+
def __mul__(self, other):
|
508
|
+
return _AnsibleLazyTemplateList(_LazyValueSource(source=super().__mul__(other), templar=self._templar, lazy_options=self._lazy_options))
|
509
|
+
|
510
|
+
def __rmul__(self, other):
|
511
|
+
return _AnsibleLazyTemplateList(_LazyValueSource(source=super().__rmul__(other), templar=self._templar, lazy_options=self._lazy_options))
|
512
|
+
|
513
|
+
def index(self, *args, **kwargs) -> int:
|
514
|
+
self._proxy_or_render_lazy_values()
|
515
|
+
return super().index(*args, **kwargs)
|
516
|
+
|
517
|
+
def remove(self, *args, **kwargs) -> None:
|
518
|
+
self._proxy_or_render_lazy_values()
|
519
|
+
super().remove(*args, **kwargs)
|
520
|
+
|
521
|
+
def sort(self, *args, **kwargs) -> None:
|
522
|
+
self._proxy_or_render_lazy_values()
|
523
|
+
super().sort(*args, **kwargs)
|
524
|
+
|
525
|
+
def __deepcopy__(self, memo):
|
526
|
+
return _AnsibleLazyTemplateList(
|
527
|
+
_LazyValueSource(
|
528
|
+
source=(copy.deepcopy(v) for v in super().__iter__()),
|
529
|
+
templar=copy.deepcopy(self._templar),
|
530
|
+
lazy_options=copy.deepcopy(self._lazy_options),
|
531
|
+
)
|
532
|
+
)
|
533
|
+
|
534
|
+
|
535
|
+
@t.final # consumers of lazy collections rely heavily on the concrete types being final
|
536
|
+
class _AnsibleLazyAccessTuple(_AnsibleTaggedTuple, _AnsibleLazyTemplateMixin):
|
537
|
+
"""
|
538
|
+
A tagged tuple subclass that provides only managed access for existing lazy values.
|
539
|
+
|
540
|
+
Since tuples are immutable, they cannot support lazy templating (which would change the tuple's value as templates were resolved).
|
541
|
+
When this type is created, each value in the source tuple is lazified:
|
542
|
+
|
543
|
+
* template strings are templated immediately (possibly resulting in lazy containers)
|
544
|
+
* non-tuple containers are lazy-wrapped
|
545
|
+
* tuples are immediately recursively lazy-wrapped
|
546
|
+
* transformations are applied immediately
|
547
|
+
|
548
|
+
The resulting object provides only managed access to its values (e.g., deprecation warnings, tripwires), and propagates to new lazy containers
|
549
|
+
created as a results of managed access.
|
550
|
+
"""
|
551
|
+
|
552
|
+
# DTFIX-RELEASE: ensure we have tests that explicitly verify this behavior
|
553
|
+
|
554
|
+
# nonempty __slots__ not supported for subtype of 'tuple'
|
555
|
+
|
556
|
+
def __new__(cls, contents: t.Iterable | _LazyValueSource, /) -> t.Self:
|
557
|
+
if isinstance(contents, _AnsibleLazyAccessTuple):
|
558
|
+
return super().__new__(cls, tuple.__iter__(contents))
|
559
|
+
|
560
|
+
if isinstance(contents, _LazyValueSource):
|
561
|
+
return super().__new__(cls, contents.source)
|
562
|
+
|
563
|
+
raise UnsupportedConstructionMethodError()
|
564
|
+
|
565
|
+
def __init__(self, contents: t.Iterable | _LazyValueSource, /) -> None:
|
566
|
+
_AnsibleLazyTemplateMixin.__init__(self, contents)
|
567
|
+
|
568
|
+
def __getitem__(self, key: t.SupportsIndex | slice, /) -> t.Any:
|
569
|
+
if type(key) is slice: # pylint: disable=unidiomatic-typecheck
|
570
|
+
return _AnsibleLazyAccessTuple(super().__getitem__(key))
|
571
|
+
|
572
|
+
value = super().__getitem__(key)
|
573
|
+
|
574
|
+
if self._lazy_options.access:
|
575
|
+
AnsibleAccessContext.current().access(value)
|
576
|
+
|
577
|
+
return value
|
578
|
+
|
579
|
+
@staticmethod
|
580
|
+
def _item_source(value: tuple) -> tuple | _LazyValueSource:
|
581
|
+
if isinstance(value, _AnsibleLazyAccessTuple):
|
582
|
+
return _LazyValueSource(source=tuple.__iter__(value), templar=value._templar, lazy_options=value._lazy_options)
|
583
|
+
|
584
|
+
return value
|
585
|
+
|
586
|
+
@staticmethod
|
587
|
+
def _lazy_values(values: t.Any, lazy_options: LazyOptions) -> _LazyValueSource:
|
588
|
+
templar = TemplateContext.current().templar
|
589
|
+
|
590
|
+
return _LazyValueSource(source=(templar.template(value, lazy_options=lazy_options) for value in values), templar=templar, lazy_options=lazy_options)
|
591
|
+
|
592
|
+
def _non_lazy_copy(self) -> tuple:
|
593
|
+
return AnsibleTagHelper.tag_copy(self, self, value_type=tuple)
|
594
|
+
|
595
|
+
def __deepcopy__(self, memo):
|
596
|
+
return _AnsibleLazyAccessTuple(
|
597
|
+
_LazyValueSource(
|
598
|
+
source=(copy.deepcopy(v) for v in super().__iter__()),
|
599
|
+
templar=copy.deepcopy(self._templar),
|
600
|
+
lazy_options=copy.deepcopy(self._lazy_options),
|
601
|
+
)
|
602
|
+
)
|
603
|
+
|
604
|
+
|
605
|
+
def lazify_container(value: t.Any) -> t.Any:
|
606
|
+
"""
|
607
|
+
If the given value is a supported container type, return its lazy version, otherwise return the value as-is.
|
608
|
+
This is used to ensure that managed access and templating occur on args and kwargs to a callable, even if they were sourced from Jinja constants.
|
609
|
+
|
610
|
+
Since both variable access and plugin output are already lazified, this mostly affects Jinja constant containers.
|
611
|
+
However, plugins that directly invoke other plugins (e.g., `Environment.call_filter`) are another potential source of non-lazy containers.
|
612
|
+
In these cases, templating will occur for trusted templates automatically upon access.
|
613
|
+
|
614
|
+
Sets, tuples, and dictionary keys cannot be lazy, since their correct operation requires hashability and equality.
|
615
|
+
These properties are mutually exclusive with the following lazy features:
|
616
|
+
|
617
|
+
- managed access on encrypted strings - may raise errors on both operations when decryption fails
|
618
|
+
- managed access on markers - must raise errors on both operations
|
619
|
+
- templating - mutates values
|
620
|
+
|
621
|
+
That leaves non-raising managed access as the only remaining feature, which is insufficient to warrant lazy support.
|
622
|
+
"""
|
623
|
+
return _AnsibleLazyTemplateMixin._try_create(value)
|
624
|
+
|
625
|
+
|
626
|
+
def lazify_container_args(item: tuple) -> tuple:
|
627
|
+
"""Return the given args with values converted to lazy containers as needed."""
|
628
|
+
return tuple(lazify_container(value) for value in item)
|
629
|
+
|
630
|
+
|
631
|
+
def lazify_container_kwargs(item: dict[str, t.Any]) -> dict[str, t.Any]:
|
632
|
+
"""Return the given kwargs with values converted to lazy containers as needed."""
|
633
|
+
return {key: lazify_container(value) for key, value in item.items()}
|