ansible-core 2.18.4rc1__py3-none-any.whl → 2.19.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (709) hide show
  1. ansible/_internal/__init__.py +53 -0
  2. ansible/_internal/_ansiballz.py +265 -0
  3. ansible/_internal/_datatag/__init__.py +0 -0
  4. ansible/_internal/_datatag/_tags.py +130 -0
  5. ansible/_internal/_datatag/_utils.py +19 -0
  6. ansible/_internal/_datatag/_wrappers.py +33 -0
  7. ansible/_internal/_errors/__init__.py +0 -0
  8. ansible/_internal/_errors/_captured.py +128 -0
  9. ansible/_internal/_errors/_handler.py +91 -0
  10. ansible/_internal/_errors/_utils.py +310 -0
  11. ansible/_internal/_json/__init__.py +160 -0
  12. ansible/_internal/_json/_legacy_encoder.py +34 -0
  13. ansible/_internal/_json/_profiles/__init__.py +0 -0
  14. ansible/_internal/_json/_profiles/_cache_persistence.py +55 -0
  15. ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
  16. ansible/_internal/_json/_profiles/_legacy.py +198 -0
  17. ansible/_internal/_locking.py +21 -0
  18. ansible/_internal/_plugins/__init__.py +0 -0
  19. ansible/_internal/_plugins/_cache.py +57 -0
  20. ansible/_internal/_task.py +78 -0
  21. ansible/_internal/_templating/__init__.py +10 -0
  22. ansible/_internal/_templating/_access.py +86 -0
  23. ansible/_internal/_templating/_chain_templar.py +63 -0
  24. ansible/_internal/_templating/_datatag.py +95 -0
  25. ansible/_internal/_templating/_engine.py +588 -0
  26. ansible/_internal/_templating/_errors.py +28 -0
  27. ansible/_internal/_templating/_jinja_bits.py +1066 -0
  28. ansible/_internal/_templating/_jinja_common.py +332 -0
  29. ansible/_internal/_templating/_jinja_patches.py +44 -0
  30. ansible/_internal/_templating/_jinja_plugins.py +351 -0
  31. ansible/_internal/_templating/_lazy_containers.py +633 -0
  32. ansible/_internal/_templating/_marker_behaviors.py +103 -0
  33. ansible/_internal/_templating/_transform.py +63 -0
  34. ansible/_internal/_templating/_utils.py +107 -0
  35. ansible/_internal/_wrapt.py +1052 -0
  36. ansible/_internal/_yaml/__init__.py +0 -0
  37. ansible/_internal/_yaml/_constructor.py +240 -0
  38. ansible/_internal/_yaml/_dumper.py +62 -0
  39. ansible/_internal/_yaml/_errors.py +166 -0
  40. ansible/_internal/_yaml/_loader.py +66 -0
  41. ansible/_internal/ansible_collections/ansible/_protomatter/README.md +11 -0
  42. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/action/debug.py +36 -0
  43. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/apply_trust.py +19 -0
  44. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +18 -0
  45. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/finalize.py +16 -0
  46. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/origin.py +18 -0
  47. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.py +24 -0
  48. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.yml +33 -0
  49. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/tag_names.py +16 -0
  50. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +17 -0
  51. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +49 -0
  52. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.py +21 -0
  53. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.yml +2 -0
  54. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.py +15 -0
  55. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.yml +19 -0
  56. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.py +18 -0
  57. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.yml +19 -0
  58. ansible/cli/__init__.py +153 -89
  59. ansible/cli/_ssh_askpass.py +47 -0
  60. ansible/cli/adhoc.py +14 -7
  61. ansible/cli/arguments/option_helpers.py +154 -7
  62. ansible/cli/config.py +43 -68
  63. ansible/cli/console.py +10 -8
  64. ansible/cli/doc.py +48 -46
  65. ansible/cli/galaxy.py +27 -20
  66. ansible/cli/inventory.py +28 -26
  67. ansible/cli/playbook.py +4 -12
  68. ansible/cli/pull.py +51 -11
  69. ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
  70. ansible/cli/vault.py +12 -11
  71. ansible/compat/__init__.py +2 -2
  72. ansible/config/base.yml +165 -108
  73. ansible/config/manager.py +52 -49
  74. ansible/constants.py +3 -4
  75. ansible/errors/__init__.py +277 -235
  76. ansible/executor/interpreter_discovery.py +28 -149
  77. ansible/executor/module_common.py +426 -493
  78. ansible/executor/play_iterator.py +22 -27
  79. ansible/executor/playbook_executor.py +11 -11
  80. ansible/executor/powershell/async_watchdog.ps1 +97 -102
  81. ansible/executor/powershell/async_wrapper.ps1 +202 -151
  82. ansible/executor/powershell/become_wrapper.ps1 +89 -144
  83. ansible/executor/powershell/bootstrap_wrapper.ps1 +24 -9
  84. ansible/executor/powershell/coverage_wrapper.ps1 +82 -135
  85. ansible/executor/powershell/exec_wrapper.ps1 +462 -196
  86. ansible/executor/powershell/module_manifest.py +417 -265
  87. ansible/executor/powershell/module_wrapper.ps1 +169 -186
  88. ansible/executor/powershell/psrp_fetch_file.ps1 +41 -0
  89. ansible/executor/powershell/psrp_put_file.ps1 +122 -0
  90. ansible/executor/powershell/winrm_fetch_file.ps1 +46 -0
  91. ansible/executor/powershell/winrm_put_file.ps1 +36 -0
  92. ansible/executor/process/worker.py +136 -76
  93. ansible/executor/stats.py +5 -5
  94. ansible/executor/task_executor.py +237 -236
  95. ansible/executor/task_queue_manager.py +62 -38
  96. ansible/executor/task_result.py +21 -12
  97. ansible/galaxy/__init__.py +2 -2
  98. ansible/galaxy/api.py +22 -18
  99. ansible/galaxy/collection/__init__.py +1 -1
  100. ansible/galaxy/collection/concrete_artifact_manager.py +8 -11
  101. ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
  102. ansible/galaxy/dependency_resolution/providers.py +1 -1
  103. ansible/galaxy/dependency_resolution/reporters.py +81 -0
  104. ansible/galaxy/role.py +4 -8
  105. ansible/galaxy/token.py +28 -21
  106. ansible/inventory/data.py +47 -57
  107. ansible/inventory/group.py +44 -72
  108. ansible/inventory/helpers.py +9 -0
  109. ansible/inventory/host.py +32 -54
  110. ansible/inventory/manager.py +77 -34
  111. ansible/keyword_desc.yml +1 -1
  112. ansible/module_utils/_internal/__init__.py +55 -0
  113. ansible/module_utils/_internal/_ambient_context.py +58 -0
  114. ansible/module_utils/_internal/_ansiballz.py +133 -0
  115. ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
  116. ansible/module_utils/_internal/_dataclass_annotation_patch.py +64 -0
  117. ansible/module_utils/_internal/_dataclass_validation.py +217 -0
  118. ansible/module_utils/_internal/_datatag/__init__.py +928 -0
  119. ansible/module_utils/_internal/_datatag/_tags.py +38 -0
  120. ansible/module_utils/_internal/_debugging.py +31 -0
  121. ansible/module_utils/_internal/_errors.py +30 -0
  122. ansible/module_utils/_internal/_json/__init__.py +63 -0
  123. ansible/module_utils/_internal/_json/_legacy_encoder.py +26 -0
  124. ansible/module_utils/_internal/_json/_profiles/__init__.py +410 -0
  125. ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
  126. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +31 -0
  127. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +35 -0
  128. ansible/module_utils/_internal/_json/_profiles/_module_modern_c2m.py +35 -0
  129. ansible/module_utils/_internal/_json/_profiles/_module_modern_m2c.py +33 -0
  130. ansible/module_utils/_internal/_json/_profiles/_tagless.py +50 -0
  131. ansible/module_utils/_internal/_patches/__init__.py +66 -0
  132. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +55 -0
  133. ansible/module_utils/_internal/_patches/_socket_patch.py +34 -0
  134. ansible/module_utils/_internal/_patches/_sys_intern_patch.py +34 -0
  135. ansible/module_utils/_internal/_plugin_exec_context.py +49 -0
  136. ansible/module_utils/_internal/_testing.py +0 -0
  137. ansible/module_utils/_internal/_traceback.py +89 -0
  138. ansible/module_utils/ansible_release.py +2 -2
  139. ansible/module_utils/api.py +1 -2
  140. ansible/module_utils/basic.py +154 -120
  141. ansible/module_utils/common/_utils.py +24 -28
  142. ansible/module_utils/common/collections.py +1 -2
  143. ansible/module_utils/common/dict_transformations.py +2 -2
  144. ansible/module_utils/common/file.py +2 -2
  145. ansible/module_utils/common/json.py +90 -84
  146. ansible/module_utils/common/locale.py +2 -2
  147. ansible/module_utils/common/messages.py +108 -0
  148. ansible/module_utils/common/parameters.py +27 -24
  149. ansible/module_utils/common/process.py +2 -2
  150. ansible/module_utils/common/respawn.py +41 -19
  151. ansible/module_utils/common/sentinel.py +66 -0
  152. ansible/module_utils/common/sys_info.py +8 -8
  153. ansible/module_utils/common/text/converters.py +16 -37
  154. ansible/module_utils/common/validation.py +35 -24
  155. ansible/module_utils/common/warnings.py +86 -25
  156. ansible/module_utils/common/yaml.py +29 -3
  157. ansible/module_utils/compat/datetime.py +33 -21
  158. ansible/module_utils/compat/paramiko.py +21 -10
  159. ansible/module_utils/compat/typing.py +6 -5
  160. ansible/module_utils/connection.py +2 -2
  161. ansible/module_utils/csharp/Ansible.Basic.cs +14 -11
  162. ansible/module_utils/csharp/Ansible.Become.cs +1 -0
  163. ansible/module_utils/csharp/Ansible._Async.cs +517 -0
  164. ansible/module_utils/datatag.py +46 -0
  165. ansible/module_utils/distro/__init__.py +2 -2
  166. ansible/module_utils/facts/ansible_collector.py +4 -5
  167. ansible/module_utils/facts/collector.py +13 -14
  168. ansible/module_utils/facts/compat.py +4 -4
  169. ansible/module_utils/facts/default_collectors.py +1 -1
  170. ansible/module_utils/facts/hardware/aix.py +34 -0
  171. ansible/module_utils/facts/hardware/base.py +1 -1
  172. ansible/module_utils/facts/hardware/darwin.py +1 -3
  173. ansible/module_utils/facts/hardware/freebsd.py +2 -2
  174. ansible/module_utils/facts/hardware/linux.py +4 -4
  175. ansible/module_utils/facts/namespace.py +1 -1
  176. ansible/module_utils/facts/network/base.py +1 -1
  177. ansible/module_utils/facts/network/fc_wwn.py +1 -2
  178. ansible/module_utils/facts/network/iscsi.py +1 -2
  179. ansible/module_utils/facts/network/nvme.py +1 -2
  180. ansible/module_utils/facts/other/facter.py +1 -2
  181. ansible/module_utils/facts/other/ohai.py +2 -3
  182. ansible/module_utils/facts/system/apparmor.py +1 -2
  183. ansible/module_utils/facts/system/caps.py +1 -1
  184. ansible/module_utils/facts/system/chroot.py +1 -2
  185. ansible/module_utils/facts/system/cmdline.py +1 -2
  186. ansible/module_utils/facts/system/date_time.py +5 -3
  187. ansible/module_utils/facts/system/distribution.py +9 -8
  188. ansible/module_utils/facts/system/dns.py +1 -1
  189. ansible/module_utils/facts/system/env.py +1 -2
  190. ansible/module_utils/facts/system/fips.py +7 -20
  191. ansible/module_utils/facts/system/loadavg.py +1 -2
  192. ansible/module_utils/facts/system/local.py +1 -2
  193. ansible/module_utils/facts/system/lsb.py +1 -2
  194. ansible/module_utils/facts/system/pkg_mgr.py +1 -2
  195. ansible/module_utils/facts/system/platform.py +1 -2
  196. ansible/module_utils/facts/system/python.py +1 -2
  197. ansible/module_utils/facts/system/selinux.py +1 -1
  198. ansible/module_utils/facts/system/service_mgr.py +1 -2
  199. ansible/module_utils/facts/system/ssh_pub_keys.py +1 -1
  200. ansible/module_utils/facts/system/systemd.py +1 -1
  201. ansible/module_utils/facts/system/user.py +1 -2
  202. ansible/module_utils/facts/utils.py +3 -3
  203. ansible/module_utils/facts/virtual/base.py +1 -1
  204. ansible/module_utils/facts/virtual/sunos.py +3 -15
  205. ansible/module_utils/facts/virtual/sysctl.py +3 -16
  206. ansible/module_utils/json_utils.py +2 -2
  207. ansible/module_utils/parsing/convert_bool.py +1 -1
  208. ansible/module_utils/service.py +18 -21
  209. ansible/module_utils/splitter.py +7 -7
  210. ansible/module_utils/testing.py +31 -0
  211. ansible/module_utils/urls.py +60 -31
  212. ansible/modules/add_host.py +4 -4
  213. ansible/modules/apt.py +60 -46
  214. ansible/modules/apt_key.py +19 -12
  215. ansible/modules/apt_repository.py +19 -16
  216. ansible/modules/assemble.py +6 -6
  217. ansible/modules/assert.py +4 -4
  218. ansible/modules/async_status.py +10 -12
  219. ansible/modules/async_wrapper.py +8 -3
  220. ansible/modules/blockinfile.py +6 -7
  221. ansible/modules/command.py +10 -17
  222. ansible/modules/copy.py +57 -144
  223. ansible/modules/cron.py +20 -15
  224. ansible/modules/deb822_repository.py +8 -9
  225. ansible/modules/debconf.py +5 -5
  226. ansible/modules/debug.py +4 -4
  227. ansible/modules/dnf.py +8 -8
  228. ansible/modules/dnf5.py +52 -17
  229. ansible/modules/dpkg_selections.py +4 -4
  230. ansible/modules/expect.py +8 -10
  231. ansible/modules/fail.py +4 -4
  232. ansible/modules/fetch.py +4 -4
  233. ansible/modules/file.py +174 -133
  234. ansible/modules/find.py +20 -18
  235. ansible/modules/gather_facts.py +3 -3
  236. ansible/modules/get_url.py +59 -53
  237. ansible/modules/getent.py +7 -9
  238. ansible/modules/git.py +28 -25
  239. ansible/modules/group.py +6 -6
  240. ansible/modules/group_by.py +4 -4
  241. ansible/modules/hostname.py +13 -29
  242. ansible/modules/import_playbook.py +6 -6
  243. ansible/modules/import_role.py +7 -7
  244. ansible/modules/import_tasks.py +6 -6
  245. ansible/modules/include_role.py +6 -6
  246. ansible/modules/include_tasks.py +6 -6
  247. ansible/modules/include_vars.py +6 -6
  248. ansible/modules/iptables.py +86 -73
  249. ansible/modules/known_hosts.py +10 -10
  250. ansible/modules/lineinfile.py +5 -5
  251. ansible/modules/meta.py +4 -4
  252. ansible/modules/mount_facts.py +2 -2
  253. ansible/modules/package.py +4 -4
  254. ansible/modules/package_facts.py +22 -10
  255. ansible/modules/pause.py +6 -6
  256. ansible/modules/ping.py +6 -6
  257. ansible/modules/pip.py +10 -11
  258. ansible/modules/raw.py +4 -4
  259. ansible/modules/reboot.py +6 -6
  260. ansible/modules/replace.py +9 -13
  261. ansible/modules/rpm_key.py +7 -8
  262. ansible/modules/script.py +4 -4
  263. ansible/modules/service.py +7 -8
  264. ansible/modules/service_facts.py +87 -10
  265. ansible/modules/set_fact.py +5 -5
  266. ansible/modules/set_stats.py +4 -4
  267. ansible/modules/setup.py +2 -2
  268. ansible/modules/shell.py +6 -6
  269. ansible/modules/slurp.py +6 -6
  270. ansible/modules/stat.py +9 -23
  271. ansible/modules/subversion.py +15 -15
  272. ansible/modules/systemd.py +6 -6
  273. ansible/modules/systemd_service.py +6 -6
  274. ansible/modules/sysvinit.py +6 -6
  275. ansible/modules/tempfile.py +5 -6
  276. ansible/modules/template.py +6 -6
  277. ansible/modules/unarchive.py +32 -11
  278. ansible/modules/uri.py +35 -49
  279. ansible/modules/user.py +53 -34
  280. ansible/modules/validate_argument_spec.py +10 -7
  281. ansible/modules/wait_for.py +39 -32
  282. ansible/modules/wait_for_connection.py +6 -6
  283. ansible/modules/yum_repository.py +6 -6
  284. ansible/parsing/ajson.py +14 -32
  285. ansible/parsing/dataloader.py +99 -54
  286. ansible/parsing/mod_args.py +28 -44
  287. ansible/parsing/plugin_docs.py +21 -86
  288. ansible/parsing/quoting.py +1 -1
  289. ansible/parsing/splitter.py +27 -12
  290. ansible/parsing/utils/addresses.py +24 -24
  291. ansible/parsing/utils/yaml.py +32 -61
  292. ansible/parsing/vault/__init__.py +319 -87
  293. ansible/parsing/yaml/__init__.py +0 -18
  294. ansible/parsing/yaml/dumper.py +6 -120
  295. ansible/parsing/yaml/loader.py +6 -39
  296. ansible/parsing/yaml/objects.py +36 -339
  297. ansible/playbook/__init__.py +1 -1
  298. ansible/playbook/attribute.py +8 -3
  299. ansible/playbook/base.py +182 -132
  300. ansible/playbook/block.py +26 -24
  301. ansible/playbook/collectionsearch.py +1 -15
  302. ansible/playbook/conditional.py +3 -77
  303. ansible/playbook/handler.py +8 -2
  304. ansible/playbook/helpers.py +41 -53
  305. ansible/playbook/included_file.py +6 -15
  306. ansible/playbook/loop_control.py +2 -2
  307. ansible/playbook/play.py +85 -44
  308. ansible/playbook/play_context.py +12 -17
  309. ansible/playbook/playbook_include.py +14 -15
  310. ansible/playbook/role/__init__.py +24 -26
  311. ansible/playbook/role/definition.py +15 -17
  312. ansible/playbook/role/include.py +2 -4
  313. ansible/playbook/role/metadata.py +10 -11
  314. ansible/playbook/role_include.py +3 -3
  315. ansible/playbook/taggable.py +13 -8
  316. ansible/playbook/task.py +188 -118
  317. ansible/playbook/task_include.py +5 -5
  318. ansible/plugins/__init__.py +68 -21
  319. ansible/plugins/action/__init__.py +209 -176
  320. ansible/plugins/action/add_host.py +1 -1
  321. ansible/plugins/action/assemble.py +1 -1
  322. ansible/plugins/action/assert.py +54 -66
  323. ansible/plugins/action/copy.py +7 -11
  324. ansible/plugins/action/debug.py +37 -31
  325. ansible/plugins/action/dnf.py +3 -4
  326. ansible/plugins/action/fail.py +1 -1
  327. ansible/plugins/action/fetch.py +4 -5
  328. ansible/plugins/action/gather_facts.py +8 -7
  329. ansible/plugins/action/group_by.py +1 -1
  330. ansible/plugins/action/include_vars.py +10 -11
  331. ansible/plugins/action/package.py +3 -6
  332. ansible/plugins/action/pause.py +2 -2
  333. ansible/plugins/action/script.py +15 -8
  334. ansible/plugins/action/service.py +6 -11
  335. ansible/plugins/action/set_fact.py +3 -12
  336. ansible/plugins/action/set_stats.py +3 -8
  337. ansible/plugins/action/template.py +35 -59
  338. ansible/plugins/action/unarchive.py +1 -1
  339. ansible/plugins/action/validate_argument_spec.py +5 -5
  340. ansible/plugins/action/wait_for_connection.py +1 -1
  341. ansible/plugins/become/__init__.py +31 -8
  342. ansible/plugins/become/runas.py +71 -0
  343. ansible/plugins/become/su.py +13 -8
  344. ansible/plugins/become/sudo.py +19 -0
  345. ansible/plugins/cache/__init__.py +35 -44
  346. ansible/plugins/cache/base.py +8 -0
  347. ansible/plugins/cache/jsonfile.py +10 -16
  348. ansible/plugins/cache/memory.py +6 -12
  349. ansible/plugins/callback/__init__.py +141 -123
  350. ansible/plugins/callback/default.py +30 -23
  351. ansible/plugins/callback/junit.py +28 -24
  352. ansible/plugins/callback/minimal.py +17 -14
  353. ansible/plugins/callback/oneline.py +13 -7
  354. ansible/plugins/callback/tree.py +10 -6
  355. ansible/plugins/connection/__init__.py +47 -34
  356. ansible/plugins/connection/local.py +150 -54
  357. ansible/plugins/connection/paramiko_ssh.py +21 -18
  358. ansible/plugins/connection/psrp.py +76 -165
  359. ansible/plugins/connection/ssh.py +301 -78
  360. ansible/plugins/connection/winrm.py +58 -140
  361. ansible/plugins/doc_fragments/action_common_attributes.py +14 -14
  362. ansible/plugins/doc_fragments/action_core.py +6 -6
  363. ansible/plugins/doc_fragments/backup.py +2 -2
  364. ansible/plugins/doc_fragments/checksum_common.py +27 -0
  365. ansible/plugins/doc_fragments/constructed.py +6 -2
  366. ansible/plugins/doc_fragments/decrypt.py +2 -2
  367. ansible/plugins/doc_fragments/default_callback.py +2 -2
  368. ansible/plugins/doc_fragments/files.py +2 -2
  369. ansible/plugins/doc_fragments/inventory_cache.py +2 -2
  370. ansible/plugins/doc_fragments/result_format_callback.py +2 -2
  371. ansible/plugins/doc_fragments/return_common.py +2 -2
  372. ansible/plugins/doc_fragments/template_common.py +4 -4
  373. ansible/plugins/doc_fragments/url.py +17 -1
  374. ansible/plugins/doc_fragments/url_windows.py +2 -2
  375. ansible/plugins/doc_fragments/validate.py +2 -2
  376. ansible/plugins/doc_fragments/vars_plugin_staging.py +2 -2
  377. ansible/plugins/filter/__init__.py +6 -2
  378. ansible/plugins/filter/b64decode.yml +22 -0
  379. ansible/plugins/filter/b64encode.yml +22 -0
  380. ansible/plugins/filter/bool.yml +11 -4
  381. ansible/plugins/filter/core.py +218 -108
  382. ansible/plugins/filter/encryption.py +32 -32
  383. ansible/plugins/filter/flatten.yml +3 -2
  384. ansible/plugins/filter/human_to_bytes.yml +2 -2
  385. ansible/plugins/filter/mathstuff.py +30 -37
  386. ansible/plugins/filter/password_hash.yml +8 -0
  387. ansible/plugins/filter/regex_search.yml +1 -4
  388. ansible/plugins/filter/split.yml +1 -1
  389. ansible/plugins/filter/to_nice_yaml.yml +0 -4
  390. ansible/plugins/filter/to_yaml.yml +0 -4
  391. ansible/plugins/filter/unvault.yml +1 -1
  392. ansible/plugins/filter/urls.py +1 -1
  393. ansible/plugins/filter/urlsplit.py +8 -9
  394. ansible/plugins/filter/vault.yml +14 -9
  395. ansible/plugins/filter/win_basename.yml +6 -1
  396. ansible/plugins/filter/win_dirname.yml +5 -0
  397. ansible/plugins/inventory/__init__.py +97 -77
  398. ansible/plugins/inventory/advanced_host_list.py +7 -5
  399. ansible/plugins/inventory/auto.py +11 -4
  400. ansible/plugins/inventory/constructed.py +21 -24
  401. ansible/plugins/inventory/generator.py +16 -11
  402. ansible/plugins/inventory/host_list.py +7 -5
  403. ansible/plugins/inventory/ini.py +78 -44
  404. ansible/plugins/inventory/script.py +189 -119
  405. ansible/plugins/inventory/toml.py +16 -126
  406. ansible/plugins/inventory/yaml.py +10 -8
  407. ansible/plugins/list.py +3 -3
  408. ansible/plugins/loader.py +197 -82
  409. ansible/plugins/lookup/__init__.py +21 -4
  410. ansible/plugins/lookup/config.py +21 -35
  411. ansible/plugins/lookup/csvfile.py +3 -2
  412. ansible/plugins/lookup/dict.py +1 -6
  413. ansible/plugins/lookup/env.py +12 -9
  414. ansible/plugins/lookup/file.py +5 -8
  415. ansible/plugins/lookup/first_found.py +86 -55
  416. ansible/plugins/lookup/indexed_items.py +1 -10
  417. ansible/plugins/lookup/ini.py +14 -13
  418. ansible/plugins/lookup/items.py +1 -1
  419. ansible/plugins/lookup/lines.py +8 -1
  420. ansible/plugins/lookup/list.py +1 -1
  421. ansible/plugins/lookup/nested.py +2 -18
  422. ansible/plugins/lookup/password.py +5 -5
  423. ansible/plugins/lookup/pipe.py +5 -7
  424. ansible/plugins/lookup/sequence.py +18 -8
  425. ansible/plugins/lookup/subelements.py +1 -4
  426. ansible/plugins/lookup/template.py +42 -36
  427. ansible/plugins/lookup/together.py +0 -12
  428. ansible/plugins/lookup/unvault.py +1 -5
  429. ansible/plugins/lookup/url.py +2 -8
  430. ansible/plugins/lookup/vars.py +16 -24
  431. ansible/plugins/shell/__init__.py +2 -2
  432. ansible/plugins/shell/cmd.py +2 -2
  433. ansible/plugins/shell/powershell.py +39 -22
  434. ansible/plugins/shell/sh.py +3 -2
  435. ansible/plugins/strategy/__init__.py +94 -113
  436. ansible/plugins/strategy/debug.py +2 -2
  437. ansible/plugins/strategy/free.py +13 -28
  438. ansible/plugins/strategy/host_pinned.py +2 -2
  439. ansible/plugins/strategy/linear.py +31 -33
  440. ansible/plugins/terminal/__init__.py +4 -4
  441. ansible/plugins/test/__init__.py +7 -2
  442. ansible/plugins/test/core.py +54 -20
  443. ansible/plugins/test/files.py +1 -1
  444. ansible/plugins/test/mathstuff.py +3 -3
  445. ansible/plugins/test/uri.py +3 -3
  446. ansible/plugins/vars/host_group_vars.py +7 -14
  447. ansible/release.py +2 -2
  448. ansible/template/__init__.py +368 -944
  449. ansible/utils/__init__.py +0 -18
  450. ansible/utils/_ssh_agent.py +657 -0
  451. ansible/utils/collection_loader/__init__.py +52 -5
  452. ansible/utils/collection_loader/_collection_config.py +5 -6
  453. ansible/utils/collection_loader/_collection_finder.py +79 -93
  454. ansible/utils/collection_loader/_collection_meta.py +13 -8
  455. ansible/utils/display.py +428 -58
  456. ansible/utils/encrypt.py +27 -19
  457. ansible/utils/fqcn.py +2 -2
  458. ansible/utils/hashing.py +2 -2
  459. ansible/utils/helpers.py +2 -2
  460. ansible/utils/listify.py +8 -8
  461. ansible/utils/lock.py +2 -2
  462. ansible/utils/path.py +4 -4
  463. ansible/utils/plugin_docs.py +14 -13
  464. ansible/utils/sentinel.py +4 -62
  465. ansible/utils/singleton.py +2 -0
  466. ansible/utils/ssh_functions.py +1 -1
  467. ansible/utils/unsafe_proxy.py +23 -332
  468. ansible/utils/vars.py +28 -8
  469. ansible/utils/version.py +2 -2
  470. ansible/vars/clean.py +4 -4
  471. ansible/vars/hostvars.py +60 -90
  472. ansible/vars/manager.py +205 -264
  473. ansible/vars/reserved.py +8 -9
  474. ansible_core-2.19.0b1.dist-info/BSD-3-Clause.txt +28 -0
  475. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/METADATA +5 -4
  476. ansible_core-2.19.0b1.dist-info/RECORD +1070 -0
  477. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/WHEEL +1 -1
  478. ansible_test/_data/completion/docker.txt +7 -7
  479. ansible_test/_data/completion/remote.txt +6 -6
  480. ansible_test/_data/completion/windows.txt +1 -0
  481. ansible_test/_data/requirements/ansible.txt +2 -2
  482. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
  483. ansible_test/_data/requirements/sanity.changelog.txt +1 -1
  484. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  485. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  486. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
  487. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  488. ansible_test/_data/requirements/units.txt +1 -0
  489. ansible_test/_internal/__init__.py +1 -0
  490. ansible_test/_internal/ansible_util.py +2 -0
  491. ansible_test/_internal/become.py +1 -0
  492. ansible_test/_internal/bootstrap.py +1 -0
  493. ansible_test/_internal/cache.py +1 -0
  494. ansible_test/_internal/cgroup.py +1 -0
  495. ansible_test/_internal/ci/__init__.py +1 -0
  496. ansible_test/_internal/ci/azp.py +1 -0
  497. ansible_test/_internal/ci/local.py +1 -0
  498. ansible_test/_internal/classification/__init__.py +1 -0
  499. ansible_test/_internal/classification/common.py +1 -0
  500. ansible_test/_internal/classification/csharp.py +1 -0
  501. ansible_test/_internal/classification/powershell.py +1 -0
  502. ansible_test/_internal/classification/python.py +1 -0
  503. ansible_test/_internal/cli/__init__.py +1 -0
  504. ansible_test/_internal/cli/actions.py +1 -0
  505. ansible_test/_internal/cli/argparsing/__init__.py +1 -0
  506. ansible_test/_internal/cli/argparsing/actions.py +1 -0
  507. ansible_test/_internal/cli/argparsing/argcompletion.py +1 -0
  508. ansible_test/_internal/cli/argparsing/parsers.py +1 -0
  509. ansible_test/_internal/cli/commands/__init__.py +11 -0
  510. ansible_test/_internal/cli/commands/coverage/__init__.py +1 -0
  511. ansible_test/_internal/cli/commands/coverage/analyze/__init__.py +1 -0
  512. ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py +1 -0
  513. ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py +1 -0
  514. ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py +1 -0
  515. ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py +1 -0
  516. ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py +1 -0
  517. ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py +1 -0
  518. ansible_test/_internal/cli/commands/coverage/combine.py +1 -0
  519. ansible_test/_internal/cli/commands/coverage/erase.py +1 -0
  520. ansible_test/_internal/cli/commands/coverage/html.py +1 -0
  521. ansible_test/_internal/cli/commands/coverage/report.py +1 -0
  522. ansible_test/_internal/cli/commands/coverage/xml.py +1 -0
  523. ansible_test/_internal/cli/commands/env.py +1 -0
  524. ansible_test/_internal/cli/commands/integration/__init__.py +1 -0
  525. ansible_test/_internal/cli/commands/integration/network.py +1 -0
  526. ansible_test/_internal/cli/commands/integration/posix.py +1 -0
  527. ansible_test/_internal/cli/commands/integration/windows.py +1 -0
  528. ansible_test/_internal/cli/commands/sanity.py +9 -0
  529. ansible_test/_internal/cli/commands/shell.py +1 -0
  530. ansible_test/_internal/cli/commands/units.py +1 -0
  531. ansible_test/_internal/cli/compat.py +1 -0
  532. ansible_test/_internal/cli/completers.py +1 -0
  533. ansible_test/_internal/cli/converters.py +1 -0
  534. ansible_test/_internal/cli/environments.py +1 -0
  535. ansible_test/_internal/cli/epilog.py +1 -0
  536. ansible_test/_internal/cli/parsers/__init__.py +1 -0
  537. ansible_test/_internal/cli/parsers/base_argument_parsers.py +1 -0
  538. ansible_test/_internal/cli/parsers/helpers.py +1 -0
  539. ansible_test/_internal/cli/parsers/host_config_parsers.py +1 -0
  540. ansible_test/_internal/cli/parsers/key_value_parsers.py +1 -0
  541. ansible_test/_internal/cli/parsers/value_parsers.py +1 -0
  542. ansible_test/_internal/commands/__init__.py +1 -0
  543. ansible_test/_internal/commands/coverage/__init__.py +2 -1
  544. ansible_test/_internal/commands/coverage/analyze/__init__.py +1 -0
  545. ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +1 -0
  546. ansible_test/_internal/commands/coverage/analyze/targets/combine.py +1 -0
  547. ansible_test/_internal/commands/coverage/analyze/targets/expand.py +1 -0
  548. ansible_test/_internal/commands/coverage/analyze/targets/filter.py +1 -0
  549. ansible_test/_internal/commands/coverage/analyze/targets/generate.py +1 -0
  550. ansible_test/_internal/commands/coverage/analyze/targets/missing.py +1 -0
  551. ansible_test/_internal/commands/coverage/combine.py +2 -1
  552. ansible_test/_internal/commands/coverage/erase.py +1 -0
  553. ansible_test/_internal/commands/coverage/html.py +1 -0
  554. ansible_test/_internal/commands/coverage/report.py +1 -0
  555. ansible_test/_internal/commands/coverage/xml.py +1 -0
  556. ansible_test/_internal/commands/env/__init__.py +2 -0
  557. ansible_test/_internal/commands/integration/__init__.py +4 -0
  558. ansible_test/_internal/commands/integration/cloud/__init__.py +1 -0
  559. ansible_test/_internal/commands/integration/cloud/acme.py +2 -1
  560. ansible_test/_internal/commands/integration/cloud/aws.py +1 -0
  561. ansible_test/_internal/commands/integration/cloud/azure.py +1 -0
  562. ansible_test/_internal/commands/integration/cloud/cs.py +1 -0
  563. ansible_test/_internal/commands/integration/cloud/digitalocean.py +1 -0
  564. ansible_test/_internal/commands/integration/cloud/galaxy.py +3 -2
  565. ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -0
  566. ansible_test/_internal/commands/integration/cloud/httptester.py +2 -1
  567. ansible_test/_internal/commands/integration/cloud/nios.py +2 -1
  568. ansible_test/_internal/commands/integration/cloud/opennebula.py +1 -0
  569. ansible_test/_internal/commands/integration/cloud/openshift.py +1 -0
  570. ansible_test/_internal/commands/integration/cloud/scaleway.py +1 -0
  571. ansible_test/_internal/commands/integration/cloud/vcenter.py +1 -0
  572. ansible_test/_internal/commands/integration/cloud/vultr.py +1 -0
  573. ansible_test/_internal/commands/integration/coverage.py +1 -0
  574. ansible_test/_internal/commands/integration/filters.py +1 -0
  575. ansible_test/_internal/commands/integration/network.py +1 -0
  576. ansible_test/_internal/commands/integration/posix.py +1 -0
  577. ansible_test/_internal/commands/integration/windows.py +1 -0
  578. ansible_test/_internal/commands/sanity/__init__.py +16 -1
  579. ansible_test/_internal/commands/sanity/ansible_doc.py +1 -0
  580. ansible_test/_internal/commands/sanity/bin_symlinks.py +1 -0
  581. ansible_test/_internal/commands/sanity/compile.py +1 -0
  582. ansible_test/_internal/commands/sanity/ignores.py +1 -0
  583. ansible_test/_internal/commands/sanity/import.py +1 -0
  584. ansible_test/_internal/commands/sanity/integration_aliases.py +1 -0
  585. ansible_test/_internal/commands/sanity/pep8.py +1 -0
  586. ansible_test/_internal/commands/sanity/pslint.py +1 -0
  587. ansible_test/_internal/commands/sanity/pylint.py +24 -26
  588. ansible_test/_internal/commands/sanity/shellcheck.py +1 -0
  589. ansible_test/_internal/commands/sanity/validate_modules.py +1 -0
  590. ansible_test/_internal/commands/sanity/yamllint.py +1 -0
  591. ansible_test/_internal/commands/shell/__init__.py +1 -0
  592. ansible_test/_internal/commands/units/__init__.py +1 -0
  593. ansible_test/_internal/compat/__init__.py +1 -0
  594. ansible_test/_internal/compat/packaging.py +1 -0
  595. ansible_test/_internal/compat/yaml.py +1 -0
  596. ansible_test/_internal/completion.py +1 -0
  597. ansible_test/_internal/config.py +2 -0
  598. ansible_test/_internal/connections.py +1 -0
  599. ansible_test/_internal/constants.py +1 -0
  600. ansible_test/_internal/containers.py +1 -0
  601. ansible_test/_internal/content_config.py +1 -0
  602. ansible_test/_internal/core_ci.py +1 -0
  603. ansible_test/_internal/coverage_util.py +11 -10
  604. ansible_test/_internal/data.py +1 -0
  605. ansible_test/_internal/delegation.py +1 -0
  606. ansible_test/_internal/dev/__init__.py +1 -0
  607. ansible_test/_internal/dev/container_probe.py +1 -0
  608. ansible_test/_internal/diff.py +3 -2
  609. ansible_test/_internal/docker_util.py +2 -1
  610. ansible_test/_internal/encoding.py +1 -0
  611. ansible_test/_internal/executor.py +1 -0
  612. ansible_test/_internal/git.py +1 -0
  613. ansible_test/_internal/host_configs.py +1 -0
  614. ansible_test/_internal/host_profiles.py +1 -0
  615. ansible_test/_internal/http.py +1 -0
  616. ansible_test/_internal/init.py +1 -0
  617. ansible_test/_internal/inventory.py +35 -3
  618. ansible_test/_internal/io.py +1 -0
  619. ansible_test/_internal/metadata.py +1 -0
  620. ansible_test/_internal/payload.py +1 -0
  621. ansible_test/_internal/provider/__init__.py +1 -0
  622. ansible_test/_internal/provider/layout/__init__.py +1 -0
  623. ansible_test/_internal/provider/layout/ansible.py +1 -0
  624. ansible_test/_internal/provider/layout/collection.py +1 -0
  625. ansible_test/_internal/provider/layout/unsupported.py +1 -0
  626. ansible_test/_internal/provider/source/__init__.py +1 -0
  627. ansible_test/_internal/provider/source/git.py +1 -0
  628. ansible_test/_internal/provider/source/installed.py +1 -0
  629. ansible_test/_internal/provider/source/unsupported.py +1 -0
  630. ansible_test/_internal/provider/source/unversioned.py +1 -0
  631. ansible_test/_internal/provisioning.py +1 -0
  632. ansible_test/_internal/pypi_proxy.py +6 -5
  633. ansible_test/_internal/python_requirements.py +1 -0
  634. ansible_test/_internal/ssh.py +1 -0
  635. ansible_test/_internal/target.py +1 -0
  636. ansible_test/_internal/test.py +3 -2
  637. ansible_test/_internal/thread.py +1 -0
  638. ansible_test/_internal/timeout.py +1 -0
  639. ansible_test/_internal/util.py +1 -0
  640. ansible_test/_internal/util_common.py +5 -2
  641. ansible_test/_internal/venv.py +1 -0
  642. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +1 -0
  643. ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py +1 -0
  644. ansible_test/_util/controller/sanity/code-smell/changelog.py +1 -0
  645. ansible_test/_util/controller/sanity/code-smell/empty-init.py +1 -0
  646. ansible_test/_util/controller/sanity/code-smell/line-endings.py +1 -0
  647. ansible_test/_util/controller/sanity/code-smell/no-assert.py +1 -0
  648. ansible_test/_util/controller/sanity/code-smell/no-get-exception.py +1 -0
  649. ansible_test/_util/controller/sanity/code-smell/no-illegal-filenames.py +1 -0
  650. ansible_test/_util/controller/sanity/code-smell/no-smart-quotes.py +1 -0
  651. ansible_test/_util/controller/sanity/code-smell/replace-urlopen.py +1 -0
  652. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +28 -1
  653. ansible_test/_util/controller/sanity/code-smell/shebang.py +1 -0
  654. ansible_test/_util/controller/sanity/code-smell/symlinks.py +1 -0
  655. ansible_test/_util/controller/sanity/code-smell/use-argspec-type-path.py +1 -0
  656. ansible_test/_util/controller/sanity/code-smell/use-compat-six.py +1 -0
  657. ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py +2 -1
  658. ansible_test/_util/controller/sanity/pep8/current-ignore.txt +4 -0
  659. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +7 -5
  660. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +7 -5
  661. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +7 -5
  662. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +3 -5
  663. ansible_test/_util/controller/sanity/pylint/config/default.cfg +7 -7
  664. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -13
  665. ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +1 -0
  666. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +1 -8
  667. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +1 -8
  668. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +55 -28
  669. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +12 -5
  670. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +13 -2
  671. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -0
  672. ansible_test/_util/controller/sanity/yamllint/yamllinter.py +35 -17
  673. ansible_test/_util/controller/tools/collection_detail.py +1 -0
  674. ansible_test/_util/controller/tools/yaml_to_json.py +2 -1
  675. ansible_test/_util/target/pytest/plugins/ansible_forked.py +6 -1
  676. ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +2 -1
  677. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -0
  678. ansible_test/_util/target/sanity/compile/compile.py +1 -0
  679. ansible_test/_util/target/sanity/import/importer.py +15 -16
  680. ansible_test/_util/target/setup/bootstrap.sh +9 -20
  681. ansible_test/_util/target/setup/probe_cgroups.py +1 -0
  682. ansible_test/_util/target/setup/quiet_pip.py +1 -0
  683. ansible_test/_util/target/setup/requirements.py +35 -27
  684. ansible_test/_util/target/tools/virtualenvcheck.py +2 -1
  685. ansible_test/_util/target/tools/yamlcheck.py +2 -1
  686. ansible/compat/selectors.py +0 -32
  687. ansible/errors/yaml_strings.py +0 -138
  688. ansible/executor/action_write_locks.py +0 -44
  689. ansible/executor/discovery/python_target.py +0 -47
  690. ansible/executor/powershell/module_powershell_wrapper.ps1 +0 -86
  691. ansible/executor/powershell/module_script_wrapper.ps1 +0 -22
  692. ansible/module_utils/compat/importlib.py +0 -26
  693. ansible/module_utils/compat/selectors.py +0 -32
  694. ansible/module_utils/pycompat24.py +0 -73
  695. ansible/parsing/utils/jsonify.py +0 -36
  696. ansible/parsing/yaml/constructor.py +0 -178
  697. ansible/template/native_helpers.py +0 -251
  698. ansible/template/template.py +0 -43
  699. ansible/template/vars.py +0 -77
  700. ansible/utils/native_jinja.py +0 -11
  701. ansible/vars/fact_cache.py +0 -71
  702. ansible_core-2.18.4rc1.dist-info/RECORD +0 -992
  703. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/Apache-License.txt +0 -0
  704. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/COPYING +0 -0
  705. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/MIT-license.txt +0 -0
  706. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/PSF-license.txt +0 -0
  707. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/entry_points.txt +0 -0
  708. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/simplified_bsd.txt +0 -0
  709. {ansible_core-2.18.4rc1.dist-info → ansible_core-2.19.0b1.dist-info}/top_level.txt +0 -0
