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/plugins/loader.py CHANGED
@@ -6,35 +6,41 @@
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import functools
9
10
  import glob
10
11
  import os
11
12
  import os.path
12
13
  import pkgutil
13
14
  import sys
15
+ import types
14
16
  import warnings
17
+ import typing as t
18
+
19
+ import yaml
15
20
 
16
21
  from collections import defaultdict, namedtuple
17
22
  from importlib import import_module
18
- from traceback import format_exc
19
-
20
- import ansible.module_utils.compat.typing as t
21
-
22
- from .filter import AnsibleJinja2Filter
23
- from .test import AnsibleJinja2Test
23
+ from yaml.parser import ParserError
24
24
 
25
25
  from ansible import __version__ as ansible_version
26
- from ansible import constants as C
26
+ from ansible import _internal, constants as C
27
27
  from ansible.errors import AnsibleError, AnsiblePluginCircularRedirect, AnsiblePluginRemovedError, AnsibleCollectionUnsupportedVersionError
28
28
  from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
29
29
  from ansible.module_utils.six import string_types
30
- from ansible.parsing.utils.yaml import from_yaml
31
30
  from ansible.parsing.yaml.loader import AnsibleLoader
31
+ from ansible._internal._yaml._loader import AnsibleInstrumentedLoader
32
32
  from ansible.plugins import get_plugin_class, MODULE_CACHE, PATH_CACHE, PLUGIN_PATH_CACHE
33
33
  from ansible.utils.collection_loader import AnsibleCollectionConfig, AnsibleCollectionRef
34
34
  from ansible.utils.collection_loader._collection_finder import _AnsibleCollectionFinder, _get_collection_metadata
35
35
  from ansible.utils.display import Display
36
36
  from ansible.utils.plugin_docs import add_fragments
37
- from ansible.utils.unsafe_proxy import _is_unsafe
37
+ from ansible._internal._datatag import _tags
38
+
39
+ from . import _AnsiblePluginInfoMixin
40
+ from .filter import AnsibleJinja2Filter
41
+ from .test import AnsibleJinja2Test
42
+ from .._internal._plugins import _cache
43
+ from ..module_utils.common.messages import PluginInfo
38
44
 
39
45
  # TODO: take the packaging dep, or vendor SpecifierSet?
40
46
 
@@ -47,18 +53,30 @@ except ImportError:
47
53
 
48
54
  import importlib.util
49
55
 
56
+ if t.TYPE_CHECKING:
57
+ from ansible.plugins.cache import BaseCacheModule
58
+
50
59
  _PLUGIN_FILTERS = defaultdict(frozenset) # type: t.DefaultDict[str, frozenset]
51
60
  display = Display()
52
61
 
53
62
  get_with_context_result = namedtuple('get_with_context_result', ['object', 'plugin_load_context'])
54
63
 
55
64
 
56
- def get_all_plugin_loaders():
65
+ @functools.cache
66
+ def get_all_plugin_loaders() -> list[tuple[str, 'PluginLoader']]:
57
67
  return [(name, obj) for (name, obj) in globals().items() if isinstance(obj, PluginLoader)]
58
68
 
59
69
 
70
+ @functools.cache
71
+ def get_plugin_loader_namespace() -> types.SimpleNamespace:
72
+ ns = types.SimpleNamespace()
73
+ for name, obj in get_all_plugin_loaders():
74
+ setattr(ns, name, obj)
75
+ return ns
76
+
77
+
60
78
  def add_all_plugin_dirs(path):
61
- ''' add any existing plugin dirs in the path provided '''
79
+ """ add any existing plugin dirs in the path provided """
62
80
  b_path = os.path.expanduser(to_bytes(path, errors='surrogate_or_strict'))
63
81
  if os.path.isdir(b_path):
64
82
  for name, obj in get_all_plugin_loaders():
@@ -199,12 +217,12 @@ class PluginLoadContext(object):
199
217
 
200
218
 
201
219
  class PluginLoader:
