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/inventory/host.py CHANGED
@@ -17,28 +17,26 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
+ import collections.abc as c
21
+ import typing as t
22
+
20
23
  from collections.abc import Mapping, MutableMapping
21
24
 
22
25
  from ansible.inventory.group import Group, InventoryObjectType
23
26
  from ansible.parsing.utils.addresses import patterns
24
- from ansible.utils.vars import combine_vars, get_unique_id
27
+ from ansible.utils.vars import combine_vars, get_unique_id, validate_variable_name
25
28
 
29
+ from . import helpers # this is left as a module import to facilitate easier unit test patching
26
30
 
27
31
  __all__ = ['Host']
28
32
 
29
33
 
30
34
  class Host:
31
- ''' a single ansible host '''
35
+ """A single ansible host."""
32
36
  base_type = InventoryObjectType.HOST
33
37
 
34
38
  # __slots__ = [ 'name', 'vars', 'groups' ]
35
39
 
36
- def __getstate__(self):
37
- return self.serialize()
38
-
39
- def __setstate__(self, data):
40
- return self.deserialize(data)
41
-
42
40
  def __eq__(self, other):
43
41
  if not isinstance(other, Host):
44
42
  return False
@@ -56,55 +54,28 @@ class Host:
56
54
  def __repr__(self):
57
55
  return self.get_name()
58
56
 
59
- def serialize(self):
60
- groups = []
61
- for group in self.groups:
62
- groups.append(group.serialize())
63
-
64
- return dict(
65
- name=self.name,
66
- vars=self.vars.copy(),
67
- address=self.address,
68
- uuid=self._uuid,
69
- groups=groups,
70
- implicit=self.implicit,
71
- )
72
-
73
- def deserialize(self, data):
74
- self.__init__(gen_uuid=False) # used by __setstate__ to deserialize in place # pylint: disable=unnecessary-dunder-call
75
-
76
- self.name = data.get('name')
77
- self.vars = data.get('vars', dict())
78
- self.address = data.get('address', '')
79
- self._uuid = data.get('uuid', None)
80
- self.implicit = data.get('implicit', False)
81
-
82
- groups = data.get('groups', [])
83
- for group_data in groups:
84
- g = Group()
85
- g.deserialize(group_data)
86
- self.groups.append(g)
57
+ def __init__(self, name: str, port: int | str | None = None, gen_uuid: bool = True) -> None:
58
+ name = helpers.remove_trust(name)
87
59
 
88
- def __init__(self, name=None, port=None, gen_uuid=True):
60
+ self.vars: dict[str, t.Any] = {}
61
+ self.groups: list[Group] = []
62
+ self._uuid: str | None = None
89
63
 
90
- self.vars = {}
91
- self.groups = []
92
- self._uuid = None
93
-
94
- self.name = name
95
- self.address = name
64
+ self.name: str = name
65
+ self.address: str = name
96
66
 
97
67
  if port:
98
68
  self.set_variable('ansible_port', int(port))
99
69
 
100
70
  if gen_uuid:
101
71
  self._uuid = get_unique_id()
102
- self.implicit = False
103
72
 
104
- def get_name(self):
73
+ self.implicit: bool = False
74
+
75
+ def get_name(self) -> str:
105
76
  return self.name
106
77
 
107
- def populate_ancestors(self, additions=None):
78
+ def populate_ancestors(self, additions: c.Iterable[Group] | None = None) -> None:
108
79
  # populate ancestors
109
80
  if additions is None:
110
81
  for group in self.groups:
@@ -114,7 +85,7 @@ class Host:
114
85
  if group not in self.groups:
115
86
  self.groups.append(group)
116
87
 
117
- def add_group(self, group):
88
+ def add_group(self, group: Group) -> bool:
118
89
  added = False
119
90
  # populate ancestors first
120
91
  for oldg in group.get_ancestors():
@@ -127,7 +98,7 @@ class Host:
127
98
  added = True
128
99
  return added
129
100
 
130
- def remove_group(self, group):
101
+ def remove_group(self, group: Group) -> bool:
131
102
  removed = False
132
103
  if group in self.groups:
133
104
  self.groups.remove(group)
@@ -143,18 +114,25 @@ class Host:
143
114
  self.remove_group(oldg)
144
115
  return removed
