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
ansible/playbook/task.py CHANGED
@@ -17,13 +17,18 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
+ import typing as t
21
+
20
22
  from ansible import constants as C
21
- from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleAssertionError
23
+ from ansible.module_utils.common.sentinel import Sentinel
24
+ from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleAssertionError, AnsibleValueOmittedError
25
+ from ansible.executor.module_common import _get_action_arg_defaults
22
26
  from ansible.module_utils.common.text.converters import to_native
27
+ from ansible.module_utils._internal._datatag import AnsibleTagHelper
23
28
  from ansible.module_utils.six import string_types
24
- from ansible.parsing.mod_args import ModuleArgsParser
25
- from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject, AnsibleMapping
26
- from ansible.plugins.loader import lookup_loader
29
+ from ansible.parsing.mod_args import ModuleArgsParser, RAW_PARAM_MODULES
30
+ from ansible.plugins.action import ActionBase
31
+ from ansible.plugins.loader import action_loader, module_loader, lookup_loader
27
32
  from ansible.playbook.attribute import NonInheritableFieldAttribute
28
33
  from ansible.playbook.base import Base
29
34
  from ansible.playbook.block import Block
@@ -34,10 +39,14 @@ from ansible.playbook.loop_control import LoopControl
34
39
  from ansible.playbook.notifiable import Notifiable
35
40
  from ansible.playbook.role import Role
36
41
  from ansible.playbook.taggable import Taggable
42
+ from ansible._internal import _task
43
+ from ansible._internal._templating import _marker_behaviors
44
+ from ansible._internal._templating._jinja_bits import is_possibly_all_template
45
+ from ansible._internal._templating._engine import TemplateEngine, TemplateOptions
37
46
  from ansible.utils.collection_loader import AnsibleCollectionConfig
38
47
  from ansible.utils.display import Display
39
- from ansible.utils.sentinel import Sentinel
40
- from ansible.utils.vars import isidentifier
48
+
49
+ from ansible.utils.vars import validate_variable_name
41
50
 
42
51
  __all__ = ['Task']
43
52
 
@@ -67,8 +76,8 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
67
76
  # inheritance is only triggered if the 'current value' is Sentinel,
68
77
  # default can be set at play/top level object and inheritance will take it's course.
69
78
 
70
- args = NonInheritableFieldAttribute(isa='dict', default=dict)
71
- action = NonInheritableFieldAttribute(isa='string')
79
+ args = t.cast(dict, NonInheritableFieldAttribute(isa='dict', default=dict))
80
+ action = t.cast(str, NonInheritableFieldAttribute(isa='string'))
72
81
 
73
82
  async_val = NonInheritableFieldAttribute(isa='int', default=0, alias='async')
74
83
  changed_when = NonInheritableFieldAttribute(isa='list', default=list)
@@ -84,13 +93,13 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
84
93
  # deprecated, used to be loop and loop_args but loop has been repurposed
85
94
  loop_with = NonInheritableFieldAttribute(isa='string', private=True)
86
95
 
87
- def __init__(self, block=None, role=None, task_include=None):
88
- ''' constructors a task, without the Task.load classmethod, it will be pretty blank '''
96
+ def __init__(self, block=None, role=None, task_include=None) -> None:
97
+ """ constructors a task, without the Task.load classmethod, it will be pretty blank """
89
98
 
90
99
  self._role = role
91
100
  self._parent = None
92
101
  self.implicit = False
93
- self.resolved_action = None
102
+ self.resolved_action: str | None = None
94
103
 
95
104
  if task_include:
96
105
  self._parent = task_include
@@ -100,7 +109,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
100
109
  super(Task, self).__init__()
101
110
 
102
111
  def get_name(self, include_role_fqcn=True):
103
- ''' return the name of the task '''
112
+ """ return the name of the task """
104
113
 
105
114
  if self._role:
106
115
  role_name = self._role.get_name(include_role_fqcn=include_role_fqcn)
@@ -131,18 +140,85 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
131
140
 
132
141
  @staticmethod
133
142
  def load(data, block=None, role=None, task_include=None, variable_manager=None, loader=None):