@@ -10,29 +10,39 @@ import pathlib
10
10
  import signal
11
11
  import subprocess
12
12
  import sys
13
+
13
14
  import traceback
15
+ import typing as t
14
16
 
15
17
  from ansible import constants as C
16
18
  from ansible.cli import scripts
17
- from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleConnectionFailure, AnsibleActionFail, AnsibleActionSkip
19
+ from ansible.errors import (
20
+ AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleConnectionFailure, AnsibleActionFail, AnsibleActionSkip, AnsibleTaskError,
21
+ AnsibleValueOmittedError,
22
+ )
18
23
  from ansible.executor.task_result import TaskResult
19
- from ansible.executor.module_common import get_action_args_with_defaults
24
+ from ansible._internal._datatag import _utils
25
+ from ansible.module_utils._internal._plugin_exec_context import PluginExecContext
26
+ from ansible.module_utils.common.messages import Detail, WarningSummary, DeprecationSummary
27
+ from ansible.module_utils.datatag import native_type_name
28
+ from ansible._internal._datatag._tags import TrustedAsTemplate
20
29
  from ansible.module_utils.parsing.convert_bool import boolean
21
- from ansible.module_utils.six import binary_type
22
30
  from ansible.module_utils.common.text.converters import to_text, to_native
23
31
  from ansible.module_utils.connection import write_to_stream