145
116
 
146
- def set_variable(self, key, value):
117
+ def set_variable(self, key: str, value: t.Any) -> None:
118
+ key = helpers.remove_trust(key)
119
+
120
+ validate_variable_name(key)
121
+
147
122
  if key in self.vars and isinstance(self.vars[key], MutableMapping) and isinstance(value, Mapping):
148
123
  self.vars = combine_vars(self.vars, {key: value})
149
124
  else:
150
125
  self.vars[key] = value
151
126
 
152
- def get_groups(self):
127
+ def get_groups(self) -> list[Group]:
153
128
  return self.groups
154
129
 
155
- def get_magic_vars(self):
156
- results = {}
157
- results['inventory_hostname'] = self.name
130
+ def get_magic_vars(self) -> dict[str, t.Any]:
131
+ results: dict[str, t.Any] = dict(
132
+ inventory_hostname=self.name,
133
+ )
134
+
135
+ # FUTURE: these values should be dynamically calculated on access ala the rest of magic vars
158
136
  if patterns['ipv4'].match(self.name) or patterns['ipv6'].match(self.name):
159
137
  results['inventory_hostname_short'] = self.name
160
138
  else:
@@ -164,5 +142,5 @@ class Host:
164
142
 
165
143
  return results
166
144
 
167
- def get_vars(self):
145
+ def get_vars(self) -> dict[str, t.Any]:
168
146
  return combine_vars(self.vars, self.get_magic_vars())
@@ -19,28 +19,34 @@
19
19
  from __future__ import annotations
20
20
 
21
21
  import fnmatch
22
+ import functools
22
23
  import os
23
- import sys
24
24
  import re
25
25
  import itertools
26
- import traceback
26
+ import typing as t
27
27
 
28
28
  from operator import attrgetter
29
29
  from random import shuffle
30
30
 
31
31
  from ansible import constants as C
32
- from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
32
+ from ansible._internal import _json, _wrapt
33
+ from ansible._internal._json import EncryptedStringBehavior
34
+ from ansible.errors import AnsibleError, AnsibleOptionsError
33
35
  from ansible.inventory.data import InventoryData
34
36
  from ansible.module_utils.six import string_types
35
37
  from ansible.module_utils.common.text.converters import to_bytes, to_text
36
38
  from ansible.parsing.utils.addresses import parse_address
37
39
  from ansible.plugins.loader import inventory_loader
40
+ from ansible._internal._datatag._tags import Origin
38
41
  from ansible.utils.helpers import deduplicate_list
39
42
  from ansible.utils.path import unfrackpath
40
43
  from ansible.utils.display import Display
41
44
  from ansible.utils.vars import combine_vars
42
45
  from ansible.vars.plugins import get_vars_from_inventory_sources
43
46
 
47
+ if t.TYPE_CHECKING:
48
+ from ansible.plugins.inventory import BaseInventoryPlugin
49
+
44
50
  display = Display()
45
51
 
46
52
  IGNORED_ALWAYS = [br"^\.", b"^host_vars$", b"^group_vars$", b"^vars_plugins$"]