134
- t = Task(block=block, role=role, task_include=task_include)
135
- return t.load_data(data, variable_manager=variable_manager, loader=loader)
143
+ task = Task(block=block, role=role, task_include=task_include)
144
+ return task.load_data(data, variable_manager=variable_manager, loader=loader)
136
145
 
137
- def __repr__(self):
138
- ''' returns a human-readable representation of the task '''
146
+ def _post_validate_module_defaults(self, attr: str, value: t.Any, templar: TemplateEngine) -> t.Any:
147
+ """Override module_defaults post validation to disable templating, which is handled by args post validation."""
148
+ return value
149
+
150
+ def _post_validate_args(self, attr: str, value: t.Any, templar: TemplateEngine) -> dict[str, t.Any]:
151
+ try:
152
+ self.action = templar.template(self.action)
153
+ except AnsibleValueOmittedError:
154
+ # some strategies may trigger this error when templating task.action, but backstop here if not
155
+ raise AnsibleParserError("Omit is not valid for the `action` keyword.", obj=self.action) from None
156
+
157
+ action_context = action_loader.get_with_context(self.action, collection_list=self.collections, class_only=True)
158
+
159
+ if not action_context.plugin_load_context.resolved:
160
+ module_or_action_context = module_loader.find_plugin_with_context(self.action, collection_list=self.collections)
161
+
162
+ if not module_or_action_context.resolved:
163
+ raise AnsibleError(f"Cannot resolve {self.action!r} to an action or module.", obj=self.action)
164
+
165
+ action_context = action_loader.get_with_context('ansible.legacy.normal', collection_list=self.collections, class_only=True)
166
+ else:
167
+ module_or_action_context = action_context.plugin_load_context
168
+
169
+ self.resolved_action = module_or_action_context.resolved_fqcn
170
+
171
+ action_type: type[ActionBase] = action_context.object
172
+
173
+ vp = value.pop('_variable_params', None)
174
+
175
+ supports_raw_params = action_type.supports_raw_params or module_or_action_context.resolved_fqcn in RAW_PARAM_MODULES
176
+
177
+ if supports_raw_params:
178
+ raw_params_to_finalize = None
179
+ else:
180
+ raw_params_to_finalize = value.pop('_raw_params', None) # always str or None
181
+
182
+ # TaskArgsFinalizer performs more thorough type checking, but this provides a friendlier error message for a subset of detected cases.
183
+ if raw_params_to_finalize and not is_possibly_all_template(raw_params_to_finalize):
184
+ raise AnsibleError(f'Action {module_or_action_context.resolved_fqcn!r} does not support raw params.', obj=self.action)
185
+
186
+ args_finalizer = _task.TaskArgsFinalizer(
187
+ _get_action_arg_defaults(module_or_action_context.resolved_fqcn, self, templar),
188
+ vp,
189
+ raw_params_to_finalize,
190
+ value,
191
+ templar=templar,
192
+ )
193
+
194
+ try:
195
+ with action_type.get_finalize_task_args_context() as finalize_context:
196
+ args = args_finalizer.finalize(action_type.finalize_task_arg, context=finalize_context)
197
+ except Exception as ex:
198
+ raise AnsibleError(f'Finalization of task args for {module_or_action_context.resolved_fqcn!r} failed.', obj=self.action) from ex
199
+
200
+ if self._origin:
201
+ args = self._origin.tag(args)
202
+
203
+ return args
204
+
205
+ def _get_meta(self) -> str | None:
206
+ # FUTURE: validate meta and return an enum instead of a str
207
+ # meta currently does not support being templated, so we can cheat
139
208
  if self.action in C._ACTION_META:
140
- return "TASK: meta (%s)" % self.args['_raw_params']
209
+ return self.args.get('_raw_params')
210
+
211
+ return None
212
+
213
+ def __repr__(self):
214
+ """ returns a human-readable representation of the task """
215
+ if meta := self._get_meta():
216
+ return f"TASK: meta ({meta})"
141
217
  else:
142
218
  return "TASK: %s" % self.get_name()
143
219
 
144
220
  def _preprocess_with_loop(self, ds, new_ds, k, v):