24
32
  from ansible.module_utils.six import string_types
25
- from ansible.playbook.conditional import Conditional
26
33
  from ansible.playbook.task import Task
27
34
  from ansible.plugins import get_plugin_class
35
+ from ansible.plugins.action import ActionBase
28
36
  from ansible.plugins.loader import become_loader, cliconf_loader, connection_loader, httpapi_loader, netconf_loader, terminal_loader
37
+ from ansible._internal._templating._jinja_plugins import _invoke_lookup, _DirectCall
38
+ from ansible._internal._templating._engine import TemplateEngine
29
39
  from ansible.template import Templar
30
40
  from ansible.utils.collection_loader import AnsibleCollectionConfig
31
- from ansible.utils.listify import listify_lookup_plugin_terms
32
- from ansible.utils.unsafe_proxy import to_unsafe_text, wrap_var
33
- from ansible.vars.clean import namespace_facts, clean_facts
34
- from ansible.utils.display import Display
41
+ from ansible.utils.display import Display, _DeferredWarningContext
35
42
  from ansible.utils.vars import combine_vars
43
+ from ansible.vars.clean import namespace_facts, clean_facts
44
+ from ansible.vars.manager import _deprecate_top_level_fact
45
+ from ansible._internal._errors import _captured
36
46
 
