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
@@ -10,29 +10,42 @@ 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
18
- from ansible.executor.task_result import TaskResult
19
- from ansible.executor.module_common import get_action_args_with_defaults
19
+ from ansible.errors import (
20
+ AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleConnectionFailure, AnsibleActionFail, AnsibleActionSkip, AnsibleTaskError,
21
+ AnsibleValueOmittedError,
22
+ )
23
+ from ansible.executor.task_result import _RawTaskResult
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
46
+
47
+ if t.TYPE_CHECKING:
48
+ from ansible.executor.task_queue_manager import FinalQueue
36
49
 
37
50
  display = Display()
38
51
 
@@ -60,60 +73,37 @@ def task_timeout(signum, frame):
60
73
  raise TaskTimeoutError(frame=frame)
61
74
 
62
75
 
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
76
  class TaskExecutor:
87
77
 
88
- '''
78
+ """
89
79
  This is the main worker class for the executor pipeline, which
90
80
  handles loading an action plugin to actually dispatch the task to
91
81
  a given host. This class roughly corresponds to the old Runner()
92
82
  class.
93
- '''
83
+ """
94
84
 
95
- def __init__(self, host, task, job_vars, play_context, new_stdin, loader, shared_loader_obj, final_q, variable_manager):
85
+ def __init__(self, host, task: Task, job_vars, play_context, loader, shared_loader_obj, final_q: FinalQueue, variable_manager):
96
86
  self._host = host
97
87
  self._task = task
98
88
  self._job_vars = job_vars
99
89
  self._play_context = play_context
100
- self._new_stdin = new_stdin
101
90
  self._loader = loader
102
91
  self._shared_loader_obj = shared_loader_obj
103
92
  self._connection = None
104
93
  self._final_q = final_q
105
94
  self._variable_manager = variable_manager
106
95
  self._loop_eval_error = None
96
+ self._task_templar = TemplateEngine(loader=self._loader, variables=self._job_vars)
107
97
 
108
98
  self._task.squash()
109
99
 
110
100
  def run(self):
111
- '''
101
+ """
112
102
  The main executor entrypoint, where we determine if the specified
113
103
  task requires looping and either runs the task with self._run_loop()
114
104
  or self._execute(). After that, the returned results are parsed and
115
105
  returned as a dict.
116
- '''
106
+ """
117
107
 
118
108
  display.debug("in run() - task %s" % self._task._uuid)
119
109
 
@@ -135,10 +125,14 @@ class TaskExecutor:
135
125
  # loop through the item results and set the global changed/failed/skipped result flags based on any item.
136
126
  res['skipped'] = True
137
127
  for item in item_results:
128
+ if item.get('_ansible_no_log'):
129
+ res.update(_ansible_no_log=True) # ensure no_log processing recognizes at least one item needs to be censored
130
+
138
131
  if 'changed' in item and item['changed'] and not res.get('changed'):
139
132
  res['changed'] = True
140
133
  if res['skipped'] and ('skipped' not in item or ('skipped' in item and not item['skipped'])):
141
134
  res['skipped'] = False
135
+ # FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
142
136
  if 'failed' in item and item['failed']:
143
137
  item_ignore = item.pop('_ansible_ignore_errors')
144
138
  if not res.get('failed'):
@@ -165,6 +159,7 @@ class TaskExecutor:
165
159
  res[array] = res[array] + item[array]
166
160
  del item[array]
167
161
 
162
+ # FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
168
163
  if not res.get('failed', False):
169
164
  res['msg'] = 'All items completed'
170
165
  if res['skipped']:
@@ -173,43 +168,23 @@ class TaskExecutor:
173
168
  res = dict(changed=False, skipped=True, skipped_reason='No items in the list', results=[])
174
169
  else:
175
170
  display.debug("calling self._execute()")
176
- res = self._execute()
171
+ res = self._execute(self._task_templar, self._job_vars)
177
172
  display.debug("_execute() done")
178
173
 
179
174
  # make sure changed is set in the result, if it's not present
180
175
  if 'changed' not in res:
181
176
  res['changed'] = False
182
177
 
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
178
  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)
179
+ except Exception as ex:
180
+ result = ActionBase.result_dict_from_exception(ex)
181
+
182
+ self._task.update_result_no_log(self._task_templar, result)
183
+
184
+ if not isinstance(ex, AnsibleError):
185
+ result.update(msg=f'Unexpected failure during task execution: {result["msg"]}')
186
+
187
+ return result
213
188
  finally:
214
189
  try:
215
190
  self._connection.close()
@@ -218,11 +193,11 @@ class TaskExecutor:
218
193
  except Exception as e:
219
194
  display.debug(u"error closing connection: %s" % to_text(e))
220
195
 
221
- def _get_loop_items(self):
222
- '''
196
+ def _get_loop_items(self) -> list[t.Any] | None:
197
+ """
223
198
  Loads a lookup plugin to handle the with_* portion of a task (if specified),
224
199
  and returns the items result.
225
- '''
200
+ """
226
201
 