145
- ''' take a lookup plugin name and store it correctly '''
221
+ """ take a lookup plugin name and store it correctly """
146
222
 
147
223
  loop_name = k.removeprefix("with_")
148
224
  if new_ds.get('loop') is not None or new_ds.get('loop_with') is not None:
@@ -155,20 +231,17 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
155
231
  # version="2.10", collection_name='ansible.builtin')
156
232
 
157
233
  def preprocess_data(self, ds):
158
- '''
234
+ """
159
235
  tasks are especially complex arguments so need pre-processing.
160
236
  keep it short.
161
- '''
237
+ """
162
238
 
163
239
  if not isinstance(ds, dict):
164
240
  raise AnsibleAssertionError('ds (%s) should be a dict but was a %s' % (ds, type(ds)))
165
241
 
166
- # the new, cleaned datastructure, which will have legacy
167
- # items reduced to a standard structure suitable for the
168
- # attributes of the task class
169
- new_ds = AnsibleMapping()
170
- if isinstance(ds, AnsibleBaseYAMLObject):
171
- new_ds.ansible_pos = ds.ansible_pos
242
+ # the new, cleaned datastructure, which will have legacy items reduced to a standard structure suitable for the
243
+ # attributes of the task class; copy any tagged data to preserve things like origin
244
+ new_ds = AnsibleTagHelper.tag_copy(ds, {})
172
245
 
173
246
  # since this affects the task action parsing, we have to resolve in preprocess instead of in typical validator
174
247
  default_collection = AnsibleCollectionConfig.default_collection
@@ -201,26 +274,13 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
201
274
  args_parser = ModuleArgsParser(task_ds=ds, collection_list=collections_list)
202
275
  try:
203
276
  (action, args, delegate_to) = args_parser.parse()
204
- except AnsibleParserError as e:
277
+ except AnsibleParserError as ex:
205
278
  # if the raises exception was created with obj=ds args, then it includes the detail
206
279
  # so we dont need to add it so we can just re raise.
207
- if e.obj:
280
+ if ex.obj:
208
281
  raise
209
282
  # But if it wasn't, we can add the yaml object now to get more detail
210
- raise AnsibleParserError(to_native(e), obj=ds, orig_exc=e)
211
- else:
212
- # Set the resolved action plugin (or if it does not exist, module) for callbacks.
213
- self.resolved_action = args_parser.resolved_action
214
-
215
- # the command/shell/script modules used to support the `cmd` arg,
216
- # which corresponds to what we now call _raw_params, so move that
217
- # value over to _raw_params (assuming it is empty)
218
- if action in C._ACTION_HAS_CMD:
219
- if 'cmd' in args:
220
- if args.get('_raw_params', '') != '':
221
- raise AnsibleError("The 'cmd' argument cannot be used when other raw parameters are specified."
222
- " Please put everything in one or the other place.", obj=ds)
223
- args['_raw_params'] = args.pop('cmd')
283
+ raise AnsibleParserError("Error parsing task arguments.", obj=ds) from ex
224
284
 
225
285
  new_ds['action'] = action
226
286
  new_ds['args'] = args
@@ -276,114 +336,99 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
276
336
  setattr(self, name, [value])
277
337
 
278
338
  def _validate_register(self, attr, name, value):
279
- if value is not None and not isidentifier(value):
280
- raise AnsibleParserError(f"Invalid variable name in 'register' specified: '{value}'")
339
+ if value is not None:
340
+ try:
341
+ validate_variable_name(value)
342
+ except Exception as ex:
343
+ raise AnsibleParserError("Invalid 'register' specified.", obj=value) from ex
281
344
 
282
345
  def post_validate(self, templar):
283
- '''
346
+ """
284
347
  Override of base class post_validate, to also do final validation on
285
348
  the block and task include (if any) to which this task belongs.