37
47
  display = Display()
38
48
 
@@ -60,60 +70,37 @@ def task_timeout(signum, frame):
60
70
  raise TaskTimeoutError(frame=frame)
61
71
 
62
72
 
63
- def remove_omit(task_args, omit_token):
64
- '''
65
- Remove args with a value equal to the ``omit_token`` recursively
66
- to align with now having suboptions in the argument_spec
67
- '''
68
-
69
- if not isinstance(task_args, dict):
70
- return task_args
71
-
72
- new_args = {}
73
- for i in task_args.items():
74
- if i[1] == omit_token:
75
- continue
76
- elif isinstance(i[1], dict):
77
- new_args[i[0]] = remove_omit(i[1], omit_token)
78
- elif isinstance(i[1], list):
79
- new_args[i[0]] = [remove_omit(v, omit_token) for v in i[1]]
80
- else:
81
- new_args[i[0]] = i[1]
82
-
83
- return new_args
84
-
85
-
86
73
  class TaskExecutor:
87
74
 
88
- '''
75
+ """
89
76
  This is the main worker class for the executor pipeline, which
90
77
  handles loading an action plugin to actually dispatch the task to
91
78
  a given host. This class roughly corresponds to the old Runner()
92
79
  class.
93
- '''
80
+ """
94
81
 
95
- def __init__(self, host, task, job_vars, play_context, new_stdin, loader, shared_loader_obj, final_q, variable_manager):
82
+ def __init__(self, host, task: Task, job_vars, play_context, loader, shared_loader_obj, final_q, variable_manager):
96
83
  self._host = host
97
84
  self._task = task
98
85
  self._job_vars = job_vars
99
86
  self._play_context = play_context
100
- self._new_stdin = new_stdin
101
87
  self._loader = loader
102
88
  self._shared_loader_obj = shared_loader_obj
103
89
  self._connection = None
104
90
  self._final_q = final_q
105
91
  self._variable_manager = variable_manager
106
92
  self._loop_eval_error = None
93
+ self._task_templar = TemplateEngine(loader=self._loader, variables=self._job_vars)
107
94
 
108
95
  self._task.squash()
109
96
 
110
97
  def run(self):
111
- '''
98
+ """
112
99
  The main executor entrypoint, where we determine if the specified
