ansible-core 2.18.5rc1__py3-none-any.whl → 2.19.0b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (710) hide show
  1. ansible/_internal/__init__.py +53 -0
  2. ansible/_internal/_ansiballz.py +265 -0
  3. ansible/_internal/_collection_proxy.py +47 -0
  4. ansible/_internal/_datatag/__init__.py +0 -0
  5. ansible/_internal/_datatag/_tags.py +130 -0
  6. ansible/_internal/_datatag/_utils.py +19 -0
  7. ansible/_internal/_datatag/_wrappers.py +33 -0
  8. ansible/_internal/_errors/__init__.py +0 -0
  9. ansible/_internal/_errors/_captured.py +128 -0
  10. ansible/_internal/_errors/_handler.py +91 -0
  11. ansible/_internal/_errors/_utils.py +310 -0
  12. ansible/_internal/_json/__init__.py +203 -0
  13. ansible/_internal/_json/_legacy_encoder.py +34 -0
  14. ansible/_internal/_json/_profiles/__init__.py +0 -0
  15. ansible/_internal/_json/_profiles/_cache_persistence.py +55 -0
  16. ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
  17. ansible/_internal/_json/_profiles/_legacy.py +197 -0
  18. ansible/_internal/_locking.py +21 -0
  19. ansible/_internal/_plugins/__init__.py +0 -0
  20. ansible/_internal/_plugins/_cache.py +57 -0
  21. ansible/_internal/_task.py +78 -0
  22. ansible/_internal/_templating/__init__.py +10 -0
  23. ansible/_internal/_templating/_access.py +86 -0
  24. ansible/_internal/_templating/_chain_templar.py +63 -0
  25. ansible/_internal/_templating/_datatag.py +95 -0
  26. ansible/_internal/_templating/_engine.py +588 -0
  27. ansible/_internal/_templating/_errors.py +28 -0
  28. ansible/_internal/_templating/_jinja_bits.py +1066 -0
  29. ansible/_internal/_templating/_jinja_common.py +332 -0
  30. ansible/_internal/_templating/_jinja_patches.py +44 -0
  31. ansible/_internal/_templating/_jinja_plugins.py +345 -0
  32. ansible/_internal/_templating/_lazy_containers.py +633 -0
  33. ansible/_internal/_templating/_marker_behaviors.py +103 -0
  34. ansible/_internal/_templating/_transform.py +63 -0
  35. ansible/_internal/_templating/_utils.py +107 -0
  36. ansible/_internal/_wrapt.py +1052 -0
  37. ansible/_internal/_yaml/__init__.py +0 -0
  38. ansible/_internal/_yaml/_constructor.py +240 -0
  39. ansible/_internal/_yaml/_dumper.py +62 -0
  40. ansible/_internal/_yaml/_errors.py +166 -0
  41. ansible/_internal/_yaml/_loader.py +66 -0
  42. ansible/_internal/ansible_collections/ansible/_protomatter/README.md +11 -0
  43. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/action/debug.py +36 -0
  44. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/apply_trust.py +19 -0
  45. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +18 -0
  46. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/finalize.py +16 -0
  47. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/origin.py +18 -0
  48. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.py +24 -0
  49. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.yml +33 -0
  50. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/tag_names.py +16 -0
  51. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +17 -0
  52. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +49 -0
  53. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.py +21 -0
  54. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.yml +2 -0
  55. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.py +15 -0
  56. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.yml +19 -0
  57. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.py +18 -0
  58. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.yml +19 -0
  59. ansible/cli/__init__.py +159 -89
  60. ansible/cli/_ssh_askpass.py +47 -0
  61. ansible/cli/adhoc.py +14 -7
  62. ansible/cli/arguments/option_helpers.py +154 -7
  63. ansible/cli/config.py +43 -68
  64. ansible/cli/console.py +10 -8
  65. ansible/cli/doc.py +62 -53
  66. ansible/cli/galaxy.py +27 -20
  67. ansible/cli/inventory.py +28 -26
  68. ansible/cli/playbook.py +4 -12
  69. ansible/cli/pull.py +51 -11
  70. ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
  71. ansible/cli/vault.py +12 -11
  72. ansible/compat/__init__.py +2 -2
  73. ansible/config/base.yml +166 -112
  74. ansible/config/manager.py +52 -49
  75. ansible/constants.py +3 -4
  76. ansible/errors/__init__.py +277 -235
  77. ansible/executor/interpreter_discovery.py +28 -149
  78. ansible/executor/module_common.py +426 -493
  79. ansible/executor/play_iterator.py +22 -27
  80. ansible/executor/playbook_executor.py +11 -11
  81. ansible/executor/powershell/async_watchdog.ps1 +97 -102
  82. ansible/executor/powershell/async_wrapper.ps1 +202 -151
  83. ansible/executor/powershell/become_wrapper.ps1 +89 -144
  84. ansible/executor/powershell/bootstrap_wrapper.ps1 +24 -9
  85. ansible/executor/powershell/coverage_wrapper.ps1 +82 -135
  86. ansible/executor/powershell/exec_wrapper.ps1 +462 -196
  87. ansible/executor/powershell/module_manifest.py +417 -265
  88. ansible/executor/powershell/module_wrapper.ps1 +169 -186
  89. ansible/executor/powershell/psrp_fetch_file.ps1 +41 -0
  90. ansible/executor/powershell/psrp_put_file.ps1 +122 -0
  91. ansible/executor/powershell/winrm_fetch_file.ps1 +46 -0
  92. ansible/executor/powershell/winrm_put_file.ps1 +36 -0
  93. ansible/executor/process/worker.py +161 -96
  94. ansible/executor/stats.py +5 -5
  95. ansible/executor/task_executor.py +268 -258
  96. ansible/executor/task_queue_manager.py +124 -90
  97. ansible/executor/task_result.py +183 -78
  98. ansible/galaxy/__init__.py +2 -2
  99. ansible/galaxy/api.py +22 -18
  100. ansible/galaxy/collection/__init__.py +1 -1
  101. ansible/galaxy/collection/concrete_artifact_manager.py +8 -11
  102. ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
  103. ansible/galaxy/dependency_resolution/providers.py +1 -1
  104. ansible/galaxy/dependency_resolution/reporters.py +81 -0
  105. ansible/galaxy/role.py +4 -8
  106. ansible/galaxy/token.py +28 -21
  107. ansible/inventory/data.py +47 -57
  108. ansible/inventory/group.py +44 -72
  109. ansible/inventory/helpers.py +9 -0
  110. ansible/inventory/host.py +32 -54
  111. ansible/inventory/manager.py +78 -34
  112. ansible/keyword_desc.yml +1 -1
  113. ansible/module_utils/_internal/__init__.py +55 -0
  114. ansible/module_utils/_internal/_ambient_context.py +58 -0
  115. ansible/module_utils/_internal/_ansiballz.py +133 -0
  116. ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
  117. ansible/module_utils/_internal/_dataclass_annotation_patch.py +64 -0
  118. ansible/module_utils/_internal/_dataclass_validation.py +217 -0
  119. ansible/module_utils/_internal/_datatag/__init__.py +928 -0
  120. ansible/module_utils/_internal/_datatag/_tags.py +38 -0
  121. ansible/module_utils/_internal/_debugging.py +31 -0
  122. ansible/module_utils/_internal/_errors.py +30 -0
  123. ansible/module_utils/_internal/_json/__init__.py +63 -0
  124. ansible/module_utils/_internal/_json/_legacy_encoder.py +26 -0
  125. ansible/module_utils/_internal/_json/_profiles/__init__.py +410 -0
  126. ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
  127. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +31 -0
  128. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +35 -0
  129. ansible/module_utils/_internal/_json/_profiles/_module_modern_c2m.py +35 -0
  130. ansible/module_utils/_internal/_json/_profiles/_module_modern_m2c.py +33 -0
  131. ansible/module_utils/_internal/_json/_profiles/_tagless.py +50 -0
  132. ansible/module_utils/_internal/_patches/__init__.py +66 -0
  133. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +55 -0
  134. ansible/module_utils/_internal/_patches/_socket_patch.py +34 -0
  135. ansible/module_utils/_internal/_patches/_sys_intern_patch.py +34 -0
  136. ansible/module_utils/_internal/_plugin_exec_context.py +49 -0
  137. ansible/module_utils/_internal/_testing.py +0 -0
  138. ansible/module_utils/_internal/_traceback.py +89 -0
  139. ansible/module_utils/ansible_release.py +2 -2
  140. ansible/module_utils/api.py +1 -2
  141. ansible/module_utils/basic.py +152 -120
  142. ansible/module_utils/common/_utils.py +24 -28
  143. ansible/module_utils/common/collections.py +1 -2
  144. ansible/module_utils/common/dict_transformations.py +2 -2
  145. ansible/module_utils/common/file.py +2 -2
  146. ansible/module_utils/common/json.py +90 -84
  147. ansible/module_utils/common/locale.py +2 -2
  148. ansible/module_utils/common/messages.py +108 -0
  149. ansible/module_utils/common/parameters.py +27 -24
  150. ansible/module_utils/common/process.py +2 -2
  151. ansible/module_utils/common/respawn.py +41 -19
  152. ansible/module_utils/common/sentinel.py +66 -0
  153. ansible/module_utils/common/sys_info.py +8 -8
  154. ansible/module_utils/common/text/converters.py +16 -37
  155. ansible/module_utils/common/validation.py +35 -24
  156. ansible/module_utils/common/warnings.py +86 -25
  157. ansible/module_utils/common/yaml.py +29 -3
  158. ansible/module_utils/compat/datetime.py +33 -21
  159. ansible/module_utils/compat/paramiko.py +21 -10
  160. ansible/module_utils/compat/typing.py +6 -5
  161. ansible/module_utils/connection.py +2 -2
  162. ansible/module_utils/csharp/Ansible.Basic.cs +14 -11
  163. ansible/module_utils/csharp/Ansible.Become.cs +1 -0
  164. ansible/module_utils/csharp/Ansible._Async.cs +517 -0
  165. ansible/module_utils/datatag.py +46 -0
  166. ansible/module_utils/distro/__init__.py +2 -2
  167. ansible/module_utils/facts/ansible_collector.py +4 -5
  168. ansible/module_utils/facts/collector.py +13 -14
  169. ansible/module_utils/facts/compat.py +4 -4
  170. ansible/module_utils/facts/default_collectors.py +1 -1
  171. ansible/module_utils/facts/hardware/aix.py +34 -0
  172. ansible/module_utils/facts/hardware/base.py +1 -1
  173. ansible/module_utils/facts/hardware/darwin.py +1 -3
  174. ansible/module_utils/facts/hardware/freebsd.py +2 -2
  175. ansible/module_utils/facts/hardware/linux.py +4 -4
  176. ansible/module_utils/facts/namespace.py +1 -1
  177. ansible/module_utils/facts/network/base.py +1 -1
  178. ansible/module_utils/facts/network/fc_wwn.py +1 -2
  179. ansible/module_utils/facts/network/iscsi.py +1 -2
  180. ansible/module_utils/facts/network/nvme.py +1 -2
  181. ansible/module_utils/facts/other/facter.py +1 -2
  182. ansible/module_utils/facts/other/ohai.py +2 -3
  183. ansible/module_utils/facts/system/apparmor.py +1 -2
  184. ansible/module_utils/facts/system/caps.py +1 -1
  185. ansible/module_utils/facts/system/chroot.py +1 -2
  186. ansible/module_utils/facts/system/cmdline.py +1 -2
  187. ansible/module_utils/facts/system/date_time.py +5 -3
  188. ansible/module_utils/facts/system/distribution.py +9 -8
  189. ansible/module_utils/facts/system/dns.py +1 -1
  190. ansible/module_utils/facts/system/env.py +1 -2
  191. ansible/module_utils/facts/system/fips.py +7 -20
  192. ansible/module_utils/facts/system/loadavg.py +1 -2
  193. ansible/module_utils/facts/system/local.py +1 -2
  194. ansible/module_utils/facts/system/lsb.py +1 -2
  195. ansible/module_utils/facts/system/pkg_mgr.py +1 -2
  196. ansible/module_utils/facts/system/platform.py +1 -2
  197. ansible/module_utils/facts/system/python.py +1 -2
  198. ansible/module_utils/facts/system/selinux.py +1 -1
  199. ansible/module_utils/facts/system/service_mgr.py +1 -2
  200. ansible/module_utils/facts/system/ssh_pub_keys.py +1 -1
  201. ansible/module_utils/facts/system/systemd.py +1 -1
  202. ansible/module_utils/facts/system/user.py +1 -2
  203. ansible/module_utils/facts/utils.py +3 -3
  204. ansible/module_utils/facts/virtual/base.py +1 -1
  205. ansible/module_utils/facts/virtual/sunos.py +3 -15
  206. ansible/module_utils/facts/virtual/sysctl.py +3 -16
  207. ansible/module_utils/json_utils.py +2 -2
  208. ansible/module_utils/parsing/convert_bool.py +1 -1
  209. ansible/module_utils/service.py +18 -21
  210. ansible/module_utils/splitter.py +7 -7
  211. ansible/module_utils/testing.py +31 -0
  212. ansible/module_utils/urls.py +60 -31
  213. ansible/modules/add_host.py +4 -4
  214. ansible/modules/apt.py +60 -46
  215. ansible/modules/apt_key.py +19 -12
  216. ansible/modules/apt_repository.py +19 -16
  217. ansible/modules/assemble.py +6 -6
  218. ansible/modules/assert.py +4 -4
  219. ansible/modules/async_status.py +10 -12
  220. ansible/modules/async_wrapper.py +8 -3
  221. ansible/modules/blockinfile.py +6 -7
  222. ansible/modules/command.py +10 -17
  223. ansible/modules/copy.py +57 -144
  224. ansible/modules/cron.py +20 -15
  225. ansible/modules/deb822_repository.py +8 -9
  226. ansible/modules/debconf.py +5 -5
  227. ansible/modules/debug.py +4 -4
  228. ansible/modules/dnf.py +8 -8
  229. ansible/modules/dnf5.py +39 -13
  230. ansible/modules/dpkg_selections.py +4 -4
  231. ansible/modules/expect.py +8 -10
  232. ansible/modules/fail.py +4 -4
  233. ansible/modules/fetch.py +4 -4
  234. ansible/modules/file.py +174 -133
  235. ansible/modules/find.py +19 -17
  236. ansible/modules/gather_facts.py +3 -3
  237. ansible/modules/get_url.py +59 -53
  238. ansible/modules/getent.py +7 -9
  239. ansible/modules/git.py +28 -25
  240. ansible/modules/group.py +6 -6
  241. ansible/modules/group_by.py +4 -4
  242. ansible/modules/hostname.py +13 -29
  243. ansible/modules/import_playbook.py +6 -6
  244. ansible/modules/import_role.py +6 -6
  245. ansible/modules/import_tasks.py +6 -6
  246. ansible/modules/include_role.py +6 -6
  247. ansible/modules/include_tasks.py +6 -6
  248. ansible/modules/include_vars.py +6 -6
  249. ansible/modules/iptables.py +86 -73
  250. ansible/modules/known_hosts.py +10 -10
  251. ansible/modules/lineinfile.py +5 -5
  252. ansible/modules/meta.py +4 -4
  253. ansible/modules/mount_facts.py +2 -2
  254. ansible/modules/package.py +4 -4
  255. ansible/modules/package_facts.py +22 -10
  256. ansible/modules/pause.py +6 -6
  257. ansible/modules/ping.py +6 -6
  258. ansible/modules/pip.py +10 -11
  259. ansible/modules/raw.py +4 -4
  260. ansible/modules/reboot.py +6 -6
  261. ansible/modules/replace.py +9 -13
  262. ansible/modules/rpm_key.py +7 -8
  263. ansible/modules/script.py +4 -4
  264. ansible/modules/service.py +7 -8
  265. ansible/modules/service_facts.py +87 -10
  266. ansible/modules/set_fact.py +5 -5
  267. ansible/modules/set_stats.py +4 -4
  268. ansible/modules/setup.py +2 -2
  269. ansible/modules/shell.py +6 -6
  270. ansible/modules/slurp.py +6 -6
  271. ansible/modules/stat.py +9 -23
  272. ansible/modules/subversion.py +15 -15
  273. ansible/modules/systemd.py +6 -6
  274. ansible/modules/systemd_service.py +6 -6
  275. ansible/modules/sysvinit.py +6 -6
  276. ansible/modules/tempfile.py +5 -6
  277. ansible/modules/template.py +6 -6
  278. ansible/modules/unarchive.py +32 -11
  279. ansible/modules/uri.py +33 -26
  280. ansible/modules/user.py +53 -34
  281. ansible/modules/validate_argument_spec.py +10 -7
  282. ansible/modules/wait_for.py +32 -27
  283. ansible/modules/wait_for_connection.py +6 -6
  284. ansible/modules/yum_repository.py +6 -6
  285. ansible/parsing/ajson.py +14 -32
  286. ansible/parsing/dataloader.py +99 -54
  287. ansible/parsing/mod_args.py +28 -44
  288. ansible/parsing/plugin_docs.py +21 -86
  289. ansible/parsing/quoting.py +1 -1
  290. ansible/parsing/splitter.py +27 -12
  291. ansible/parsing/utils/addresses.py +24 -24
  292. ansible/parsing/utils/jsonify.py +5 -1
  293. ansible/parsing/utils/yaml.py +32 -61
  294. ansible/parsing/vault/__init__.py +319 -87
  295. ansible/parsing/yaml/__init__.py +0 -18
  296. ansible/parsing/yaml/dumper.py +6 -120
  297. ansible/parsing/yaml/loader.py +6 -39
  298. ansible/parsing/yaml/objects.py +43 -335
  299. ansible/playbook/__init__.py +1 -1
  300. ansible/playbook/attribute.py +8 -3
  301. ansible/playbook/base.py +182 -132
  302. ansible/playbook/block.py +26 -24
  303. ansible/playbook/collectionsearch.py +1 -15
  304. ansible/playbook/conditional.py +3 -77
  305. ansible/playbook/handler.py +8 -2
  306. ansible/playbook/helpers.py +41 -53
  307. ansible/playbook/included_file.py +31 -27
  308. ansible/playbook/loop_control.py +2 -2
  309. ansible/playbook/play.py +85 -44
  310. ansible/playbook/play_context.py +12 -17
  311. ansible/playbook/playbook_include.py +14 -15
  312. ansible/playbook/role/__init__.py +24 -26
  313. ansible/playbook/role/definition.py +15 -17
  314. ansible/playbook/role/include.py +2 -4
  315. ansible/playbook/role/metadata.py +10 -11
  316. ansible/playbook/role_include.py +3 -3
  317. ansible/playbook/taggable.py +13 -8
  318. ansible/playbook/task.py +188 -118
  319. ansible/playbook/task_include.py +5 -5
  320. ansible/plugins/__init__.py +68 -21
  321. ansible/plugins/action/__init__.py +209 -176
  322. ansible/plugins/action/add_host.py +1 -1
  323. ansible/plugins/action/assemble.py +1 -1
  324. ansible/plugins/action/assert.py +54 -66
  325. ansible/plugins/action/copy.py +7 -11
  326. ansible/plugins/action/debug.py +37 -31
  327. ansible/plugins/action/dnf.py +3 -4
  328. ansible/plugins/action/fail.py +1 -1
  329. ansible/plugins/action/fetch.py +4 -5
  330. ansible/plugins/action/gather_facts.py +7 -6
  331. ansible/plugins/action/group_by.py +1 -1
  332. ansible/plugins/action/include_vars.py +10 -11
  333. ansible/plugins/action/package.py +3 -6
  334. ansible/plugins/action/pause.py +2 -2
  335. ansible/plugins/action/script.py +15 -8
  336. ansible/plugins/action/service.py +6 -11
  337. ansible/plugins/action/set_fact.py +3 -12
  338. ansible/plugins/action/set_stats.py +3 -8
  339. ansible/plugins/action/template.py +35 -59
  340. ansible/plugins/action/unarchive.py +1 -1
  341. ansible/plugins/action/validate_argument_spec.py +5 -5
  342. ansible/plugins/action/wait_for_connection.py +1 -1
  343. ansible/plugins/become/__init__.py +31 -8
  344. ansible/plugins/become/runas.py +71 -0
  345. ansible/plugins/become/su.py +13 -8
  346. ansible/plugins/become/sudo.py +19 -0
  347. ansible/plugins/cache/__init__.py +35 -44
  348. ansible/plugins/cache/base.py +8 -0
  349. ansible/plugins/cache/jsonfile.py +10 -16
  350. ansible/plugins/cache/memory.py +6 -12
  351. ansible/plugins/callback/__init__.py +284 -179
  352. ansible/plugins/callback/default.py +99 -92
  353. ansible/plugins/callback/junit.py +44 -39
  354. ansible/plugins/callback/minimal.py +28 -25
  355. ansible/plugins/callback/oneline.py +28 -21
  356. ansible/plugins/callback/tree.py +16 -11
  357. ansible/plugins/connection/__init__.py +47 -34
  358. ansible/plugins/connection/local.py +150 -54
  359. ansible/plugins/connection/paramiko_ssh.py +21 -18
  360. ansible/plugins/connection/psrp.py +76 -165
  361. ansible/plugins/connection/ssh.py +301 -78
  362. ansible/plugins/connection/winrm.py +58 -140
  363. ansible/plugins/doc_fragments/action_common_attributes.py +14 -14
  364. ansible/plugins/doc_fragments/action_core.py +6 -6
  365. ansible/plugins/doc_fragments/backup.py +2 -2
  366. ansible/plugins/doc_fragments/checksum_common.py +27 -0
  367. ansible/plugins/doc_fragments/constructed.py +6 -2
  368. ansible/plugins/doc_fragments/decrypt.py +2 -2
  369. ansible/plugins/doc_fragments/default_callback.py +2 -2
  370. ansible/plugins/doc_fragments/files.py +2 -2
  371. ansible/plugins/doc_fragments/inventory_cache.py +2 -2
  372. ansible/plugins/doc_fragments/result_format_callback.py +2 -2
  373. ansible/plugins/doc_fragments/return_common.py +2 -2
  374. ansible/plugins/doc_fragments/template_common.py +4 -4
  375. ansible/plugins/doc_fragments/url.py +17 -1
  376. ansible/plugins/doc_fragments/url_windows.py +2 -2
  377. ansible/plugins/doc_fragments/validate.py +2 -2
  378. ansible/plugins/doc_fragments/vars_plugin_staging.py +2 -2
  379. ansible/plugins/filter/__init__.py +6 -2
  380. ansible/plugins/filter/b64decode.yml +22 -0
  381. ansible/plugins/filter/b64encode.yml +22 -0
  382. ansible/plugins/filter/bool.yml +11 -4
  383. ansible/plugins/filter/core.py +225 -108
  384. ansible/plugins/filter/encryption.py +32 -32
  385. ansible/plugins/filter/flatten.yml +3 -2
  386. ansible/plugins/filter/human_to_bytes.yml +1 -1
  387. ansible/plugins/filter/mathstuff.py +30 -37
  388. ansible/plugins/filter/password_hash.yml +8 -0
  389. ansible/plugins/filter/regex_search.yml +1 -4
  390. ansible/plugins/filter/split.yml +1 -1
  391. ansible/plugins/filter/to_nice_yaml.yml +0 -4
  392. ansible/plugins/filter/to_yaml.yml +0 -4
  393. ansible/plugins/filter/unvault.yml +1 -1
  394. ansible/plugins/filter/urls.py +1 -1
  395. ansible/plugins/filter/urlsplit.py +8 -9
  396. ansible/plugins/filter/vault.yml +14 -9
  397. ansible/plugins/filter/win_basename.yml +6 -1
  398. ansible/plugins/filter/win_dirname.yml +5 -0
  399. ansible/plugins/inventory/__init__.py +97 -77
  400. ansible/plugins/inventory/advanced_host_list.py +7 -5
  401. ansible/plugins/inventory/auto.py +11 -4
  402. ansible/plugins/inventory/constructed.py +21 -24
  403. ansible/plugins/inventory/generator.py +16 -11
  404. ansible/plugins/inventory/host_list.py +7 -5
  405. ansible/plugins/inventory/ini.py +78 -44
  406. ansible/plugins/inventory/script.py +189 -119
  407. ansible/plugins/inventory/toml.py +16 -126
  408. ansible/plugins/inventory/yaml.py +10 -8
  409. ansible/plugins/list.py +3 -3
  410. ansible/plugins/loader.py +197 -82
  411. ansible/plugins/lookup/__init__.py +21 -4
  412. ansible/plugins/lookup/config.py +21 -35
  413. ansible/plugins/lookup/csvfile.py +3 -2
  414. ansible/plugins/lookup/dict.py +1 -6
  415. ansible/plugins/lookup/env.py +12 -9
  416. ansible/plugins/lookup/file.py +5 -8
  417. ansible/plugins/lookup/first_found.py +86 -55
  418. ansible/plugins/lookup/indexed_items.py +1 -10
  419. ansible/plugins/lookup/ini.py +14 -13
  420. ansible/plugins/lookup/items.py +1 -1
  421. ansible/plugins/lookup/lines.py +8 -1
  422. ansible/plugins/lookup/list.py +1 -1
  423. ansible/plugins/lookup/nested.py +2 -18
  424. ansible/plugins/lookup/password.py +5 -5
  425. ansible/plugins/lookup/pipe.py +5 -7
  426. ansible/plugins/lookup/sequence.py +18 -8
  427. ansible/plugins/lookup/subelements.py +1 -4
  428. ansible/plugins/lookup/template.py +42 -36
  429. ansible/plugins/lookup/together.py +0 -12
  430. ansible/plugins/lookup/unvault.py +1 -5
  431. ansible/plugins/lookup/url.py +2 -8
  432. ansible/plugins/lookup/vars.py +16 -24
  433. ansible/plugins/shell/__init__.py +2 -2
  434. ansible/plugins/shell/cmd.py +2 -2
  435. ansible/plugins/shell/powershell.py +39 -22
  436. ansible/plugins/shell/sh.py +3 -2
  437. ansible/plugins/strategy/__init__.py +159 -184
  438. ansible/plugins/strategy/debug.py +2 -2
  439. ansible/plugins/strategy/free.py +16 -31
  440. ansible/plugins/strategy/host_pinned.py +2 -2
  441. ansible/plugins/strategy/linear.py +41 -41
  442. ansible/plugins/terminal/__init__.py +4 -4
  443. ansible/plugins/test/__init__.py +7 -2
  444. ansible/plugins/test/core.py +55 -21
  445. ansible/plugins/test/files.py +1 -1
  446. ansible/plugins/test/mathstuff.py +3 -3
  447. ansible/plugins/test/uri.py +3 -3
  448. ansible/plugins/vars/host_group_vars.py +7 -14
  449. ansible/release.py +2 -2
  450. ansible/template/__init__.py +370 -944
  451. ansible/utils/__init__.py +0 -18
  452. ansible/utils/_ssh_agent.py +657 -0
  453. ansible/utils/collection_loader/__init__.py +52 -5
  454. ansible/utils/collection_loader/_collection_config.py +5 -6
  455. ansible/utils/collection_loader/_collection_finder.py +79 -93
  456. ansible/utils/collection_loader/_collection_meta.py +13 -8
  457. ansible/utils/display.py +433 -63
  458. ansible/utils/encrypt.py +27 -19
  459. ansible/utils/fqcn.py +2 -2
  460. ansible/utils/hashing.py +2 -2
  461. ansible/utils/helpers.py +2 -2
  462. ansible/utils/listify.py +8 -8
  463. ansible/utils/lock.py +2 -2
  464. ansible/utils/path.py +4 -4
  465. ansible/utils/plugin_docs.py +14 -13
  466. ansible/utils/sentinel.py +4 -62
  467. ansible/utils/singleton.py +2 -0
  468. ansible/utils/ssh_functions.py +1 -1
  469. ansible/utils/unsafe_proxy.py +23 -332
  470. ansible/utils/vars.py +51 -8
  471. ansible/utils/version.py +2 -2
  472. ansible/vars/clean.py +5 -5
  473. ansible/vars/hostvars.py +60 -90
  474. ansible/vars/manager.py +206 -282
  475. ansible/vars/reserved.py +8 -9
  476. ansible_core-2.19.0b2.dist-info/BSD-3-Clause.txt +28 -0
  477. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/METADATA +5 -4
  478. ansible_core-2.19.0b2.dist-info/RECORD +1072 -0
  479. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/WHEEL +1 -1
  480. ansible_test/_data/completion/docker.txt +7 -7
  481. ansible_test/_data/completion/remote.txt +6 -6
  482. ansible_test/_data/completion/windows.txt +1 -0
  483. ansible_test/_data/requirements/ansible.txt +2 -2
  484. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
  485. ansible_test/_data/requirements/sanity.changelog.txt +1 -1
  486. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  487. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  488. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
  489. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  490. ansible_test/_data/requirements/units.txt +1 -0
  491. ansible_test/_internal/__init__.py +1 -0
  492. ansible_test/_internal/ansible_util.py +2 -0
  493. ansible_test/_internal/become.py +1 -0
  494. ansible_test/_internal/bootstrap.py +1 -0
  495. ansible_test/_internal/cache.py +1 -0
  496. ansible_test/_internal/cgroup.py +1 -0
  497. ansible_test/_internal/ci/__init__.py +1 -0
  498. ansible_test/_internal/ci/azp.py +1 -0
  499. ansible_test/_internal/ci/local.py +1 -0
  500. ansible_test/_internal/classification/__init__.py +1 -0
  501. ansible_test/_internal/classification/common.py +1 -0
  502. ansible_test/_internal/classification/csharp.py +1 -0
  503. ansible_test/_internal/classification/powershell.py +1 -0
  504. ansible_test/_internal/classification/python.py +1 -0
  505. ansible_test/_internal/cli/__init__.py +1 -0
  506. ansible_test/_internal/cli/actions.py +1 -0
  507. ansible_test/_internal/cli/argparsing/__init__.py +1 -0
  508. ansible_test/_internal/cli/argparsing/actions.py +1 -0
  509. ansible_test/_internal/cli/argparsing/argcompletion.py +1 -0
  510. ansible_test/_internal/cli/argparsing/parsers.py +1 -0
  511. ansible_test/_internal/cli/commands/__init__.py +11 -0
  512. ansible_test/_internal/cli/commands/coverage/__init__.py +1 -0
  513. ansible_test/_internal/cli/commands/coverage/analyze/__init__.py +1 -0
  514. ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py +1 -0
  515. ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py +1 -0
  516. ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py +1 -0
  517. ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py +1 -0
  518. ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py +1 -0
  519. ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py +1 -0
  520. ansible_test/_internal/cli/commands/coverage/combine.py +1 -0
  521. ansible_test/_internal/cli/commands/coverage/erase.py +1 -0
  522. ansible_test/_internal/cli/commands/coverage/html.py +1 -0
  523. ansible_test/_internal/cli/commands/coverage/report.py +1 -0
  524. ansible_test/_internal/cli/commands/coverage/xml.py +1 -0
  525. ansible_test/_internal/cli/commands/env.py +1 -0
  526. ansible_test/_internal/cli/commands/integration/__init__.py +1 -0
  527. ansible_test/_internal/cli/commands/integration/network.py +1 -0
  528. ansible_test/_internal/cli/commands/integration/posix.py +1 -0
  529. ansible_test/_internal/cli/commands/integration/windows.py +1 -0
  530. ansible_test/_internal/cli/commands/sanity.py +9 -0
  531. ansible_test/_internal/cli/commands/shell.py +1 -0
  532. ansible_test/_internal/cli/commands/units.py +1 -0
  533. ansible_test/_internal/cli/compat.py +1 -0
  534. ansible_test/_internal/cli/completers.py +1 -0
  535. ansible_test/_internal/cli/converters.py +1 -0
  536. ansible_test/_internal/cli/environments.py +1 -0
  537. ansible_test/_internal/cli/epilog.py +1 -0
  538. ansible_test/_internal/cli/parsers/__init__.py +1 -0
  539. ansible_test/_internal/cli/parsers/base_argument_parsers.py +1 -0
  540. ansible_test/_internal/cli/parsers/helpers.py +1 -0
  541. ansible_test/_internal/cli/parsers/host_config_parsers.py +1 -0
  542. ansible_test/_internal/cli/parsers/key_value_parsers.py +1 -0
  543. ansible_test/_internal/cli/parsers/value_parsers.py +1 -0
  544. ansible_test/_internal/commands/__init__.py +1 -0
  545. ansible_test/_internal/commands/coverage/__init__.py +2 -1
  546. ansible_test/_internal/commands/coverage/analyze/__init__.py +1 -0
  547. ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +1 -0
  548. ansible_test/_internal/commands/coverage/analyze/targets/combine.py +1 -0
  549. ansible_test/_internal/commands/coverage/analyze/targets/expand.py +1 -0
  550. ansible_test/_internal/commands/coverage/analyze/targets/filter.py +1 -0
  551. ansible_test/_internal/commands/coverage/analyze/targets/generate.py +1 -0
  552. ansible_test/_internal/commands/coverage/analyze/targets/missing.py +1 -0
  553. ansible_test/_internal/commands/coverage/combine.py +2 -1
  554. ansible_test/_internal/commands/coverage/erase.py +1 -0
  555. ansible_test/_internal/commands/coverage/html.py +1 -0
  556. ansible_test/_internal/commands/coverage/report.py +1 -0
  557. ansible_test/_internal/commands/coverage/xml.py +1 -0
  558. ansible_test/_internal/commands/env/__init__.py +2 -0
  559. ansible_test/_internal/commands/integration/__init__.py +4 -0
  560. ansible_test/_internal/commands/integration/cloud/__init__.py +1 -0
  561. ansible_test/_internal/commands/integration/cloud/acme.py +2 -1
  562. ansible_test/_internal/commands/integration/cloud/aws.py +1 -0
  563. ansible_test/_internal/commands/integration/cloud/azure.py +1 -0
  564. ansible_test/_internal/commands/integration/cloud/cs.py +1 -0
  565. ansible_test/_internal/commands/integration/cloud/digitalocean.py +1 -0
  566. ansible_test/_internal/commands/integration/cloud/galaxy.py +3 -2
  567. ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -0
  568. ansible_test/_internal/commands/integration/cloud/httptester.py +2 -1
  569. ansible_test/_internal/commands/integration/cloud/nios.py +2 -1
  570. ansible_test/_internal/commands/integration/cloud/opennebula.py +1 -0
  571. ansible_test/_internal/commands/integration/cloud/openshift.py +1 -0
  572. ansible_test/_internal/commands/integration/cloud/scaleway.py +1 -0
  573. ansible_test/_internal/commands/integration/cloud/vcenter.py +1 -0
  574. ansible_test/_internal/commands/integration/cloud/vultr.py +1 -0
  575. ansible_test/_internal/commands/integration/coverage.py +1 -0
  576. ansible_test/_internal/commands/integration/filters.py +1 -0
  577. ansible_test/_internal/commands/integration/network.py +1 -0
  578. ansible_test/_internal/commands/integration/posix.py +1 -0
  579. ansible_test/_internal/commands/integration/windows.py +1 -0
  580. ansible_test/_internal/commands/sanity/__init__.py +16 -1
  581. ansible_test/_internal/commands/sanity/ansible_doc.py +1 -0
  582. ansible_test/_internal/commands/sanity/bin_symlinks.py +1 -0
  583. ansible_test/_internal/commands/sanity/compile.py +1 -0
  584. ansible_test/_internal/commands/sanity/ignores.py +1 -0
  585. ansible_test/_internal/commands/sanity/import.py +1 -0
  586. ansible_test/_internal/commands/sanity/integration_aliases.py +1 -0
  587. ansible_test/_internal/commands/sanity/pep8.py +1 -0
  588. ansible_test/_internal/commands/sanity/pslint.py +1 -0
  589. ansible_test/_internal/commands/sanity/pylint.py +24 -26
  590. ansible_test/_internal/commands/sanity/shellcheck.py +1 -0
  591. ansible_test/_internal/commands/sanity/validate_modules.py +1 -0
  592. ansible_test/_internal/commands/sanity/yamllint.py +1 -0
  593. ansible_test/_internal/commands/shell/__init__.py +1 -0
  594. ansible_test/_internal/commands/units/__init__.py +1 -0
  595. ansible_test/_internal/compat/__init__.py +1 -0
  596. ansible_test/_internal/compat/packaging.py +1 -0
  597. ansible_test/_internal/compat/yaml.py +1 -0
  598. ansible_test/_internal/completion.py +1 -0
  599. ansible_test/_internal/config.py +2 -0
  600. ansible_test/_internal/connections.py +1 -0
  601. ansible_test/_internal/constants.py +1 -0
  602. ansible_test/_internal/containers.py +1 -0
  603. ansible_test/_internal/content_config.py +1 -0
  604. ansible_test/_internal/core_ci.py +1 -0
  605. ansible_test/_internal/coverage_util.py +11 -10
  606. ansible_test/_internal/data.py +1 -0
  607. ansible_test/_internal/delegation.py +1 -0
  608. ansible_test/_internal/dev/__init__.py +1 -0
  609. ansible_test/_internal/dev/container_probe.py +1 -0
  610. ansible_test/_internal/diff.py +3 -2
  611. ansible_test/_internal/docker_util.py +2 -1
  612. ansible_test/_internal/encoding.py +1 -0
  613. ansible_test/_internal/executor.py +1 -0
  614. ansible_test/_internal/git.py +1 -0
  615. ansible_test/_internal/host_configs.py +1 -0
  616. ansible_test/_internal/host_profiles.py +1 -0
  617. ansible_test/_internal/http.py +1 -0
  618. ansible_test/_internal/init.py +1 -0
  619. ansible_test/_internal/inventory.py +35 -3
  620. ansible_test/_internal/io.py +1 -0
  621. ansible_test/_internal/metadata.py +1 -0
  622. ansible_test/_internal/payload.py +1 -0
  623. ansible_test/_internal/provider/__init__.py +1 -0
  624. ansible_test/_internal/provider/layout/__init__.py +1 -0
  625. ansible_test/_internal/provider/layout/ansible.py +1 -0
  626. ansible_test/_internal/provider/layout/collection.py +1 -0
  627. ansible_test/_internal/provider/layout/unsupported.py +1 -0
  628. ansible_test/_internal/provider/source/__init__.py +1 -0
  629. ansible_test/_internal/provider/source/git.py +1 -0
  630. ansible_test/_internal/provider/source/installed.py +1 -0
  631. ansible_test/_internal/provider/source/unsupported.py +1 -0
  632. ansible_test/_internal/provider/source/unversioned.py +1 -0
  633. ansible_test/_internal/provisioning.py +1 -0
  634. ansible_test/_internal/pypi_proxy.py +6 -5
  635. ansible_test/_internal/python_requirements.py +1 -0
  636. ansible_test/_internal/ssh.py +1 -0
  637. ansible_test/_internal/target.py +1 -0
  638. ansible_test/_internal/test.py +3 -2
  639. ansible_test/_internal/thread.py +1 -0
  640. ansible_test/_internal/timeout.py +1 -0
  641. ansible_test/_internal/util.py +1 -0
  642. ansible_test/_internal/util_common.py +5 -2
  643. ansible_test/_internal/venv.py +1 -0
  644. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +1 -0
  645. ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py +1 -0
  646. ansible_test/_util/controller/sanity/code-smell/changelog.py +1 -0
  647. ansible_test/_util/controller/sanity/code-smell/empty-init.py +1 -0
  648. ansible_test/_util/controller/sanity/code-smell/line-endings.py +1 -0
  649. ansible_test/_util/controller/sanity/code-smell/no-assert.py +1 -0
  650. ansible_test/_util/controller/sanity/code-smell/no-get-exception.py +1 -0
  651. ansible_test/_util/controller/sanity/code-smell/no-illegal-filenames.py +1 -0
  652. ansible_test/_util/controller/sanity/code-smell/no-smart-quotes.py +1 -0
  653. ansible_test/_util/controller/sanity/code-smell/replace-urlopen.py +1 -0
  654. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +28 -1
  655. ansible_test/_util/controller/sanity/code-smell/shebang.py +1 -0
  656. ansible_test/_util/controller/sanity/code-smell/symlinks.py +1 -0
  657. ansible_test/_util/controller/sanity/code-smell/use-argspec-type-path.py +1 -0
  658. ansible_test/_util/controller/sanity/code-smell/use-compat-six.py +1 -0
  659. ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py +2 -1
  660. ansible_test/_util/controller/sanity/pep8/current-ignore.txt +4 -0
  661. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +7 -5
  662. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +7 -5
  663. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +7 -5
  664. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +3 -5
  665. ansible_test/_util/controller/sanity/pylint/config/default.cfg +7 -7
  666. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -13
  667. ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +1 -0
  668. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +1 -8
  669. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +1 -8
  670. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +55 -28
  671. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +12 -5
  672. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +13 -2
  673. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -0
  674. ansible_test/_util/controller/sanity/yamllint/yamllinter.py +35 -17
  675. ansible_test/_util/controller/tools/collection_detail.py +1 -0
  676. ansible_test/_util/controller/tools/yaml_to_json.py +2 -1
  677. ansible_test/_util/target/pytest/plugins/ansible_forked.py +6 -1
  678. ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +2 -1
  679. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -0
  680. ansible_test/_util/target/sanity/compile/compile.py +1 -0
  681. ansible_test/_util/target/sanity/import/importer.py +15 -16
  682. ansible_test/_util/target/setup/bootstrap.sh +9 -20
  683. ansible_test/_util/target/setup/probe_cgroups.py +1 -0
  684. ansible_test/_util/target/setup/quiet_pip.py +1 -0
  685. ansible_test/_util/target/setup/requirements.py +35 -27
  686. ansible_test/_util/target/tools/virtualenvcheck.py +2 -1
  687. ansible_test/_util/target/tools/yamlcheck.py +2 -1
  688. ansible/compat/selectors.py +0 -32
  689. ansible/errors/yaml_strings.py +0 -138
  690. ansible/executor/action_write_locks.py +0 -44
  691. ansible/executor/discovery/python_target.py +0 -47
  692. ansible/executor/powershell/module_powershell_wrapper.ps1 +0 -86
  693. ansible/executor/powershell/module_script_wrapper.ps1 +0 -22
  694. ansible/module_utils/compat/importlib.py +0 -26
  695. ansible/module_utils/compat/selectors.py +0 -32
  696. ansible/module_utils/pycompat24.py +0 -73
  697. ansible/parsing/yaml/constructor.py +0 -178
  698. ansible/template/native_helpers.py +0 -251
  699. ansible/template/template.py +0 -43
  700. ansible/template/vars.py +0 -77
  701. ansible/utils/native_jinja.py +0 -11
  702. ansible/vars/fact_cache.py +0 -71
  703. ansible_core-2.18.5rc1.dist-info/RECORD +0 -992
  704. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/Apache-License.txt +0 -0
  705. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/COPYING +0 -0
  706. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/MIT-license.txt +0 -0
  707. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/PSF-license.txt +0 -0
  708. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/entry_points.txt +0 -0
  709. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/simplified_bsd.txt +0 -0
  710. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,95 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib as _contextlib