286
- '''
349
+ """
287
350
 
288
351
  if self._parent:
289
352
  self._parent.post_validate(templar)
290
353
 
291
- if AnsibleCollectionConfig.default_collection:
292
- pass
293
-
294
354
  super(Task, self).post_validate(templar)
295
355
 
296
- def _post_validate_args(self, attr, value, templar):
297
- # smuggle an untemplated copy of the task args for actions that need more control over the templating of their
298
- # input (eg, debug's var/msg, assert's "that" conditional expressions)
299
- self.untemplated_args = value
300
-
301
- # now recursively template the args dict
302
- args = templar.template(value)
303
-
304
- # FIXME: could we just nuke this entirely and/or wrap it up in ModuleArgsParser or something?
305
- if '_variable_params' in args:
306
- variable_params = args.pop('_variable_params')
307
- if isinstance(variable_params, dict):
308
- if C.INJECT_FACTS_AS_VARS:
309
- display.warning("Using a variable for a task's 'args' is unsafe in some situations "
310
- "(see https://docs.ansible.com/ansible/devel/reference_appendices/faq.html#argsplat-unsafe)")
311
- variable_params.update(args)
312
- args = variable_params
313
- else:
314
- # if we didn't get a dict, it means there's garbage remaining after k=v parsing, just give up
315
- # see https://github.com/ansible/ansible/issues/79862
316
- raise AnsibleError(f"invalid or malformed argument: '{variable_params}'")
317
-
318
- return args
319
-
320
356
  def _post_validate_loop(self, attr, value, templar):
321
- '''
357
+ """
322
358
  Override post validation for the loop field, which is templated
323
359
  specially in the TaskExecutor class when evaluating loops.
324
- '''
360
+ """
325
361
  return value
326
362
 
363
+ def _post_validate_name(self, attr, value, templar):
364
+ """
365
+ Override post-validation behavior for `name` to be best-effort for the vars available.
366
+ Direct access via `post_validate_attribute` writes the value back to provide a stable value.
367
+ This value is individually post-validated early by strategies for the benefit of callbacks.
368
+ """
369
+ with _marker_behaviors.ReplacingMarkerBehavior.warning_context() as replacing_behavior:
370
+ self.name = templar.extend(marker_behavior=replacing_behavior).template(value, options=TemplateOptions(value_for_omit=None))
371
+
372
+ return self.name
373
+
327
374
  def _post_validate_environment(self, attr, value, templar):
328
- '''
375
+ """
329
376
  Override post validation of vars on the play, as we don't want to
330
377
  template these too early.
331
- '''
378
+ """
332
379
  env = {}
333
- if value is not None:
334
380
 
335
- def _parse_env_kv(k, v):
336
- try:
337
- env[k] = templar.template(v, convert_bare=False)
338
- except AnsibleUndefinedVariable as e:
339
- error = to_native(e)
340
- if self.action in C._ACTION_FACT_GATHERING and 'ansible_facts.env' in error or 'ansible_env' in error:
341
- # ignore as fact gathering is required for 'env' facts
342
- return
343
- raise
344
-
345
- if isinstance(value, list):
346
- for env_item in value:
347
- if isinstance(env_item, dict):
348
- for k in env_item:
349
- _parse_env_kv(k, env_item[k])
350
- else:
351
- isdict = templar.template(env_item, convert_bare=False)
352
- if isinstance(isdict, dict):
353
- env |= isdict
354
- else:
355
- display.warning("could not parse environment value, skipping: %s" % value)
356
-
357
- elif isinstance(value, dict):
358
- # should not really happen
359
- env = dict()
360
- for env_item in value:
361
- _parse_env_kv(env_item, value[env_item])
381
+ # FUTURE: kill this with fire
382
+ def _parse_env_kv(k, v):
383
+ try:
384
+ env[k] = templar.template(v)
385
+ except AnsibleValueOmittedError:
386
+ # skip this value
387
+ return
388
+ except AnsibleUndefinedVariable as e:
389
+ error = to_native(e)
390
+ if self.action in C._ACTION_FACT_GATHERING and 'ansible_facts.env' in error or 'ansible_env' in error:
391
+ # ignore as fact gathering is required for 'env' facts
392
+ return
393
+ raise
394
+
395
+ # NB: the environment FieldAttribute definition ensures that value is always a list
396
+ for env_item in value:
397
+ if isinstance(env_item, dict):
398
+ for k in env_item:
399
+ _parse_env_kv(k, env_item[k])
362
400
  else:
363
- # at this point it should be a simple string, also should not happen
364
- env = templar.template(value, convert_bare=False)
401
+ try:
402
+ isdict = templar.template(env_item)
403
+ except AnsibleValueOmittedError:
404
+ continue
405
+
406
+ if isinstance(isdict, dict):
407
+ env |= isdict
408
+ else:
409
+ display.warning("could not parse environment value, skipping: %s" % value)
365
410
 
366
411
  return env
367
412
 
368
413
  def _post_validate_changed_when(self, attr, value, templar):
369
- '''
414
+ """
370
415
  changed_when is evaluated after the execution of the task is complete,