227
202
  # get search path for this task to pass to lookup plugins
228
203
  self._job_vars['ansible_search_path'] = self._task.get_search_path()
@@ -231,49 +206,51 @@ class TaskExecutor:
231
206
  if self._loader.get_basedir() not in self._job_vars['ansible_search_path']:
232
207
  self._job_vars['ansible_search_path'].append(self._loader.get_basedir())
233
208
 
234
- templar = Templar(loader=self._loader, variables=self._job_vars)
235
209
  items = None
236
210
  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')
211
+ templar = self._task_templar
212
+ terms = self._task.loop
213
+
214
+ if isinstance(terms, str):
215
+ terms = templar.resolve_to_container(_utils.str_problematic_strip(terms))
216
+
217
+ if not isinstance(terms, list):
218
+ terms = [terms]
219
+
220
+ @_DirectCall.mark
221
+ def invoke_lookup() -> t.Any:
222
+ """Scope-capturing wrapper around _invoke_lookup to avoid functools.partial obscuring its usage from type-checking tools."""
223
+ return _invoke_lookup(
224
+ plugin_name=self._task.loop_with,
225
+ lookup_terms=terms,
226
+ lookup_kwargs=dict(wantlist=True),
227
+ invoked_as_with=True,
228
+ )
252
229
 
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)
230
+ # Smuggle a special wrapped lookup invocation in as a local variable for its exclusive use when being evaluated as `with_(lookup)`.
231
+ # This value will not be visible to other users of this templar or its `available_variables`.
232
+ items = templar.evaluate_expression(expression=TrustedAsTemplate().tag("invoke_lookup()"), local_variables=dict(invoke_lookup=invoke_lookup))
257
233
 
258
234
  elif self._task.loop is not None:
259
- items = templar.template(self._task.loop)
235
+ items = self._task_templar.template(self._task.loop)
236
+
260
237
  if not isinstance(items, list):
261
238
  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
239
+ f"The `loop` value must resolve to a 'list', not {native_type_name(items)!r}.",
240
+ help_text="Provide a list of items/templates, or a template resolving to a list.",
241
+ obj=self._task.loop,
265
242
  )
266
243
 
267
244
  return items
268
245
 