113
100
  task requires looping and either runs the task with self._run_loop()
114
101
  or self._execute(). After that, the returned results are parsed and
115
102
  returned as a dict.
116
- '''
103
+ """
117
104
 
118
105
  display.debug("in run() - task %s" % self._task._uuid)
119
106
 
@@ -135,10 +122,14 @@ class TaskExecutor:
135
122
  # loop through the item results and set the global changed/failed/skipped result flags based on any item.
136
123
  res['skipped'] = True
137
124
  for item in item_results:
125
+ if item.get('_ansible_no_log'):
126
+ res.update(_ansible_no_log=True) # ensure no_log processing recognizes at least one item needs to be censored
127
+
138
128
  if 'changed' in item and item['changed'] and not res.get('changed'):
139
129
  res['changed'] = True
140
130
  if res['skipped'] and ('skipped' not in item or ('skipped' in item and not item['skipped'])):
141
131
  res['skipped'] = False
132
+ # FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
142
133
  if 'failed' in item and item['failed']:
143
134
  item_ignore = item.pop('_ansible_ignore_errors')
144
135
  if not res.get('failed'):
@@ -165,6 +156,7 @@ class TaskExecutor:
165
156
  res[array] = res[array] + item[array]
166
157
  del item[array]
167
158
 
159
+ # FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
168
160
  if not res.get('failed', False):
169
161
  res['msg'] = 'All items completed'
170
162
  if res['skipped']:
@@ -173,43 +165,23 @@ class TaskExecutor:
173
165
  res = dict(changed=False, skipped=True, skipped_reason='No items in the list', results=[])
174
166
  else:
175
167
  display.debug("calling self._execute()")
176
- res = self._execute()
168
+ res = self._execute(self._task_templar, self._job_vars)
177
169
  display.debug("_execute() done")
178
170
 
179
171
  # make sure changed is set in the result, if it's not present
180
172
  if 'changed' not in res:
181
173
  res['changed'] = False
182
174
 
183
- def _clean_res(res, errors='surrogate_or_strict'):
184
- if isinstance(res, binary_type):
185
- return to_unsafe_text(res, errors=errors)
186
- elif isinstance(res, dict):
187
- for k in res:
188
- try:
189
- res[k] = _clean_res(res[k], errors=errors)
190
- except UnicodeError:
191
- if k == 'diff':
192
- # If this is a diff, substitute a replacement character if the value
193
- # is undecodable as utf8. (Fix #21804)
194
- display.warning("We were unable to decode all characters in the module return data."
195
- " Replaced some in an effort to return as much as possible")
196
- res[k] = _clean_res(res[k], errors='surrogate_then_replace')
197
- else:
198
- raise
199
- elif isinstance(res, list):
200
- for idx, item in enumerate(res):
201
- res[idx] = _clean_res(item, errors=errors)
202
- return res
203
-
204
- display.debug("dumping result to json")
205
- res = _clean_res(res)
206
- display.debug("done dumping result, returning")
207
175
  return res
208
- except AnsibleError as e:
209
- return dict(failed=True, msg=wrap_var(to_text(e, nonstring='simplerepr')), _ansible_no_log=self._play_context.no_log)
210
- except Exception as e:
211
- return dict(failed=True, msg=wrap_var('Unexpected failure during module execution: %s' % (to_native(e, nonstring='simplerepr'))),
212
- exception=to_text(traceback.format_exc()), stdout='', _ansible_no_log=self._play_context.no_log)
176
+ except Exception as ex:
177
+ result = ActionBase.result_dict_from_exception(ex)
178
+
179
+ self._task.update_result_no_log(self._task_templar, result)
180
+
181
+ if not isinstance(ex, AnsibleError):
182
+ result.update(msg=f'Unexpected failure during task execution: {result["msg"]}')
183
+
184
+ return result
213
185
  finally:
214
186
  try:
215
187
  self._connection.close()
@@ -218,11 +190,11 @@ class TaskExecutor:
218
190
  except Exception as e:
219
191
  display.debug(u"error closing connection: %s" % to_text(e))
220
192
 
221
- def _get_loop_items(self):
222
- '''
193
+ def _get_loop_items(self) -> list[t.Any] | None:
194
+ """
223
195
  Loads a lookup plugin to handle the with_* portion of a task (if specified),
224
196
  and returns the items result.
225
- '''
197
+ """
226
198
 