371
416
  and should not be templated during the regular post_validate step.
372
- '''
417
+ """
373
418
  return value
374
419
 
375
420
  def _post_validate_failed_when(self, attr, value, templar):
376
- '''
421
+ """
377
422
  failed_when is evaluated after the execution of the task is complete,
378
423
  and should not be templated during the regular post_validate step.
379
- '''
424
+ """
380
425
  return value
381
426
 
382
427
  def _post_validate_until(self, attr, value, templar):
383
- '''
428
+ """
384
429
  until is evaluated after the execution of the task is complete,
385
430
  and should not be templated during the regular post_validate step.
386
- '''
431
+ """
387
432
  return value
388
433
 
389
434
  def get_vars(self):
@@ -408,7 +453,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
408
453
  all_vars |= self.vars
409
454
  return all_vars
410
455
 
411
- def copy(self, exclude_parent=False, exclude_tasks=False):
456
+ def copy(self, exclude_parent: bool = False, exclude_tasks: bool = False) -> Task:
412
457
  new_me = super(Task, self).copy()
413
458
 
414
459
  new_me._parent = None
@@ -473,11 +518,11 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
473
518
  super(Task, self).deserialize(data)
474
519
 
475
520
  def set_loader(self, loader):
476
- '''
521
+ """
477
522
  Sets the loader on this object and recursively on parent, child objects.
478
523
  This is used primarily after the Task has been serialized/deserialized, which
479
524
  does not preserve the loader.
480
- '''
525
+ """
481
526
 
482
527
  self._loader = loader
483
528
 
@@ -485,9 +530,9 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
485
530
  self._parent.set_loader(loader)
486
531
 
487
532
  def _get_parent_attribute(self, attr, omit=False):
488
- '''
533
+ """
489
534
  Generic logic to get the attribute or parent attribute for a task value.
490
- '''
535
+ """
491
536
  fattr = self.fattributes[attr]
492
537
 
493
538
  extend = fattr.extend
@@ -542,3 +587,28 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
542
587
  while not isinstance(parent, Block):
543
588
  parent = parent._parent
544
589
  return parent._play
590
+
591
+ def dump_attrs(self):
592
+ """Override to smuggle important non-FieldAttribute values back to the controller."""
593
+ attrs = super().dump_attrs()
594
+ attrs.update(resolved_action=self.resolved_action)
595
+ return attrs
596
+
597
+ def _resolve_conditional(
598
+ self,
599
+ conditional: list[str | bool],
600
+ variables: dict[str, t.Any],
601
+ *,
602
+ result_context: dict[str, t.Any] | None = None,
603
+ ) -> bool:
604
+ """Loops through the conditionals set on this object, returning False if any of them evaluate as such, as well as the condition that was False."""
605
+ engine = TemplateEngine(self._loader, variables=variables)
606
+
607
+ for item in conditional:
608
+ if not engine.evaluate_conditional(item):
609
+ if result_context is not None:
610
+ result_context.update(false_condition=item)
611
+
612
+ return False
613
+
614
+ return True
@@ -19,10 +19,10 @@ from __future__ import annotations
19
19
 
20
20
  import ansible.constants as C
21
21
  from ansible.errors import AnsibleParserError
22
+ from ansible.module_utils.common.sentinel import Sentinel
22
23
  from ansible.playbook.block import Block
23
24
  from ansible.playbook.task import Task
24
25
  from ansible.utils.display import Display
25
- from ansible.utils.sentinel import Sentinel
26
26
 
27
27
  __all__ = ['TaskInclude']
28
28
 
