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.
Files changed (709) hide show
  1. ansible/_internal/__init__.py +53 -0
  2. ansible/_internal/_ansiballz.py +265 -0
  3. ansible/_internal/_datatag/__init__.py +0 -0
  4. ansible/_internal/_datatag/_tags.py +130 -0
  5. ansible/_internal/_datatag/_utils.py +19 -0
  6. ansible/_internal/_datatag/_wrappers.py +33 -0
  7. ansible/_internal/_errors/__init__.py +0 -0
  8. ansible/_internal/_errors/_captured.py +128 -0
  9. ansible/_internal/_errors/_handler.py +91 -0
  10. ansible/_internal/_errors/_utils.py +310 -0
  11. ansible/_internal/_json/__init__.py +160 -0
  12. ansible/_internal/_json/_legacy_encoder.py +34 -0
  13. ansible/_internal/_json/_profiles/__init__.py +0 -0
  14. ansible/_internal/_json/_profiles/_cache_persistence.py +55 -0
  15. ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
  16. ansible/_internal/_json/_profiles/_legacy.py +198 -0
  17. ansible/_internal/_locking.py +21 -0
  18. ansible/_internal/_plugins/__init__.py +0 -0
  19. ansible/_internal/_plugins/_cache.py +57 -0
  20. ansible/_internal/_task.py +78 -0
  21. ansible/_internal/_templating/__init__.py +10 -0
  22. ansible/_internal/_templating/_access.py +86 -0
  23. ansible/_internal/_templating/_chain_templar.py +63 -0
  24. ansible/_internal/_templating/_datatag.py +95 -0
  25. ansible/_internal/_templating/_engine.py +588 -0
  26. ansible/_internal/_templating/_errors.py +28 -0
  27. ansible/_internal/_templating/_jinja_bits.py +1066 -0
  28. ansible/_internal/_templating/_jinja_common.py +332 -0
  29. ansible/_internal/_templating/_jinja_patches.py +44 -0
  30. ansible/_internal/_templating/_jinja_plugins.py +351 -0
  31. ansible/_internal/_templating/_lazy_containers.py +633 -0
  32. ansible/_internal/_templating/_marker_behaviors.py +103 -0
  33. ansible/_internal/_templating/_transform.py +63 -0
  34. ansible/_internal/_templating/_utils.py +107 -0
  35. ansible/_internal/_wrapt.py +1052 -0
  36. ansible/_internal/_yaml/__init__.py +0 -0
  37. ansible/_internal/_yaml/_constructor.py +240 -0
  38. ansible/_internal/_yaml/_dumper.py +62 -0
  39. ansible/_internal/_yaml/_errors.py +166 -0
  40. ansible/_internal/_yaml/_loader.py +66 -0
  41. ansible/_internal/ansible_collections/ansible/_protomatter/README.md +11 -0
  42. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/action/debug.py +36 -0
  43. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/apply_trust.py +19 -0
  44. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +18 -0
  45. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/finalize.py +16 -0
  46. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/origin.py +18 -0
  47. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.py +24 -0
  48. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.yml +33 -0
  49. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/tag_names.py +16 -0
  50. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +17 -0
  51. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +49 -0
  52. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.py +21 -0
  53. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.yml +2 -0
  54. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.py +15 -0
  55. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.yml +19 -0
  56. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.py +18 -0
  57. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.yml +19 -0
  58. ansible/cli/__init__.py +153 -89
  59. ansible/cli/_ssh_askpass.py +47 -0
  60. ansible/cli/adhoc.py +14 -7
  61. ansible/cli/arguments/option_helpers.py +154 -7
  62. ansible/cli/config.py +43 -68
  63. ansible/cli/console.py +10 -8
  64. ansible/cli/doc.py +48 -46
  65. ansible/cli/galaxy.py +27 -20
  66. ansible/cli/inventory.py +28 -26
  67. ansible/cli/playbook.py +4 -12
  68. ansible/cli/pull.py +51 -11
  69. ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
  70. ansible/cli/vault.py +12 -11
  71. ansible/compat/__init__.py +2 -2
  72. ansible/config/base.yml +165 -108
  73. ansible/config/manager.py +52 -49
  74. ansible/constants.py +3 -4
  75. ansible/errors/__init__.py +277 -235
  76. ansible/executor/interpreter_discovery.py +28 -149
  77. ansible/executor/module_common.py +426 -493
  78. ansible/executor/play_iterator.py +22 -27
  79. ansible/executor/playbook_executor.py +11 -11
  80. ansible/executor/powershell/async_watchdog.ps1 +97 -102
  81. ansible/executor/powershell/async_wrapper.ps1 +202 -151
  82. ansible/executor/powershell/become_wrapper.ps1 +89 -144
  83. ansible/executor/powershell/bootstrap_wrapper.ps1 +24 -9
  84. ansible/executor/powershell/coverage_wrapper.ps1 +82 -135
  85. ansible/executor/powershell/exec_wrapper.ps1 +462 -196
  86. ansible/executor/powershell/module_manifest.py +417 -265
  87. ansible/executor/powershell/module_wrapper.ps1 +169 -186
  88. ansible/executor/powershell/psrp_fetch_file.ps1 +41 -0
  89. ansible/executor/powershell/psrp_put_file.ps1 +122 -0
  90. ansible/executor/powershell/winrm_fetch_file.ps1 +46 -0
  91. ansible/executor/powershell/winrm_put_file.ps1 +36 -0
  92. ansible/executor/process/worker.py +136 -76
  93. ansible/executor/stats.py +5 -5
  94. ansible/executor/task_executor.py +237 -236
  95. ansible/executor/task_queue_manager.py +62 -38
  96. ansible/executor/task_result.py +21 -12
  97. ansible/galaxy/__init__.py +2 -2
  98. ansible/galaxy/api.py +22 -18
  99. ansible/galaxy/collection/__init__.py +1 -1
  100. ansible/galaxy/collection/concrete_artifact_manager.py +8 -11
  101. ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
  102. ansible/galaxy/dependency_resolution/providers.py +1 -1
  103. ansible/galaxy/dependency_resolution/reporters.py +81 -0
  104. ansible/galaxy/role.py +4 -8
  105. ansible/galaxy/token.py +28 -21
  106. ansible/inventory/data.py +47 -57
  107. ansible/inventory/group.py +44 -72
  108. ansible/inventory/helpers.py +9 -0
  109. ansible/inventory/host.py +32 -54
  110. ansible/inventory/manager.py +77 -34
  111. ansible/keyword_desc.yml +1 -1
  112. ansible/module_utils/_internal/__init__.py +55 -0
  113. ansible/module_utils/_internal/_ambient_context.py +58 -0
  114. ansible/module_utils/_internal/_ansiballz.py +133 -0
  115. ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
  116. ansible/module_utils/_internal/_dataclass_annotation_patch.py +64 -0
  117. ansible/module_utils/_internal/_dataclass_validation.py +217 -0
  118. ansible/module_utils/_internal/_datatag/__init__.py +928 -0
  119. ansible/module_utils/_internal/_datatag/_tags.py +38 -0
  120. ansible/module_utils/_internal/_debugging.py +31 -0
  121. ansible/module_utils/_internal/_errors.py +30 -0
  122. ansible/module_utils/_internal/_json/__init__.py +63 -0
  123. ansible/module_utils/_internal/_json/_legacy_encoder.py +26 -0
  124. ansible/module_utils/_internal/_json/_profiles/__init__.py +410 -0
  125. ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
  126. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +31 -0
  127. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +35 -0
  128. ansible/module_utils/_internal/_json/_profiles/_module_modern_c2m.py +35 -0
  129. ansible/module_utils/_internal/_json/_profiles/_module_modern_m2c.py +33 -0
  130. ansible/module_utils/_internal/_json/_profiles/_tagless.py +50 -0
  131. ansible/module_utils/_internal/_patches/__init__.py +66 -0
  132. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +55 -0
  133. ansible/module_utils/_internal/_patches/_socket_patch.py +34 -0
  134. ansible/module_utils/_internal/_patches/_sys_intern_patch.py +34 -0
  135. ansible/module_utils/_internal/_plugin_exec_context.py +49 -0
  136. ansible/module_utils/_internal/_testing.py +0 -0
  137. ansible/module_utils/_internal/_traceback.py +89 -0
  138. ansible/module_utils/ansible_release.py +2 -2
  139. ansible/module_utils/api.py +1 -2
  140. ansible/module_utils/basic.py +154 -120
  141. ansible/module_utils/common/_utils.py +24 -28
  142. ansible/module_utils/common/collections.py +1 -2
  143. ansible/module_utils/common/dict_transformations.py +2 -2
  144. ansible/module_utils/common/file.py +2 -2
  145. ansible/module_utils/common/json.py +90 -84
  146. ansible/module_utils/common/locale.py +2 -2
  147. ansible/module_utils/common/messages.py +108 -0
  148. ansible/module_utils/common/parameters.py +27 -24
  149. ansible/module_utils/common/process.py +2 -2
  150. ansible/module_utils/common/respawn.py +41 -19
  151. ansible/module_utils/common/sentinel.py +66 -0
  152. ansible/module_utils/common/sys_info.py +8 -8
  153. ansible/module_utils/common/text/converters.py +16 -37
  154. ansible/module_utils/common/validation.py +35 -24
  155. ansible/module_utils/common/warnings.py +86 -25
  156. ansible/module_utils/common/yaml.py +29 -3
  157. ansible/module_utils/compat/datetime.py +33 -21
  158. ansible/module_utils/compat/paramiko.py +21 -10
  159. ansible/module_utils/compat/typing.py +6 -5
  160. ansible/module_utils/connection.py +2 -2
  161. ansible/module_utils/csharp/Ansible.Basic.cs +14 -11
  162. ansible/module_utils/csharp/Ansible.Become.cs +1 -0
  163. ansible/module_utils/csharp/Ansible._Async.cs +517 -0
  164. ansible/module_utils/datatag.py +46 -0
  165. ansible/module_utils/distro/__init__.py +2 -2
  166. ansible/module_utils/facts/ansible_collector.py +4 -5
  167. ansible/module_utils/facts/collector.py +13 -14
  168. ansible/module_utils/facts/compat.py +4 -4
  169. ansible/module_utils/facts/default_collectors.py +1 -1
  170. ansible/module_utils/facts/hardware/aix.py +34 -0
  171. ansible/module_utils/facts/hardware/base.py +1 -1
  172. ansible/module_utils/facts/hardware/darwin.py +1 -3
  173. ansible/module_utils/facts/hardware/freebsd.py +2 -2
  174. ansible/module_utils/facts/hardware/linux.py +4 -4
  175. ansible/module_utils/facts/namespace.py +1 -1
  176. ansible/module_utils/facts/network/base.py +1 -1
  177. ansible/module_utils/facts/network/fc_wwn.py +1 -2
  178. ansible/module_utils/facts/network/iscsi.py +1 -2
  179. ansible/module_utils/facts/network/nvme.py +1 -2
  180. ansible/module_utils/facts/other/facter.py +1 -2
  181. ansible/module_utils/facts/other/ohai.py +2 -3
  182. ansible/module_utils/facts/system/apparmor.py +1 -2
  183. ansible/module_utils/facts/system/caps.py +1 -1
  184. ansible/module_utils/facts/system/chroot.py +1 -2
  185. ansible/module_utils/facts/system/cmdline.py +1 -2
  186. ansible/module_utils/facts/system/date_time.py +5 -3
  187. ansible/module_utils/facts/system/distribution.py +9 -8
  188. ansible/module_utils/facts/system/dns.py +1 -1
  189. ansible/module_utils/facts/system/env.py +1 -2
  190. ansible/module_utils/facts/system/fips.py +7 -20
  191. ansible/module_utils/facts/system/loadavg.py +1 -2
  192. ansible/module_utils/facts/system/local.py +1 -2
  193. ansible/module_utils/facts/system/lsb.py +1 -2
  194. ansible/module_utils/facts/system/pkg_mgr.py +1 -2
  195. ansible/module_utils/facts/system/platform.py +1 -2
  196. ansible/module_utils/facts/system/python.py +1 -2
  197. ansible/module_utils/facts/system/selinux.py +1 -1
  198. ansible/module_utils/facts/system/service_mgr.py +1 -2
  199. ansible/module_utils/facts/system/ssh_pub_keys.py +1 -1
  200. ansible/module_utils/facts/system/systemd.py +1 -1
  201. ansible/module_utils/facts/system/user.py +1 -2
  202. ansible/module_utils/facts/utils.py +3 -3
  203. ansible/module_utils/facts/virtual/base.py +1 -1
  204. ansible/module_utils/facts/virtual/sunos.py +3 -15
  205. ansible/module_utils/facts/virtual/sysctl.py +3 -16
  206. ansible/module_utils/json_utils.py +2 -2
  207. ansible/module_utils/parsing/convert_bool.py +1 -1
  208. ansible/module_utils/service.py +18 -21
  209. ansible/module_utils/splitter.py +7 -7
  210. ansible/module_utils/testing.py +31 -0
  211. ansible/module_utils/urls.py +60 -31
  212. ansible/modules/add_host.py +4 -4
  213. ansible/modules/apt.py +60 -46
  214. ansible/modules/apt_key.py +19 -12
  215. ansible/modules/apt_repository.py +19 -16
  216. ansible/modules/assemble.py +6 -6
  217. ansible/modules/assert.py +4 -4
  218. ansible/modules/async_status.py +10 -12
  219. ansible/modules/async_wrapper.py +8 -3
  220. ansible/modules/blockinfile.py +6 -7
  221. ansible/modules/command.py +10 -17
  222. ansible/modules/copy.py +57 -144
  223. ansible/modules/cron.py +20 -15
  224. ansible/modules/deb822_repository.py +8 -9
  225. ansible/modules/debconf.py +5 -5
  226. ansible/modules/debug.py +4 -4
  227. ansible/modules/dnf.py +8 -8
  228. ansible/modules/dnf5.py +39 -13
  229. ansible/modules/dpkg_selections.py +4 -4
  230. ansible/modules/expect.py +8 -10
  231. ansible/modules/fail.py +4 -4
  232. ansible/modules/fetch.py +4 -4
  233. ansible/modules/file.py +174 -133
  234. ansible/modules/find.py +19 -17
  235. ansible/modules/gather_facts.py +3 -3
  236. ansible/modules/get_url.py +59 -53
  237. ansible/modules/getent.py +7 -9
  238. ansible/modules/git.py +28 -25
  239. ansible/modules/group.py +6 -6
  240. ansible/modules/group_by.py +4 -4
  241. ansible/modules/hostname.py +13 -29
  242. ansible/modules/import_playbook.py +6 -6
  243. ansible/modules/import_role.py +6 -6
  244. ansible/modules/import_tasks.py +6 -6
  245. ansible/modules/include_role.py +6 -6
  246. ansible/modules/include_tasks.py +6 -6
  247. ansible/modules/include_vars.py +6 -6
  248. ansible/modules/iptables.py +86 -73
  249. ansible/modules/known_hosts.py +10 -10
  250. ansible/modules/lineinfile.py +5 -5
  251. ansible/modules/meta.py +4 -4
  252. ansible/modules/mount_facts.py +2 -2
  253. ansible/modules/package.py +4 -4
  254. ansible/modules/package_facts.py +22 -10
  255. ansible/modules/pause.py +6 -6
  256. ansible/modules/ping.py +6 -6
  257. ansible/modules/pip.py +10 -11
  258. ansible/modules/raw.py +4 -4
  259. ansible/modules/reboot.py +6 -6
  260. ansible/modules/replace.py +9 -13
  261. ansible/modules/rpm_key.py +7 -8
  262. ansible/modules/script.py +4 -4
  263. ansible/modules/service.py +7 -8
  264. ansible/modules/service_facts.py +87 -10
  265. ansible/modules/set_fact.py +5 -5
  266. ansible/modules/set_stats.py +4 -4
  267. ansible/modules/setup.py +2 -2
  268. ansible/modules/shell.py +6 -6
  269. ansible/modules/slurp.py +6 -6
  270. ansible/modules/stat.py +9 -23
  271. ansible/modules/subversion.py +15 -15
  272. ansible/modules/systemd.py +6 -6
  273. ansible/modules/systemd_service.py +6 -6
  274. ansible/modules/sysvinit.py +6 -6
  275. ansible/modules/tempfile.py +5 -6
  276. ansible/modules/template.py +6 -6
  277. ansible/modules/unarchive.py +32 -11
  278. ansible/modules/uri.py +33 -26
  279. ansible/modules/user.py +53 -34
  280. ansible/modules/validate_argument_spec.py +10 -7
  281. ansible/modules/wait_for.py +32 -27
  282. ansible/modules/wait_for_connection.py +6 -6
  283. ansible/modules/yum_repository.py +6 -6
  284. ansible/parsing/ajson.py +14 -32
  285. ansible/parsing/dataloader.py +99 -54
  286. ansible/parsing/mod_args.py +28 -44
  287. ansible/parsing/plugin_docs.py +21 -86
  288. ansible/parsing/quoting.py +1 -1
  289. ansible/parsing/splitter.py +27 -12
  290. ansible/parsing/utils/addresses.py +24 -24
  291. ansible/parsing/utils/yaml.py +32 -61
  292. ansible/parsing/vault/__init__.py +319 -87
  293. ansible/parsing/yaml/__init__.py +0 -18
  294. ansible/parsing/yaml/dumper.py +6 -120
  295. ansible/parsing/yaml/loader.py +6 -39
  296. ansible/parsing/yaml/objects.py +36 -339
  297. ansible/playbook/__init__.py +1 -1
  298. ansible/playbook/attribute.py +8 -3
  299. ansible/playbook/base.py +182 -132
  300. ansible/playbook/block.py +26 -24
  301. ansible/playbook/collectionsearch.py +1 -15
  302. ansible/playbook/conditional.py +3 -77
  303. ansible/playbook/handler.py +8 -2
  304. ansible/playbook/helpers.py +41 -53
  305. ansible/playbook/included_file.py +6 -15
  306. ansible/playbook/loop_control.py +2 -2
  307. ansible/playbook/play.py +85 -44
  308. ansible/playbook/play_context.py +12 -17
  309. ansible/playbook/playbook_include.py +14 -15
  310. ansible/playbook/role/__init__.py +24 -26
  311. ansible/playbook/role/definition.py +15 -17
  312. ansible/playbook/role/include.py +2 -4
  313. ansible/playbook/role/metadata.py +10 -11
  314. ansible/playbook/role_include.py +3 -3
  315. ansible/playbook/taggable.py +13 -8
  316. ansible/playbook/task.py +188 -118
  317. ansible/playbook/task_include.py +5 -5
  318. ansible/plugins/__init__.py +68 -21
  319. ansible/plugins/action/__init__.py +209 -176
  320. ansible/plugins/action/add_host.py +1 -1
  321. ansible/plugins/action/assemble.py +1 -1
  322. ansible/plugins/action/assert.py +54 -66
  323. ansible/plugins/action/copy.py +7 -11
  324. ansible/plugins/action/debug.py +37 -31
  325. ansible/plugins/action/dnf.py +3 -4
  326. ansible/plugins/action/fail.py +1 -1
  327. ansible/plugins/action/fetch.py +4 -5
  328. ansible/plugins/action/gather_facts.py +7 -6
  329. ansible/plugins/action/group_by.py +1 -1
  330. ansible/plugins/action/include_vars.py +10 -11
  331. ansible/plugins/action/package.py +3 -6
  332. ansible/plugins/action/pause.py +2 -2
  333. ansible/plugins/action/script.py +15 -8
  334. ansible/plugins/action/service.py +6 -11
  335. ansible/plugins/action/set_fact.py +3 -12
  336. ansible/plugins/action/set_stats.py +3 -8
  337. ansible/plugins/action/template.py +35 -59
  338. ansible/plugins/action/unarchive.py +1 -1
  339. ansible/plugins/action/validate_argument_spec.py +5 -5
  340. ansible/plugins/action/wait_for_connection.py +1 -1
  341. ansible/plugins/become/__init__.py +31 -8
  342. ansible/plugins/become/runas.py +71 -0
  343. ansible/plugins/become/su.py +13 -8
  344. ansible/plugins/become/sudo.py +19 -0
  345. ansible/plugins/cache/__init__.py +35 -44
  346. ansible/plugins/cache/base.py +8 -0
  347. ansible/plugins/cache/jsonfile.py +10 -16
  348. ansible/plugins/cache/memory.py +6 -12
  349. ansible/plugins/callback/__init__.py +141 -123
  350. ansible/plugins/callback/default.py +30 -23
  351. ansible/plugins/callback/junit.py +28 -24
  352. ansible/plugins/callback/minimal.py +17 -14
  353. ansible/plugins/callback/oneline.py +13 -7
  354. ansible/plugins/callback/tree.py +10 -6
  355. ansible/plugins/connection/__init__.py +47 -34
  356. ansible/plugins/connection/local.py +150 -54
  357. ansible/plugins/connection/paramiko_ssh.py +21 -18
  358. ansible/plugins/connection/psrp.py +76 -165
  359. ansible/plugins/connection/ssh.py +301 -78
  360. ansible/plugins/connection/winrm.py +58 -140
  361. ansible/plugins/doc_fragments/action_common_attributes.py +14 -14
  362. ansible/plugins/doc_fragments/action_core.py +6 -6
  363. ansible/plugins/doc_fragments/backup.py +2 -2
  364. ansible/plugins/doc_fragments/checksum_common.py +27 -0
  365. ansible/plugins/doc_fragments/constructed.py +6 -2
  366. ansible/plugins/doc_fragments/decrypt.py +2 -2
  367. ansible/plugins/doc_fragments/default_callback.py +2 -2
  368. ansible/plugins/doc_fragments/files.py +2 -2
  369. ansible/plugins/doc_fragments/inventory_cache.py +2 -2
  370. ansible/plugins/doc_fragments/result_format_callback.py +2 -2
  371. ansible/plugins/doc_fragments/return_common.py +2 -2
  372. ansible/plugins/doc_fragments/template_common.py +4 -4
  373. ansible/plugins/doc_fragments/url.py +17 -1
  374. ansible/plugins/doc_fragments/url_windows.py +2 -2
  375. ansible/plugins/doc_fragments/validate.py +2 -2
  376. ansible/plugins/doc_fragments/vars_plugin_staging.py +2 -2
  377. ansible/plugins/filter/__init__.py +6 -2
  378. ansible/plugins/filter/b64decode.yml +22 -0
  379. ansible/plugins/filter/b64encode.yml +22 -0
  380. ansible/plugins/filter/bool.yml +11 -4
  381. ansible/plugins/filter/core.py +218 -108
  382. ansible/plugins/filter/encryption.py +32 -32
  383. ansible/plugins/filter/flatten.yml +3 -2
  384. ansible/plugins/filter/human_to_bytes.yml +1 -1
  385. ansible/plugins/filter/mathstuff.py +30 -37
  386. ansible/plugins/filter/password_hash.yml +8 -0
  387. ansible/plugins/filter/regex_search.yml +1 -4
  388. ansible/plugins/filter/split.yml +1 -1
  389. ansible/plugins/filter/to_nice_yaml.yml +0 -4
  390. ansible/plugins/filter/to_yaml.yml +0 -4
  391. ansible/plugins/filter/unvault.yml +1 -1
  392. ansible/plugins/filter/urls.py +1 -1
  393. ansible/plugins/filter/urlsplit.py +8 -9
  394. ansible/plugins/filter/vault.yml +14 -9
  395. ansible/plugins/filter/win_basename.yml +6 -1
  396. ansible/plugins/filter/win_dirname.yml +5 -0
  397. ansible/plugins/inventory/__init__.py +97 -77
  398. ansible/plugins/inventory/advanced_host_list.py +7 -5
  399. ansible/plugins/inventory/auto.py +11 -4
  400. ansible/plugins/inventory/constructed.py +21 -24
  401. ansible/plugins/inventory/generator.py +16 -11
  402. ansible/plugins/inventory/host_list.py +7 -5
  403. ansible/plugins/inventory/ini.py +78 -44
  404. ansible/plugins/inventory/script.py +189 -119
  405. ansible/plugins/inventory/toml.py +16 -126
  406. ansible/plugins/inventory/yaml.py +10 -8
  407. ansible/plugins/list.py +3 -3
  408. ansible/plugins/loader.py +197 -82
  409. ansible/plugins/lookup/__init__.py +21 -4
  410. ansible/plugins/lookup/config.py +21 -35
  411. ansible/plugins/lookup/csvfile.py +3 -2
  412. ansible/plugins/lookup/dict.py +1 -6
  413. ansible/plugins/lookup/env.py +12 -9
  414. ansible/plugins/lookup/file.py +5 -8
  415. ansible/plugins/lookup/first_found.py +86 -55
  416. ansible/plugins/lookup/indexed_items.py +1 -10
  417. ansible/plugins/lookup/ini.py +14 -13
  418. ansible/plugins/lookup/items.py +1 -1
  419. ansible/plugins/lookup/lines.py +8 -1
  420. ansible/plugins/lookup/list.py +1 -1
  421. ansible/plugins/lookup/nested.py +2 -18
  422. ansible/plugins/lookup/password.py +5 -5
  423. ansible/plugins/lookup/pipe.py +5 -7
  424. ansible/plugins/lookup/sequence.py +18 -8
  425. ansible/plugins/lookup/subelements.py +1 -4
  426. ansible/plugins/lookup/template.py +42 -36
  427. ansible/plugins/lookup/together.py +0 -12
  428. ansible/plugins/lookup/unvault.py +1 -5
  429. ansible/plugins/lookup/url.py +2 -8
  430. ansible/plugins/lookup/vars.py +16 -24
  431. ansible/plugins/shell/__init__.py +2 -2
  432. ansible/plugins/shell/cmd.py +2 -2
  433. ansible/plugins/shell/powershell.py +39 -22
  434. ansible/plugins/shell/sh.py +3 -2
  435. ansible/plugins/strategy/__init__.py +94 -113
  436. ansible/plugins/strategy/debug.py +2 -2
  437. ansible/plugins/strategy/free.py +13 -28
  438. ansible/plugins/strategy/host_pinned.py +2 -2
  439. ansible/plugins/strategy/linear.py +31 -33
  440. ansible/plugins/terminal/__init__.py +4 -4
  441. ansible/plugins/test/__init__.py +7 -2
  442. ansible/plugins/test/core.py +54 -20
  443. ansible/plugins/test/files.py +1 -1
  444. ansible/plugins/test/mathstuff.py +3 -3
  445. ansible/plugins/test/uri.py +3 -3
  446. ansible/plugins/vars/host_group_vars.py +7 -14
  447. ansible/release.py +2 -2
  448. ansible/template/__init__.py +368 -944
  449. ansible/utils/__init__.py +0 -18
  450. ansible/utils/_ssh_agent.py +657 -0
  451. ansible/utils/collection_loader/__init__.py +52 -5
  452. ansible/utils/collection_loader/_collection_config.py +5 -6
  453. ansible/utils/collection_loader/_collection_finder.py +79 -93
  454. ansible/utils/collection_loader/_collection_meta.py +13 -8
  455. ansible/utils/display.py +428 -58
  456. ansible/utils/encrypt.py +27 -19
  457. ansible/utils/fqcn.py +2 -2
  458. ansible/utils/hashing.py +2 -2
  459. ansible/utils/helpers.py +2 -2
  460. ansible/utils/listify.py +8 -8
  461. ansible/utils/lock.py +2 -2
  462. ansible/utils/path.py +4 -4
  463. ansible/utils/plugin_docs.py +14 -13
  464. ansible/utils/sentinel.py +4 -62
  465. ansible/utils/singleton.py +2 -0
  466. ansible/utils/ssh_functions.py +1 -1
  467. ansible/utils/unsafe_proxy.py +23 -332
  468. ansible/utils/vars.py +28 -8
  469. ansible/utils/version.py +2 -2
  470. ansible/vars/clean.py +4 -4
  471. ansible/vars/hostvars.py +60 -90
  472. ansible/vars/manager.py +205 -264
  473. ansible/vars/reserved.py +8 -9
  474. ansible_core-2.19.0b1.dist-info/BSD-3-Clause.txt +28 -0
  475. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/METADATA +5 -4
  476. ansible_core-2.19.0b1.dist-info/RECORD +1070 -0
  477. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/WHEEL +1 -1
  478. ansible_test/_data/completion/docker.txt +7 -7
  479. ansible_test/_data/completion/remote.txt +6 -6
  480. ansible_test/_data/completion/windows.txt +1 -0
  481. ansible_test/_data/requirements/ansible.txt +2 -2
  482. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
  483. ansible_test/_data/requirements/sanity.changelog.txt +1 -1
  484. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  485. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  486. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
  487. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  488. ansible_test/_data/requirements/units.txt +1 -0
  489. ansible_test/_internal/__init__.py +1 -0
  490. ansible_test/_internal/ansible_util.py +2 -0
  491. ansible_test/_internal/become.py +1 -0
  492. ansible_test/_internal/bootstrap.py +1 -0
  493. ansible_test/_internal/cache.py +1 -0
  494. ansible_test/_internal/cgroup.py +1 -0
  495. ansible_test/_internal/ci/__init__.py +1 -0
  496. ansible_test/_internal/ci/azp.py +1 -0
  497. ansible_test/_internal/ci/local.py +1 -0
  498. ansible_test/_internal/classification/__init__.py +1 -0
  499. ansible_test/_internal/classification/common.py +1 -0
  500. ansible_test/_internal/classification/csharp.py +1 -0
  501. ansible_test/_internal/classification/powershell.py +1 -0
  502. ansible_test/_internal/classification/python.py +1 -0
  503. ansible_test/_internal/cli/__init__.py +1 -0
  504. ansible_test/_internal/cli/actions.py +1 -0
  505. ansible_test/_internal/cli/argparsing/__init__.py +1 -0
  506. ansible_test/_internal/cli/argparsing/actions.py +1 -0
  507. ansible_test/_internal/cli/argparsing/argcompletion.py +1 -0
  508. ansible_test/_internal/cli/argparsing/parsers.py +1 -0
  509. ansible_test/_internal/cli/commands/__init__.py +11 -0
  510. ansible_test/_internal/cli/commands/coverage/__init__.py +1 -0
  511. ansible_test/_internal/cli/commands/coverage/analyze/__init__.py +1 -0
  512. ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py +1 -0
  513. ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py +1 -0
  514. ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py +1 -0
  515. ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py +1 -0
  516. ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py +1 -0
  517. ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py +1 -0
  518. ansible_test/_internal/cli/commands/coverage/combine.py +1 -0
  519. ansible_test/_internal/cli/commands/coverage/erase.py +1 -0
  520. ansible_test/_internal/cli/commands/coverage/html.py +1 -0
  521. ansible_test/_internal/cli/commands/coverage/report.py +1 -0
  522. ansible_test/_internal/cli/commands/coverage/xml.py +1 -0
  523. ansible_test/_internal/cli/commands/env.py +1 -0
  524. ansible_test/_internal/cli/commands/integration/__init__.py +1 -0
  525. ansible_test/_internal/cli/commands/integration/network.py +1 -0
  526. ansible_test/_internal/cli/commands/integration/posix.py +1 -0
  527. ansible_test/_internal/cli/commands/integration/windows.py +1 -0
  528. ansible_test/_internal/cli/commands/sanity.py +9 -0
  529. ansible_test/_internal/cli/commands/shell.py +1 -0
  530. ansible_test/_internal/cli/commands/units.py +1 -0
  531. ansible_test/_internal/cli/compat.py +1 -0
  532. ansible_test/_internal/cli/completers.py +1 -0
  533. ansible_test/_internal/cli/converters.py +1 -0
  534. ansible_test/_internal/cli/environments.py +1 -0
  535. ansible_test/_internal/cli/epilog.py +1 -0
  536. ansible_test/_internal/cli/parsers/__init__.py +1 -0
  537. ansible_test/_internal/cli/parsers/base_argument_parsers.py +1 -0
  538. ansible_test/_internal/cli/parsers/helpers.py +1 -0
  539. ansible_test/_internal/cli/parsers/host_config_parsers.py +1 -0
  540. ansible_test/_internal/cli/parsers/key_value_parsers.py +1 -0
  541. ansible_test/_internal/cli/parsers/value_parsers.py +1 -0
  542. ansible_test/_internal/commands/__init__.py +1 -0
  543. ansible_test/_internal/commands/coverage/__init__.py +2 -1
  544. ansible_test/_internal/commands/coverage/analyze/__init__.py +1 -0
  545. ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +1 -0
  546. ansible_test/_internal/commands/coverage/analyze/targets/combine.py +1 -0
  547. ansible_test/_internal/commands/coverage/analyze/targets/expand.py +1 -0
  548. ansible_test/_internal/commands/coverage/analyze/targets/filter.py +1 -0
  549. ansible_test/_internal/commands/coverage/analyze/targets/generate.py +1 -0
  550. ansible_test/_internal/commands/coverage/analyze/targets/missing.py +1 -0
  551. ansible_test/_internal/commands/coverage/combine.py +2 -1
  552. ansible_test/_internal/commands/coverage/erase.py +1 -0
  553. ansible_test/_internal/commands/coverage/html.py +1 -0
  554. ansible_test/_internal/commands/coverage/report.py +1 -0
  555. ansible_test/_internal/commands/coverage/xml.py +1 -0
  556. ansible_test/_internal/commands/env/__init__.py +2 -0
  557. ansible_test/_internal/commands/integration/__init__.py +4 -0
  558. ansible_test/_internal/commands/integration/cloud/__init__.py +1 -0
  559. ansible_test/_internal/commands/integration/cloud/acme.py +2 -1
  560. ansible_test/_internal/commands/integration/cloud/aws.py +1 -0
  561. ansible_test/_internal/commands/integration/cloud/azure.py +1 -0
  562. ansible_test/_internal/commands/integration/cloud/cs.py +1 -0
  563. ansible_test/_internal/commands/integration/cloud/digitalocean.py +1 -0
  564. ansible_test/_internal/commands/integration/cloud/galaxy.py +3 -2
  565. ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -0
  566. ansible_test/_internal/commands/integration/cloud/httptester.py +2 -1
  567. ansible_test/_internal/commands/integration/cloud/nios.py +2 -1
  568. ansible_test/_internal/commands/integration/cloud/opennebula.py +1 -0
  569. ansible_test/_internal/commands/integration/cloud/openshift.py +1 -0
  570. ansible_test/_internal/commands/integration/cloud/scaleway.py +1 -0
  571. ansible_test/_internal/commands/integration/cloud/vcenter.py +1 -0
  572. ansible_test/_internal/commands/integration/cloud/vultr.py +1 -0
  573. ansible_test/_internal/commands/integration/coverage.py +1 -0
  574. ansible_test/_internal/commands/integration/filters.py +1 -0
  575. ansible_test/_internal/commands/integration/network.py +1 -0
  576. ansible_test/_internal/commands/integration/posix.py +1 -0
  577. ansible_test/_internal/commands/integration/windows.py +1 -0
  578. ansible_test/_internal/commands/sanity/__init__.py +16 -1
  579. ansible_test/_internal/commands/sanity/ansible_doc.py +1 -0
  580. ansible_test/_internal/commands/sanity/bin_symlinks.py +1 -0
  581. ansible_test/_internal/commands/sanity/compile.py +1 -0
  582. ansible_test/_internal/commands/sanity/ignores.py +1 -0
  583. ansible_test/_internal/commands/sanity/import.py +1 -0
  584. ansible_test/_internal/commands/sanity/integration_aliases.py +1 -0
  585. ansible_test/_internal/commands/sanity/pep8.py +1 -0
  586. ansible_test/_internal/commands/sanity/pslint.py +1 -0
  587. ansible_test/_internal/commands/sanity/pylint.py +24 -26
  588. ansible_test/_internal/commands/sanity/shellcheck.py +1 -0
  589. ansible_test/_internal/commands/sanity/validate_modules.py +1 -0
  590. ansible_test/_internal/commands/sanity/yamllint.py +1 -0
  591. ansible_test/_internal/commands/shell/__init__.py +1 -0
  592. ansible_test/_internal/commands/units/__init__.py +1 -0
  593. ansible_test/_internal/compat/__init__.py +1 -0
  594. ansible_test/_internal/compat/packaging.py +1 -0
  595. ansible_test/_internal/compat/yaml.py +1 -0
  596. ansible_test/_internal/completion.py +1 -0
  597. ansible_test/_internal/config.py +2 -0
  598. ansible_test/_internal/connections.py +1 -0
  599. ansible_test/_internal/constants.py +1 -0
  600. ansible_test/_internal/containers.py +1 -0
  601. ansible_test/_internal/content_config.py +1 -0
  602. ansible_test/_internal/core_ci.py +1 -0
  603. ansible_test/_internal/coverage_util.py +11 -10
  604. ansible_test/_internal/data.py +1 -0
  605. ansible_test/_internal/delegation.py +1 -0
  606. ansible_test/_internal/dev/__init__.py +1 -0
  607. ansible_test/_internal/dev/container_probe.py +1 -0
  608. ansible_test/_internal/diff.py +3 -2
  609. ansible_test/_internal/docker_util.py +2 -1
  610. ansible_test/_internal/encoding.py +1 -0
  611. ansible_test/_internal/executor.py +1 -0
  612. ansible_test/_internal/git.py +1 -0
  613. ansible_test/_internal/host_configs.py +1 -0
  614. ansible_test/_internal/host_profiles.py +1 -0
  615. ansible_test/_internal/http.py +1 -0
  616. ansible_test/_internal/init.py +1 -0
  617. ansible_test/_internal/inventory.py +35 -3
  618. ansible_test/_internal/io.py +1 -0
  619. ansible_test/_internal/metadata.py +1 -0
  620. ansible_test/_internal/payload.py +1 -0
  621. ansible_test/_internal/provider/__init__.py +1 -0
  622. ansible_test/_internal/provider/layout/__init__.py +1 -0
  623. ansible_test/_internal/provider/layout/ansible.py +1 -0
  624. ansible_test/_internal/provider/layout/collection.py +1 -0
  625. ansible_test/_internal/provider/layout/unsupported.py +1 -0
  626. ansible_test/_internal/provider/source/__init__.py +1 -0
  627. ansible_test/_internal/provider/source/git.py +1 -0
  628. ansible_test/_internal/provider/source/installed.py +1 -0
  629. ansible_test/_internal/provider/source/unsupported.py +1 -0
  630. ansible_test/_internal/provider/source/unversioned.py +1 -0
  631. ansible_test/_internal/provisioning.py +1 -0
  632. ansible_test/_internal/pypi_proxy.py +6 -5
  633. ansible_test/_internal/python_requirements.py +1 -0
  634. ansible_test/_internal/ssh.py +1 -0
  635. ansible_test/_internal/target.py +1 -0
  636. ansible_test/_internal/test.py +3 -2
  637. ansible_test/_internal/thread.py +1 -0
  638. ansible_test/_internal/timeout.py +1 -0
  639. ansible_test/_internal/util.py +1 -0
  640. ansible_test/_internal/util_common.py +5 -2
  641. ansible_test/_internal/venv.py +1 -0
  642. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +1 -0
  643. ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py +1 -0
  644. ansible_test/_util/controller/sanity/code-smell/changelog.py +1 -0
  645. ansible_test/_util/controller/sanity/code-smell/empty-init.py +1 -0
  646. ansible_test/_util/controller/sanity/code-smell/line-endings.py +1 -0
  647. ansible_test/_util/controller/sanity/code-smell/no-assert.py +1 -0
  648. ansible_test/_util/controller/sanity/code-smell/no-get-exception.py +1 -0
  649. ansible_test/_util/controller/sanity/code-smell/no-illegal-filenames.py +1 -0
  650. ansible_test/_util/controller/sanity/code-smell/no-smart-quotes.py +1 -0
  651. ansible_test/_util/controller/sanity/code-smell/replace-urlopen.py +1 -0
  652. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +28 -1
  653. ansible_test/_util/controller/sanity/code-smell/shebang.py +1 -0
  654. ansible_test/_util/controller/sanity/code-smell/symlinks.py +1 -0
  655. ansible_test/_util/controller/sanity/code-smell/use-argspec-type-path.py +1 -0
  656. ansible_test/_util/controller/sanity/code-smell/use-compat-six.py +1 -0
  657. ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py +2 -1
  658. ansible_test/_util/controller/sanity/pep8/current-ignore.txt +4 -0
  659. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +7 -5
  660. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +7 -5
  661. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +7 -5
  662. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +3 -5
  663. ansible_test/_util/controller/sanity/pylint/config/default.cfg +7 -7
  664. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -13
  665. ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +1 -0
  666. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +1 -8
  667. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +1 -8
  668. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +55 -28
  669. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +12 -5
  670. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +13 -2
  671. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -0
  672. ansible_test/_util/controller/sanity/yamllint/yamllinter.py +35 -17
  673. ansible_test/_util/controller/tools/collection_detail.py +1 -0
  674. ansible_test/_util/controller/tools/yaml_to_json.py +2 -1
  675. ansible_test/_util/target/pytest/plugins/ansible_forked.py +6 -1
  676. ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +2 -1
  677. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -0
  678. ansible_test/_util/target/sanity/compile/compile.py +1 -0
  679. ansible_test/_util/target/sanity/import/importer.py +15 -16
  680. ansible_test/_util/target/setup/bootstrap.sh +9 -20
  681. ansible_test/_util/target/setup/probe_cgroups.py +1 -0
  682. ansible_test/_util/target/setup/quiet_pip.py +1 -0
  683. ansible_test/_util/target/setup/requirements.py +35 -27
  684. ansible_test/_util/target/tools/virtualenvcheck.py +2 -1
  685. ansible_test/_util/target/tools/yamlcheck.py +2 -1
  686. ansible/compat/selectors.py +0 -32
  687. ansible/errors/yaml_strings.py +0 -138
  688. ansible/executor/action_write_locks.py +0 -44
  689. ansible/executor/discovery/python_target.py +0 -47
  690. ansible/executor/powershell/module_powershell_wrapper.ps1 +0 -86
  691. ansible/executor/powershell/module_script_wrapper.ps1 +0 -22
  692. ansible/module_utils/compat/importlib.py +0 -26
  693. ansible/module_utils/compat/selectors.py +0 -32
  694. ansible/module_utils/pycompat24.py +0 -73
  695. ansible/parsing/utils/jsonify.py +0 -36
  696. ansible/parsing/yaml/constructor.py +0 -178
  697. ansible/template/native_helpers.py +0 -251
  698. ansible/template/template.py +0 -43
  699. ansible/template/vars.py +0 -77
  700. ansible/utils/native_jinja.py +0 -11
  701. ansible/vars/fact_cache.py +0 -71
  702. ansible_core-2.18.5rc1.dist-info/RECORD +0 -992
  703. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/Apache-License.txt +0 -0
  704. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/COPYING +0 -0
  705. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/MIT-license.txt +0 -0
  706. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/PSF-license.txt +0 -0
  707. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/entry_points.txt +0 -0
  708. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/simplified_bsd.txt +0 -0
  709. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1066 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ import collections.abc as c