202
- '''
220
+ """
203
221
  PluginLoader loads plugins from the configured plugin directories.
204
222
 
205
223
  It searches for plugins by iterating through the combined list of play basedirs, configured
206
224
  paths, and the python path. The first match is used.
207
- '''
225
+ """
208
226
 
209
227
  def __init__(self, class_name, package, config, subdir, aliases=None, required_base_class=None):
210
228
  aliases = {} if aliases is None else aliases
@@ -267,9 +285,9 @@ class PluginLoader:
267
285
  self._searched_paths = set()
268
286
 
269
287
  def __setstate__(self, data):
270
- '''
288
+ """
271
289
  Deserializer.
272
- '''
290
+ """
273
291
 
274
292
  class_name = data.get('class_name')
275
293
  package = data.get('package')
@@ -286,9 +304,9 @@ class PluginLoader:
286
304
  self._searched_paths = data.get('_searched_paths', set())
287
305
 
288
306
  def __getstate__(self):
289
- '''
307
+ """
290
308
  Serializer.
291
- '''
309
+ """
292
310
 
293
311
  return dict(
294
312
  class_name=self.class_name,
@@ -304,7 +322,7 @@ class PluginLoader:
304
322
  )
305
323
 
306
324
  def format_paths(self, paths):
307
- ''' Returns a string suitable for printing of the search path '''
325
+ """ Returns a string suitable for printing of the search path """
308
326
 
309
327
  # Uses a list to get the order right
310
328
  ret = []
@@ -326,7 +344,7 @@ class PluginLoader:
326
344
  return results
327
345
 
328
346
  def _get_package_paths(self, subdirs=True):
329
- ''' Gets the path of a Python package '''
347
+ """ Gets the path of a Python package """
330
348
 
331
349
  if not self.package:
332
350
  return []
@@ -341,7 +359,7 @@ class PluginLoader:
341
359
  return [self.package_path]
342
360
 
343
361
  def _get_paths_with_context(self, subdirs=True):
344
- ''' Return a list of PluginPathContext objects to search for plugins in '''
362
+ """ Return a list of PluginPathContext objects to search for plugins in """
345
363
 
346
364
  # FIXME: This is potentially buggy if subdirs is sometimes True and sometimes False.
347
365
  # In current usage, everything calls this with subdirs=True except for module_utils_loader and ansible-doc
@@ -394,13 +412,13 @@ class PluginLoader:
394
412
  return ret
395
413
 
396
414
  def _get_paths(self, subdirs=True):
397
- ''' Return a list of paths to search for plugins in '''
415
+ """ Return a list of paths to search for plugins in """
398
416
 
399
417
  paths_with_context = self._get_paths_with_context(subdirs=subdirs)
400
418
  return [path_with_context.path for path_with_context in paths_with_context]
401
419
 
402
420
  def _load_config_defs(self, name, module, path):
403
- ''' Reads plugin docs to find configuration setting definitions, to push to config manager for later use '''
421
+ """ Reads plugin docs to find configuration setting definitions, to push to config manager for later use """
404
422
 
405
423
  # plugins w/o class name don't support config
406
424
  if self.class_name:
@@ -408,12 +426,15 @@ class PluginLoader:
408
426
 
409
427
  # if type name != 'module_doc_fragment':
410
428
  if type_name in C.CONFIGURABLE_PLUGINS and not C.config.has_configuration_definition(type_name, name):
411
- dstring = AnsibleLoader(getattr(module, 'DOCUMENTATION', ''), file_name=path).get_single_data()
429
+ documentation_source = getattr(module, 'DOCUMENTATION', '')
430
+ try:
431
+ dstring = yaml.load(_tags.Origin(path=path).tag(documentation_source), Loader=AnsibleLoader)
432
+ except ParserError as e:
433
+ raise AnsibleError(f"plugin {name} has malformed documentation!") from e
412
434
 
413
435
  # TODO: allow configurable plugins to use sidecar
414
436
  # if not dstring:
415
437
  # filename, cn = find_plugin_docfile( name, type_name, self, [os.path.dirname(path)], C.YAML_DOC_EXTENSIONS)
416
- # # TODO: dstring = AnsibleLoader(, file_name=path).get_single_data()
417
438
 
418
439
  if dstring:
419
440
  add_fragments(dstring, path, fragment_loader=fragment_loader, is_module=(type_name == 'module'))