@@ -58,12 +58,12 @@ class TaskInclude(Task):
58
58
  return task
59
59
 
60
60
  def check_options(self, task, data):
61
- '''
61
+ """
62
62
  Method for options validation to use in 'load_data' for TaskInclude and HandlerTaskInclude
63
63
  since they share the same validations. It is not named 'validate_options' on purpose
64
64
  to prevent confusion with '_validate_*" methods. Note that the task passed might be changed
65
65
  as a side-effect of this method.
66
- '''
66
+ """
67
67
  my_arg_names = frozenset(task.args.keys())
68
68
 
69
69
  # validate bad args, otherwise we silently ignore
@@ -104,10 +104,10 @@ class TaskInclude(Task):
104
104
  return new_me
105
105
 
106
106
  def build_parent_block(self):
107
- '''
107
+ """
108
108
  This method is used to create the parent block for the included tasks
109
109
  when ``apply`` is specified
110
- '''
110
+ """
111
111
  apply_attrs = self.args.pop('apply', {})
112
112
  if apply_attrs:
113
113
  apply_attrs['block'] = []
@@ -19,17 +19,16 @@
19
19
 
20
20
  from __future__ import annotations
21
21
 
22
- from abc import ABC
23
-
22
+ import abc
24
23
  import types
25
24
  import typing as t
26
25
 
27
26
  from ansible import constants as C
28
27
  from ansible.errors import AnsibleError
29
- from ansible.module_utils.common.text.converters import to_native
30
- from ansible.module_utils.six import string_types
31
28
  from ansible.utils.display import Display
32
29
 
30
+ from ansible.module_utils._internal import _plugin_exec_context
31
+
33
32
  display = Display()
34
33
 
35
34
  if t.TYPE_CHECKING:
@@ -42,13 +41,32 @@ PLUGIN_PATH_CACHE = {} # type: dict[str, dict[str, dict[str, PluginPathContext]
42
41
 
43
42
 
44
43
  def get_plugin_class(obj):
45
- if isinstance(obj, string_types):
44
+ if isinstance(obj, str):
46
45
  return obj.lower().replace('module', '')
47
46
  else:
48
47
  return obj.__class__.__name__.lower().replace('module', '')
49
48
 
50
49
 
51
- class AnsiblePlugin(ABC):
50
+ class _ConfigurablePlugin(t.Protocol):
51
+ """Protocol to provide type-safe access to config for plugin-related mixins."""
52
+
53
+ def get_option(self, option: str, hostvars: dict[str, object] | None = None) -> object: ...
54
+
55
+
56
+ class _AnsiblePluginInfoMixin(_plugin_exec_context.HasPluginInfo):
57
+ """Mixin to provide type annotations and default values for existing PluginLoader-set load-time attrs."""
58
+ _original_path: str | None = None
59
+ _load_name: str | None = None
60
+ _redirected_names: list[str] | None = None
61
+ ansible_aliases: list[str] | None = None
62
+ ansible_name: str | None = None
63
+
64
+ @property
65
+ def plugin_type(self) -> str:
66
+ return self.__class__.__name__.lower().replace('module', '')
67
+
68
+
69
+ class AnsiblePlugin(_AnsiblePluginInfoMixin, _ConfigurablePlugin, metaclass=abc.ABCMeta):
52
70
 
53
71
  # Set by plugin loader
54
72
  _load_name: str
@@ -81,7 +99,7 @@ class AnsiblePlugin(ABC):
81
99
  try:
82
100
  option_value, origin = C.config.get_config_value_and_origin(option, plugin_type=self.plugin_type, plugin_name=self._load_name, variables=hostvars)
83
101
  except AnsibleError as e:
84
- raise KeyError(to_native(e))
102
+ raise KeyError(str(e))
85
103
  return option_value, origin
86
104
 
87
105
  def get_option(self, option, hostvars=None):
@@ -102,13 +120,13 @@ class AnsiblePlugin(ABC):
102
120
  C.handle_config_noise(display)
103
121
 
104
122
  def set_options(self, task_keys=None, var_options=None, direct=None):
105
- '''
123
+ """
106
124
  Sets the _options attribute with the configuration/keyword information for this plugin
107
125
 
108
126
  :arg task_keys: Dict with playbook keywords that affect this option
109
127
  :arg var_options: Dict with either 'connection variables'
110
128
  :arg direct: Dict with 'direct assignment'
111
- '''
129
+ """
112
130
  self._options = C.config.get_plugin_options(self.plugin_type, self._load_name, keys=task_keys, variables=var_options, direct=direct)