269
- def _run_loop(self, items):
270
- '''
246
+ def _run_loop(self, items: list[t.Any]) -> list[dict[str, t.Any]]:
247
+ """
271
248
  Runs the task with the loop items specified and collates the result
272
249
  into an array named 'results' which is inserted into the final result
273
250
  along with the item for which the loop ran.
274
- '''
251
+ """
275
252
  task_vars = self._job_vars
276
- templar = Templar(loader=self._loader, variables=task_vars)
253
+ templar = TemplateEngine(loader=self._loader, variables=task_vars)
277
254
 
278
255
  self._task.loop_control.post_validate(templar=templar)
279
256
 
@@ -282,17 +259,20 @@ class TaskExecutor:
282
259
  loop_pause = self._task.loop_control.pause
283
260
  extended = self._task.loop_control.extended
284
261
  extended_allitems = self._task.loop_control.extended_allitems
262
+
285
263
  # ensure we always have a label
286
- label = self._task.loop_control.label or '{{' + loop_var + '}}'
264
+ label = self._task.loop_control.label or templar.variable_name_as_template(loop_var)
287
265
 
288
266
  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))
267
+ display.warning(
268
+ msg=f"The loop variable {loop_var!r} is already in use.",
269
+ help_text="You should set the `loop_var` value in the `loop_control` option for the task "
270
+ "to something else to avoid variable collisions and unexpected behavior.",
271
+ obj=loop_var,
272
+ )
292
273
 
293
274
  ran_once = False
294
275
  task_fields = None
295
- no_log = False
296
276
  items_len = len(items)
297
277
  results = []
298
278
  for item_index, item in enumerate(items):
@@ -332,7 +312,7 @@ class TaskExecutor:
332
312
  ran_once = True
333
313
 
334
314
  try:
335
- tmp_task = self._task.copy(exclude_parent=True, exclude_tasks=True)
315
+ tmp_task: Task = self._task.copy(exclude_parent=True, exclude_tasks=True)
336
316
  tmp_task._parent = self._task._parent
337
317
  tmp_play_context = self._play_context.copy()
338
318
  except AnsibleParserError as e:
@@ -341,9 +321,11 @@ class TaskExecutor:
341
321
 
342
322
  # now we swap the internal task and play context with their copies,
343
323
  # execute, and swap them back so we can do the next iteration cleanly
324
+ # NB: this swap-a-dee-doo confuses some type checkers about the type of tmp_task/self._task
344
325
  (self._task, tmp_task) = (tmp_task, self._task)
345
326
  (self._play_context, tmp_play_context) = (tmp_play_context, self._play_context)
346
- res = self._execute(variables=task_vars)
327
+
328
+ res = self._execute(templar=templar, variables=task_vars)
347
329
 
348
330
  if self._task.register:
349
331
  # Ensure per loop iteration results are registered in case `_execute()`
@@ -355,9 +337,6 @@ class TaskExecutor:
355
337
  (self._task, tmp_task) = (tmp_task, self._task)
356
338
  (self._play_context, tmp_play_context) = (tmp_play_context, self._play_context)
357
339
 
358
- # update 'general no_log' based on specific no_log
359
- no_log = no_log or tmp_task.no_log
360
-
361
340
  # now update the result with the item info, and append the result
362
341
  # to the list of results
363
342
  res[loop_var] = item
@@ -385,13 +364,14 @@ class TaskExecutor:
385
364
  if self._connection and not isinstance(self._connection, string_types):
386
365
  task_fields['connection'] = getattr(self._connection, 'ansible_name')
387
366
 
388
- tr = TaskResult(
389
- self._host.name,
390
- self._task._uuid,
391
- res,
367
+ tr = _RawTaskResult(
368
+ host=self._host,
369
+ task=self._task,
370
+ return_data=res,
392
371
  task_fields=task_fields,
393
372
  )
394
373
 
374
+ # FIXME: normalize `failed` to a bool, warn if the action/module used non-bool
395
375
  if tr.is_failed() or tr.is_unreachable():
396
376
  self._final_q.send_callback('v2_runner_item_on_failed', tr)
397
377
  elif tr.is_skipped():
@@ -406,11 +386,14 @@ class TaskExecutor:
406
386
 
407
387
  # break loop if break_when conditions are met
408
388
  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
389
+ break_when = self._task.loop_control.get_validated_value(
390
+ 'break_when',
391
+ self._task.loop_control.fattributes.get('break_when'),
392
+ self._task.loop_control.break_when,
393
+ templar,
412
394
  )
413
- if cond.evaluate_conditional(templar, task_vars):
395
+
396
+ if self._task._resolve_conditional(break_when, task_vars):
414
397
  # delete loop vars before exiting loop
415
398
  del task_vars[loop_var]
416
399
  break
@@ -432,7 +415,6 @@ class TaskExecutor:
432
415
  if var in task_vars and var not in self._job_vars:
433
416
  del task_vars[var]
434
417
 
435
- self._task.no_log = no_log
436
418
  # NOTE: run_once cannot contain loop vars because it's templated earlier also
437
419
  # This is saving the post-validated field from the last loop so the strategy can use the templated value post task execution
438
420
  self._task.run_once = task_fields.get('run_once')
@@ -448,21 +430,49 @@ class TaskExecutor:
448
430
  # At the point this is executed it is safe to mutate self._task,
449
431
  # since `self._task` is either a copy referred to by `tmp_task` in `_run_loop`
450
432
  # 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
433
 
455
- def _execute(self, variables=None):
456
- '''
434
+ self._task.delegate_to = delegated_host_name # always override, since a templated result could be an omit (-> None)
435
+ variables.update(delegated_vars)
436
+
437
+ def _execute(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
438
+ result: dict[str, t.Any]
439
+
440
+ with _DeferredWarningContext(variables=variables) as warning_ctx:
441
+ try:
442
+ # DTFIX-FUTURE: improve error handling to prioritize the earliest exception, turning the remaining ones into warnings
443
+ result = self._execute_internal(templar, variables)
444
+ self._apply_task_result_compat(result, warning_ctx)
445
+ _captured.AnsibleActionCapturedError.maybe_raise_on_result(result)
446
+ except Exception as ex:
447
+ try:
448
+ raise AnsibleTaskError(obj=self._task.get_ds()) from ex
449
+ except AnsibleTaskError as atex:
450
+ result = ActionBase.result_dict_from_exception(atex)
451
+ result.setdefault('changed', False)
452
+
453
+ self._task.update_result_no_log(templar, result)
454
+
455
+ # The warnings/deprecations in the result have already been captured in the _DeferredWarningContext by _apply_task_result_compat.
456
+ # The captured warnings/deprecations are a superset of the ones from the result, and may have been converted from a dict to a dataclass.
457
+ # These are then used to supersede the entries in the result.
458
+
459
+ result.pop('warnings', None)
460
+ result.pop('deprecations', None)
461
+
462
+ if warnings := warning_ctx.get_warnings():
463
+ result.update(warnings=warnings)
464
+
465
+ if deprecation_warnings := warning_ctx.get_deprecation_warnings():
466
+ result.update(deprecations=deprecation_warnings)
467
+
468
+ return result
469
+
470
+ def _execute_internal(self, templar: TemplateEngine, variables: dict[str, t.Any]) -> dict[str, t.Any]:
471
+ """
457
472
  The primary workhorse of the executor system, this runs the task