@@ -423,7 +444,7 @@ class PluginLoader:
423
444
  display.debug('Loaded config def from plugin (%s/%s)' % (type_name, name))
424
445
 
425
446
  def add_directory(self, directory, with_subdir=False):
426
- ''' Adds an additional directory to the search path '''
447
+ """ Adds an additional directory to the search path """
427
448
 
428
449
  directory = os.path.realpath(directory)
429
450
 
@@ -497,10 +518,19 @@ class PluginLoader:
497
518
  removal_date = tombstone.get('removal_date')
498
519
  removal_version = tombstone.get('removal_version')
499
520
  warning_text = tombstone.get('warning_text') or ''
500
- warning_text = '{0} has been removed.{1}{2}'.format(fq_name, ' ' if warning_text else '', warning_text)
501
- removed_msg = display.get_deprecation_message(msg=warning_text, version=removal_version,
502
- date=removal_date, removed=True,
503
- collection_name=acr.collection)
521
+ warning_plugin_type = "module" if self.type == "modules" else f'{self.type} plugin'
522
+ warning_text = f'The {fq_name!r} {warning_plugin_type} has been removed.{" " if warning_text else ""}{warning_text}'
523
+ removed_msg = display._get_deprecation_message_with_plugin_info(
524
+ msg=warning_text,
525
+ version=removal_version,
526
+ date=removal_date,
527
+ removed=True,
528
+ plugin=PluginInfo(
529
+ requested_name=acr.collection,
530
+ resolved_name=acr.collection,
531
+ type='collection',
532
+ ),
533
+ )
504
534
  plugin_load_context.removal_date = removal_date
505
535
  plugin_load_context.removal_version = removal_version
506
536
  plugin_load_context.resolved = True
@@ -577,15 +607,22 @@ class PluginLoader:
577
607
  'found fuzzy extension match for {0} in {1}'.format(full_name, acr.collection), action_plugin)
578
608
 
579
609
  def find_plugin(self, name, mod_type='', ignore_deprecated=False, check_aliases=False, collection_list=None):
580
- ''' Find a plugin named name '''
610
+ """ Find a plugin named name """
581
611
  result = self.find_plugin_with_context(name, mod_type, ignore_deprecated, check_aliases, collection_list)
582
612
  if result.resolved and result.plugin_resolved_path:
583
613
  return result.plugin_resolved_path
584
614
 
585
615
  return None
586
616
 
587
- def find_plugin_with_context(self, name, mod_type='', ignore_deprecated=False, check_aliases=False, collection_list=None):
588
- ''' Find a plugin named name, returning contextual info about the load, recursively resolving redirection '''
617
+ def find_plugin_with_context(
618
+ self,
619
+ name: str,
620
+ mod_type: str = '',
621
+ ignore_deprecated: bool = False,
622
+ check_aliases: bool = False,
623
+ collection_list: list[str] | None = None,
624
+ ) -> PluginLoadContext:
625
+ """ Find a plugin named name, returning contextual info about the load, recursively resolving redirection """
589
626
  plugin_load_context = PluginLoadContext()
590
627
  plugin_load_context.original_name = name
591
628
  while True:
@@ -795,7 +832,7 @@ class PluginLoader:
795
832
  return plugin_load_context.nope('{0} is not eligible for last-chance resolution'.format(name))
796
833
 
797
834
  def has_plugin(self, name, collection_list=None):
798
- ''' Checks if a plugin named name exists '''
835
+ """ Checks if a plugin named name exists """
799
836
 
800
837
  try:
801
838
  return self.find_plugin(name, collection_list=collection_list) is not None
@@ -838,12 +875,20 @@ class PluginLoader:
838
875
 
839
876
  return module
840
877
 
841
- def _update_object(self, obj, name, path, redirected_names=None, resolved=None):
878
+ def _update_object(
879
+ self,
880
+ obj: _AnsiblePluginInfoMixin,
881
+ name: str,
882
+ path: str,
883
+ redirected_names: list[str] | None = None,
884
+ resolved: str | None = None,
885
+ ) -> None:
886
+ # DTFIX-RELEASE: clean this up- standardize types, document, split/remove redundant bits
842
887
 