113
131
 
114
132
  # allow extras/wildcards from vars that are not directly consumed in configuration
@@ -123,13 +141,9 @@ class AnsiblePlugin(ABC):
123
141
  self.set_options()
124
142
  return option in self._options
125
143
 
126
- @property
127
- def plugin_type(self):
128
- return self.__class__.__name__.lower().replace('module', '')
129
-
130
144
  @property
131
145
  def option_definitions(self):
132
- if self._defs is None:
146
+ if (not hasattr(self, "_defs")) or self._defs is None:
133
147
  self._defs = C.config.get_configuration_definitions(plugin_type=self.plugin_type, name=self._load_name)
134
148
  return self._defs
135
149
 
@@ -137,23 +151,56 @@ class AnsiblePlugin(ABC):
137
151
  # FIXME: standardize required check based on config
138
152
  pass
139
153
 
154
+ def __repr__(self):
155
+ ansible_name = getattr(self, 'ansible_name', '(unknown)')
156
+ load_name = getattr(self, '_load_name', '(unknown)')
157
+ return f'{type(self).__name__}(plugin_type={self.plugin_type!r}, {ansible_name=!r}, {load_name=!r})'
140
158
 
141
- class AnsibleJinja2Plugin(AnsiblePlugin):
142
-
143
- def __init__(self, function):
144
159
 
160
+ class AnsibleJinja2Plugin(AnsiblePlugin, metaclass=abc.ABCMeta):
161
+ def __init__(self, function: t.Callable) -> None:
145
162
  super(AnsibleJinja2Plugin, self).__init__()
146
163
  self._function = function
147
164
 
165
+ # Declare support for markers. Plugins with `False` here will never be invoked with markers for top-level arguments.
166
+ self.accept_args_markers = getattr(self._function, 'accept_args_markers', False)
167
+ self.accept_lazy_markers = getattr(self._function, 'accept_lazy_markers', False)
168
+
148
169
  @property
149
- def plugin_type(self):
150
- return self.__class__.__name__.lower().replace('ansiblejinja2', '')
170
+ @abc.abstractmethod
171
+ def plugin_type(self) -> str:
172
+ ...
151
173
 
152
- def _no_options(self, *args, **kwargs):
174
+ def _no_options(self, *args, **kwargs) -> t.NoReturn:
153
175
  raise NotImplementedError()
154
176
 
155
177
  has_option = get_option = get_options = option_definitions = set_option = set_options = _no_options
156
178
 
157
179
  @property
158
- def j2_function(self):
180
+ def j2_function(self) -> t.Callable:
159
181
  return self._function
182
+
183
+
184
+ _TCallable = t.TypeVar('_TCallable', bound=t.Callable)
185
+
186
+
187
+ def accept_args_markers(plugin: _TCallable) -> _TCallable:
188
+ """
189
+ A decorator to mark a Jinja plugin as capable of handling `Marker` values for its top-level arguments.
190
+ Non-decorated plugin invocation is skipped when a top-level argument is a `Marker`, with the first such value substituted as the plugin result.
191
+ This ensures that only plugins which understand `Marker` instances for top-level arguments will encounter them.
192
+ """
193
+ plugin.accept_args_markers = True
194
+
195
+ return plugin
196
+
197
+
198
+ def accept_lazy_markers(plugin: _TCallable) -> _TCallable:
199
+ """
200
+ A decorator to mark a Jinja plugin as capable of handling `Marker` values retrieved from lazy containers.
201
+ Non-decorated plugins will trigger a `MarkerError` exception when attempting to retrieve a `Marker` from a lazy container.
202
+ This ensures that only plugins which understand lazy retrieval of `Marker` instances will encounter them.
203
+ """
204
+ plugin.accept_lazy_markers = True
205
+
206
+ return plugin