227
199
  # get search path for this task to pass to lookup plugins
228
200
  self._job_vars['ansible_search_path'] = self._task.get_search_path()
@@ -231,49 +203,51 @@ class TaskExecutor:
231
203
  if self._loader.get_basedir() not in self._job_vars['ansible_search_path']:
232
204
  self._job_vars['ansible_search_path'].append(self._loader.get_basedir())
233
205
 
234
- templar = Templar(loader=self._loader, variables=self._job_vars)
235
206
  items = None
236
207
  if self._task.loop_with:
237
- if self._task.loop_with in self._shared_loader_obj.lookup_loader:
238
-
239
- # TODO: hardcoded so it fails for non first_found lookups, but this should be generalized for those that don't do their own templating
240
- # lookup prop/attribute?
241
- fail = bool(self._task.loop_with != 'first_found')
242
- loop_terms = listify_lookup_plugin_terms(terms=self._task.loop, templar=templar, fail_on_undefined=fail, convert_bare=False)
243
-
244
- # get lookup
245
- mylookup = self._shared_loader_obj.lookup_loader.get(self._task.loop_with, loader=self._loader, templar=templar)
246
-
247
- # give lookup task 'context' for subdir (mostly needed for first_found)
248
- for subdir in ['template', 'var', 'file']: # TODO: move this to constants?
249
- if subdir in self._task.action:
250
- break
251
- setattr(mylookup, '_subdir', subdir + 's')
208
+ templar = self._task_templar
209
+ terms = self._task.loop
210
+
211
+ if isinstance(terms, str):
212
+ terms = templar.resolve_to_container(_utils.str_problematic_strip(terms))
213
+
214
+ if not isinstance(terms, list):
215
+ terms = [terms]
216
+
217
+ @_DirectCall.mark
218
+ def invoke_lookup() -> t.Any:
219
+ """Scope-capturing wrapper around _invoke_lookup to avoid functools.partial obscuring its usage from type-checking tools."""
220
+ return _invoke_lookup(
221
+ plugin_name=self._task.loop_with,
222
+ lookup_terms=terms,
223
+ lookup_kwargs=dict(wantlist=True),
224
+ invoked_as_with=True,
225
+ )
252
226
 
253
- # run lookup
254
- items = wrap_var(mylookup.run(terms=loop_terms, variables=self._job_vars, wantlist=True))
255
- else:
256
- raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop_with)
227
+ # Smuggle a special wrapped lookup invocation in as a local variable for its exclusive use when being evaluated as `with_(lookup)`.
228
+ # This value will not be visible to other users of this templar or its `available_variables`.
229
+ items = templar.evaluate_expression(expression=TrustedAsTemplate().tag("invoke_lookup()"), local_variables=dict(invoke_lookup=invoke_lookup))
257
230
 
258
231
  elif self._task.loop is not None:
259
- items = templar.template(self._task.loop)
232
+ items = self._task_templar.template(self._task.loop)
233
+
260
234
  if not isinstance(items, list):
261
235
  raise AnsibleError(
262
- "Invalid data passed to 'loop', it requires a list, got this instead: %s."
263
- " Hint: If you passed a list/dict of just one element,"
264
- " try adding wantlist=True to your lookup invocation or use q/query instead of lookup." % items
236
+ f"The `loop` value must resolve to a 'list', not {native_type_name(items)!r}.",
237
+ help_text="Provide a list of items/templates, or a template resolving to a list.",
238
+ obj=self._task.loop,
265
239
  )
266
240
 
267
241
  return items
268
242
 
269
- def _run_loop(self, items):
270
- '''
243
+ def _run_loop(self, items: list[t.Any]) -> list[dict[str, t.Any]]:
244
+ """
271
245
  Runs the task with the loop items specified and collates the result
272
246
  into an array named 'results' which is inserted into the final result
273
247
  along with the item for which the loop ran.
274
- '''
248
+ """
275
249
  task_vars = self._job_vars
276
- templar = Templar(loader=self._loader, variables=task_vars)
250
+ templar = TemplateEngine(loader=self._loader, variables=task_vars)
277
251
 
278
252
  self._task.loop_control.post_validate(templar=templar)
279
253
 
@@ -282,17 +256,20 @@ class TaskExecutor:
282
256
  loop_pause = self._task.loop_control.pause
283
257
  extended = self._task.loop_control.extended
284
258
  extended_allitems = self._task.loop_control.extended_allitems
259
+
285
260
  # ensure we always have a label
286
- label = self._task.loop_control.label or '{{' + loop_var + '}}'
261
+ label = self._task.loop_control.label or templar.variable_name_as_template(loop_var)
287
262
 
288
263
  if loop_var in task_vars:
289
- display.warning(u"%s: The loop variable '%s' is already in use. "
290
- u"You should set the `loop_var` value in the `loop_control` option for the task"
291
- u" to something else to avoid variable collisions and unexpected behavior." % (self._task, loop_var))
264
+ display.warning(
265
+ msg=f"The loop variable {loop_var!r} is already in use.",
266
+ help_text="You should set the `loop_var` value in the `loop_control` option for the task "
267
+ "to something else to avoid variable collisions and unexpected behavior.",
268
+ obj=loop_var,
269
+ )
292
270
 
293
271
  ran_once = False
294
272
  task_fields = None
295
- no_log = False
296
273
  items_len = len(items)
297
274
  results = []
298
275
  for item_index, item in enumerate(items):
@@ -332,7 +309,7 @@ class TaskExecutor:
332
309
  ran_once = True
333
310
 
334
311
  try:
335
- tmp_task = self._task.copy(exclude_parent=True, exclude_tasks=True)
312
+ tmp_task: Task = self._task.copy(exclude_parent=True, exclude_tasks=True)
336
313
  tmp_task._parent = self._task._parent
337
314
  tmp_play_context = self._play_context.copy()
338
315
  except AnsibleParserError as e:
@@ -341,9 +318,11 @@ class TaskExecutor:
341
318
 
342
319
  # now we swap the internal task and play context with their copies,
343
320
  # execute, and swap them back so we can do the next iteration cleanly
321
+ # NB: this swap-a-dee-doo confuses some type checkers about the type of tmp_task/self._task
344
322
  (self._task, tmp_task) = (tmp_task, self._task)
345
323
  (self._play_context, tmp_play_context) = (tmp_play_context, self._play_context)
346
- res = self._execute(variables=task_vars)
324
+
325
+ res = self._execute(templar=templar, variables=task_vars)
347
326
 
348
327
  if self._task.register:
349
328
  # Ensure per loop iteration results are registered in case `_execute()`
@@ -355,9 +334,6 @@ class TaskExecutor:
355
334
  (self._task, tmp_task) = (tmp_task, self._task)
356
335
  (self._play_context, tmp_play_context) = (tmp_play_context, self._play_context)
357
336
 
358
- # update 'general no_log' based on specific no_log
359
- no_log = no_log or tmp_task.no_log
360
-
361
337
  # now update the result with the item info, and append the result
362
338
  # to the list of results
363
339
  res[loop_var] = item
@@ -392,6 +368,7 @@ class TaskExecutor:
392
368
  task_fields=task_fields,
393
369
  )
394
370
 
371
+ # FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
395
372
  if tr.is_failed() or tr.is_unreachable():
396
373
  self._final_q.send_callback('v2_runner_item_on_failed', tr)
397
374
  elif tr.is_skipped():
@@ -406,11 +383,14 @@ class TaskExecutor:
406
383
 
407
384
  # break loop if break_when conditions are met
408
385
  if self._task.loop_control and self._task.loop_control.break_when:
409
- cond = Conditional(loader=self._loader)
410
- cond.when = self._task.loop_control.get_validated_value(
411
- 'break_when', self._task.loop_control.fattributes.get('break_when'), self._task.loop_control.break_when, templar
386
+ break_when = self._task.loop_control.get_validated_value(
387
+ 'break_when',
388
+ self._task.loop_control.fattributes.get('break_when'),
389
+ self._task.loop_control.break_when,
390
+ templar,
412
391
  )
413
- if cond.evaluate_conditional(templar, task_vars):
392
+
393
+ if self._task._resolve_conditional(break_when, task_vars):
414
394
  # delete loop vars before exiting loop
415
395
  del task_vars[loop_var]
416
396
  break
@@ -432,7 +412,6 @@ class TaskExecutor:
432
412
  if var in task_vars and var not in self._job_vars:
433
413
  del task_vars[var]
434
414
 
435
- self._task.no_log = no_log
436
415
  # NOTE: run_once cannot contain loop vars because it's templated earlier also
437
416
  # This is saving the post-validated field from the last loop so the strategy can use the templated value post task execution
438
417
  self._task.run_once = task_fields.get('run_once')
@@ -448,21 +427,49 @@ class TaskExecutor:
448
427
  # At the point this is executed it is safe to mutate self._task,
449
428
  # since `self._task` is either a copy referred to by `tmp_task` in `_run_loop`
450
429
  # or just a singular non-looped task
451
- if delegated_host_name:
452
- self._task.delegate_to = delegated_host_name
453
- variables.update(delegated_vars)
454
430
 
455
- def _execute(self, variables=None):
456
- '''
431
+ self._task.delegate_to = delegated_host_name # always override, since a templated result could be an omit (-> None)
432
+ variables.update(delegated_vars)
433
+
434
+ def _execute(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
435
+ result: dict[str, t.Any]
436
+
437
+ with _DeferredWarningContext(variables=variables) as warning_ctx:
438
+ try:
439
+ # DTFIX-FUTURE: improve error handling to prioritize the earliest exception, turning the remaining ones into warnings
440
+ result = self._execute_internal(templar, variables)
441
+ self._apply_task_result_compat(result, warning_ctx)
442
+ _captured.AnsibleActionCapturedError.maybe_raise_on_result(result)
443
+ except Exception as ex:
444
+ try:
445
+ raise AnsibleTaskError(obj=self._task.get_ds()) from ex
446
+ except AnsibleTaskError as atex:
447
+ result = ActionBase.result_dict_from_exception(atex)
448
+ result.setdefault('changed', False)
449
+
450
+ self._task.update_result_no_log(templar, result)
451
+
452
+ # The warnings/deprecations in the result have already been captured in the _DeferredWarningContext by _apply_task_result_compat.
453
+ # The captured warnings/deprecations are a superset of the ones from the result, and may have been converted from a dict to a dataclass.
454
+ # These are then used to supersede the entries in the result.
455
+
456
+ result.pop('warnings', None)
457
+ result.pop('deprecations', None)
458
+
459
+ if warnings := warning_ctx.get_warnings():
460
+ result.update(warnings=warnings)
461
+
462
+ if deprecation_warnings := warning_ctx.get_deprecation_warnings():
463
+ result.update(deprecations=deprecation_warnings)
464
+
465
+ return result
466
+
467
+ def _execute_internal(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
468
+ """
457
469
  The primary workhorse of the executor system, this runs the task