5
+ import dataclasses
6
+ import enum
7
+ import pathlib
8
+ import tempfile
9
+ import types
10
+ import typing as t
11
+
12
+ from collections import ChainMap
13
+
14
+ import jinja2.nodes
15
+
16
+ from jinja2 import pass_context, defaults, TemplateSyntaxError, FileSystemLoader
17
+ from jinja2.environment import Environment, Template, TemplateModule, TemplateExpression
18
+ from jinja2.compiler import Frame
19
+ from jinja2.lexer import TOKEN_VARIABLE_BEGIN, TOKEN_VARIABLE_END, TOKEN_STRING, Lexer
20
+ from jinja2.nativetypes import NativeCodeGenerator
21
+ from jinja2.nodes import Const, EvalContext
22
+ from jinja2.runtime import Context
23
+ from jinja2.sandbox import ImmutableSandboxedEnvironment
24
+ from jinja2.utils import missing, LRUCache
25
+
26
+ from ansible.utils.display import Display
27
+ from ansible.errors import AnsibleVariableTypeError, AnsibleTemplateSyntaxError, AnsibleTemplateError
28
+ from ansible.module_utils.common.text.converters import to_text
29
+ from ansible.module_utils._internal._datatag import (
30
+ _AnsibleTaggedDict,
31
+ _AnsibleTaggedList,
32
+ _AnsibleTaggedTuple,
33
+ _AnsibleTaggedStr,
34
+ AnsibleTagHelper,
35
+ )
36
+
37
+ from ansible._internal._errors._handler import ErrorAction
38
+ from ansible._internal._datatag._tags import Origin, TrustedAsTemplate
39
+
40
+ from ._access import AnsibleAccessContext
41
+ from ._datatag import _JinjaConstTemplate
42
+ from ._utils import LazyOptions
43
+ from ._jinja_common import (
44
+ MarkerError,
45
+ Marker,
46
+ CapturedExceptionMarker,
47
+ UndefinedMarker,
48
+ _TemplateConfig,
49
+ TruncationMarker,
50
+ validate_arg_type,
51
+ JinjaCallContext,
52
+ )
53
+ from ._jinja_plugins import JinjaPluginIntercept, _query, _lookup, _now, _wrap_plugin_output, get_first_marker_arg, _DirectCall, _jinja_const_template_warning
54
+ from ._lazy_containers import (
55
+ _AnsibleLazyTemplateMixin,
56
+ _AnsibleLazyTemplateDict,
57
+ _AnsibleLazyTemplateList,
58
+ _AnsibleLazyAccessTuple,
59
+ lazify_container_args,
60
+ lazify_container_kwargs,
61
+ lazify_container,
62
+ register_known_types,
63
+ )
64
+ from ._utils import Omit, TemplateContext, PASS_THROUGH_SCALAR_VAR_TYPES
65
+
66
+ from ansible.module_utils._internal._json._profiles import _json_subclassable_scalar_types
67
+ from ansible.module_utils import _internal
68
+ from ansible.module_utils._internal import _ambient_context, _dataclass_validation
69
+ from ansible.plugins.loader import filter_loader, test_loader
70
+ from ansible.vars.hostvars import HostVars, HostVarsVars
71
+ from ...module_utils.datatag import native_type_name
72
+
73
+ JINJA2_OVERRIDE = '#jinja2:'
74
+
75
+ display = Display()
76
+
77
+
78
+ @dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
79
+ class TemplateOverrides:
80
+ DEFAULT: t.ClassVar[t.Self]
81
+
82
+ block_start_string: str = defaults.BLOCK_START_STRING
83
+ block_end_string: str = defaults.BLOCK_END_STRING
84
+ variable_start_string: str = defaults.VARIABLE_START_STRING
85
+ variable_end_string: str = defaults.VARIABLE_END_STRING
86
+ comment_start_string: str = defaults.COMMENT_START_STRING
87
+ comment_end_string: str = defaults.COMMENT_END_STRING
88
+ line_statement_prefix: str | None = defaults.LINE_STATEMENT_PREFIX
89
+ line_comment_prefix: str | None = defaults.LINE_COMMENT_PREFIX
90
+ trim_blocks: bool = True # AnsibleEnvironment overrides this default, so don't use the Jinja default here
91
+ lstrip_blocks: bool = defaults.LSTRIP_BLOCKS
92
+ newline_sequence: t.Literal['\n', '\r\n', '\r'] = defaults.NEWLINE_SEQUENCE
93
+ keep_trailing_newline: bool = defaults.KEEP_TRAILING_NEWLINE
94
+
95
+ def __post_init__(self) -> None:
96
+ pass # overridden by _dataclass_validation._inject_post_init_validation
97
+
98
+ def _post_validate(self) -> None:
99
+ if not (self.block_start_string != self.variable_start_string != self.comment_start_string != self.block_start_string):
100
+ raise ValueError('Block, variable and comment start strings must be different.')
101
+
102
+ def overlay_kwargs(self) -> dict[str, t.Any]:
103
+ """
104
+ Return a dictionary of arguments for passing to Environment.overlay.
105
+ The dictionary will be empty if all fields have their default value.
106
+ """
107
+ # DTFIX-FUTURE: calculate default/non-default during __post_init__
108
+ fields = [(field, getattr(self, field.name)) for field in dataclasses.fields(self)]
109
+ kwargs = {field.name: value for field, value in fields if value != field.default}
110
+
111
+ return kwargs
112
+
113
+ def _contains_start_string(self, value: str) -> bool:
114
+ """Returns True if the given value contains a variable, block or comment start string."""
115
+ # DTFIX-FUTURE: this is inefficient, use a compiled regex instead
116
+
117
+ for marker in (self.block_start_string, self.variable_start_string, self.comment_start_string):
118
+ if marker in value:
119
+ return True
120
+
121
+ return False
122
+
123
+ def _starts_and_ends_with_jinja_delimiters(self, value: str) -> bool:
124
+ """Returns True if the given value starts and ends with Jinja variable, block or comment delimiters."""
125
+ # DTFIX-FUTURE: this is inefficient, use a compiled regex instead
126
+
127
+ for marker in (self.block_start_string, self.variable_start_string, self.comment_start_string):
128
+ if value.startswith(marker):
129
+ break
130
+ else:
131
+ return False
132
+
133
+ for marker in (self.block_end_string, self.variable_end_string, self.comment_end_string):
134
+ if value.endswith(marker):
135
+ return True
136
+
137
+ return False
138
+
139
+ def _extract_template_overrides(self, template: str) -> tuple[str, TemplateOverrides]:
140
+ if template.startswith(JINJA2_OVERRIDE):
141
+ eol = template.find('\n')
142
+
143
+ if eol == -1:
144
+ raise ValueError(f"Missing newline after {JINJA2_OVERRIDE!r} override.")
145
+
146
+ line = template[len(JINJA2_OVERRIDE) : eol]
147
+ template = template[eol + 1 :]
148
+ override_kwargs = {}
149
+
150
+ for pair in line.split(','):
151
+ if not pair.strip():
152
+ raise ValueError(f"Empty {JINJA2_OVERRIDE!r} override pair not allowed.")
153
+
154
+ if ':' not in pair:
155
+ raise ValueError(f"Missing key-value separator `:` in {JINJA2_OVERRIDE!r} override pair {pair!r}.")
156
+
157
+ key, val = pair.split(':', 1)
158
+ key = key.strip()
159
+
160
+ if key not in _TEMPLATE_OVERRIDE_FIELD_NAMES:
161
+ raise ValueError(f"Invalid {JINJA2_OVERRIDE!r} override key {key!r}.")
162
+
163
+ override_kwargs[key] = ast.literal_eval(val)
164
+
165
+ overrides = dataclasses.replace(self, **override_kwargs)
166
+ else:
167
+ overrides = self
168
+
169
+ return template, overrides
170
+
171
+ def merge(self, kwargs: dict[str, t.Any] | None, /) -> TemplateOverrides:
172
+ """Return a new instance based on the current instance with the given kwargs overridden."""
173
+ if kwargs:
174
+ return self.from_kwargs(dataclasses.asdict(self) | kwargs)
175
+
176
+ return self
177
+
178
+ @classmethod
179
+ def from_kwargs(cls, kwargs: dict[str, t.Any] | None, /) -> TemplateOverrides:
180
+ """TemplateOverrides instance factory; instances resolving to all default values will instead return the DEFAULT singleton for optimization."""
181
+ if kwargs:
182
+ value = cls(**kwargs)
183
+
184
+ if value.overlay_kwargs():
185
+ return value
186
+
187
+ return cls.DEFAULT
188
+
189
+
190
+ _dataclass_validation.inject_post_init_validation(TemplateOverrides, allow_subclasses=True)
191
+
192
+ TemplateOverrides.DEFAULT = TemplateOverrides()
193
+
194
+ _TEMPLATE_OVERRIDE_FIELD_NAMES: t.Final[tuple[str, ...]] = tuple(sorted(field.name for field in dataclasses.fields(TemplateOverrides)))
195
+
196
+
197
+ class AnsibleContext(Context):
198
+ """
199
+ A custom context which intercepts resolve_or_missing() calls and
200
+ runs them through AnsibleAccessContext. This allows usage of variables
201
+ to be tracked. If needed, values can also be modified before being returned.
202
+ """
203
+
204
+ environment: AnsibleEnvironment # narrow the type specified by the base
205
+
206
+ def __init__(self, *args, **kwargs):
207
+ super(AnsibleContext, self).__init__(*args, **kwargs)
208
+
209
+ __repr__ = object.__repr__ # prevent Jinja from dumping vars in case this gets repr'd
210
+
211
+ def get_all(self):
212
+ """
213
+ Override Jinja's default get_all to return all vars in the context as a ChainMap with a mutable layer at the bottom.
214
+ This provides some isolation against accidental changes to inherited variable contexts without requiring copies.
215
+ """
216
+ layers = []
217
+
218
+ if self.vars:
219
+ layers.append(self.vars)
220
+ if self.parent:
221
+ layers.append(self.parent)
222
+
223
+ # HACK: always include a sacrificial plain-dict on the bottom layer, since Jinja's debug and stacktrace rewrite code invokes
224
+ # `__setitem__` outside a call context; this will ensure that it always occurs on a plain dict instead of a lazy one.
225
+ return ChainMap({}, *layers)
226
+
227
+ # noinspection PyShadowingBuiltins
228
+ def derived(self, locals: t.Optional[t.Dict[str, t.Any]] = None) -> Context:
229
+ # this is a clone of Jinja's impl of derived, but using our lazy-aware _new_context
230
+
231
+ context = _new_context(
232
+ environment=self.environment,
233
+ template_name=self.name,
234
+ blocks={},
235
+ shared=True,
236
+ jinja_locals=locals,
237
+ jinja_vars=self.get_all(),
238
+ )
239
+ context.eval_ctx = self.eval_ctx
240
+ context.blocks.update((k, list(v)) for k, v in self.blocks.items())
241
+ return context
242
+
243
+ def keys(self, *args, **kwargs):
244
+ """Base Context delegates to `dict.keys` against `get_all`, which would fail since we return a ChainMap. No known usage."""
245
+ raise NotImplementedError()
246
+
247
+ def values(self, *args, **kwargs):
248
+ """Base Context delegates to `dict.values` against `get_all`, which would fail since we return a ChainMap. No known usage."""
249
+ raise NotImplementedError()
250
+
251
+ def items(self, *args, **kwargs):
252
+ """Base Context delegates to built-in `dict.items` against `get_all`, which would fail since we return a ChainMap. No known usage."""
253
+ raise NotImplementedError()
254
+
255
+
256
+ @dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
257
+ class ArgSmuggler:
258
+ """
259
+ Utility wrapper to wrap/unwrap args passed to Jinja `Template.render` and `TemplateExpression.__call__`.
260
+ e.g., see https://github.com/pallets/jinja/blob/3.1.3/src/jinja2/environment.py#L1296 and
261
+ https://github.com/pallets/jinja/blob/3.1.3/src/jinja2/environment.py#L1566.
262
+ """
263
+
264
+ jinja_vars: c.Mapping[str, t.Any] | None
265
+
266
+ @classmethod
267
+ def package_jinja_vars(cls, jinja_vars: c.Mapping[str, t.Any]) -> dict[str, ArgSmuggler]:
268
+ """Wrap the supplied vars dict in an ArgSmuggler to prevent premature templating from Jinja's internal dict copy."""
269
+ return dict(_smuggled_vars=ArgSmuggler(jinja_vars=jinja_vars))
270
+
271
+ @classmethod
272
+ def extract_jinja_vars(cls, maybe_smuggled_vars: c.Mapping[str, t.Any] | None) -> c.Mapping[str, t.Any]:
273
+ """
274
+ If the supplied vars dict contains an ArgSmuggler instance with the expected key, unwrap it and return the smuggled value.
275
+ Otherwise, return the supplied dict as-is.
276
+ """
277
+ if maybe_smuggled_vars and ((smuggler := maybe_smuggled_vars.get('_smuggled_vars')) and isinstance(smuggler, ArgSmuggler)):
278
+ return smuggler.jinja_vars
279
+
280
+ return maybe_smuggled_vars
281
+
282
+
283
+ class AnsibleTemplateExpression:
284
+ """
285
+ Wrapper around Jinja's TemplateExpression for converting MarkerError back into Marker.
286
+ This is needed to make expression error handling consistent with templates, since Jinja does not support a custom type for Environment.compile_expression.
287
+ """
288
+
289
+ def __init__(self, template_expression: TemplateExpression) -> None:
290
+ self._template_expression = template_expression
291
+
292
+ def __call__(self, jinja_vars: c.Mapping[str, t.Any]) -> t.Any:
293
+ try:
294
+ return self._template_expression(ArgSmuggler.package_jinja_vars(jinja_vars))
295
+ except MarkerError as ex:
296
+ return ex.source
297
+
298
+
299
+ class AnsibleTemplate(Template):
300
+ """
301
+ A helper class, which prevents Jinja2 from running lazy containers through dict().
302
+ """
303
+
304
+ _python_source_temp_path: pathlib.Path | None = None
305
+
306
+ def __del__(self):
307
+ # DTFIX-RELEASE: this still isn't working reliably; something else must be keeping the template object alive
308
+ if self._python_source_temp_path:
309
+ self._python_source_temp_path.unlink(missing_ok=True)
310
+
311
+ def __call__(self, jinja_vars: c.Mapping[str, t.Any]) -> t.Any:
312
+ return self.render(ArgSmuggler.package_jinja_vars(jinja_vars))
313
+
314
+ # noinspection PyShadowingBuiltins
315
+ def new_context(
316
+ self,
317
+ vars: c.Mapping[str, t.Any] | None = None,
318
+ shared: bool = False,
319
+ locals: c.Mapping[str, t.Any] | None = None,
320
+ ) -> Context:
321
+ return _new_context(
322
+ environment=self.environment,
323
+ template_name=self.name,
324
+ blocks=self.blocks,
325
+ shared=shared,
326
+ jinja_locals=locals,
327
+ jinja_vars=ArgSmuggler.extract_jinja_vars(vars),
328
+ jinja_globals=self.globals,
329
+ )
330
+
331
+
332
+ class AnsibleCodeGenerator(NativeCodeGenerator):
333
+ """
334
+ Custom code generation behavior to support deprecated Ansible features and fill in gaps in Jinja native.
335
+ This can be removed once the deprecated Ansible features are removed and the native fixes are upstreamed in Jinja.
336
+ """
337
+
338
+ def _output_const_repr(self, group: t.Iterable[t.Any]) -> str:
339
+ """
340
+ Prevent Jinja's code generation from stringifying single nodes before generating its repr.
341
+ This complements the behavioral change in AnsibleEnvironment.concat which returns single nodes without stringifying them.
342
+ """
343
+ # DTFIX-FUTURE: contribute this upstream as a fix to Jinja's native support
344
+ group_list = list(group)
345
+
346
+ if len(group_list) == 1:
347
+ return repr(group_list[0])
348
+
349
+ # NB: This is slightly more efficient than Jinja's _output_const_repr, which generates a throw-away list instance to pass to join.
350
+ # Before removing this, ensure that upstream Jinja has this change.
351
+ return repr("".join(map(str, group_list)))
352
+
353
+ def visit_Const(self, node: Const, frame: Frame) -> None:
354
+ """
355
+ Override Jinja's visit_Const to inject a runtime call to AnsibleEnvironment._access_const for constant strings that are possibly templates, which
356
+ may require special handling at runtime. See that method for details. An example that hits this path:
357
+ {{ lookup("file", "{{ output_dir }}/bla") }}
358
+ """
359
+ value = node.as_const(frame.eval_ctx)
360
+
361
+ if _TemplateConfig.allow_embedded_templates and type(value) is str and is_possibly_template(value): # pylint: disable=unidiomatic-typecheck
362
+ # deprecated: description='embedded Jinja constant string template support' core_version='2.23'
363
+ self.write(f'environment._access_const({value!r})')
364
+ else:
365
+ # NB: This is actually more efficient than Jinja's visit_Const, which contains obsolete (as of Py2.7/3.1) float conversion instance checks. Before
366
+ # removing this override entirely, ensure that upstream Jinja has removed the obsolete code.
367
+ # See https://docs.python.org/release/2.7/whatsnew/2.7.html#python-3-1-features for more details.
368
+ self.write(repr(value))
369
+
370
+
371
+ @pass_context
372
+ def _ansible_finalize(_ctx: AnsibleContext, value: t.Any) -> t.Any:
373
+ """
374
+ This function is called by Jinja with the result of each variable template block (e.g., {{ }}) encountered in a template.
375
+ The pass_context decorator prevents finalize from being called on constants at template compile time.
376
+ The passed in AnsibleContext is unused -- it is the result of using the pass_context decorator.
377
+ The important part for us is that this blocks constant folding, which ensures our custom visit_Const is used.
378
+ It also ensures that template results are wrapped in lazy containers.
379
+ """
380
+ return lazify_container(value)
381
+
382
+
383
+ @dataclasses.dataclass(kw_only=True, slots=True)
384
+ class _TemplateCompileContext(_ambient_context.AmbientContextBase):
385
+ """
386
+ This context is active during Ansible's explicit compilation of templates/expressions, but not during Jinja's runtime compilation.
387
+ Historically, Ansible-specific pre-processing like `escape_backslashes` was not applied to imported/included templates.
388
+ """
389
+
390
+ escape_backslashes: bool
391
+
392
+
393
+ class _CompileStateSmugglingCtx(_ambient_context.AmbientContextBase):
394
+ template_source: str | None = None
395
+ python_source: str | None = None
396
+ python_source_temp_path: pathlib.Path | None = None
397
+
398
+
399
+ class AnsibleLexer(Lexer):
400
+ """
401
+ Lexer override to escape backslashes in string constants within Jinja expressions; prevents Jinja from double-escaping them.
402
+
403
+ NOTE: This behavior is only applied to string constants within Jinja expressions (eg {{ "c:\newfile" }}), *not* statements ("{% set foo="c:\\newfile" %}").
404
+
405
+ This is useful when templates are sourced from YAML double-quoted strings, as it avoids having backslashes processed twice: first by the
406
+ YAML parser, and then again by the Jinja parser. Instead, backslashes are only processed by YAML.
407
+
408
+ Example YAML:
409
+
410
+ - debug:
411
+ msg: "Test Case 1\\3; {{ test1_name | regex_replace('^(.*)_name$', '\\1')}}"
412
+
413
+ Since the outermost YAML string is double-quoted, the YAML parser converts the double backslashes to single backslashes. Without escaping, Jinja
414
+ would see only a single backslash ('\1') while processing the embedded template expression, interpret it as an escape sequence, and convert it
415
+ to '\x01' (ASCII "SOH"). This is clearly not the intended `\1` backreference argument to the `regex_replace` filter (which would require the
416
+ double-escaped string '\\\\1' to yield the intended result).
417
+
418
+ Since the "\\3" in the input YAML was not part of a template expression, the YAML-parsed "\3" remains after Jinja rendering. This would be
419
+ confusing for playbook authors, as different escaping rules would be needed inside and outside the template expression.
420
+
421
+ When templates are not sourced from YAML, escaping backslashes will prevent use of backslash escape sequences such as "\n" and "\t".
422
+
423
+ See relevant Jinja lexer impl at e.g.: https://github.com/pallets/jinja/blob/3.1.2/src/jinja2/lexer.py#L646-L653.
424
+ """
425
+
426
+ def tokeniter(self, *args, **kwargs) -> t.Iterator[t.Tuple[int, str, str]]:
427
+ """Pre-escape backslashes in expression ({{ }}) raw string constants before Jinja's Lexer.wrap() can interpret them as ASCII escape sequences."""
428
+ token_stream = super().tokeniter(*args, **kwargs)
429
+
430
+ # if we have no context, Jinja's doing a nested compile at runtime (eg, import/include); historically, no backslash escaping is performed
431
+ if not (tcc := _TemplateCompileContext.current(optional=True)) or not tcc.escape_backslashes:
432
+ yield from token_stream
433
+ return
434
+
435
+ in_variable = False
436
+
437
+ for token in token_stream:
438
+ token_type = token[1]
439
+
440
+ if token_type == TOKEN_VARIABLE_BEGIN:
441
+ in_variable = True
442
+ elif token_type == TOKEN_VARIABLE_END:
443
+ in_variable = False
444
+ elif in_variable and token_type == TOKEN_STRING:
445
+ token = token[0], token_type, token[2].replace('\\', '\\\\')
446
+
447
+ yield token
448
+
449
+
450
+ def defer_template_error(ex: Exception, variable: t.Any, *, is_expression: bool) -> Marker:
451
+ if not ex.__traceback__:
452
+ raise AssertionError('ex must be a previously raised exception')
453
+
454
+ if isinstance(ex, MarkerError):
455
+ return ex.source
456
+
457
+ exception_to_raise = create_template_error(ex, variable, is_expression)
458
+
459
+ if exception_to_raise is ex:
460
+ return CapturedExceptionMarker(ex) # capture the previously raised exception
461
+
462
+ try:
463
+ raise exception_to_raise from ex # raise the newly synthesized exception before capturing it
464
+ except Exception as captured_ex:
465
+ return CapturedExceptionMarker(captured_ex)
466
+
467
+
468
+ def create_template_error(ex: Exception, variable: t.Any, is_expression: bool) -> AnsibleTemplateError:
469
+ if isinstance(ex, AnsibleTemplateError):
470
+ exception_to_raise = ex
471
+ else:
472
+ kind = "expression" if is_expression else "template"
473
+ ex_type = AnsibleTemplateError # always raise an AnsibleTemplateError/subclass
474
+
475
+ if isinstance(ex, RecursionError):
476
+ msg = f"Recursive loop detected in {kind}."
477
+ elif isinstance(ex, TemplateSyntaxError):
478
+ msg = f"Syntax error in {kind}."
479
+
480
+ if is_expression and is_possibly_template(variable):
481
+ msg += " Template delimiters are not supported in expressions."
482
+
483
+ ex_type = AnsibleTemplateSyntaxError
484
+ else:
485
+ msg = f"Error rendering {kind}."
486
+
487
+ exception_to_raise = ex_type(msg, obj=variable)
488
+
489
+ if exception_to_raise.obj is None:
490
+ exception_to_raise.obj = TemplateContext.current().template_value
491
+
492
+ # DTFIX-FUTURE: Look through the TemplateContext hierarchy to find the most recent non-template
493
+ # caller and use that for origin when no origin is available on obj. This could be useful for situations where the template
494
+ # was embedded in a plugin, or a plugin is otherwise responsible for losing the origin and/or trust. We can't just use the first
495
+ # non-template caller as that will lead to false positives for re-entrant calls (e.g. template plugins that call into templar).
496
+
497
+ return exception_to_raise
498
+
499
+
500
+ # DTFIX-RELEASE: implement CapturedExceptionMarker deferral support on call (and lookup), filter/test plugins, etc.
501
+ # also update the protomatter integration test once this is done (the test was written differently since this wasn't done yet)
502
+
503
+ _BUILTIN_FILTER_ALIASES: dict[str, str] = {}
504
+ _BUILTIN_TEST_ALIASES: dict[str, str] = {
505
+ '!=': 'ne',
506
+ '<': 'lt',
507
+ '<=': 'le',
508
+ '==': 'eq',
509
+ '>': 'gt',
510
+ '>=': 'ge',
511
+ }
512
+
513
+ _BUILTIN_FILTERS = filter_loader._wrap_funcs(defaults.DEFAULT_FILTERS, _BUILTIN_FILTER_ALIASES)
514
+ _BUILTIN_TESTS = test_loader._wrap_funcs(t.cast(dict[str, t.Callable], defaults.DEFAULT_TESTS), _BUILTIN_TEST_ALIASES)
515
+
516
+
517
+ class AnsibleEnvironment(ImmutableSandboxedEnvironment):
518
+ """
519
+ Our custom environment, which simply allows us to override the class-level
520
+ values for the Template and Context classes used by jinja2 internally.
521
+ """
522
+
523
+ context_class = AnsibleContext
524
+ template_class = AnsibleTemplate
525
+ code_generator_class = AnsibleCodeGenerator
526
+ intercepted_binops = frozenset(('eq',))
527
+
528
+ _lexer_cache = LRUCache(50)
529
+
530
+ # DTFIX-FUTURE: bikeshed a name/mechanism to control template debugging
531
+ _debuggable_template_source = False
532
+ _debuggable_template_source_path: pathlib.Path = pathlib.Path(__file__).parent.parent.parent.parent / '.template_debug_source'
533
+
534
+ def __init__(self, *args, ansible_basedir: str | None = None, **kwargs) -> None:
535
+ if ansible_basedir:
536
+ kwargs.update(loader=FileSystemLoader(ansible_basedir))
537
+
538
+ super().__init__(*args, extensions=_TemplateConfig.jinja_extensions, **kwargs)
539
+
540
+ self.filters = JinjaPluginIntercept(_BUILTIN_FILTERS, filter_loader) # type: ignore[assignment]
541
+ self.tests = JinjaPluginIntercept(_BUILTIN_TESTS, test_loader) # type: ignore[assignment,arg-type]
542
+
543
+ # future Jinja releases may default-enable autoescape; force-disable to prevent the problems it could cause
544
+ # see https://github.com/pallets/jinja/blob/3.1.2/docs/api.rst?plain=1#L69
545
+ self.autoescape = False
546
+
547
+ self.trim_blocks = True
548
+
549
+ self.undefined = UndefinedMarker
550
+ self.finalize = _ansible_finalize
551
+
552
+ self.globals.update(
553
+ range=range, # the sandboxed environment limits range in ways that may cause us problems; use the real Python one
554
+ now=_now,
555
+ undef=_undef,
556
+ omit=Omit,
557
+ lookup=_lookup,
558
+ query=_query,
559
+ q=_query,
560
+ )
561
+
562
+ # Disabling the optimizer prevents compile-time constant expression folding, which prevents our
563
+ # visit_Const recursive inline template expansion tricks from working in many cases where Jinja's
564
+ # ignorance of our embedded templates are optimized away as fully-constant expressions,
565
+ # eg {{ "{{'hi'}}" == "hi" }}. As of Jinja ~3.1, this specifically avoids cases where the @optimizeconst
566
+ # visitor decorator performs constant folding, which bypasses our visit_Const impl and causes embedded
567
+ # templates to be lost.
568
+ # See also optimizeconst impl: https://github.com/pallets/jinja/blob/3.1.0/src/jinja2/compiler.py#L48-L49
569
+ self.optimized = False
570
+
571
+ def get_template(
572
+ self,
573
+ name: str | Template,
574
+ parent: str | None = None,
575
+ globals: c.MutableMapping[str, t.Any] | None = None,
576
+ ) -> Template:
577
+ """Ensures that templates built via `get_template` are also source debuggable."""
578
+ with _CompileStateSmugglingCtx.when(self._debuggable_template_source) as ctx:
579
+ template_obj = t.cast(AnsibleTemplate, super().get_template(name, parent, globals))
580
+
581
+ if isinstance(ctx, _CompileStateSmugglingCtx): # only present if debugging is enabled
582
+ template_obj._python_source_temp_path = ctx.python_source_temp_path # facilitate deletion of the temp file when template_obj is deleted
583
+
584
+ return template_obj
585
+
586
+ @property
587
+ def lexer(self) -> AnsibleLexer:
588
+ """Return/cache an AnsibleLexer with settings from the current AnsibleEnvironment"""
589
+ # DTFIX-RELEASE: optimization - we should pre-generate the default cached lexer before forking, not leave it to chance (e.g. simple playbooks)
590
+ key = tuple(getattr(self, name) for name in _TEMPLATE_OVERRIDE_FIELD_NAMES)
591
+
592
+ lex = self._lexer_cache.get(key)
593
+
594
+ if lex is None:
595
+ self._lexer_cache[key] = lex = AnsibleLexer(self)
596
+
597
+ return lex
598
+
599
+ def call_filter(
600
+ self,
601
+ name: str,
602
+ value: t.Any,
603
+ args: c.Sequence[t.Any] | None = None,
604
+ kwargs: c.Mapping[str, t.Any] | None = None,
605
+ context: Context | None = None,
606
+ eval_ctx: EvalContext | None = None,
607
+ ) -> t.Any:
608
+ """
609
+ Ensure that filters directly invoked by plugins will see non-templating lazy containers.
610
+ Without this, `_wrap_filter` will wrap `args` and `kwargs` in templating lazy containers.
611
+ This provides consistency with plugin output handling by preventing auto-templating of trusted templates passed in native containers.
612
+ """
613
+ # DTFIX-RELEASE: need better logic to handle non-list/non-dict inputs for args/kwargs
614
+ args = _AnsibleLazyTemplateMixin._try_create(list(args or []), LazyOptions.SKIP_TEMPLATES)
615
+ kwargs = _AnsibleLazyTemplateMixin._try_create(kwargs, LazyOptions.SKIP_TEMPLATES)
616
+
617
+ return super().call_filter(name, value, args, kwargs, context, eval_ctx)
618
+
619
+ def call_test(
620
+ self,
621
+ name: str,
622
+ value: t.Any,
623
+ args: c.Sequence[t.Any] | None = None,
624
+ kwargs: c.Mapping[str, t.Any] | None = None,
625
+ context: Context | None = None,
626
+ eval_ctx: EvalContext | None = None,
627
+ ) -> t.Any:
628
+ """
629
+ Ensure that tests directly invoked by plugins will see non-templating lazy containers.
630
+ Without this, `_wrap_test` will wrap `args` and `kwargs` in templating lazy containers.
631
+ This provides consistency with plugin output handling by preventing auto-templating of trusted templates passed in native containers.
632
+ """
633
+ # DTFIX-RELEASE: need better logic to handle non-list/non-dict inputs for args/kwargs
634
+ args = _AnsibleLazyTemplateMixin._try_create(list(args or []), LazyOptions.SKIP_TEMPLATES)
635
+ kwargs = _AnsibleLazyTemplateMixin._try_create(kwargs, LazyOptions.SKIP_TEMPLATES)
636
+
637
+ return super().call_test(name, value, args, kwargs, context, eval_ctx)
638
+
639
+ def compile_expression(self, source: str, *args, **kwargs) -> TemplateExpression:
640
+ # compile_expression parses and passes the tree to from_string; for debug support, activate the context here to capture the intermediate results
641
+ with _CompileStateSmugglingCtx.when(self._debuggable_template_source) as ctx:
642
+ if isinstance(ctx, _CompileStateSmugglingCtx): # only present if debugging is enabled
643
+ ctx.template_source = source
644
+
645
+ return super().compile_expression(source, *args, **kwargs)
646
+
647
+ def from_string(self, source: str | jinja2.nodes.Template, *args, **kwargs) -> AnsibleTemplate:
648
+ # if debugging is enabled, use existing context when present (e.g., from compile_expression)
649
+ current_ctx = _CompileStateSmugglingCtx.current(optional=True) if self._debuggable_template_source else None
650
+
651
+ with _CompileStateSmugglingCtx.when(self._debuggable_template_source and not current_ctx) as new_ctx:
652
+ template_obj = t.cast(AnsibleTemplate, super().from_string(source, *args, **kwargs))
653
+
654
+ if isinstance(ctx := current_ctx or new_ctx, _CompileStateSmugglingCtx): # only present if debugging is enabled
655
+ template_obj._python_source_temp_path = ctx.python_source_temp_path # facilitate deletion of the temp file when template_obj is deleted
656
+
657
+ return template_obj
658
+
659
+ def _parse(self, source: str, *args, **kwargs) -> jinja2.nodes.Template:
660
+ if csc := _CompileStateSmugglingCtx.current(optional=True):
661
+ csc.template_source = source
662
+
663
+ return super()._parse(source, *args, **kwargs)
664
+
665
+ def _compile(self, source: str, filename: str) -> types.CodeType:
666
+ if csc := _CompileStateSmugglingCtx.current(optional=True):
667
+ origin = Origin.get_tag(csc.template_source) or Origin.UNKNOWN
668
+
669
+ source = '\n'.join(
670
+ (
671
+ "import sys; breakpoint() if type(sys.breakpointhook) is not type(breakpoint) else None",
672
+ f"# original template source from {str(origin)!r}: ",
673
+ '\n'.join(f'# {line}' for line in (csc.template_source or '').splitlines()),
674
+ source,
675
+ )
676
+ )
677
+
678
+ source_temp_dir = self._debuggable_template_source_path
679
+ source_temp_dir.mkdir(parents=True, exist_ok=True)
680
+
681
+ with tempfile.NamedTemporaryFile(dir=source_temp_dir, mode='w', suffix='.py', prefix='j2_src_', delete=False) as source_file:
682
+ filename = source_file.name
683
+
684
+ source_file.write(source)
685
+ source_file.flush()
686
+
687
+ csc.python_source = source
688
+ csc.python_source_temp_path = pathlib.Path(filename)
689
+
690
+ res = super()._compile(source, filename)
691
+
692
+ return res
693
+
694
+ @staticmethod
695
+ def concat(nodes: t.Iterable[t.Any]) -> t.Any: # type: ignore[override]
696
+ node_list = list(_flatten_nodes(nodes))
697
+
698
+ if not node_list:
699
+ return None
700
+
701
+ # this code is complemented by our tweaked CodeGenerator _output_const_repr that ensures that literal constants
702
+ # in templates aren't double-repr'd in the generated code
703
+ if len(node_list) == 1:
704
+ # DTFIX-RELEASE: determine if we should do managed access here (we *should* have hit them all during templating/resolve, but ?)
705
+ return node_list[0]
706
+
707
+ # In order to ensure that all markers are tripped, do a recursive finalize before we repr (otherwise we can end up
708
+ # repr'ing a Marker). This requires two passes, but avoids the need for a parallel reimplementation of all repr methods.
709
+ try:
710
+ node_list = _finalize_template_result(node_list, FinalizeMode.CONCAT)
711
+ except MarkerError as ex:
712
+ return ex.source # return the first Marker encountered
713
+
714
+ return ''.join([to_text(v) for v in node_list])
715
+
716
+ @staticmethod
717
+ def _access_const(const_template: t.LiteralString) -> t.Any:
718
+ """
719
+ Called during template rendering on template-looking string constants embedded in the template.
720
+ It provides the following functionality:
721
+ * Propagates origin from the containing template.
722
+ * For backward compatibility when embedded templates are enabled:
723
+ * Conditionals - Renders embedded template constants and accesses the result. Warns on each constant immediately.
724
+ * Non-conditionals - Tags constants for deferred rendering of templates in lookup terms. Warns on each constant during lookup invocation.
725
+ """
726
+ ctx = TemplateContext.current()
727
+
728
+ if (tv := ctx.template_value) and (origin := Origin.get_tag(tv)):
729
+ const_template = origin.tag(const_template)
730
+
731
+ if ctx._render_jinja_const_template:
732
+ _jinja_const_template_warning(const_template, is_conditional=True)
733
+
734
+ result = ctx.templar.template(TrustedAsTemplate().tag(const_template))
735
+ AnsibleAccessContext.current().access(result)
736
+ else:
737
+ # warnings will be issued when lookup terms processing occurs, to avoid false positives
738
+ result = _JinjaConstTemplate().tag(const_template)
739
+
740
+ return result
741
+
742
+ def getitem(self, obj: t.Any, argument: t.Any) -> t.Any:
743
+ value = super().getitem(obj, argument)
744
+
745
+ AnsibleAccessContext.current().access(value)
746
+
747
+ return value
748
+
749
+ def getattr(self, obj: t.Any, attribute: str) -> t.Any:
750
+ """
751
+ Get `attribute` from the attributes of `obj`, falling back to items in `obj`.
752
+ If no item was found, return a sandbox-specific `UndefinedMarker` if `attribute` is protected by the sandbox,
753
+ otherwise return a normal `UndefinedMarker` instance.
754
+ This differs from the built-in Jinja behavior which will not fall back to items if `attribute` is protected by the sandbox.
755
+ """
756
+ # example template that uses this: "{{ some.thing }}" -- obj is the "some" dict, attribute is "thing"
757
+
758
+ is_safe = True
759
+
760
+ try:
761
+ value = getattr(obj, attribute)
762
+ except AttributeError:
763
+ value = _sentinel
764
+ else:
765
+ if not (is_safe := self.is_safe_attribute(obj, attribute, value)):
766
+ value = _sentinel
767
+
768
+ if value is _sentinel:
769
+ try:
770
+ value = obj[attribute]
771
+ except (TypeError, LookupError):
772
+ return self.undefined(obj=obj, name=attribute) if is_safe else self.unsafe_undefined(obj, attribute)
773
+
774
+ AnsibleAccessContext.current().access(value)
775
+
776
+ return value
777
+
778
+ def call(
779
+ self,
780
+ __context: Context,
781
+ __obj: t.Any,
782
+ *args: t.Any,
783
+ **kwargs: t.Any,
784
+ ) -> t.Any:
785
+ if _DirectCall.is_marked(__obj):
786
+ # Both `_lookup` and `_query` handle arg proxying and `Marker` args internally.
787
+ # Performing either before calling them will interfere with that processing.
788
+ return super().call(__context, __obj, *args, **kwargs)
789
+
790
+ if (first_marker := get_first_marker_arg(args, kwargs)) is not None:
791
+ return first_marker
792
+
793
+ try:
794
+ with JinjaCallContext(accept_lazy_markers=False):
795
+ call_res = super().call(__context, __obj, *lazify_container_args(args), **lazify_container_kwargs(kwargs))
796
+
797
+ if __obj is range:
798
+ # Preserve the ability to do `range(1000000000) | random` by not converting range objects to lists.
799
+ # Historically, range objects were only converted on Jinja finalize and filter outputs, so they've always been floating around in templating
800
+ # code and visible to user plugins.
801
+ return call_res
802
+
803
+ return _wrap_plugin_output(call_res)
804
+
805
+ except MarkerError as ex:
806
+ return ex.source
807
+
808
+
809
+ AnsibleTemplate.environment_class = AnsibleEnvironment
810
+
811
+ _DEFAULT_UNDEF = UndefinedMarker("Mandatory variable has not been overridden", _no_template_source=True)
812
+
813
+ _sentinel: t.Final[object] = object()
814
+
815
+
816
+ @_DirectCall.mark
817
+ def _undef(hint=None):
818
+ """Jinja2 global function (undef) for creating getting a `UndefinedMarker` instance, optionally with a custom hint."""
819
+ validate_arg_type('hint', hint, (str, type(None)))
820
+
821
+ if not hint:
822
+ return _DEFAULT_UNDEF
823
+
824
+ return UndefinedMarker(hint)
825
+
826
+
827
+ def _flatten_nodes(nodes: t.Iterable[t.Any]) -> t.Iterable[t.Any]:
828
+ """
829
+ Yield nodes from a potentially recursive iterable of nodes.
830
+ The recursion is required to expand template imports (TemplateModule).
831
+ Any exception raised while consuming a template node will be yielded as a Marker for that node.
832
+ """
833
+ iterator = iter(nodes)
834
+
835
+ while True:
836
+ try:
837
+ node = next(iterator)
838
+ except StopIteration:
839
+ break
840
+ except Exception as ex:
841
+ yield defer_template_error(ex, TemplateContext.current().template_value, is_expression=False)
842
+ # DTFIX-FUTURE: We should be able to determine if truncation occurred by having the code generator smuggle out the number of expected nodes.
843
+ yield TruncationMarker()
844
+ else:
845
+ if type(node) is TemplateModule: # pylint: disable=unidiomatic-typecheck
846
+ yield from _flatten_nodes(node._body_stream)
847
+ else:
848
+ yield node
849
+
850
+
851
+ def _flatten_and_lazify_vars(mapping: c.Mapping) -> t.Iterable[c.Mapping]:
852
+ """Prevent deeply-nested Jinja vars ChainMaps from being created by nested contexts and ensure that all top-level containers support lazy templating."""
853
+ mapping_type = type(mapping)
854
+ if mapping_type is ChainMap:
855
+ # noinspection PyUnresolvedReferences
856
+ for m in mapping.maps:
857
+ yield from _flatten_and_lazify_vars(m)
858
+ elif mapping_type is _AnsibleLazyTemplateDict:
859
+ if not mapping:
860
+ # DTFIX-RELEASE: handle or remove?
861
+ raise Exception("we didn't think it was possible to have an empty lazy here...")
862
+ yield mapping
863
+ elif mapping_type in (dict, _AnsibleTaggedDict):
864
+ # don't propagate empty dictionary layers
865
+ if mapping:
866
+ yield _AnsibleLazyTemplateMixin._try_create(mapping)
867
+ else:
868
+ raise NotImplementedError(f"unsupported mapping type in Jinja vars: {mapping_type}")
869
+
870
+
871
+ def _new_context(
872
+ *,
873
+ environment: Environment,
874
+ template_name: str | None,
875
+ blocks: dict[str, t.Callable[[Context], c.Iterator[str]]],
876
+ shared: bool = False,
877
+ jinja_locals: c.Mapping[str, t.Any] | None = None,
878
+ jinja_vars: c.Mapping[str, t.Any] | None = None,
879
+ jinja_globals: c.MutableMapping[str, t.Any] | None = None,
880
+ ) -> Context:
881
+ """Override Jinja's context vars setup to use ChainMaps and containers that support lazy templating."""
882
+ layers = []
883
+
884
+ if jinja_locals:
885
+ # DTFIX-RELEASE: if we can't trip this in coverage, kill it off?
886
+ if type(jinja_locals) is not dict: # pylint: disable=unidiomatic-typecheck
887
+ raise NotImplementedError("locals must be a dict")
888
+
889
+ # Omit values set to Jinja's internal `missing` sentinel; they are locals that have not yet been
890
+ # initialized in the current context, and should not be exposed to child contexts. e.g.: {% import 'a' as b with context %}.
891
+ # The `b` local will be `missing` in the `a` context and should not be propagated as a local to the child context we're creating.
892
+ layers.append(_AnsibleLazyTemplateMixin._try_create({k: v for k, v in jinja_locals.items() if v is not missing}))
893
+
894
+ if jinja_vars:
895
+ layers.extend(_flatten_and_lazify_vars(jinja_vars))
896
+
897
+ if jinja_globals and not shared:
898
+ # Even though we don't currently support templating globals, it's easier to ensure that everything is template-able rather than trying to
899
+ # pick apart the ChainMaps to enforce non-template-able globals, or to risk things that *should* be template-able not being lazified.
900
+ layers.extend(_flatten_and_lazify_vars(jinja_globals))
901
+
902
+ if not layers:
903
+ # ensure we have at least one layer (which should be lazy), since _flatten_and_lazify_vars eliminates most empty layers
904
+ layers.append(_AnsibleLazyTemplateMixin._try_create({}))
905
+
906
+ # only return a ChainMap if we're combining layers, or we have none
907
+ parent = layers[0] if len(layers) == 1 else ChainMap(*layers)
908
+
909
+ # the `parent` cast is only to satisfy Jinja's overly-strict type hint
910
+ return environment.context_class(environment, t.cast(dict, parent), template_name, blocks, globals=jinja_globals)
911
+
912
+
913
+ def is_possibly_template(value: str, overrides: TemplateOverrides = TemplateOverrides.DEFAULT):
914
+ """
915
+ A lightweight check to determine if the given string looks like it contains a template, even if that template is invalid.
916
+ Returns `True` if the given string starts with a Jinja overrides header or if it contains template start strings.
917
+ """
918
+ return value.startswith(JINJA2_OVERRIDE) or overrides._contains_start_string(value)
919
+
920
+
921
+ def is_possibly_all_template(value: str, overrides: TemplateOverrides = TemplateOverrides.DEFAULT):
922
+ """
923
+ A lightweight check to determine if the given string looks like it contains *only* a template, even if that template is invalid.
924
+ Returns `True` if the given string starts with a Jinja overrides header or if it starts and ends with Jinja template delimiters.
925
+ """
926
+ return value.startswith(JINJA2_OVERRIDE) or overrides._starts_and_ends_with_jinja_delimiters(value)
927
+
928
+
929
+ class FinalizeMode(enum.Enum):
930
+ TOP_LEVEL = enum.auto()
931
+ CONCAT = enum.auto()
932
+
933
+
934
+ _FINALIZE_FAST_PATH_EXACT_MAPPING_TYPES = frozenset(
935
+ (
936
+ dict,
937
+ _AnsibleTaggedDict,
938
+ _AnsibleLazyTemplateDict,
939
+ HostVars,
940
+ HostVarsVars,
941
+ )
942
+ )
943
+ """Fast-path exact mapping types for finalization. These types bypass diagnostic warnings for type conversion."""
944
+
945
+ _FINALIZE_FAST_PATH_EXACT_ITERABLE_TYPES = frozenset(
946
+ (
947
+ list,
948
+ _AnsibleTaggedList,
949
+ _AnsibleLazyTemplateList,
950
+ tuple,
951
+ _AnsibleTaggedTuple,
952
+ _AnsibleLazyAccessTuple,
953
+ )
954
+ )
955
+ """Fast-path exact iterable types for finalization. These types bypass diagnostic warnings for type conversion."""
956
+
957
+ _FINALIZE_DISALLOWED_EXACT_TYPES = frozenset((range,))
958
+ """Exact types that cannot be finalized."""
959
+
960
+ # Jinja passes these into filters/tests via @pass_environment
961
+ register_known_types(
962
+ AnsibleContext,
963
+ AnsibleEnvironment,
964
+ EvalContext,
965
+ )
966
+
967
+
968
+ def _finalize_dict(o: t.Any, mode: FinalizeMode) -> t.Iterator[tuple[t.Any, t.Any]]:
969
+ for k, v in o.items():
970
+ if v is not Omit:
971
+ yield _finalize_template_result(k, mode), _finalize_template_result(v, mode)
972
+
973
+
974
+ def _finalize_list(o: t.Any, mode: FinalizeMode) -> t.Iterator[t.Any]:
975
+ for v in o:
976
+ if v is not Omit:
977
+ yield _finalize_template_result(v, mode)
978
+
979
+
980
+ def _maybe_finalize_scalar(o: t.Any) -> t.Any:
981
+ # DTFIX-RELEASE: this should check all supported scalar subclasses, not just JSON ones (also, does the JSON serializer handle these cases?)
982
+ for target_type in _json_subclassable_scalar_types:
983
+ if not isinstance(o, target_type):
984
+ continue
985
+
986
+ match _TemplateConfig.unknown_type_conversion_handler.action:
987
+ # we don't want to show the object value, and it can't be Origin-tagged; send the current template value for best effort
988
+ case ErrorAction.WARN:
989
+ display.warning(
990
+ msg=f'Type {native_type_name(o)!r} is unsupported in variable storage, converting to {native_type_name(target_type)!r}.',
991
+ obj=TemplateContext.current(optional=True).template_value,
992
+ )
993
+ case ErrorAction.FAIL:
994
+ raise AnsibleVariableTypeError.from_value(obj=TemplateContext.current(optional=True).template_value)
995
+
996
+ return target_type(o)
997
+
998
+ return None
999
+
1000
+
1001
+ def _finalize_fallback_collection(
1002
+ o: t.Any,
1003
+ mode: FinalizeMode,
1004
+ finalizer: t.Callable[[t.Any, FinalizeMode], t.Iterator],
1005
+ target_type: type[list | dict],
1006
+ ) -> t.Collection[t.Any]:
1007
+ match _TemplateConfig.unknown_type_conversion_handler.action:
1008
+ # we don't want to show the object value, and it can't be Origin-tagged; send the current template value for best effort
1009
+ case ErrorAction.WARN:
1010
+ display.warning(
1011
+ msg=f'Type {native_type_name(o)!r} is unsupported in variable storage, converting to {native_type_name(target_type)!r}.',
1012
+ obj=TemplateContext.current(optional=True).template_value,
1013
+ )
1014
+ case ErrorAction.FAIL:
1015
+ raise AnsibleVariableTypeError.from_value(obj=TemplateContext.current(optional=True).template_value)
1016
+
1017
+ return _finalize_collection(o, mode, finalizer, target_type)
1018
+
1019
+
1020
+ def _finalize_collection(
1021
+ o: t.Any,
1022
+ mode: FinalizeMode,
1023
+ finalizer: t.Callable[[t.Any, FinalizeMode], t.Iterator],
1024
+ target_type: type[list | dict],
1025
+ ) -> t.Collection[t.Any]:
1026
+ return AnsibleTagHelper.tag(finalizer(o, mode), AnsibleTagHelper.tags(o), value_type=target_type)
1027
+
1028
+
1029
+ def _finalize_template_result(o: t.Any, mode: FinalizeMode) -> t.Any:
1030
+ """Recurse the template result, rendering any encountered templates, converting containers to non-lazy versions."""
1031
+ # DTFIX-RELEASE: add tests to ensure this method doesn't drift from allowed types
1032
+ o_type = type(o)
1033
+
1034
+ # DTFIX-FUTURE: provide an optional way to check for trusted templates leaking out of templating (injected, but not passed through templar.template)
1035
+
1036
+ if o_type is _AnsibleTaggedStr:
1037
+ return _JinjaConstTemplate.untag(o) # prevent _JinjaConstTemplate from leaking into finalized results
1038
+
1039
+ if o_type in PASS_THROUGH_SCALAR_VAR_TYPES:
1040
+ return o
1041
+
1042
+ if o_type in _FINALIZE_FAST_PATH_EXACT_MAPPING_TYPES: # silently convert known mapping types to dict
1043
+ return _finalize_collection(o, mode, _finalize_dict, dict)
1044
+
1045
+ if o_type in _FINALIZE_FAST_PATH_EXACT_ITERABLE_TYPES: # silently convert known sequence types to list
1046
+ return _finalize_collection(o, mode, _finalize_list, list)
1047
+
1048
+ if o_type in Marker.concrete_subclasses: # this early return assumes handle_marker follows our variable type rules
1049
+ return TemplateContext.current().templar.marker_behavior.handle_marker(o)
1050
+
1051
+ if mode is not FinalizeMode.TOP_LEVEL: # unsupported type (do not raise)
1052
+ return o
1053
+
1054
+ if o_type in _FINALIZE_DISALLOWED_EXACT_TYPES: # early abort for disallowed types that would otherwise be handled below
1055
+ raise AnsibleVariableTypeError.from_value(obj=o)
1056
+
1057
+ if _internal.is_intermediate_mapping(o): # since isinstance checks are slower, this is separate from the exact type check above
1058
+ return _finalize_fallback_collection(o, mode, _finalize_dict, dict)
1059
+
1060
+ if _internal.is_intermediate_iterable(o): # since isinstance checks are slower, this is separate from the exact type check above
1061
+ return _finalize_fallback_collection(o, mode, _finalize_list, list)
1062
+
1063
+ if (result := _maybe_finalize_scalar(o)) is not None:
1064
+ return result
1065
+
1066
+ raise AnsibleVariableTypeError.from_value(obj=o)