843
888
  # set extra info on the module, in case we want it later
844
- setattr(obj, '_original_path', path)
845
- setattr(obj, '_load_name', name)
846
- setattr(obj, '_redirected_names', redirected_names or [])
889
+ obj._original_path = path
890
+ obj._load_name = name
891
+ obj._redirected_names = redirected_names or []
847
892
 
848
893
  names = []
849
894
  if resolved:
@@ -854,25 +899,23 @@ class PluginLoader:
854
899
  if not names:
855
900
  raise AnsibleError(f"Missing FQCN for plugin source {name}")
856
901
 
857
- setattr(obj, 'ansible_aliases', names)
858
- setattr(obj, 'ansible_name', names[0])
902
+ obj.ansible_aliases = names
903
+ obj.ansible_name = names[0]
859
904
 
860
905
  def get(self, name, *args, **kwargs):
861
- return self.get_with_context(name, *args, **kwargs).object
862
-
863
- def get_with_context(self, name, *args, **kwargs):
864
- ''' instantiates a plugin of the given name using arguments '''
865
- if _is_unsafe(name):
866
- # Objects constructed using the name wrapped as unsafe remain
867
- # (correctly) unsafe. Using such unsafe objects in places
868
- # where underlying types (builtin string in this case) are
869
- # expected can cause problems.
870
- # One such case is importlib.abc.Loader.exec_module failing
871
- # with "ValueError: unmarshallable object" because the module
872
- # object is created with the __path__ attribute being wrapped
873
- # as unsafe which isn't marshallable.
874
- # Manually removing the unsafe wrapper prevents such issues.
875
- name = name._strip_unsafe()
906
+ ctx = self.get_with_context(name, *args, **kwargs)
907
+ is_core_plugin = ctx.plugin_load_context.plugin_resolved_collection == 'ansible.builtin'
908
+ if self.class_name == 'StrategyModule' and not is_core_plugin:
909
+ display.deprecated( # pylint: disable=ansible-deprecated-no-version
910
+ 'Use of strategy plugins not included in ansible.builtin are deprecated and do not carry '
911
+ 'any backwards compatibility guarantees. No alternative for third party strategy plugins '
912
+ 'is currently planned.'
913
+ )
914
+
915
+ return ctx.object
916
+
917
+ def get_with_context(self, name, *args, **kwargs) -> get_with_context_result:
918
+ """ instantiates a plugin of the given name using arguments """
876
919
 
877
920
  found_in_cache = True
878
921
  class_only = kwargs.pop('class_only', False)
@@ -950,7 +993,7 @@ class PluginLoader:
950
993
  return get_with_context_result(obj, plugin_load_context)
951
994
 
952
995
  def _display_plugin_load(self, class_name, name, searched_paths, path, found_in_cache=None, class_only=None):
953
- ''' formats data to display debug info for plugin loading, also avoids processing unless really needed '''
996
+ """ formats data to display debug info for plugin loading, also avoids processing unless really needed """
954
997
  if C.DEFAULT_DEBUG:
955
998
  msg = 'Loading %s \'%s\' from %s' % (class_name, os.path.basename(name), path)
956
999
 
@@ -963,7 +1006,7 @@ class PluginLoader:
963
1006
  display.debug(msg)
964
1007
 
965
1008
  def all(self, *args, **kwargs):
966
- '''
1009
+ """
967
1010
  Iterate through all plugins of this type, in configured paths (no collections)
968
1011
 
969
1012
  A plugin loader is initialized with a specific type. This function is an iterator returning
@@ -984,7 +1027,7 @@ class PluginLoader:
984
1027
  want to manage their own deduplication of the plugins.
985
1028
  :*args: Any extra arguments are passed to each plugin when it is instantiated.
986
1029
  :**kwargs: Any extra keyword arguments are passed to each plugin when it is instantiated.
987
- '''
1030
+ """
988
1031
  # TODO: Change the signature of this method to:
989
1032
  # def all(return_type='instance', args=None, kwargs=None):
990
1033
  # if args is None: args = []
@@ -1117,6 +1160,21 @@ class PluginLoader:
1117
1160
  yield obj
1118
1161
 
1119
1162
 
1163
+ class _CacheLoader(PluginLoader):
1164
+ """Customized loader for cache plugins that wraps the requested plugin with an interposer that schema-qualifies keys and JSON encodes the values."""
1165
+
1166
+ def get(self, name: str, *args, **kwargs) -> BaseCacheModule:
1167
+ plugin = super().get(name, *args, **kwargs)
1168
+
1169
+ if not plugin:
1170
+ raise AnsibleError(f'Unable to load the cache plugin {name!r}.')
1171
+
1172
+ if plugin._persistent:
1173
+ return _cache.PluginInterposer(plugin)
1174
+
1175
+ return plugin
1176
+
1177
+
1120
1178
  class Jinja2Loader(PluginLoader):
1121
1179
  """