458
470
  on the specified host (which may be the delegated_to host) and handles
459
471
  the retry/until and block rescue/always execution
460
- '''
461
-
462
- if variables is None:
463
- variables = self._job_vars
464
-
465
- templar = Templar(loader=self._loader, variables=variables)
472
+ """
466
473
 
467
474
  self._calculate_delegate_to(templar, variables)
468
475
 
@@ -498,18 +505,13 @@ class TaskExecutor:
498
505
  # skipping this task during the conditional evaluation step
499
506
  context_validation_error = e
500
507
 
501
- no_log = self._play_context.no_log
502
-
503
508
  # Evaluate the conditional (if any) for this task, which we do before running
504
509
  # the final task post-validation. We do this before the post validation due to
505
510
  # the fact that the conditional may specify that the task be skipped due to a
506
511
  # variable not being present which would otherwise cause validation to fail
507
512
  try:
508
- conditional_result, false_condition = self._task.evaluate_conditional_with_result(templar, tempvars)
509
- if not conditional_result:
510
- display.debug("when evaluation is False, skipping this task")
511
- return dict(changed=False, skipped=True, skip_reason='Conditional result was False',
512
- false_condition=false_condition, _ansible_no_log=no_log)
513
+ if not self._task._resolve_conditional(self._task.when, tempvars, result_context=(rc := t.cast(dict[str, t.Any], {}))):
514
+ return dict(changed=False, skipped=True, skip_reason='Conditional result was False') | rc
513
515
  except AnsibleError as e:
514
516
  # loop error takes precedence
515
517
  if self._loop_eval_error is not None:
@@ -525,22 +527,27 @@ class TaskExecutor:
525
527
 
526
528
  # if we ran into an error while setting up the PlayContext, raise it now, unless is known issue with delegation
527
529
  # and undefined vars (correct values are in cvars later on and connection plugins, if still error, blows up there)
530
+
531
+ # DTFIX-RELEASE: this should probably be declaratively handled in post_validate (or better, get rid of play_context)
528
532
  if context_validation_error is not None:
529
533
  raiseit = True
530
534
  if self._task.delegate_to:
531
- if isinstance(context_validation_error, AnsibleUndefinedVariable):
532
- raiseit = False
533
- elif isinstance(context_validation_error, AnsibleParserError):
535
+ if isinstance(context_validation_error, AnsibleParserError):
534
536
  # parser error, might be cause by undef too
535
- orig_exc = getattr(context_validation_error, 'orig_exc', None)
536
- if isinstance(orig_exc, AnsibleUndefinedVariable):
537
+ if isinstance(context_validation_error.__cause__, AnsibleUndefinedVariable):
537
538
  raiseit = False
539
+ elif isinstance(context_validation_error, AnsibleUndefinedVariable):
540
+ # DTFIX-RELEASE: should not be possible to hit this now (all are AnsibleFieldAttributeError)?
541
+ raiseit = False
538
542
  if raiseit:
539
543
  raise context_validation_error # pylint: disable=raising-bad-type
540
544
 
541
545
  # set templar to use temp variables until loop is evaluated
542
546
  templar.available_variables = tempvars
543
547
 
548
+ # Now we do final validation on the task, which sets all fields to their final values.
549
+ self._task.post_validate(templar=templar)
550
+
544
551
  # if this task is a TaskInclude, we just return now with a success code so the
545
552
  # main thread can expand the task list for the given host
546
553
  if self._task.action in C._ACTION_INCLUDE_TASKS:
@@ -549,7 +556,6 @@ class TaskExecutor:
549
556
  if not include_file:
550
557
  return dict(failed=True, msg="No include file was specified to the include")
551
558
 
552
- include_file = templar.template(include_file)
553
559
  return dict(include=include_file, include_args=include_args)
554
560
 
555
561
  # if this task is a IncludeRole, we just return now with a success code so the main thread can expand the task list for the given host
@@ -557,32 +563,9 @@ class TaskExecutor:
557
563
  include_args = self._task.args.copy()
558
564
  return dict(include_args=include_args)
559
565
 
560
- # Now we do final validation on the task, which sets all fields to their final values.
561
- try:
562
- self._task.post_validate(templar=templar)
563
- except AnsibleError:
564
- raise
565
- except Exception:
566
- return dict(changed=False, failed=True, _ansible_no_log=no_log, exception=to_text(traceback.format_exc()))
567
- if '_variable_params' in self._task.args:
568
- variable_params = self._task.args.pop('_variable_params')
569
- if isinstance(variable_params, dict):
570
- if C.INJECT_FACTS_AS_VARS:
571
- display.warning("Using a variable for a task's 'args' is unsafe in some situations "
572
- "(see https://docs.ansible.com/ansible/devel/reference_appendices/faq.html#argsplat-unsafe)")
573
- variable_params.update(self._task.args)
574
- self._task.args = variable_params
575
- else:
576
- # if we didn't get a dict, it means there's garbage remaining after k=v parsing, just give up
577
- # see https://github.com/ansible/ansible/issues/79862
578
- raise AnsibleError(f"invalid or malformed argument: '{variable_params}'")
579
-
580
- # update no_log to task value, now that we have it templated
581
- no_log = self._task.no_log
582
-
583
566
  # free tempvars up, not used anymore, cvars and vars_copy should be mainly used after this point
584
567
  # updating the original 'variables' at the end
585
- tempvars = {}
568
+ del tempvars
586
569
 
587
570
  # setup cvars copy, used for all connection related templating
588
571
  if self._task.delegate_to:
@@ -634,23 +617,7 @@ class TaskExecutor:
634
617
  cvars['ansible_python_interpreter'] = sys.executable
635
618
 
636
619
  # get handler
637
- self._handler, module_context = self._get_action_handler_with_module_context(templar=templar)
638
-
639
- if module_context is not None:
640
- module_defaults_fqcn = module_context.resolved_fqcn
641
- else:
642
- module_defaults_fqcn = self._task.resolved_action
643
-
644
- # Apply default params for action/module, if present
645
- self._task.args = get_action_args_with_defaults(
646
- module_defaults_fqcn, self._task.args, self._task.module_defaults, templar,
647
- action_groups=self._task._parent._play._action_groups
648
- )
649
-
650
- # And filter out any fields which were set to default(omit), and got the omit token value
651
- omit_token = variables.get('omit')
652
- if omit_token is not None:
653
- self._task.args = remove_omit(self._task.args, omit_token)
620
+ self._handler, _module_context = self._get_action_handler_with_module_context(templar=templar)
654
621
 
655
622
  retries = 1 # includes the default actual run + retries set by user/default
656
623
  if self._task.retries is not None:
@@ -670,7 +637,10 @@ class TaskExecutor:
670
637
  if self._task.timeout:
671
638
  old_sig = signal.signal(signal.SIGALRM, task_timeout)
672
639
  signal.alarm(self._task.timeout)
673
- result = self._handler.run(task_vars=vars_copy)
640
+ with PluginExecContext(self._handler):
641
+ result = self._handler.run(task_vars=vars_copy)
642
+
643
+ # DTFIX-RELEASE: nuke this, it hides a lot of error detail- remove the active exception propagation hack from AnsibleActionFail at the same time
674
644
  except (AnsibleActionFail, AnsibleActionSkip) as e:
675
645
  return e.result
676
646
  except AnsibleConnectionFailure as e:
@@ -685,12 +655,6 @@ class TaskExecutor:
685
655
  self._handler.cleanup()
686
656
  display.debug("handler run complete")
687
657
 
688
- # propagate no log to result- the action can set this, so only overwrite it with the task's value if missing or falsey
689
- result["_ansible_no_log"] = bool(no_log or result.get('_ansible_no_log', False))
690
-
691
- if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
692
- result = wrap_var(result)
693
-
694
658
  # update the local copy of vars with the registered value, if specified,
695
659
  # or any facts which may have been generated by the module execution
696
660
  if self._task.register:
@@ -714,26 +678,6 @@ class TaskExecutor:
714
678
  result,
715
679
  task_fields=self._task.dump_attrs()))
716
680
 
717
- # ensure no log is preserved
718
- result["_ansible_no_log"] = no_log
719
-
720
- # helper methods for use below in evaluating changed/failed_when
721
- def _evaluate_changed_when_result(result):
722
- if self._task.changed_when is not None and self._task.changed_when:
723
- cond = Conditional(loader=self._loader)
724
- cond.when = self._task.changed_when
725
- result['changed'] = cond.evaluate_conditional(templar, vars_copy)
726
-
727
- def _evaluate_failed_when_result(result):
728
- if self._task.failed_when:
729
- cond = Conditional(loader=self._loader)
730
- cond.when = self._task.failed_when
731
- failed_when_result = cond.evaluate_conditional(templar, vars_copy)
732
- result['failed_when_result'] = result['failed'] = failed_when_result
733
- else:
734
- failed_when_result = False
735
- return failed_when_result
736
-
737
681
  if 'ansible_facts' in result and self._task.action not in C._ACTION_DEBUG:
738
682
  if self._task.action in C._ACTION_WITH_CLEAN_FACTS:
739
683
  if self._task.delegate_to and self._task.delegate_facts:
@@ -745,10 +689,11 @@ class TaskExecutor:
745
689
  vars_copy.update(result['ansible_facts'])
746
690
  else:
747
691
  # TODO: cleaning of facts should eventually become part of taskresults instead of vars
748
- af = wrap_var(result['ansible_facts'])
692
+ af = result['ansible_facts']
749
693
  vars_copy['ansible_facts'] = combine_vars(vars_copy.get('ansible_facts', {}), namespace_facts(af))
750
694
  if C.INJECT_FACTS_AS_VARS:
751
- vars_copy.update(clean_facts(af))
695
+ cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
696
+ vars_copy.update(cleaned_toplevel)
752
697
 
753
698
  # set the failed property if it was missing.
754
699
  if 'failed' not in result:
@@ -766,9 +711,6 @@ class TaskExecutor:
766
711
  if 'changed' not in result:
767
712
  result['changed'] = False
768
713
 
769
- if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
770
- result = wrap_var(result)
771
-
772
714
  # re-update the local copy of vars with the registered value, if specified,
773
715
  # or any facts which may have been generated by the module execution
774
716
  # This gives changed/failed_when access to additional recently modified
@@ -781,18 +723,30 @@ class TaskExecutor:
781
723
  if 'skipped' not in result:
782
724
  condname = 'changed'
783
725
 
726
+ # DTFIX-RELEASE: error normalization has not yet occurred; this means that the expressions used for until/failed_when/changed_when/break_when
727
+ # and when (for loops on the second and later iterations) cannot see the normalized error shapes. This, and the current impl of the expression
728
+ # handling here causes a number of problems:
729
+ # * any error in one of the post-task exec expressions is silently ignored and detail lost (eg: `failed_when: syntax ERROR @$123`)
730
+ # * they cannot reliably access error/warning details, since many of those details are inaccessible until the error normalization occurs
731
+ # * error normalization includes `msg` if present, and supplies `unknown error` if not; this leads to screwy results on True failed_when if
732
+ # `msg` is present, eg: `{debug: {}, failed_when: True` -> "Task failed: Action failed: Hello world!"
733
+ # * detail about failed_when is lost; any error details from the task could potentially be grafted in/preserved if error normalization was done
734
+
784
735
  try:
785
- _evaluate_changed_when_result(result)
736
+ if self._task.changed_when is not None and self._task.changed_when:
737
+ result['changed'] = self._task._resolve_conditional(self._task.changed_when, vars_copy)
738
+
786
739
  condname = 'failed'
787
- _evaluate_failed_when_result(result)
740
+
741
+ if self._task.failed_when:
742
+ result['failed_when_result'] = result['failed'] = self._task._resolve_conditional(self._task.failed_when, vars_copy)
743
+
788
744
  except AnsibleError as e:
789
745
  result['failed'] = True
790
746
  result['%s_when_result' % condname] = to_text(e)
791
747
 
792
748
  if retries > 1:
793
- cond = Conditional(loader=self._loader)
794
- cond.when = self._task.until or [not result['failed']]
795
- if cond.evaluate_conditional(templar, vars_copy):
749
+ if self._task._resolve_conditional(self._task.until or [not result['failed']], vars_copy):
796
750
  break
797
751
  else:
798
752
  # no conditional check, or it failed, so sleep for the specified time
@@ -817,9 +771,6 @@ class TaskExecutor:
817
771
  result['attempts'] = retries - 1
818
772
  result['failed'] = True
819
773
 
820
- if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
821
- result = wrap_var(result)
822
-
823
774
  # do the final update of the local variables here, for both registered
824
775
  # values and any facts which may have been created
825
776
  if self._task.register:
@@ -830,10 +781,12 @@ class TaskExecutor:
830
781
  variables.update(result['ansible_facts'])
831
782
  else:
832
783
  # TODO: cleaning of facts should eventually become part of taskresults instead of vars
833
- af = wrap_var(result['ansible_facts'])
784
+ af = result['ansible_facts']
834
785
  variables['ansible_facts'] = combine_vars(variables.get('ansible_facts', {}), namespace_facts(af))
835
786
  if C.INJECT_FACTS_AS_VARS:
836
- variables.update(clean_facts(af))
787
+ # DTFIX-FUTURE: why is this happening twice, esp since we're post-fork and these will be discarded?
788
+ cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
789
+ variables.update(cleaned_toplevel)
837
790
 
838
791
  # save the notification target in the result, if it was specified, as
839
792
  # this task may be running in a loop in which case the notification
@@ -858,10 +811,54 @@ class TaskExecutor:
858
811
  display.debug("attempt loop complete, returning result")
859
812
  return result
860
813
 
814
+ @staticmethod
815
+ def _apply_task_result_compat(result: dict[str, t.Any], warning_ctx: _DeferredWarningContext) -> None:
816
+ """Apply backward-compatibility mutations to the supplied task result."""
817
+ if warnings := result.get('warnings'):
818
+ if isinstance(warnings, list):
819
+ for warning in warnings:
820
+ if not isinstance(warning, WarningSummary):
821
+ # translate non-WarningMessageDetail messages
822
+ warning = WarningSummary(
823
+ details=(
824
+ Detail(msg=str(warning)),
825
+ ),
826
+ )
827
+
828
+ warning_ctx.capture(warning)
829
+ else:
830
+ display.warning(f"Task result `warnings` was {type(warnings)} instead of {list}.")
831
+
832
+ if deprecations := result.get('deprecations'):
833
+ if isinstance(deprecations, list):
834
+ for deprecation in deprecations:
835
+ if not isinstance(deprecation, DeprecationSummary):
836
+ # translate non-DeprecationMessageDetail message dicts
837
+ try:
838
+ if deprecation.pop('collection_name', ...) is not ...:
839
+ # deprecated: description='enable the deprecation message for collection_name' core_version='2.23'
840
+ # self.deprecated('The `collection_name` key in the `deprecations` dictionary is deprecated.', version='2.27')
841
+ pass
842
+
843
+ # DTFIX-RELEASE: when plugin isn't set, do it at the boundary where we receive the module/action results
844
+ # that may even allow us to never set it in modules/actions directly and to populate it at the boundary
845
+ deprecation = DeprecationSummary(
846
+ details=(
847
+ Detail(msg=deprecation.pop('msg')),
848
+ ),
849
+ **deprecation,
850
+ )
851
+ except Exception as ex:
852
+ display.error_as_warning("Task result `deprecations` contained an invalid item.", exception=ex)
853
+
854
+ warning_ctx.capture(deprecation)
855
+ else:
856
+ display.warning(f"Task result `deprecations` was {type(deprecations)} instead of {list}.")
857
+
861
858
  def _poll_async_result(self, result, templar, task_vars=None):
862
- '''
859
+ """
863
860
  Polls for the specified JID to be complete
864
- '''
861
+ """
865
862
 