4
+ import dataclasses
5
+ import typing as t
6
+
7
+ from ansible.module_utils._internal._datatag import AnsibleSingletonTagBase, _tag_dataclass_kwargs
8
+ from ansible.module_utils._internal._datatag._tags import Deprecated
9
+ from ansible._internal._datatag._tags import Origin
10
+ from ansible.utils.display import Display
11
+
12
+ from ._access import NotifiableAccessContextBase
13
+ from ._utils import TemplateContext
14
+
15
+
16
+ display = Display()
17
+
18
+
19
+ @dataclasses.dataclass(**_tag_dataclass_kwargs)
20
+ class _JinjaConstTemplate(AnsibleSingletonTagBase):
21
+ # deprecated: description='embedded Jinja constant string template support' core_version='2.23'
22
+ pass
23
+
24
+
25
+ @dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
26
+ class _TrippedDeprecationInfo:
27
+ template: str
28
+ deprecated: Deprecated
29
+
30
+
31
+ class DeprecatedAccessAuditContext(NotifiableAccessContextBase):
32
+ """When active, captures metadata about managed accesses to `Deprecated` tagged objects."""
33
+
34
+ _type_interest = frozenset([Deprecated])
35
+
36
+ @classmethod
37
+ def when(cls, condition: bool, /) -> t.Self | _contextlib.nullcontext:
38
+ """Returns a new instance if `condition` is True (usually `TemplateContext.is_top_level`), otherwise a `nullcontext` instance."""
39
+ if condition:
40
+ return cls()
41
+
42
+ return _contextlib.nullcontext()
43
+
44
+ def __init__(self) -> None:
45
+ self._tripped_deprecation_info: dict[int, _TrippedDeprecationInfo] = {}
46
+
47
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
48
+ result = super().__exit__(exc_type, exc_val, exc_tb)
49
+
50
+ for item in self._tripped_deprecation_info.values():
51
+ if Origin.is_tagged_on(item.template):
52
+ msg = item.deprecated.msg
53
+ else:
54
+ # without an origin, we need to include what context we do have (the template)
55
+ msg = f'While processing {item.template!r}: {item.deprecated.msg}'
56
+
57
+ display._deprecated_with_plugin_info(
58
+ msg=msg,
59
+ help_text=item.deprecated.help_text,
60
+ version=item.deprecated.removal_version,
61
+ date=item.deprecated.removal_date,
62
+ obj=item.template,
63
+ plugin=item.deprecated.plugin,
64
+ )
65
+
66
+ return result
67
+
68
+ def _notify(self, o: t.Any) -> None:
69
+ deprecated = Deprecated.get_required_tag(o)
70
+ deprecated_key = id(deprecated)
71
+
72
+ if deprecated_key in self._tripped_deprecation_info:
73
+ return # record only the first access for each deprecated tag in a given context
74
+
75
+ template_ctx = TemplateContext.current(optional=True)
76
+ template = template_ctx.template_value if template_ctx else None
77
+
78
+ # when the current template input is a container, provide a descriptive string with origin propagated (if possible)
79
+ if not isinstance(template, str):
80
+ # DTFIX-FUTURE: ascend the template stack to try and find the nearest string source template
81
+ origin = Origin.get_tag(template)
82
+
83
+ # DTFIX-RELEASE: this should probably use a synthesized description value on the tag
84
+ # it is reachable from the data_tagging_controller test: ../playbook_output_validator/filter.py actual_stdout.txt actual_stderr.txt
85
+ # -[DEPRECATION WARNING]: `something_old` is deprecated, don't use it! This feature will be removed in version 1.2.3.
86
+ # +[DEPRECATION WARNING]: While processing '<<container>>': `something_old` is deprecated, don't use it! This feature will be removed in ...
87
+ template = '<<container>>'
88
+
89
+ if origin:
90
+ origin.tag(template)
91
+
92
+ self._tripped_deprecation_info[deprecated_key] = _TrippedDeprecationInfo(
93
+ template=template,
94
+ deprecated=deprecated,
95
+ )
@@ -0,0 +1,588 @@
1
+ # (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
2
+ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3
+
4
+ from __future__ import annotations
5
+
6
+ import copy
7
+ import dataclasses
8
+ import enum
9
+ import textwrap
10
+ import typing as t
11
+ import collections.abc as c
12
+ import re
13
+
14
+ from collections import ChainMap
15
+
16
+ from ansible.errors import (
17
+ AnsibleError,
18
+ AnsibleValueOmittedError,
19
+ AnsibleUndefinedVariable,
20
+ AnsibleTemplateSyntaxError,
21
+ AnsibleBrokenConditionalError,
22
+ AnsibleTemplateTransformLimitError,
23
+ TemplateTrustCheckFailedError,
24
+ )
25
+
26
+ from ansible.module_utils._internal._datatag import AnsibleTaggedObject, NotTaggableError, AnsibleTagHelper
27
+ from ansible._internal._errors._handler import Skippable
28
+ from ansible._internal._datatag._tags import Origin, TrustedAsTemplate
29
+ from ansible.utils.display import Display
30
+ from ansible.utils.vars import validate_variable_name
31
+ from ansible.parsing.dataloader import DataLoader
32
+
33
+ from ._datatag import DeprecatedAccessAuditContext
34
+ from ._jinja_bits import (
35
+ AnsibleTemplate,
36
+ _TemplateCompileContext,
37
+ TemplateOverrides,
38
+ AnsibleEnvironment,
39
+ defer_template_error,
40
+ create_template_error,
41
+ is_possibly_template,
42
+ is_possibly_all_template,
43
+ AnsibleTemplateExpression,
44
+ _finalize_template_result,
45
+ FinalizeMode,
46
+ )
47
+ from ._jinja_common import _TemplateConfig, MarkerError, ExceptionMarker
48
+ from ._lazy_containers import _AnsibleLazyTemplateMixin
49
+ from ._marker_behaviors import MarkerBehavior, FAIL_ON_UNDEFINED
50
+ from ._transform import _type_transform_mapping
51
+ from ._utils import Omit, TemplateContext, IGNORE_SCALAR_VAR_TYPES, LazyOptions
52
+ from ...module_utils.datatag import native_type_name
53
+
54
+ _display = Display()
55
+
56
+
57
+ _shared_empty_unmask_type_names: frozenset[str] = frozenset()
58
+
59
+ TRANSFORM_CHAIN_LIMIT: int = 10
60
+ """Arbitrary limit for chained transforms to prevent cycles; an exception will be raised if exceeded."""
61
+
62
+
63
+ class TemplateMode(enum.Enum):
64
+ # DTFIX-FUTURE: this enum ideally wouldn't exist - revisit/rename before making public
65
+ DEFAULT = enum.auto()
66
+ STOP_ON_TEMPLATE = enum.auto()
67
+ STOP_ON_CONTAINER = enum.auto()
68
+ ALWAYS_FINALIZE = enum.auto()
69
+
70
+
71
+ @dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
72
+ class TemplateOptions:
73
+ DEFAULT: t.ClassVar[t.Self]
74
+
75
+ value_for_omit: object = Omit
76
+ escape_backslashes: bool = True
77
+ preserve_trailing_newlines: bool = True
78
+ # DTFIX-RELEASE: these aren't really overrides anymore, rename the dataclass and this field
79
+ # also mention in docstring this has no effect unless used to template a string
80
+ overrides: TemplateOverrides = TemplateOverrides.DEFAULT
81
+
82
+
83
+ TemplateOptions.DEFAULT = TemplateOptions()
84
+
85
+
86
+ class TemplateEncountered(Exception):
87
+ pass
88
+
89
+
90
+ class TemplateEngine:
91
+ """
92
+ The main class for templating, with the main entry-point of template().
93
+ """
94
+
95
+ _sentinel = object()
96
+
97
+ def __init__(
98
+ self,
99
+ loader: DataLoader | None = None,
100
+ variables: dict[str, t.Any] | ChainMap[str, t.Any] | None = None,
101
+ variables_factory: t.Callable[[], dict[str, t.Any] | ChainMap[str, t.Any]] | None = None,
102
+ marker_behavior: MarkerBehavior | None = None,
103
+ ):
104
+ self._loader = loader
105
+ self._variables = variables
106
+ self._variables_factory = variables_factory
107
+ self._environment: AnsibleEnvironment | None = None
108
+
109
+ # inherit marker behavior from the active template context's templar unless otherwise specified
110
+ if not marker_behavior:
111
+ if template_ctx := TemplateContext.current(optional=True):
112
+ marker_behavior = template_ctx.templar.marker_behavior
113
+ else:
114
+ marker_behavior = FAIL_ON_UNDEFINED
115
+
116
+ self._marker_behavior = marker_behavior
117
+
118
+ def copy(self) -> t.Self:
119
+ new_engine = copy.copy(self)
120
+ new_engine._environment = None
121
+
122
+ return new_engine
123
+
124
+ def extend(self, marker_behavior: MarkerBehavior | None = None) -> t.Self:
125
+ # DTFIX-RELEASE: bikeshed name, supported features
126
+ new_templar = type(self)(
127
+ loader=self._loader,
128
+ variables=self._variables,
129
+ variables_factory=self._variables_factory,
130
+ marker_behavior=marker_behavior or self._marker_behavior,
131
+ )
132
+
133
+ if self._environment:
134
+ new_templar._environment = self._environment
135
+
136
+ return new_templar
137
+
138
+ @property
139
+ def marker_behavior(self) -> MarkerBehavior:
140
+ return self._marker_behavior
141
+
142
+ @property
143
+ def basedir(self) -> str:
144
+ """The basedir from DataLoader."""
145
+ return self._loader.get_basedir() if self._loader else '.'
146
+
147
+ @property
148
+ def environment(self) -> AnsibleEnvironment:
149
+ if not self._environment:
150
+ self._environment = AnsibleEnvironment(ansible_basedir=self.basedir)
151
+
152
+ return self._environment
153
+
154
+ def _create_overlay(self, template: str, overrides: TemplateOverrides) -> tuple[str, AnsibleEnvironment]:
155
+ try:
156
+ template, overrides = overrides._extract_template_overrides(template)
157
+ except Exception as ex:
158
+ raise AnsibleTemplateSyntaxError("Syntax error in template.", obj=template) from ex
159
+
160
+ env = self.environment
161
+
162
+ if overrides is not TemplateOverrides.DEFAULT and (overlay_kwargs := overrides.overlay_kwargs()):
163
+ env = t.cast(AnsibleEnvironment, env.overlay(**overlay_kwargs))
164
+
165
+ return template, env
166
+
167
+ @staticmethod
168
+ def _count_newlines_from_end(in_str):
169
+ """
170
+ Counts the number of newlines at the end of a string. This is used during
171
+ the jinja2 templating to ensure the count matches the input, since some newlines
172
+ may be thrown away during the templating.
173
+ """
174
+
175
+ i = len(in_str)
176
+ j = i - 1
177
+
178
+ try:
179
+ while in_str[j] == '\n':
180
+ j -= 1
181
+ except IndexError:
182
+ # Uncommon cases: zero length string and string containing only newlines
183
+ return i
184
+
185
+ return i - 1 - j
186
+
187
+ @property
188
+ def available_variables(self) -> dict[str, t.Any] | ChainMap[str, t.Any]:
189
+ """Available variables this instance will use when templating."""
190
+ # DTFIX-RELEASE: ensure that we're always accessing this as a shallow container-level snapshot, and eliminate uses of anything
191
+ # that directly mutates this value. _new_context may resolve this for us?
192
+ if self._variables is None:
193
+ self._variables = self._variables_factory() if self._variables_factory else {}
194
+
195
+ return self._variables
196
+
197
+ @available_variables.setter
198
+ def available_variables(self, variables: dict[str, t.Any]) -> None:
199
+ self._variables = variables
200
+
201
+ def resolve_variable_expression(
202
+ self,
203
+ expression: str,
204
+ *,
205
+ local_variables: dict[str, t.Any] | None = None,
206
+ ) -> t.Any:
207
+ """
208
+ Resolve a potentially untrusted string variable expression consisting only of valid identifiers, integers, dots, and indexing containing these.
209
+ Optional local variables may be provided, which can only be referenced directly by the given expression.
210
+ Valid: x, x.y, x[y].z, x[1], 1, x[y.z]
211
+ Error: 'x', x['y'], q('env')
212
+ """
213
+ components = re.split(r'[.\[\]]', expression)
214
+
215
+ try:
216
+ for component in components:
217
+ if re.fullmatch('[0-9]*', component):
218
+ continue # allow empty strings and integers
219
+
220
+ validate_variable_name(component)
221
+ except Exception as ex:
222
+ raise AnsibleError(f'Invalid variable expression: {expression}', obj=expression) from ex
223
+
224
+ return self.evaluate_expression(TrustedAsTemplate().tag(expression), local_variables=local_variables)
225
+
226
+ @staticmethod
227
+ def variable_name_as_template(name: str) -> str:
228
+ """Return a trusted template string that will resolve the provided variable name. Raises an error if `name` is not a valid identifier."""
229
+ validate_variable_name(name)
230
+ return AnsibleTagHelper.tag('{{' + name + '}}', (AnsibleTagHelper.tags(name) | {TrustedAsTemplate()}))
231
+
232
+ def transform(self, variable: t.Any) -> t.Any:
233
+ """Recursively apply transformations to the given value and return the result."""
234
+ return self.template(variable, mode=TemplateMode.ALWAYS_FINALIZE, lazy_options=LazyOptions.SKIP_TEMPLATES_AND_ACCESS)
235
+
236
+ def template(
237
+ self,
238
+ variable: t.Any, # DTFIX-RELEASE: once we settle the new/old API boundaries, rename this (here and in other methods)
239
+ *,
240
+ options: TemplateOptions = TemplateOptions.DEFAULT,
241
+ mode: TemplateMode = TemplateMode.DEFAULT,
242
+ lazy_options: LazyOptions = LazyOptions.DEFAULT,
243
+ ) -> t.Any:
244
+ """Templates (possibly recursively) any given data as input."""
245
+ original_variable = variable
246
+
247
+ for _attempt in range(TRANSFORM_CHAIN_LIMIT):
248
+ if variable is None or (value_type := type(variable)) in IGNORE_SCALAR_VAR_TYPES:
249
+ return variable # quickly ignore supported scalar types which are not be templated
250
+
251
+ value_is_str = isinstance(variable, str)
252
+
253
+ if template_ctx := TemplateContext.current(optional=True):
254
+ stop_on_template = template_ctx.stop_on_template
255
+ else:
256
+ stop_on_template = False
257
+
258
+ if mode is TemplateMode.STOP_ON_TEMPLATE:
259
+ stop_on_template = True
260
+
261
+ with (
262
+ TemplateContext(template_value=variable, templar=self, options=options, stop_on_template=stop_on_template) as ctx,
263
+ DeprecatedAccessAuditContext.when(ctx.is_top_level),
264
+ ):
265
+ try:
266
+ if not value_is_str:
267
+ # transforms are currently limited to non-str types as an optimization
268
+ if (transform := _type_transform_mapping.get(value_type)) and value_type.__name__ not in lazy_options.unmask_type_names:
269
+ variable = transform(variable)
270
+ continue
271
+
272
+ template_result = _AnsibleLazyTemplateMixin._try_create(variable, lazy_options)
273
+ elif not lazy_options.template:
274
+ template_result = variable
275
+ elif not is_possibly_template(variable, options.overrides):
276
+ template_result = variable
277
+ elif not self._trust_check(variable, skip_handler=stop_on_template):
278
+ template_result = variable
279
+ elif stop_on_template:
280
+ raise TemplateEncountered()
281
+ else:
282
+ compiled_template = self._compile_template(variable, options)
283
+
284
+ template_result = compiled_template(self.available_variables)
285
+ template_result = self._post_render_mutation(variable, template_result, options)
286
+ except TemplateEncountered:
287
+ raise
288
+ except Exception as ex:
289
+ template_result = defer_template_error(ex, variable, is_expression=False)
290
+
291
+ if ctx.is_top_level or mode is TemplateMode.ALWAYS_FINALIZE:
292
+ template_result = self._finalize_top_level_template_result(
293
+ variable, options, template_result, stop_on_container=mode is TemplateMode.STOP_ON_CONTAINER
294
+ )
295
+
296
+ return template_result
297
+
298
+ raise AnsibleTemplateTransformLimitError(obj=original_variable)
299
+
300
+ @staticmethod
301
+ def _finalize_top_level_template_result(
302
+ variable: t.Any,
303
+ options: TemplateOptions,
304
+ template_result: t.Any,
305
+ is_expression: bool = False,
306
+ stop_on_container: bool = False,
307
+ ) -> t.Any:
308
+ """
309
+ This method must be called for expressions and top-level templates to recursively finalize the result.
310
+ This renders any embedded templates and triggers `Marker` and omit behaviors.
311
+ """
312
+ try:
313
+ if template_result is Omit:
314
+ # When the template result is Omit, raise an AnsibleValueOmittedError if value_for_omit is Omit, otherwise return value_for_omit.
315
+ # Other occurrences of Omit will simply drop out of containers during _finalize_template_result.
316
+ if options.value_for_omit is Omit:
317
+ raise AnsibleValueOmittedError()
318
+
319
+ return options.value_for_omit # trust that value_for_omit is an allowed type
320
+
321
+ if stop_on_container and type(template_result) in AnsibleTaggedObject._collection_types:
322
+ # Use of stop_on_container implies the caller will perform necessary checks on values,
323
+ # most likely by passing them back into the templating system.
324
+ try:
325
+ return template_result._non_lazy_copy()
326
+ except AttributeError:
327
+ return template_result # non-lazy containers are returned as-is
328
+
329
+ return _finalize_template_result(template_result, FinalizeMode.TOP_LEVEL)
330
+ except TemplateEncountered:
331
+ raise
332
+ except Exception as ex:
333
+ raise_from: BaseException
334
+
335
+ if isinstance(ex, MarkerError):
336
+ exception_to_raise = ex.source._as_exception()
337
+
338
+ # MarkerError is never suitable for use as the cause of another exception, it is merely a raiseable container for the source marker
339
+ # used for flow control (so its stack trace is rarely useful). However, if the source derives from a ExceptionMarker, its contained
340
+ # exception (previously raised) should be used as the cause. Other sources do not contain exceptions, so cannot provide a cause.
341
+ raise_from = exception_to_raise if isinstance(ex.source, ExceptionMarker) else None
342
+ else:
343
+ exception_to_raise = ex
344
+ raise_from = ex
345
+
346
+ exception_to_raise = create_template_error(exception_to_raise, variable, is_expression)
347
+
348
+ if exception_to_raise is ex:
349
+ raise # when the exception to raise is the active exception, just re-raise it
350
+
351
+ if exception_to_raise is raise_from:
352
+ raise_from = exception_to_raise.__cause__ # preserve the exception's cause, if any, otherwise no cause will be used
353
+
354
+ raise exception_to_raise from raise_from # always raise from something to avoid the currently active exception becoming __context__
355
+
356
+ def _compile_template(self, template: str, options: TemplateOptions) -> t.Callable[[c.Mapping[str, t.Any]], t.Any]:
357
+ # NOTE: Creating an overlay that lives only inside _compile_template means that overrides are not applied
358
+ # when templating nested variables, where Templar.environment is used, not the overlay. They are, however,
359
+ # applied to includes and imports.
360
+ try:
361
+ stripped_template, env = self._create_overlay(template, options.overrides)
362
+
363
+ with _TemplateCompileContext(escape_backslashes=options.escape_backslashes):
364
+ return t.cast(AnsibleTemplate, env.from_string(stripped_template))
365
+ except Exception as ex:
366
+ return self._defer_jinja_compile_error(ex, template, False)
367
+
368
+ def _compile_expression(self, expression: str, options: TemplateOptions) -> t.Callable[[c.Mapping[str, t.Any]], t.Any]:
369
+ """
370
+ Compile a Jinja expression, applying optional compile-time behavior via an environment overlay (if needed). The overlay is
371
+ necessary to avoid mutating settings on the Templar's shared environment, which could be visible to other code running concurrently.
372
+ In the specific case of escape_backslashes, the setting only applies to a top-level template at compile-time, not runtime, to
373
+ ensure that any nested template calls (e.g., include and import) do not inherit the (lack of) escaping behavior.
374
+ """
375
+ try:
376
+ with _TemplateCompileContext(escape_backslashes=options.escape_backslashes):
377
+ return AnsibleTemplateExpression(self.environment.compile_expression(expression, False))
378
+ except Exception as ex:
379
+ return self._defer_jinja_compile_error(ex, expression, True)
380
+
381
+ def _defer_jinja_compile_error(self, ex: Exception, variable: str, is_expression: bool) -> t.Callable[[c.Mapping[str, t.Any]], t.Any]:
382
+ deferred_error = defer_template_error(ex, variable, is_expression=is_expression)
383
+
384
+ def deferred_exception(_jinja_vars: c.Mapping[str, t.Any]) -> t.Any:
385
+ # a template/expression compile error always results in a single node representing the compile error
386
+ return self.marker_behavior.handle_marker(deferred_error)
387
+
388
+ return deferred_exception
389
+
390
+ def _post_render_mutation(self, template: str, result: t.Any, options: TemplateOptions) -> t.Any:
391
+ if options.preserve_trailing_newlines and isinstance(result, str):
392
+ # The low level calls above do not preserve the newline
393
+ # characters at the end of the input data, so we
394
+ # calculate the difference in newlines and append them
395
+ # to the resulting output for parity
396
+ #
397
+ # Using AnsibleEnvironment's keep_trailing_newline instead would
398
+ # result in change in behavior when trailing newlines
399
+ # would be kept also for included templates, for example:
400
+ # "Hello {% include 'world.txt' %}!" would render as
401
+ # "Hello world\n!\n" instead of "Hello world!\n".
402
+ data_newlines = self._count_newlines_from_end(template)
403
+ res_newlines = self._count_newlines_from_end(result)
404
+
405
+ if data_newlines > res_newlines:
406
+ newlines = options.overrides.newline_sequence * (data_newlines - res_newlines)
407
+ result = AnsibleTagHelper.tag_copy(result, result + newlines)
408
+
409
+ # If the input string template was source-tagged and the result is not, propagate the source tag to the new value.
410
+ # This provides further contextual information when a template-derived value/var causes an error.
411
+ if not Origin.is_tagged_on(result) and (origin := Origin.get_tag(template)):
412
+ try:
413
+ result = origin.tag(result)
414
+ except NotTaggableError:
415
+ pass # best effort- if we can't, oh well
416
+
417
+ return result
418
+
419
+ def is_template(self, data: t.Any, overrides: TemplateOverrides = TemplateOverrides.DEFAULT) -> bool:
420
+ """
421
+ Evaluate the input data to determine if it contains a template, even if that template is invalid. Containers will be recursively searched.
422
+ Objects subject to template-time transforms that do not yield a template are not considered templates by this method.
423
+ Gating a conditional call to `template` with this method is redundant and inefficient -- request templating unconditionally instead.
424
+ """
425
+ options = TemplateOptions(overrides=overrides) if overrides is not TemplateOverrides.DEFAULT else TemplateOptions.DEFAULT
426
+
427
+ try:
428
+ self.template(data, options=options, mode=TemplateMode.STOP_ON_TEMPLATE)
429
+ except TemplateEncountered:
430
+ return True
431
+ else:
432
+ return False
433
+
434
+ def resolve_to_container(self, variable: t.Any, options: TemplateOptions = TemplateOptions.DEFAULT) -> t.Any:
435
+ """
436
+ Recursively resolve scalar string template input, stopping at the first container encountered (if any).
437
+ Used for e.g., partial templating of task arguments, where the plugin needs to handle final resolution of some args internally.
438
+ """
439
+ return self.template(variable, options=options, mode=TemplateMode.STOP_ON_CONTAINER)
440
+
441
+ def evaluate_expression(
442
+ self,
443
+ expression: str,
444
+ *,
445
+ local_variables: dict[str, t.Any] | None = None,
446
+ escape_backslashes: bool = True,
447
+ _render_jinja_const_template: bool = False,
448
+ ) -> t.Any:
449
+ """
450
+ Evaluate a trusted string expression and return its result.
451
+ Optional local variables may be provided, which can only be referenced directly by the given expression.
452
+ """
453
+ if not isinstance(expression, str):
454
+ raise TypeError(f"Expressions must be {str!r}, got {type(expression)!r}.")
455
+
456
+ options = TemplateOptions(escape_backslashes=escape_backslashes, preserve_trailing_newlines=False)
457
+
458
+ with (
459
+ TemplateContext(template_value=expression, templar=self, options=options, _render_jinja_const_template=_render_jinja_const_template) as ctx,
460
+ DeprecatedAccessAuditContext.when(ctx.is_top_level),
461
+ ):
462
+ try:
463
+ if not TrustedAsTemplate.is_tagged_on(expression):
464
+ raise TemplateTrustCheckFailedError(obj=expression)
465
+
466
+ template_variables = ChainMap(local_variables, self.available_variables) if local_variables else self.available_variables
467
+ compiled_template = self._compile_expression(expression, options)
468
+
469
+ template_result = compiled_template(template_variables)
470
+ template_result = self._post_render_mutation(expression, template_result, options)
471
+ except Exception as ex:
472
+ template_result = defer_template_error(ex, expression, is_expression=True)
473
+
474
+ return self._finalize_top_level_template_result(expression, options, template_result, is_expression=True)
475
+
476
+ _BROKEN_CONDITIONAL_ALLOWED_FRAGMENT = 'Broken conditionals are currently allowed because the `ALLOW_BROKEN_CONDITIONALS` configuration option is enabled.'
477
+ _CONDITIONAL_AS_TEMPLATE_MSG = 'Conditionals should not be surrounded by templating delimiters such as {{ }} or {% %}.'
478
+
479
+ def _strip_conditional_handle_empty(self, conditional) -> t.Any:
480
+ """
481
+ Strips leading/trailing whitespace from the input expression.
482
+ If `ALLOW_BROKEN_CONDITIONALS` is enabled, None/empty is coerced to True (legacy behavior, deprecated).
483
+ Otherwise, None/empty results in a broken conditional error being raised.
484
+ """
485
+ if isinstance(conditional, str):
486
+ # Leading/trailing whitespace on conditional expressions is not a problem, except we can't tell if the expression is empty (which *is* a problem).
487
+ # Always strip conditional input strings. Neither conditional expressions nor all-template conditionals have legit reasons to preserve
488
+ # surrounding whitespace, and they complicate detection and processing of all-template fallback cases.
489
+ conditional = AnsibleTagHelper.tag_copy(conditional, conditional.strip())
490
+
491
+ if conditional in (None, ''):
492
+ # deprecated backward-compatible behavior; None/empty input conditionals are always True
493
+ if _TemplateConfig.allow_broken_conditionals:
494
+ _display.deprecated(
495
+ msg='Empty conditional expression was evaluated as True.',
496
+ help_text=self._BROKEN_CONDITIONAL_ALLOWED_FRAGMENT,
497
+ obj=conditional,
498
+ version='2.23',
499
+ )
500
+
501
+ return True
502
+
503
+ raise AnsibleBrokenConditionalError("Empty conditional expressions are not allowed.", obj=conditional)
504
+
505
+ return conditional
506
+
507
+ def _normalize_and_evaluate_conditional(self, conditional: str | bool) -> t.Any:
508
+ """Validate and normalize a conditional input value, resolving allowed embedded template cases and evaluating the resulting expression."""
509
+ conditional = self._strip_conditional_handle_empty(conditional)
510
+
511
+ # this must follow `_strip_conditional_handle_empty`, since None/empty are coerced to bool (deprecated)
512
+ if type(conditional) is bool: # pylint: disable=unidiomatic-typecheck
513
+ return conditional
514
+
515
+ try:
516
+ if not isinstance(conditional, str):
517
+ if _TemplateConfig.allow_broken_conditionals:
518
+ # because the input isn't a string, the result will never be a bool; the broken conditional warning in the caller will apply on the result
519
+ return self.template(conditional, mode=TemplateMode.ALWAYS_FINALIZE)
520
+
521
+ raise AnsibleBrokenConditionalError(message="Conditional expressions must be strings.", obj=conditional)
522
+
523
+ if is_possibly_all_template(conditional):
524
+ # Indirection of trusted expressions is always allowed. If the expression appears to be entirely wrapped in template delimiters,
525
+ # we must resolve it. e.g. `when: "{{ some_var_resolving_to_a_trusted_expression_string }}"`.
526
+ # Some invalid meta-templating corner cases may sneak through here (e.g., `when: '{{ "foo" }} == {{ "bar" }}'`); these will
527
+ # result in an untrusted expression error.
528
+ result = self.template(conditional, mode=TemplateMode.ALWAYS_FINALIZE)
529
+ result = self._strip_conditional_handle_empty(result)
530
+
531
+ if not isinstance(result, str):
532
+ _display.deprecated(msg=self._CONDITIONAL_AS_TEMPLATE_MSG, obj=conditional, version='2.23')
533
+
534
+ return result # not an expression
535
+
536
+ # The only allowed use of templates for conditionals is for indirect usage of an expression.
537
+ # Any other usage should simply be an expression, not an attempt at meta templating.
538
+ expression = result
539
+ else:
540
+ expression = conditional
541
+
542
+ # Disable escape_backslashes when processing conditionals, to maintain backwards compatibility.
543
+ # This is necessary because conditionals were previously evaluated using {% %}, which was *NOT* affected by escape_backslashes.
544
+ # Now that conditionals use expressions, they would be affected by escape_backslashes if it was not disabled.
545
+ return self.evaluate_expression(expression, escape_backslashes=False, _render_jinja_const_template=True)
546
+
547
+ except AnsibleUndefinedVariable as ex:
548
+ # DTFIX-FUTURE: we're only augmenting the message for context here; once we have proper contextual tracking, we can dump the re-raise
549
+ raise AnsibleUndefinedVariable("Error while evaluating conditional.", obj=conditional) from ex
550
+
551
+ def evaluate_conditional(self, conditional: str | bool) -> bool:
552
+ """
553
+ Evaluate a trusted string expression or boolean and return its boolean result. A non-boolean result will raise `AnsibleBrokenConditionalError`.
554
+ The ALLOW_BROKEN_CONDITIONALS configuration option can temporarily relax this requirement, allowing truthy conditionals to succeed.
555
+ """
556
+ result = self._normalize_and_evaluate_conditional(conditional)
557
+
558
+ if isinstance(result, bool):
559
+ return result
560
+
561
+ bool_result = bool(result)
562
+
563
+ msg = (
564
+ f'Conditional result was {textwrap.shorten(str(result), width=40)!r} of type {native_type_name(result)!r}, '
565
+ f'which evaluates to {bool_result}. Conditionals must have a boolean result.'
566
+ )
567
+
568
+ if _TemplateConfig.allow_broken_conditionals:
569
+ _display.deprecated(msg=msg, obj=conditional, help_text=self._BROKEN_CONDITIONAL_ALLOWED_FRAGMENT, version='2.23')
570
+
571
+ return bool_result
572
+
573
+ raise AnsibleBrokenConditionalError(msg, obj=conditional)
574
+
575
+ @staticmethod
576
+ def _trust_check(value: str, skip_handler: bool = False) -> bool:
577
+ """
578
+ Return True if the given value is trusted for templating, otherwise return False.
579
+ When the value is not trusted, a warning or error may be generated, depending on configuration.
580
+ """
581
+ if TrustedAsTemplate.is_tagged_on(value):
582
+ return True
583
+
584
+ if not skip_handler:
585
+ with Skippable, _TemplateConfig.untrusted_template_handler.handle(TemplateTrustCheckFailedError, skip_on_ignore=True):
586
+ raise TemplateTrustCheckFailedError(obj=value)
587
+
588
+ return False