458
473
  on the specified host (which may be the delegated_to host) and handles
459
474
  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)
475
+ """
466
476
 
467
477
  self._calculate_delegate_to(templar, variables)
468
478
 
@@ -498,18 +508,13 @@ class TaskExecutor:
498
508
  # skipping this task during the conditional evaluation step
499
509
  context_validation_error = e
500
510
 
501
- no_log = self._play_context.no_log
502
-
503
511
  # Evaluate the conditional (if any) for this task, which we do before running
504
512
  # the final task post-validation. We do this before the post validation due to
505
513
  # the fact that the conditional may specify that the task be skipped due to a
506
514
  # variable not being present which would otherwise cause validation to fail
507
515
  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)
516
+ if not self._task._resolve_conditional(self._task.when, tempvars, result_context=(rc := t.cast(dict[str, t.Any], {}))):
517
+ return dict(changed=False, skipped=True, skip_reason='Conditional result was False') | rc
513
518
  except AnsibleError as e:
514
519
  # loop error takes precedence
515
520
  if self._loop_eval_error is not None:
@@ -525,22 +530,27 @@ class TaskExecutor:
525
530
 
526
531
  # if we ran into an error while setting up the PlayContext, raise it now, unless is known issue with delegation
527
532
  # and undefined vars (correct values are in cvars later on and connection plugins, if still error, blows up there)
533
+
534
+ # DTFIX-RELEASE: this should probably be declaratively handled in post_validate (or better, get rid of play_context)
528
535
  if context_validation_error is not None:
529
536
  raiseit = True
530
537
  if self._task.delegate_to:
531
- if isinstance(context_validation_error, AnsibleUndefinedVariable):
532
- raiseit = False
533
- elif isinstance(context_validation_error, AnsibleParserError):
538
+ if isinstance(context_validation_error, AnsibleParserError):
534
539
  # parser error, might be cause by undef too
535
- orig_exc = getattr(context_validation_error, 'orig_exc', None)
536
- if isinstance(orig_exc, AnsibleUndefinedVariable):
540
+ if isinstance(context_validation_error.__cause__, AnsibleUndefinedVariable):
537
541
  raiseit = False
542
+ elif isinstance(context_validation_error, AnsibleUndefinedVariable):
543
+ # DTFIX-RELEASE: should not be possible to hit this now (all are AnsibleFieldAttributeError)?
544
+ raiseit = False
538
545
  if raiseit:
539
546
  raise context_validation_error # pylint: disable=raising-bad-type
540
547
 
541
548
  # set templar to use temp variables until loop is evaluated
542
549
  templar.available_variables = tempvars
543
550
 
551
+ # Now we do final validation on the task, which sets all fields to their final values.
552
+ self._task.post_validate(templar=templar)
553
+
544
554
  # if this task is a TaskInclude, we just return now with a success code so the
545
555
  # main thread can expand the task list for the given host
546
556
  if self._task.action in C._ACTION_INCLUDE_TASKS:
@@ -549,7 +559,6 @@ class TaskExecutor:
549
559
  if not include_file:
550
560
  return dict(failed=True, msg="No include file was specified to the include")
551
561
 
552
- include_file = templar.template(include_file)
553
562
  return dict(include=include_file, include_args=include_args)
554
563
 
555
564
  # 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 +566,9 @@ class TaskExecutor:
557
566
  include_args = self._task.args.copy()
558
567
  return dict(include_args=include_args)
559
568
 
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
569
  # free tempvars up, not used anymore, cvars and vars_copy should be mainly used after this point
584
570
  # updating the original 'variables' at the end
585
- tempvars = {}
571
+ del tempvars
586
572
 
587
573
  # setup cvars copy, used for all connection related templating
588
574
  if self._task.delegate_to:
@@ -634,23 +620,7 @@ class TaskExecutor:
634
620
  cvars['ansible_python_interpreter'] = sys.executable
635
621
 
636
622
  # 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)
623
+ self._handler, _module_context = self._get_action_handler_with_module_context(templar=templar)
654
624
 
655
625
  retries = 1 # includes the default actual run + retries set by user/default
656
626
  if self._task.retries is not None:
@@ -670,7 +640,10 @@ class TaskExecutor:
670
640
  if self._task.timeout:
671
641
  old_sig = signal.signal(signal.SIGALRM, task_timeout)
672
642
  signal.alarm(self._task.timeout)
673
- result = self._handler.run(task_vars=vars_copy)
643
+ with PluginExecContext(self._handler):
644
+ result = self._handler.run(task_vars=vars_copy)
645
+
646
+ # DTFIX-RELEASE: nuke this, it hides a lot of error detail- remove the active exception propagation hack from AnsibleActionFail at the same time
674
647
  except (AnsibleActionFail, AnsibleActionSkip) as e:
675
648
  return e.result
676
649
  except AnsibleConnectionFailure as e:
@@ -685,12 +658,6 @@ class TaskExecutor:
685
658
  self._handler.cleanup()
686
659
  display.debug("handler run complete")
687
660
 
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
661
  # update the local copy of vars with the registered value, if specified,
695
662
  # or any facts which may have been generated by the module execution
696
663
  if self._task.register:
@@ -702,37 +669,23 @@ class TaskExecutor:
702
669
  if result.get('failed'):
703
670
  self._final_q.send_callback(
704
671
  'v2_runner_on_async_failed',
705
- TaskResult(self._host.name,
706
- self._task._uuid,
707
- result,
708
- task_fields=self._task.dump_attrs()))
672
+ _RawTaskResult(
673
+ host=self._host,
674
+ task=self._task,
675
+ return_data=result,
676
+ task_fields=self._task.dump_attrs(),
677
+ ),
678
+ )
709
679
  else:
710
680
  self._final_q.send_callback(
711
681
  'v2_runner_on_async_ok',
712
- TaskResult(self._host.name,
713
- self._task._uuid,
714
- result,
715
- task_fields=self._task.dump_attrs()))
716
-
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
682
+ _RawTaskResult(
683
+ host=self._host,
684
+ task=self._task,
685
+ return_data=result,
686
+ task_fields=self._task.dump_attrs(),
687
+ ),
688
+ )
736
689
 
737
690
  if 'ansible_facts' in result and self._task.action not in C._ACTION_DEBUG:
738
691
  if self._task.action in C._ACTION_WITH_CLEAN_FACTS:
@@ -745,10 +698,11 @@ class TaskExecutor:
745
698
  vars_copy.update(result['ansible_facts'])
746
699
  else:
747
700
  # TODO: cleaning of facts should eventually become part of taskresults instead of vars
748
- af = wrap_var(result['ansible_facts'])
701
+ af = result['ansible_facts']
749
702
  vars_copy['ansible_facts'] = combine_vars(vars_copy.get('ansible_facts', {}), namespace_facts(af))
750
703
  if C.INJECT_FACTS_AS_VARS:
751
- vars_copy.update(clean_facts(af))
704
+ cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
705
+ vars_copy.update(cleaned_toplevel)
752
706
 
753
707
  # set the failed property if it was missing.
754
708
  if 'failed' not in result:
@@ -766,9 +720,6 @@ class TaskExecutor:
766
720
  if 'changed' not in result:
767
721
  result['changed'] = False
768
722
 
769
- if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
770
- result = wrap_var(result)
771
-
772
723
  # re-update the local copy of vars with the registered value, if specified,
773
724
  # or any facts which may have been generated by the module execution
774
725
  # This gives changed/failed_when access to additional recently modified
@@ -781,18 +732,30 @@ class TaskExecutor:
781
732
  if 'skipped' not in result:
782
733
  condname = 'changed'
783
734
 
735
+ # DTFIX-RELEASE: error normalization has not yet occurred; this means that the expressions used for until/failed_when/changed_when/break_when
736
+ # and when (for loops on the second and later iterations) cannot see the normalized error shapes. This, and the current impl of the expression
737
+ # handling here causes a number of problems:
738
+ # * any error in one of the post-task exec expressions is silently ignored and detail lost (eg: `failed_when: syntax ERROR @$123`)
739
+ # * they cannot reliably access error/warning details, since many of those details are inaccessible until the error normalization occurs
740
+ # * error normalization includes `msg` if present, and supplies `unknown error` if not; this leads to screwy results on True failed_when if
741
+ # `msg` is present, eg: `{debug: {}, failed_when: True` -> "Task failed: Action failed: Hello world!"
742
+ # * detail about failed_when is lost; any error details from the task could potentially be grafted in/preserved if error normalization was done
743
+
784
744
  try:
785
- _evaluate_changed_when_result(result)
745
+ if self._task.changed_when is not None and self._task.changed_when:
746
+ result['changed'] = self._task._resolve_conditional(self._task.changed_when, vars_copy)
747
+
786
748
  condname = 'failed'
787
- _evaluate_failed_when_result(result)
749
+
750
+ if self._task.failed_when:
751
+ result['failed_when_result'] = result['failed'] = self._task._resolve_conditional(self._task.failed_when, vars_copy)
752
+
788
753
  except AnsibleError as e:
789
754
  result['failed'] = True
790
755
  result['%s_when_result' % condname] = to_text(e)
791
756
 
792
757
  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):
758
+ if self._task._resolve_conditional(self._task.until or [not result['failed']], vars_copy):
796
759
  break
797
760
  else:
798
761
  # no conditional check, or it failed, so sleep for the specified time
@@ -802,12 +765,12 @@ class TaskExecutor:
802
765
  display.debug('Retrying task, attempt %d of %d' % (attempt, retries))
803
766
  self._final_q.send_callback(
804
767
  'v2_runner_retry',
805
- TaskResult(
806
- self._host.name,
807
- self._task._uuid,
808
- result,
768
+ _RawTaskResult(
769
+ host=self._host,
770
+ task=self._task,
771
+ return_data=result,
809
772
  task_fields=self._task.dump_attrs()
810
- )
773
+ ),
811
774
  )
812
775
  time.sleep(delay)
813
776
  self._handler = self._get_action_handler(templar=templar)
@@ -817,9 +780,6 @@ class TaskExecutor:
817
780
  result['attempts'] = retries - 1
818
781
  result['failed'] = True
819
782
 
820
- if self._task.action not in C._ACTION_WITH_CLEAN_FACTS:
821
- result = wrap_var(result)
822
-
823
783
  # do the final update of the local variables here, for both registered
824
784
  # values and any facts which may have been created
825
785
  if self._task.register:
@@ -830,10 +790,12 @@ class TaskExecutor:
830
790
  variables.update(result['ansible_facts'])
831
791
  else:
832
792
  # TODO: cleaning of facts should eventually become part of taskresults instead of vars
833
- af = wrap_var(result['ansible_facts'])
793
+ af = result['ansible_facts']
834
794
  variables['ansible_facts'] = combine_vars(variables.get('ansible_facts', {}), namespace_facts(af))
835
795
  if C.INJECT_FACTS_AS_VARS:
836
- variables.update(clean_facts(af))
796
+ # DTFIX-FUTURE: why is this happening twice, esp since we're post-fork and these will be discarded?
797
+ cleaned_toplevel = {k: _deprecate_top_level_fact(v) for k, v in clean_facts(af).items()}
798
+ variables.update(cleaned_toplevel)
837
799
 
838
800
  # save the notification target in the result, if it was specified, as
839
801
  # this task may be running in a loop in which case the notification
@@ -858,10 +820,54 @@ class TaskExecutor:
858
820
  display.debug("attempt loop complete, returning result")
859
821
  return result
860
822
 
823
+ @staticmethod
824
+ def _apply_task_result_compat(result: dict[str, t.Any], warning_ctx: _DeferredWarningContext) -> None:
825
+ """Apply backward-compatibility mutations to the supplied task result."""
826
+ if warnings := result.get('warnings'):
827
+ if isinstance(warnings, list):
828
+ for warning in warnings:
829
+ if not isinstance(warning, WarningSummary):
830
+ # translate non-WarningMessageDetail messages
831
+ warning = WarningSummary(
832
+ details=(
833
+ Detail(msg=str(warning)),
834
+ ),
835
+ )
836
+
837
+ warning_ctx.capture(warning)
838
+ else:
839
+ display.warning(f"Task result `warnings` was {type(warnings)} instead of {list}.")
840
+
841
+ if deprecations := result.get('deprecations'):
842
+ if isinstance(deprecations, list):
843
+ for deprecation in deprecations:
844
+ if not isinstance(deprecation, DeprecationSummary):
845
+ # translate non-DeprecationMessageDetail message dicts
846
+ try:
847
+ if deprecation.pop('collection_name', ...) is not ...:
848
+ # deprecated: description='enable the deprecation message for collection_name' core_version='2.23'
849
+ # self.deprecated('The `collection_name` key in the `deprecations` dictionary is deprecated.', version='2.27')
850
+ pass
851
+
852
+ # DTFIX-RELEASE: when plugin isn't set, do it at the boundary where we receive the module/action results
853
+ # that may even allow us to never set it in modules/actions directly and to populate it at the boundary
854
+ deprecation = DeprecationSummary(
855
+ details=(
856
+ Detail(msg=deprecation.pop('msg')),
857
+ ),
858
+ **deprecation,
859
+ )
860
+ except Exception as ex:
861
+ display.error_as_warning("Task result `deprecations` contained an invalid item.", exception=ex)
862
+
863
+ warning_ctx.capture(deprecation)
864
+ else:
865
+ display.warning(f"Task result `deprecations` was {type(deprecations)} instead of {list}.")
866
+
861
867
  def _poll_async_result(self, result, templar, task_vars=None):
862
- '''
868
+ """
863
869
  Polls for the specified JID to be complete
