ansible-core 2.18.7rc1__py3-none-any.whl → 2.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ansible-core might be problematic. Click here for more details.

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