1122
1180
  PluginLoader optimized for Jinja2 plugins
@@ -1125,10 +1183,12 @@ class Jinja2Loader(PluginLoader):
1125
1183
  We need to do a few things differently in the base class because of file == plugin
1126
1184
  assumptions and dedupe logic.
1127
1185
  """
1128
- def __init__(self, class_name, package, config, subdir, plugin_wrapper_type, aliases=None, required_base_class=None):
1186
+
1187
+ def __init__(self, class_name, package, config, subdir, plugin_wrapper_type, aliases=None, required_base_class=None) -> None:
1129
1188
  super(Jinja2Loader, self).__init__(class_name, package, config, subdir, aliases=aliases, required_base_class=required_base_class)
1130
1189
  self._plugin_wrapper_type = plugin_wrapper_type
1131
- self._cached_non_collection_wrappers = {}
1190
+ self._plugin_type_friendly_name = 'filter' if plugin_wrapper_type is AnsibleJinja2Filter else 'test'
1191
+ self._cached_non_collection_wrappers: dict[str, AnsibleJinja2Filter | AnsibleJinja2Test | _DeferredPluginLoadFailure] = {}
1132
1192
 
1133
1193
  def _clear_caches(self):
1134
1194
  super(Jinja2Loader, self)._clear_caches()
@@ -1141,6 +1201,36 @@ class Jinja2Loader(PluginLoader):
1141
1201
  def method_map_name(self):
1142
1202
  return get_plugin_class(self.class_name) + 's'
1143
1203
 
1204
+ def _wrap_func(self, name: str, resolved: str, func: t.Callable) -> AnsibleJinja2Test | AnsibleJinja2Filter:
1205
+ """Wrap a Jinja builtin function in a `AnsibleJinja2Plugin` instance."""
1206
+ try:
1207
+ path = sys.modules[func.__module__].__file__
1208
+ except AttributeError:
1209
+ path = None
1210
+
1211
+ wrapper = self._plugin_wrapper_type(func)
1212
+
1213
+ self._update_object(obj=wrapper, name=name, path=path, resolved=resolved)
1214
+
1215
+ return wrapper
1216
+
1217
+ def _wrap_funcs(self, plugins: dict[str, t.Callable], aliases: dict[str, str]) -> dict[str, AnsibleJinja2Test | AnsibleJinja2Filter]:
1218
+ """Map a dictionary of Jinja builtin functions to one containing `AnsibleJinja2Plugin` instances."""
1219
+ wrappers: dict[str, AnsibleJinja2Test | AnsibleJinja2Filter] = {}
1220
+
1221
+ for load_name, func in plugins.items():
1222
+ name = aliases.get(load_name, load_name)
1223
+ resolved = f'ansible.builtin.{name}'
1224
+
1225
+ wrappers[load_name] = self._wrap_func(load_name, resolved, func)
1226
+
1227
+ if resolved not in wrappers:
1228
+ # When the resolved name hasn't been cached, do so.
1229
+ # Functions that have aliases will appear more than once, and we don't need to overwrite them.
1230
+ wrappers[resolved] = self._wrap_func(resolved, resolved, func)
1231
+
1232
+ return wrappers
1233
+
1144
1234
  def get_contained_plugins(self, collection, plugin_path, name):
1145
1235
 
1146
1236
  plugins = []
@@ -1179,11 +1269,13 @@ class Jinja2Loader(PluginLoader):
1179
1269
 
1180
1270
  # FUTURE: now that the resulting plugins are closer, refactor base class method with some extra
1181
1271
  # hooks so we can avoid all the duplicated plugin metadata logic, and also cache the collection results properly here
1182
- def get_with_context(self, name, *args, **kwargs):
1272
+ def get_with_context(self, name: str, *args, **kwargs) -> get_with_context_result:
1183
1273
  # pop N/A kwargs to avoid passthrough to parent methods
1184
1274
  kwargs.pop('class_only', False)
1185
1275
  kwargs.pop('collection_list', None)
1186
1276
 
1277
+ requested_name = name
1278
+
1187
1279
  context = PluginLoadContext()
1188
1280
 
1189
1281
  # avoid collection path for legacy
@@ -1193,6 +1285,9 @@ class Jinja2Loader(PluginLoader):
1193
1285
 
1194
1286
  # check for stuff loaded via legacy/builtin paths first
1195
1287
  if known_plugin := self._cached_non_collection_wrappers.get(name):
1288
+ if isinstance(known_plugin, _DeferredPluginLoadFailure):
1289
+ raise known_plugin.ex
1290
+
1196
1291
  context.resolved = True
1197
1292
  context.plugin_resolved_name = name
1198
1293
  context.plugin_resolved_path = known_plugin._original_path
@@ -1219,7 +1314,7 @@ class Jinja2Loader(PluginLoader):
1219
1314
  ts = _get_collection_metadata(acr.collection)
1220
1315
  except ValueError as e:
1221
1316
  # no collection
1222
- raise KeyError('Invalid plugin FQCN ({0}): {1}'.format(key, to_native(e)))
1317
+ raise KeyError('Invalid plugin FQCN ({0}): {1}'.format(key, to_native(e))) from e
1223
1318
 
1224
1319
  # TODO: implement cycle detection (unified across collection redir as well)
1225
1320
  routing_entry = ts.get('plugin_routing', {}).get(self.type, {}).get(leaf_key, {})
@@ -1241,11 +1336,19 @@ class Jinja2Loader(PluginLoader):
1241
1336
  warning_text = tombstone_entry.get('warning_text') or ''
1242
1337
  removal_date = tombstone_entry.get('removal_date')
1243
1338
  removal_version = tombstone_entry.get('removal_version')
1244
-
1245
- warning_text = f'{self.type.title()} "{key}" has been removed.{" " if warning_text else ""}{warning_text}'
1246
-
1247
- exc_msg = display.get_deprecation_message(warning_text, version=removal_version, date=removal_date,
1248
- collection_name=acr.collection, removed=True)
1339
+ warning_text = f'The {key!r} {self.type} plugin has been removed.{" " if warning_text else ""}{warning_text}'
1340
+
1341
+ exc_msg = display._get_deprecation_message_with_plugin_info(
1342
+ msg=warning_text,
1343
+ version=removal_version,
1344
+ date=removal_date,
1345
+ removed=True,
1346
+ plugin=PluginInfo(
1347
+ requested_name=acr.collection,
1348
+ resolved_name=acr.collection,
1349
+ type='collection',
1350
+ ),
1351
+ )
1249
1352
 
1250
1353
  raise AnsiblePluginRemovedError(exc_msg)
1251
1354
 
@@ -1297,20 +1400,16 @@ class Jinja2Loader(PluginLoader):
1297
1400
  plugin = self._plugin_wrapper_type(func)
1298
1401
  if plugin:
1299
1402
  context = plugin_impl.plugin_load_context
1300
- self._update_object(plugin, src_name, plugin_impl.object._original_path, resolved=fq_name)
1403
+ self._update_object(plugin, requested_name, plugin_impl.object._original_path, resolved=fq_name)
1301
1404
  # context will have filename, which for tests/filters might not be correct
1302
1405
  context._resolved_fqcn = plugin.ansible_name
1303
1406
  # FIXME: once we start caching these results, we'll be missing functions that would have loaded later
1304
1407
  break # go to next file as it can override if dupe (dont break both loops)
1305
1408
 
1306
- except AnsiblePluginRemovedError as apre:
1307
- raise AnsibleError(to_native(apre), 0, orig_exc=apre)
1308
1409
  except (AnsibleError, KeyError):
1309
1410
  raise
1310
1411
  except Exception as ex:
1311
- display.warning('An unexpected error occurred during Jinja2 plugin loading: {0}'.format(to_native(ex)))
1312
- display.vvv('Unexpected error during Jinja2 plugin loading: {0}'.format(format_exc()))
1313
- raise AnsibleError(to_native(ex), 0, orig_exc=ex)
1412
+ raise AnsibleError('An unexpected error occurred during Jinja2 plugin loading.') from ex
1314
1413
 
1315
1414
  return get_with_context_result(plugin, context)
1316
1415
 
@@ -1324,10 +1423,13 @@ class Jinja2Loader(PluginLoader):
1324
1423
  raise AnsibleError('Do not set both path_only and class_only when calling PluginLoader.all()')
1325
1424
 
1326
1425
  self._ensure_non_collection_wrappers(*args, **kwargs)
1426
+
1427
+ plugins = [plugin for plugin in self._cached_non_collection_wrappers.values() if not isinstance(plugin, _DeferredPluginLoadFailure)]
1428
+
1327
1429
  if path_only:
1328
- yield from (w._original_path for w in self._cached_non_collection_wrappers.values())
1430
+ yield from (w._original_path for w in plugins)
1329
1431
  else:
1330
- yield from (w for w in self._cached_non_collection_wrappers.values())
1432
+ yield from (w for w in plugins)
1331
1433
 
1332
1434
  def _ensure_non_collection_wrappers(self, *args, **kwargs):
1333
1435
  if self._cached_non_collection_wrappers:
@@ -1354,21 +1456,26 @@ class Jinja2Loader(PluginLoader):
1354
1456
  display.debug("%s skipped due to a defined plugin filter" % plugin_name)
1355
1457
  continue
1356
1458
 
1357
- # the plugin class returned by the loader may host multiple Jinja plugins, but we wrap each plugin in
1358
- # its own surrogate wrapper instance here to ease the bookkeeping...
1359
- wrapper = self._plugin_wrapper_type(plugins[plugin_name])
1360
1459
  fqcn = plugin_name
1361
1460
  collection = '.'.join(p_map.ansible_name.split('.')[:2]) if p_map.ansible_name.count('.') >= 2 else ''
1362
1461
  if not plugin_name.startswith(collection):
1363
1462
  fqcn = f"{collection}.{plugin_name}"
1364
1463
 
1365
- self._update_object(wrapper, plugin_name, p_map._original_path, resolved=fqcn)
1366
-
1367
1464
  target_names = {plugin_name, fqcn}
1465
+
1368
1466
  if is_builtin:
1369
1467
  target_names.add(f'ansible.builtin.{plugin_name}')
1370
1468
 
1371
1469
  for target_name in target_names:
1470
+ # the plugin class returned by the loader may host multiple Jinja plugins, but we wrap each plugin in
1471
+ # its own surrogate wrapper instance here to ease the bookkeeping...
1472
+ try:
1473
+ wrapper = self._plugin_wrapper_type(plugins[plugin_name])
1474
+ except Exception as ex:
1475
+ wrapper = _DeferredPluginLoadFailure(ex)
1476
+
1477
+ self._update_object(obj=wrapper, name=target_name, path=p_map._original_path, resolved=fqcn)
1478
+
1372
1479
  if existing_plugin := self._cached_non_collection_wrappers.get(target_name):
1373
1480
  display.debug(f'Jinja plugin {target_name} from {p_map._original_path} skipped; '
1374
1481
  f'shadowed by plugin from {existing_plugin._original_path})')
@@ -1377,6 +1484,13 @@ class Jinja2Loader(PluginLoader):
1377
1484
  self._cached_non_collection_wrappers[target_name] = wrapper
1378
1485
 
1379
1486
 
1487
+ class _DeferredPluginLoadFailure:
1488
+ """Represents a plugin which failed to load. For internal use only within plugin loader."""
1489
+
1490
+ def __init__(self, ex: Exception) -> None:
1491
+ self.ex = ex
1492
+
1493
+
1380
1494
  def get_fqcr_and_name(resource, collection='ansible.builtin'):
1381
1495
  if '.' not in resource:
1382
1496
  name = resource
@@ -1400,7 +1514,7 @@ def _load_plugin_filter():
1400
1514
  if os.path.exists(filter_cfg):
1401
1515
  with open(filter_cfg, 'rb') as f:
1402
1516
  try:
1403
- filter_data = from_yaml(f.read())
1517
+ filter_data = yaml.load(f, Loader=AnsibleInstrumentedLoader)
1404
1518
  except Exception as e:
1405
1519
  display.warning(u'The plugin filter file, {0} was not parsable.'
1406
1520
  u' Skipping: {1}'.format(filter_cfg, to_text(e)))
@@ -1489,7 +1603,8 @@ def _configure_collection_loader(prefix_collections_path=None):
1489
1603
  if prefix_collections_path is None:
1490
1604
  prefix_collections_path = []
1491
1605
 
1492
- paths = list(prefix_collections_path) + C.COLLECTIONS_PATHS
1606
+ # insert the internal ansible._protomatter collection up front
1607
+ paths = [os.path.dirname(_internal.__file__)] + list(prefix_collections_path) + C.COLLECTIONS_PATHS
1493
1608
  finder = _AnsibleCollectionFinder(paths, C.COLLECTIONS_SCAN_SYS_PATH)
1494
1609
  finder._install()
1495
1610
 
@@ -1529,7 +1644,7 @@ action_loader = PluginLoader(
1529
1644
  required_base_class='ActionBase',
1530
1645
  )
1531
1646
 
1532
- cache_loader = PluginLoader(
1647
+ cache_loader = _CacheLoader(
1533
1648
  'CacheModule',
1534
1649
  'ansible.plugins.cache',
1535
1650
  C.DEFAULT_CACHE_PLUGIN_PATH,
@@ -23,14 +23,31 @@ from ansible.errors import AnsibleFileNotFound
23
23
  from ansible.plugins import AnsiblePlugin
24
24
  from ansible.utils.display import Display
25
25
 
26
+ import typing as t
27
+
28
+ if t.TYPE_CHECKING:
29
+ from ansible.parsing import dataloader as _dataloader
30
+ from ansible import template as _template
31
+
26
32
  display = Display()
27
33
 
28
34
  __all__ = ['LookupBase']
29
35
 
30
36
 
31
37
  class LookupBase(AnsiblePlugin):
38
+ accept_args_markers: t.ClassVar[bool] = False
39
+ """
40
+ When `False`, plugin invocation is skipped when a top-level argument is a `Marker`, with the first such value substituted as the plugin result.
41
+ This ensures that only plugins which understand `Marker` instances for top-level arguments will encounter them.
42
+ """
43
+
44
+ accept_lazy_markers: t.ClassVar[bool] = False
45
+ """
46
+ When `False`, plugins will trigger a `MarkerError` exception when attempting to retrieve a `Marker` from a lazy container.
47
+ This ensures that only plugins which understand lazy retrieval of `Marker` instances will encounter them.
48
+ """
32
49
 
33
- def __init__(self, loader=None, templar=None, **kwargs):
50
+ def __init__(self, loader: _dataloader.DataLoader | None = None, templar: _template.Templar | None = None, **kwargs) -> None:
34
51
 
35
52
  super(LookupBase, self).__init__()
36
53
 
@@ -72,7 +89,7 @@ class LookupBase(AnsiblePlugin):
72
89
  return ret
73
90
 
74
91
  @abstractmethod
75
- def run(self, terms, variables=None, **kwargs):
92
+ def run(self, terms, variables, **kwargs):
76
93
  """
77
94
  When the playbook specifies a lookup, this method is run. The
78
95
  arguments to the lookup become the arguments to this method. One
@@ -104,9 +121,9 @@ class LookupBase(AnsiblePlugin):
104
121
  pass
105
122
 
106
123
  def find_file_in_search_path(self, myvars, subdir, needle, ignore_missing=False):
107
- '''
124
+ """
108
125
  Return a file (needle) in the task's expected search path.
109
- '''
126
+ """
110
127
 
111
128
  if 'ansible_search_path' in myvars:
112
129
  paths = myvars['ansible_search_path']