864
- '''
870
+ """
865
871
 
866
872
  if task_vars is None:
867
873
  task_vars = self._job_vars
@@ -891,7 +897,7 @@ class TaskExecutor:
891
897
  connection=self._connection,
892
898
  play_context=self._play_context,
893
899
  loader=self._loader,
894
- templar=templar,
900
+ templar=Templar._from_template_engine(templar),
895
901
  shared_loader_obj=self._shared_loader_obj,
896
902
  )
897
903
 
@@ -929,10 +935,10 @@ class TaskExecutor:
929
935
  time_left -= self._task.poll
930
936
  self._final_q.send_callback(
931
937
  'v2_runner_on_async_poll',
932
- TaskResult(
933
- self._host.name,
934
- async_task._uuid,
935
- async_result,
938
+ _RawTaskResult(
939
+ host=self._host,
940
+ task=async_task,
941
+ return_data=async_result,
936
942
  task_fields=async_task.dump_attrs(),
937
943
  ),
938
944
  )
@@ -961,7 +967,7 @@ class TaskExecutor:
961
967
  connection=self._connection,
962
968
  play_context=self._play_context,
963
969
  loader=self._loader,
964
- templar=templar,
970
+ templar=Templar._from_template_engine(templar),
965
971
  shared_loader_obj=self._shared_loader_obj,
966
972
  )
967
973
  cleanup_handler.run(task_vars=task_vars)
@@ -977,10 +983,10 @@ class TaskExecutor:
977
983
  return become
978
984
 
979
985
  def _get_connection(self, cvars, templar, current_connection):
980
- '''
986
+ """
981
987
  Reads the connection property for the host, and returns the
982
988
  correct connection object from the list of connection plugins
983
- '''
989
+ """
984
990
 