@@ -50,7 +56,7 @@ IGNORED_EXTS = [b'%s$' % to_bytes(re.escape(x)) for x in C.INVENTORY_IGNORE_EXTS
50
56
  IGNORED = re.compile(b'|'.join(IGNORED_ALWAYS + IGNORED_PATTERNS + IGNORED_EXTS))
51
57
 
52
58
  PATTERN_WITH_SUBSCRIPT = re.compile(
53
- r'''^
59
+ r"""^
54
60
  (.+) # A pattern expression ending with...
55
61
  \[(?: # A [subscript] expression comprising:
56
62
  (-?[0-9]+)| # A single positive or negative number
@@ -58,12 +64,12 @@ PATTERN_WITH_SUBSCRIPT = re.compile(
58
64
  ([0-9]*)
59
65
  )\]
60
66
  $
61
- ''', re.X
67
+ """, re.X
62
68
  )
63
69
 
64
70
 
65
71
  def order_patterns(patterns):
66
- ''' takes a list of patterns and reorders them by modifier to apply them consistently '''
72
+ """ takes a list of patterns and reorders them by modifier to apply them consistently """
67
73
 
68
74
  # FIXME: this goes away if we apply patterns incrementally or by groups
69
75
  pattern_regular = []
@@ -125,19 +131,19 @@ def split_host_pattern(pattern):
125
131
  # This mishandles IPv6 addresses, and is retained only for backwards
126
132
  # compatibility.
127
133
  patterns = re.findall(
128
- to_text(r'''(?: # We want to match something comprising:
134
+ to_text(r"""(?: # We want to match something comprising:
129
135
  [^\s:\[\]] # (anything other than whitespace or ':[]'
130
136
  | # ...or...
131
137
  \[[^\]]*\] # a single complete bracketed expression)
132
138
  )+ # occurring once or more
133
- '''), pattern, re.X
139
+ """), pattern, re.X
134
140
  )
135
141
 
136
142
  return [p.strip() for p in patterns if p.strip()]
137
143
 
138
144
 
139
145
  class InventoryManager(object):
140
- ''' Creates and manages inventory '''
146
+ """ Creates and manages inventory """
141
147
 
142
148
  def __init__(self, loader, sources=None, parse=True, cache=True):
143
149
 
@@ -196,12 +202,12 @@ class InventoryManager(object):
196
202
  def get_host(self, hostname):
197
203
  return self._inventory.get_host(hostname)
198
204
 
199
- def _fetch_inventory_plugins(self):
200
- ''' sets up loaded inventory plugins for usage '''
205
+ def _fetch_inventory_plugins(self) -> list[BaseInventoryPlugin]:
206
+ """ sets up loaded inventory plugins for usage """
201
207
 
202
208
  display.vvvv('setting up inventory plugins')
203
209
 
204
- plugins = []
210
+ plugins: list[BaseInventoryPlugin] = []
205
211
  for name in C.INVENTORY_ENABLED:
206
212
  plugin = inventory_loader.get(name)
207
213
  if plugin:
@@ -215,7 +221,7 @@ class InventoryManager(object):
215
221
  return plugins
216
222
 
217
223
  def parse_sources(self, cache=False):
218
- ''' iterate over inventory sources and parse each one to populate it'''
224
+ """ iterate over inventory sources and parse each one to populate it"""
219
225
 
220
226
  parsed = False
221
227
  # allow for multiple inventory parsing
@@ -243,7 +249,7 @@ class InventoryManager(object):
243
249
  host.vars = combine_vars(host.vars, get_vars_from_inventory_sources(self._loader, self._sources, [host], 'inventory'))
244
250
 
245
251
  def parse_source(self, source, cache=False):
246
- ''' Generate or update inventory for the source provided '''
252
+ """ Generate or update inventory for the source provided """
247
253
 
248
254
  parsed = False
249
255
  failures = []
@@ -276,7 +282,6 @@ class InventoryManager(object):
276
282
 
277
283
  # try source with each plugin
278
284
  for plugin in self._fetch_inventory_plugins():
279
-
280
285
  plugin_name = to_text(getattr(plugin, '_load_name', getattr(plugin, '_original_path', '')))
281
286
  display.debug(u'Attempting to use plugin %s (%s)' % (plugin_name, plugin._original_path))
282
287
 
@@ -287,9 +292,14 @@ class InventoryManager(object):
287
292
  plugin_wants = False
288
293
 
289
294
  if plugin_wants:
295
+ # have this tag ready to apply to errors or output; str-ify source since it is often tagged by the CLI
296
+ origin = Origin(description=f'<inventory plugin {plugin_name!r} with source {str(source)!r}>')
290
297
  try:
291
- # FIXME in case plugin fails 1/2 way we have partial inventory
292
- plugin.parse(self._inventory, self._loader, source, cache=cache)
298
+ inventory_wrapper = _InventoryDataWrapper(self._inventory, target_plugin=plugin, origin=origin)
299
+
300
+ # FUTURE: now that we have a wrapper around inventory, we can have it use ChainMaps to preview the in-progress inventory,
301
+ # but be able to roll back partial inventory failures by discarding the outermost layer
302
+ plugin.parse(inventory_wrapper, self._loader, source, cache=cache)
293
303
  try:
294
304
  plugin.update_cache_if_changed()
295
305
  except AttributeError:
@@ -298,14 +308,16 @@ class InventoryManager(object):
298
308
  parsed = True
299
309
  display.vvv('Parsed %s inventory source with %s plugin' % (source, plugin_name))
300
310
  break
301
- except AnsibleParserError as e:
302
- display.debug('%s was not parsable by %s' % (source, plugin_name))
303
- tb = ''.join(traceback.format_tb(sys.exc_info()[2]))
304
- failures.append({'src': source, 'plugin': plugin_name, 'exc': e, 'tb': tb})
305
- except Exception as e:
306
- display.debug('%s failed while attempting to parse %s' % (plugin_name, source))
307
- tb = ''.join(traceback.format_tb(sys.exc_info()[2]))
308
- failures.append({'src': source, 'plugin': plugin_name, 'exc': AnsibleError(e), 'tb': tb})
311
+ except AnsibleError as ex:
312
+ if not ex.obj:
313
+ ex.obj = origin
314
+ failures.append({'src': source, 'plugin': plugin_name, 'exc': ex})
315
+ except Exception as ex:
316
+ try:
317
+ # omit line number to prevent contextual display of script or possibly sensitive info
318
+ raise AnsibleError(str(ex), obj=origin) from ex
319
+ except AnsibleError as ex:
320
+ failures.append({'src': source, 'plugin': plugin_name, 'exc': ex})
309
321
  else:
310
322
  display.vvv("%s declined parsing %s as it did not pass its verify_file() method" % (plugin_name, source))
311
323
 
@@ -319,9 +331,8 @@ class InventoryManager(object):
319
331
  if failures:
320
332
  # only if no plugin processed files should we show errors.
321
333
  for fail in failures:
322
- display.warning(u'\n* Failed to parse %s with %s plugin: %s' % (to_text(fail['src']), fail['plugin'], to_text(fail['exc'])))
323
- if 'tb' in fail:
324
- display.vvv(to_text(fail['tb']))
334
+ # `obj` should always be set
335
+ display.error_as_warning(msg=f'Failed to parse inventory with {fail["plugin"]!r} plugin.', exception=fail['exc'])
325
336
 
326
337
  # final error/warning on inventory source failure
327
338
  if C.INVENTORY_ANY_UNPARSED_IS_FAILED:
@@ -335,12 +346,12 @@ class InventoryManager(object):
335
346
  return parsed
336
347
 
337
348
  def clear_caches(self):
338
- ''' clear all caches '''
349
+ """ clear all caches """
339
350
  self._hosts_patterns_cache = {}
340
351
  self._pattern_cache = {}
341
352
 
342
353
  def refresh_inventory(self):
343
- ''' recalculate inventory '''
354
+ """ recalculate inventory """
344
355
 
345
356
  self.clear_caches()
346
357
  self._inventory = InventoryData()
@@ -657,9 +668,9 @@ class InventoryManager(object):
657
668
  self._pattern_cache = {}
658
669
 
659
670
  def add_dynamic_host(self, host_info, result_item):
660
- '''
671
+ """
661
672
  Helper function to add a new host to inventory based on a task result.
662
- '''
673
+ """
663
674
 
664
675
  changed = False
665
676
  if not result_item.get('refresh'):
@@ -697,10 +708,10 @@ class InventoryManager(object):
697
708
  result_item['changed'] = changed
698
709
 
699
710
  def add_dynamic_group(self, host, result_item):
700
- '''
711
+ """
701
712
  Helper function to add a group (if it does not exist), and to assign the
702
713
  specified host to that group.
703
- '''
714
+ """
704
715
 
705
716
  changed = False
706
717
 
@@ -749,3 +760,36 @@ class InventoryManager(object):
749
760
  self.reconcile_inventory()
750
761
 
751
762
  result_item['changed'] = changed
763
+
764
+
765
+ class _InventoryDataWrapper(_wrapt.ObjectProxy):
766
+ """
767
+ Proxy wrapper around InventoryData.
768
+ Allows `set_variable` calls to automatically apply template trust for plugins that don't know how.
769
+ """
770
+
771
+ # declared as class attrs to signal to ObjectProxy that we want them stored on the proxy, not the wrapped value
772
+ _target_plugin = None
773
+ _default_origin = None
774
+
775
+ def __init__(self, referent: InventoryData, target_plugin: BaseInventoryPlugin, origin: Origin) -> None:
776
+ super().__init__(referent)
777
+ self._target_plugin = target_plugin
778
+ # fallback origin to ensure that vars are tagged with at least the file they came from
779
+ self._default_origin = origin
780
+
781
+ @functools.cached_property
782
+ def _inspector(self) -> _json.AnsibleVariableVisitor:
783
+ """
784
+ Inventory plugins can delegate to other plugins (e.g. `auto`).
785
+ This hack defers sampling the target plugin's `trusted_by_default` attr until `set_variable` is called, typically inside `parse`.
786
+ Trust is then optionally applied based on the plugin's declared intent via `trusted_by_default`.
787
+ """
788
+ return _json.AnsibleVariableVisitor(
789
+ trusted_as_template=self._target_plugin.trusted_by_default,
790
+ origin=self._default_origin,
791
+ encrypted_string_behavior=EncryptedStringBehavior.PRESERVE,
792
+ )
793
+
794
+ def set_variable(self, entity: str, varname: str, value: t.Any) -> None:
795
+ self.__wrapped__.set_variable(entity, varname, self._inspector.visit(value))
ansible/keyword_desc.yml CHANGED
@@ -13,7 +13,7 @@ become_method: Which method of privilege escalation to use (such as sudo or su).
13
13
  become_user: "User that you 'become' after using privilege escalation. The remote/login user must have permissions to become this user."
14
14
  block: List of tasks in a block.
15
15
  changed_when: "Conditional expression that overrides the task's normal 'changed' status."
16
- check_mode: A boolean that controls if a task is executed in 'check' mode. See :ref:`check_mode_dry`.
16
+ check_mode: A boolean that controls if a task is run normally or avoids changes to the target and tries to report what it would have done (check mode/dry run). See :ref:`check_mode_dry`.
17
17
  collections: |
18
18
  List of collection namespaces to search for modules, plugins, and roles. See :ref:`collections_using_playbook`
19
19
 
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ import collections.abc as c
4
+
5
+ import typing as t
6
+
7
+
8
+ # DTFIX-RELEASE: bikeshed "intermediate"
9
+ INTERMEDIATE_MAPPING_TYPES = (c.Mapping,)
10
+ """
11
+ Mapping types which are supported for recursion and runtime usage, such as in serialization and templating.
12
+ These will be converted to a simple Python `dict` before serialization or storage as a variable.
13
+ """
14
+
15
+ INTERMEDIATE_ITERABLE_TYPES = (tuple, set, frozenset, c.Sequence)
16
+ """
17
+ Iterable types which are supported for recursion and runtime usage, such as in serialization and templating.
18
+ These will be converted to a simple Python `list` before serialization or storage as a variable.
19
+ CAUTION: Scalar types which are sequences should be excluded when using this.
20
+ """
21
+
22
+ ITERABLE_SCALARS_NOT_TO_ITERATE_FIXME = (str, bytes)
23
+ """Scalars which are also iterable, and should thus be excluded from iterable checks."""
24
+
25
+
26
+ def is_intermediate_mapping(value: object) -> bool:
27
+ """Returns `True` if `value` is a type supported for projection to a Python `dict`, otherwise returns `False`."""
28
+ # DTFIX-RELEASE: bikeshed name
29
+ return isinstance(value, INTERMEDIATE_MAPPING_TYPES)
30
+
31
+
32
+ def is_intermediate_iterable(value: object) -> bool:
33
+ """Returns `True` if `value` is a type supported for projection to a Python `list`, otherwise returns `False`."""
34
+ # DTFIX-RELEASE: bikeshed name
35
+ return isinstance(value, INTERMEDIATE_ITERABLE_TYPES) and not isinstance(value, ITERABLE_SCALARS_NOT_TO_ITERATE_FIXME)
36
+
37
+
38
+ is_controller: bool = False
39
+ """Set to True automatically when this module is imported into an Ansible controller context."""
40
+
41
+
42
+ def get_controller_serialize_map() -> dict[type, t.Callable]:
43
+ """
44
+ Called to augment serialization maps.
45
+ This implementation is replaced with the one from ansible._internal in controller contexts.
46
+ """
47
+ return {}
48
+
49
+
50
+ def import_controller_module(_module_name: str, /) -> t.Any:
51
+ """
52
+ Called to conditionally import the named module in a controller context, otherwise returns `None`.
53
+ This implementation is replaced with the one from ansible._internal in controller contexts.
54
+ """
55
+ return None
@@ -0,0 +1,58 @@
1
+ # Copyright (c) 2024 Ansible Project
2
+ # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
3
+
4
+ from __future__ import annotations
5
+
6
+ import contextlib
7
+ import contextvars
8
+
9
+ # deprecated: description='typing.Self exists in Python 3.11+' python_version='3.10'
10
+ from ..compat import typing as t
11
+
12
+
13
+ class AmbientContextBase:
14
+ """
15
+ An abstract base context manager that, once entered, will be accessible via its `current` classmethod to any code in the same
16
+ `contextvars` context (e.g. same thread/coroutine), until it is exited.
17
+ """
18
+
19
+ __slots__ = ('_contextvar_token',)
20
+
21
+ # DTFIX-FUTURE: subclasses need to be able to opt-in to blocking nested contexts of the same type (basically optional per-callstack singleton behavior)
22
+ # DTFIX-RELEASE: this class should enforce strict nesting of contexts; overlapping context lifetimes leads to incredibly difficult to
23
+ # debug situations with undefined behavior, so it should fail fast.
24
+ # DTFIX-RELEASE: make frozen=True dataclass subclasses work (fix the mutability of the contextvar instance)
25
+
26
+ _contextvar: t.ClassVar[contextvars.ContextVar] # pylint: disable=declare-non-slot # pylint bug, see https://github.com/pylint-dev/pylint/issues/9950
27
+ _contextvar_token: contextvars.Token
28
+
29
+ def __init_subclass__(cls, **kwargs) -> None:
30
+ cls._contextvar = contextvars.ContextVar(cls.__name__)
31
+
32
+ @classmethod
33
+ def when(cls, condition: bool, /, *args, **kwargs) -> t.Self | contextlib.nullcontext:
34
+ """Return an instance of the context if `condition` is `True`, otherwise return a `nullcontext` instance."""
35
+ return cls(*args, **kwargs) if condition else contextlib.nullcontext()
36
+
37
+ @classmethod
38
+ def current(cls, optional: bool = False) -> t.Self | None:
39
+ """
40
+ Return the currently active context value for the current thread or coroutine.
41
+ Raises ReferenceError if a context is not active, unless `optional` is `True`.
42
+ """
43
+ try:
44
+ return cls._contextvar.get()
45
+ except LookupError:
46
+ if optional:
47
+ return None
48
+
49
+ raise ReferenceError(f"A required {cls.__name__} context is not active.") from None
50
+
51
+ def __enter__(self) -> t.Self:
52
+ # DTFIX-RELEASE: actively block multiple entry
53
+ self._contextvar_token = self.__class__._contextvar.set(self)
54
+ return self
55
+
56
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
57
+ self.__class__._contextvar.reset(self._contextvar_token)
58
+ del self._contextvar_token
@@ -0,0 +1,133 @@
1
+ # Copyright (c) 2024 Ansible Project
2
+ # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
3
+
4
+ """Support code for exclusive use by the AnsiballZ wrapper."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import atexit
9
+ import dataclasses
10
+ import importlib.util
11
+ import json
12
+ import os
13
+ import runpy
14
+ import sys
15
+ import typing as t
16
+
17
+ from . import _errors
18
+ from ._plugin_exec_context import PluginExecContext, HasPluginInfo
19
+ from .. import basic
20
+ from ..common.json import get_module_encoder, Direction
21
+ from ..common.messages import PluginInfo
22
+
23
+
24
+ def run_module(
25
+ *,
26
+ json_params: bytes,
27
+ profile: str,
28
+ plugin_info_dict: dict[str, object],
29
+ module_fqn: str,
30
+ modlib_path: str,
31
+ init_globals: dict[str, t.Any] | None = None,
32
+ coverage_config: str | None = None,
33
+ coverage_output: str | None = None,
34
+ ) -> None: # pragma: nocover
35
+ """Used internally by the AnsiballZ wrapper to run an Ansible module."""
36
+ try:
37
+ _enable_coverage(coverage_config, coverage_output)
38
+ _run_module(
39
+ json_params=json_params,
40
+ profile=profile,
41
+ plugin_info_dict=plugin_info_dict,
42
+ module_fqn=module_fqn,
43
+ modlib_path=modlib_path,
44
+ init_globals=init_globals,
45
+ )
46
+ except Exception as ex: # not BaseException, since modules are expected to raise SystemExit
47
+ _handle_exception(ex, profile)
48
+
49
+
50
+ def _enable_coverage(coverage_config: str | None, coverage_output: str | None) -> None: # pragma: nocover
51
+ """Bootstrap `coverage` for the current Ansible module invocation."""
52
+ if not coverage_config:
53
+ return
54
+
55
+ if coverage_output:
56
+ # Enable code coverage analysis of the module.
57
+ # This feature is for internal testing and may change without notice.
58
+ python_version_string = '.'.join(str(v) for v in sys.version_info[:2])
59
+ os.environ['COVERAGE_FILE'] = f'{coverage_output}=python-{python_version_string}=coverage'
60
+
61
+ import coverage
62
+
63
+ cov = coverage.Coverage(config_file=coverage_config)
64
+
65
+ def atexit_coverage():
66
+ cov.stop()
67
+ cov.save()
68
+
69
+ atexit.register(atexit_coverage)
70
+
71
+ cov.start()
72
+ else:
73
+ # Verify coverage is available without importing it.
74
+ # This will detect when a module would fail with coverage enabled with minimal overhead.
75
+ if importlib.util.find_spec('coverage') is None:
76
+ raise RuntimeError('Could not find the `coverage` Python module.')
77
+
78
+
79
+ def _run_module(
80
+ *,
81
+ json_params: bytes,
82
+ profile: str,
83
+ plugin_info_dict: dict[str, object],
84
+ module_fqn: str,
85
+ modlib_path: str,
86
+ init_globals: dict[str, t.Any] | None = None,
87
+ ) -> None:
88
+ """Used internally by `_run_module` to run an Ansible module after coverage has been enabled (if applicable)."""
89
+ basic._ANSIBLE_ARGS = json_params
90
+ basic._ANSIBLE_PROFILE = profile
91
+
92
+ init_globals = init_globals or {}
93
+ init_globals.update(_module_fqn=module_fqn, _modlib_path=modlib_path)
94
+
95
+ with PluginExecContext(_ModulePluginWrapper(PluginInfo._from_dict(plugin_info_dict))):
96
+ # Run the module. By importing it as '__main__', it executes as a script.
97
+ runpy.run_module(mod_name=module_fqn, init_globals=init_globals, run_name='__main__', alter_sys=True)
98
+
99
+ # An Ansible module must print its own results and exit. If execution reaches this point, that did not happen.
100
+ raise RuntimeError('New-style module did not handle its own exit.')
101
+
102
+
103
+ def _handle_exception(exception: BaseException, profile: str) -> t.NoReturn:
104
+ """Handle the given exception."""
105
+ result = dict(
106
+ failed=True,
107
+ exception=_errors.create_error_summary(exception),
108
+ )
109
+
110
+ encoder = get_module_encoder(profile, Direction.MODULE_TO_CONTROLLER)
111
+
112
+ print(json.dumps(result, cls=encoder)) # pylint: disable=ansible-bad-function
113
+
114
+ sys.exit(1) # pylint: disable=ansible-bad-function
115
+
116
+
117
+ @dataclasses.dataclass(frozen=True)
118
+ class _ModulePluginWrapper(HasPluginInfo):
119
+ """Modules aren't plugin instances; this adapter implements the `HasPluginInfo` protocol to allow `PluginExecContext` infra to work with modules."""
120
+
121
+ plugin: PluginInfo
122
+
123
+ @property
124
+ def _load_name(self) -> str:
125
+ return self.plugin.requested_name
126
+
127
+ @property
128
+ def ansible_name(self) -> str:
129
+ return self.plugin.resolved_name
130
+
131
+ @property
132
+ def plugin_type(self) -> str:
133
+ return self.plugin.type
@@ -1,4 +1,5 @@
1
1
  """Proxy stdlib threading module that only supports non-joinable daemon threads."""
2
+
2
3
  # NB: all new local module attrs are _ prefixed to ensure an identical public attribute surface area to the module we're proxying
3
4
 
4
5
  from __future__ import annotations as _annotations