866
863
  if task_vars is None:
867
864
  task_vars = self._job_vars
@@ -891,7 +888,7 @@ class TaskExecutor:
891
888
  connection=self._connection,
892
889
  play_context=self._play_context,
893
890
  loader=self._loader,
894
- templar=templar,
891
+ templar=Templar._from_template_engine(templar),
895
892
  shared_loader_obj=self._shared_loader_obj,
896
893
  )
897
894
 
@@ -961,7 +958,7 @@ class TaskExecutor:
961
958
  connection=self._connection,
962
959
  play_context=self._play_context,
963
960
  loader=self._loader,
964
- templar=templar,
961
+ templar=Templar._from_template_engine(templar),
965
962
  shared_loader_obj=self._shared_loader_obj,
966
963
  )
967
964
  cleanup_handler.run(task_vars=task_vars)
@@ -977,10 +974,10 @@ class TaskExecutor:
977
974
  return become
978
975
 
979
976
  def _get_connection(self, cvars, templar, current_connection):
980
- '''
977
+ """
981
978
  Reads the connection property for the host, and returns the
982
979
  correct connection object from the list of connection plugins
983
- '''
980
+ """
984
981
 
985
982
  self._play_context.connection = current_connection
986
983
 
@@ -992,7 +989,7 @@ class TaskExecutor:
992
989
  connection, plugin_load_context = self._shared_loader_obj.connection_loader.get_with_context(
993
990
  conn_type,
994
991
  self._play_context,
995
- self._new_stdin,
992
+ new_stdin=None, # No longer used, kept for backwards compat for plugins that explicitly accept this as an arg
996
993
  task_uuid=self._task._uuid,
997
994
  ansible_playbook_pid=to_text(os.getppid())
998
995
  )
@@ -1058,7 +1055,11 @@ class TaskExecutor:
1058
1055
  options = {}
1059
1056
  for k in option_vars:
1060
1057
  if k in variables:
1061
- options[k] = templar.template(variables[k])
1058
+ try:
1059
+ options[k] = templar.template(variables[k])
1060
+ except AnsibleValueOmittedError:
1061
+ pass
1062
+
1062
1063
  # TODO move to task method?
1063
1064
  plugin.set_options(task_keys=task_keys, var_options=options)
1064
1065
 
@@ -1124,15 +1125,15 @@ class TaskExecutor:
1124
1125
  return varnames
1125
1126
 
1126
1127
  def _get_action_handler(self, templar):
1127
- '''
1128
+ """
1128
1129
  Returns the correct action plugin to handle the requestion task action
1129
- '''
1130
+ """
1130
1131
  return self._get_action_handler_with_module_context(templar)[0]
1131
1132
 
1132
- def _get_action_handler_with_module_context(self, templar):
1133
- '''
1133
+ def _get_action_handler_with_module_context(self, templar: TemplateEngine):
1134
+ """
1134
1135
  Returns the correct action plugin to handle the requestion task action and the module context
1135
- '''
1136
+ """
1136
1137
  module_collection, separator, module_name = self._task.action.rpartition(".")
1137
1138
  module_prefix = module_name.split('_')[0]
1138
1139
  if module_collection:
@@ -1191,7 +1192,7 @@ class TaskExecutor:
1191
1192
  connection=self._connection,
1192
1193
  play_context=self._play_context,
1193
1194
  loader=self._loader,
1194
- templar=templar,
1195
+ templar=Templar._from_template_engine(templar),
1195
1196
  shared_loader_obj=self._shared_loader_obj,
1196
1197
  collection_list=collections
1197
1198
  )
@@ -1206,9 +1207,9 @@ CLI_STUB_NAME = 'ansible_connection_cli_stub.py'
1206
1207
 
1207
1208
 
1208
1209
  def start_connection(play_context, options, task_uuid):
1209
- '''
1210
+ """
1210
1211
  Starts the persistent connection
1211
- '''
1212
+ """
1212
1213
 
1213
1214
  env = os.environ.copy()
1214
1215
  env.update({