985
991
  self._play_context.connection = current_connection
986
992
 
@@ -992,7 +998,7 @@ class TaskExecutor:
992
998
  connection, plugin_load_context = self._shared_loader_obj.connection_loader.get_with_context(
993
999
  conn_type,
994
1000
  self._play_context,
995
- self._new_stdin,
1001
+ new_stdin=None, # No longer used, kept for backwards compat for plugins that explicitly accept this as an arg
996
1002
  task_uuid=self._task._uuid,
997
1003
  ansible_playbook_pid=to_text(os.getppid())
998
1004
  )
@@ -1058,7 +1064,11 @@ class TaskExecutor:
1058
1064
  options = {}
1059
1065
  for k in option_vars:
1060
1066
  if k in variables:
1061
- options[k] = templar.template(variables[k])
1067
+ try:
1068
+ options[k] = templar.template(variables[k])
1069
+ except AnsibleValueOmittedError:
1070
+ pass
1071
+
1062
1072
  # TODO move to task method?
1063
1073
  plugin.set_options(task_keys=task_keys, var_options=options)
1064
1074
 
@@ -1124,15 +1134,15 @@ class TaskExecutor:
1124
1134
  return varnames
1125
1135
 
1126
1136
  def _get_action_handler(self, templar):
1127
- '''
1137
+ """
1128
1138
  Returns the correct action plugin to handle the requestion task action
1129
- '''
1139
+ """
1130
1140
  return self._get_action_handler_with_module_context(templar)[0]
1131
1141
 
1132
- def _get_action_handler_with_module_context(self, templar):
1133
- '''
1142
+ def _get_action_handler_with_module_context(self, templar: TemplateEngine):
1143
+ """
1134
1144
  Returns the correct action plugin to handle the requestion task action and the module context
1135
- '''
1145
+ """
1136
1146
  module_collection, separator, module_name = self._task.action.rpartition(".")
1137
1147
  module_prefix = module_name.split('_')[0]
1138
1148
  if module_collection:
@@ -1191,7 +1201,7 @@ class TaskExecutor:
1191
1201
  connection=self._connection,
1192
1202
  play_context=self._play_context,
1193
1203
  loader=self._loader,
1194
- templar=templar,
1204
+ templar=Templar._from_template_engine(templar),
1195
1205
  shared_loader_obj=self._shared_loader_obj,
1196
1206
  collection_list=collections
1197
1207
  )
@@ -1206,9 +1216,9 @@ CLI_STUB_NAME = 'ansible_connection_cli_stub.py'
1206
1216
 
1207
1217
 
1208
1218
  def start_connection(play_context, options, task_uuid):
1209
- '''
1219
+ """
1210
1220
  Starts the persistent connection
1211
- '''
1221
+ """
1212
1222
 
1213
1223
  env = os.environ.copy()
1214
1224
  env.update({