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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (709) hide show
  1. ansible/_internal/__init__.py +53 -0
  2. ansible/_internal/_ansiballz.py +265 -0
  3. ansible/_internal/_datatag/__init__.py +0 -0
  4. ansible/_internal/_datatag/_tags.py +130 -0
  5. ansible/_internal/_datatag/_utils.py +19 -0
  6. ansible/_internal/_datatag/_wrappers.py +33 -0
  7. ansible/_internal/_errors/__init__.py +0 -0
  8. ansible/_internal/_errors/_captured.py +128 -0
  9. ansible/_internal/_errors/_handler.py +91 -0
  10. ansible/_internal/_errors/_utils.py +310 -0
  11. ansible/_internal/_json/__init__.py +160 -0
  12. ansible/_internal/_json/_legacy_encoder.py +34 -0
  13. ansible/_internal/_json/_profiles/__init__.py +0 -0
  14. ansible/_internal/_json/_profiles/_cache_persistence.py +55 -0
  15. ansible/_internal/_json/_profiles/_inventory_legacy.py +40 -0
  16. ansible/_internal/_json/_profiles/_legacy.py +198 -0
  17. ansible/_internal/_locking.py +21 -0
  18. ansible/_internal/_plugins/__init__.py +0 -0
  19. ansible/_internal/_plugins/_cache.py +57 -0
  20. ansible/_internal/_task.py +78 -0
  21. ansible/_internal/_templating/__init__.py +10 -0
  22. ansible/_internal/_templating/_access.py +86 -0
  23. ansible/_internal/_templating/_chain_templar.py +63 -0
  24. ansible/_internal/_templating/_datatag.py +95 -0
  25. ansible/_internal/_templating/_engine.py +588 -0
  26. ansible/_internal/_templating/_errors.py +28 -0
  27. ansible/_internal/_templating/_jinja_bits.py +1066 -0
  28. ansible/_internal/_templating/_jinja_common.py +332 -0
  29. ansible/_internal/_templating/_jinja_patches.py +44 -0
  30. ansible/_internal/_templating/_jinja_plugins.py +351 -0
  31. ansible/_internal/_templating/_lazy_containers.py +633 -0
  32. ansible/_internal/_templating/_marker_behaviors.py +103 -0
  33. ansible/_internal/_templating/_transform.py +63 -0
  34. ansible/_internal/_templating/_utils.py +107 -0
  35. ansible/_internal/_wrapt.py +1052 -0
  36. ansible/_internal/_yaml/__init__.py +0 -0
  37. ansible/_internal/_yaml/_constructor.py +240 -0
  38. ansible/_internal/_yaml/_dumper.py +62 -0
  39. ansible/_internal/_yaml/_errors.py +166 -0
  40. ansible/_internal/_yaml/_loader.py +66 -0
  41. ansible/_internal/ansible_collections/ansible/_protomatter/README.md +11 -0
  42. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/action/debug.py +36 -0
  43. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/apply_trust.py +19 -0
  44. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/dump_object.py +18 -0
  45. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/finalize.py +16 -0
  46. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/origin.py +18 -0
  47. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.py +24 -0
  48. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/python_literal_eval.yml +33 -0
  49. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/tag_names.py +16 -0
  50. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +17 -0
  51. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +49 -0
  52. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.py +21 -0
  53. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/lookup/config.yml +2 -0
  54. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.py +15 -0
  55. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged.yml +19 -0
  56. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.py +18 -0
  57. ansible/_internal/ansible_collections/ansible/_protomatter/plugins/test/tagged_with.yml +19 -0
  58. ansible/cli/__init__.py +153 -89
  59. ansible/cli/_ssh_askpass.py +47 -0
  60. ansible/cli/adhoc.py +14 -7
  61. ansible/cli/arguments/option_helpers.py +154 -7
  62. ansible/cli/config.py +43 -68
  63. ansible/cli/console.py +10 -8
  64. ansible/cli/doc.py +48 -46
  65. ansible/cli/galaxy.py +27 -20
  66. ansible/cli/inventory.py +28 -26
  67. ansible/cli/playbook.py +4 -12
  68. ansible/cli/pull.py +51 -11
  69. ansible/cli/scripts/ansible_connection_cli_stub.py +7 -7
  70. ansible/cli/vault.py +12 -11
  71. ansible/compat/__init__.py +2 -2
  72. ansible/config/base.yml +165 -108
  73. ansible/config/manager.py +52 -49
  74. ansible/constants.py +3 -4
  75. ansible/errors/__init__.py +277 -235
  76. ansible/executor/interpreter_discovery.py +28 -149
  77. ansible/executor/module_common.py +426 -493
  78. ansible/executor/play_iterator.py +22 -27
  79. ansible/executor/playbook_executor.py +11 -11
  80. ansible/executor/powershell/async_watchdog.ps1 +97 -102
  81. ansible/executor/powershell/async_wrapper.ps1 +202 -151
  82. ansible/executor/powershell/become_wrapper.ps1 +89 -144
  83. ansible/executor/powershell/bootstrap_wrapper.ps1 +24 -9
  84. ansible/executor/powershell/coverage_wrapper.ps1 +82 -135
  85. ansible/executor/powershell/exec_wrapper.ps1 +462 -196
  86. ansible/executor/powershell/module_manifest.py +417 -265
  87. ansible/executor/powershell/module_wrapper.ps1 +169 -186
  88. ansible/executor/powershell/psrp_fetch_file.ps1 +41 -0
  89. ansible/executor/powershell/psrp_put_file.ps1 +122 -0
  90. ansible/executor/powershell/winrm_fetch_file.ps1 +46 -0
  91. ansible/executor/powershell/winrm_put_file.ps1 +36 -0
  92. ansible/executor/process/worker.py +136 -76
  93. ansible/executor/stats.py +5 -5
  94. ansible/executor/task_executor.py +237 -236
  95. ansible/executor/task_queue_manager.py +62 -38
  96. ansible/executor/task_result.py +21 -12
  97. ansible/galaxy/__init__.py +2 -2
  98. ansible/galaxy/api.py +22 -18
  99. ansible/galaxy/collection/__init__.py +1 -1
  100. ansible/galaxy/collection/concrete_artifact_manager.py +8 -11
  101. ansible/galaxy/dependency_resolution/dataclasses.py +14 -4
  102. ansible/galaxy/dependency_resolution/providers.py +1 -1
  103. ansible/galaxy/dependency_resolution/reporters.py +81 -0
  104. ansible/galaxy/role.py +4 -8
  105. ansible/galaxy/token.py +28 -21
  106. ansible/inventory/data.py +47 -57
  107. ansible/inventory/group.py +44 -72
  108. ansible/inventory/helpers.py +9 -0
  109. ansible/inventory/host.py +32 -54
  110. ansible/inventory/manager.py +77 -34
  111. ansible/keyword_desc.yml +1 -1
  112. ansible/module_utils/_internal/__init__.py +55 -0
  113. ansible/module_utils/_internal/_ambient_context.py +58 -0
  114. ansible/module_utils/_internal/_ansiballz.py +133 -0
  115. ansible/module_utils/_internal/_concurrent/_daemon_threading.py +1 -0
  116. ansible/module_utils/_internal/_dataclass_annotation_patch.py +64 -0
  117. ansible/module_utils/_internal/_dataclass_validation.py +217 -0
  118. ansible/module_utils/_internal/_datatag/__init__.py +928 -0
  119. ansible/module_utils/_internal/_datatag/_tags.py +38 -0
  120. ansible/module_utils/_internal/_debugging.py +31 -0
  121. ansible/module_utils/_internal/_errors.py +30 -0
  122. ansible/module_utils/_internal/_json/__init__.py +63 -0
  123. ansible/module_utils/_internal/_json/_legacy_encoder.py +26 -0
  124. ansible/module_utils/_internal/_json/_profiles/__init__.py +410 -0
  125. ansible/module_utils/_internal/_json/_profiles/_fallback_to_str.py +73 -0
  126. ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +31 -0
  127. ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +35 -0
  128. ansible/module_utils/_internal/_json/_profiles/_module_modern_c2m.py +35 -0
  129. ansible/module_utils/_internal/_json/_profiles/_module_modern_m2c.py +33 -0
  130. ansible/module_utils/_internal/_json/_profiles/_tagless.py +50 -0
  131. ansible/module_utils/_internal/_patches/__init__.py +66 -0
  132. ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +55 -0
  133. ansible/module_utils/_internal/_patches/_socket_patch.py +34 -0
  134. ansible/module_utils/_internal/_patches/_sys_intern_patch.py +34 -0
  135. ansible/module_utils/_internal/_plugin_exec_context.py +49 -0
  136. ansible/module_utils/_internal/_testing.py +0 -0
  137. ansible/module_utils/_internal/_traceback.py +89 -0
  138. ansible/module_utils/ansible_release.py +2 -2
  139. ansible/module_utils/api.py +1 -2
  140. ansible/module_utils/basic.py +154 -120
  141. ansible/module_utils/common/_utils.py +24 -28
  142. ansible/module_utils/common/collections.py +1 -2
  143. ansible/module_utils/common/dict_transformations.py +2 -2
  144. ansible/module_utils/common/file.py +2 -2
  145. ansible/module_utils/common/json.py +90 -84
  146. ansible/module_utils/common/locale.py +2 -2
  147. ansible/module_utils/common/messages.py +108 -0
  148. ansible/module_utils/common/parameters.py +27 -24
  149. ansible/module_utils/common/process.py +2 -2
  150. ansible/module_utils/common/respawn.py +41 -19
  151. ansible/module_utils/common/sentinel.py +66 -0
  152. ansible/module_utils/common/sys_info.py +8 -8
  153. ansible/module_utils/common/text/converters.py +16 -37
  154. ansible/module_utils/common/validation.py +35 -24
  155. ansible/module_utils/common/warnings.py +86 -25
  156. ansible/module_utils/common/yaml.py +29 -3
  157. ansible/module_utils/compat/datetime.py +33 -21
  158. ansible/module_utils/compat/paramiko.py +21 -10
  159. ansible/module_utils/compat/typing.py +6 -5
  160. ansible/module_utils/connection.py +2 -2
  161. ansible/module_utils/csharp/Ansible.Basic.cs +14 -11
  162. ansible/module_utils/csharp/Ansible.Become.cs +1 -0
  163. ansible/module_utils/csharp/Ansible._Async.cs +517 -0
  164. ansible/module_utils/datatag.py +46 -0
  165. ansible/module_utils/distro/__init__.py +2 -2
  166. ansible/module_utils/facts/ansible_collector.py +4 -5
  167. ansible/module_utils/facts/collector.py +13 -14
  168. ansible/module_utils/facts/compat.py +4 -4
  169. ansible/module_utils/facts/default_collectors.py +1 -1
  170. ansible/module_utils/facts/hardware/aix.py +34 -0
  171. ansible/module_utils/facts/hardware/base.py +1 -1
  172. ansible/module_utils/facts/hardware/darwin.py +1 -3
  173. ansible/module_utils/facts/hardware/freebsd.py +2 -2
  174. ansible/module_utils/facts/hardware/linux.py +4 -4
  175. ansible/module_utils/facts/namespace.py +1 -1
  176. ansible/module_utils/facts/network/base.py +1 -1
  177. ansible/module_utils/facts/network/fc_wwn.py +1 -2
  178. ansible/module_utils/facts/network/iscsi.py +1 -2
  179. ansible/module_utils/facts/network/nvme.py +1 -2
  180. ansible/module_utils/facts/other/facter.py +1 -2
  181. ansible/module_utils/facts/other/ohai.py +2 -3
  182. ansible/module_utils/facts/system/apparmor.py +1 -2
  183. ansible/module_utils/facts/system/caps.py +1 -1
  184. ansible/module_utils/facts/system/chroot.py +1 -2
  185. ansible/module_utils/facts/system/cmdline.py +1 -2
  186. ansible/module_utils/facts/system/date_time.py +5 -3
  187. ansible/module_utils/facts/system/distribution.py +9 -8
  188. ansible/module_utils/facts/system/dns.py +1 -1
  189. ansible/module_utils/facts/system/env.py +1 -2
  190. ansible/module_utils/facts/system/fips.py +7 -20
  191. ansible/module_utils/facts/system/loadavg.py +1 -2
  192. ansible/module_utils/facts/system/local.py +1 -2
  193. ansible/module_utils/facts/system/lsb.py +1 -2
  194. ansible/module_utils/facts/system/pkg_mgr.py +1 -2
  195. ansible/module_utils/facts/system/platform.py +1 -2
  196. ansible/module_utils/facts/system/python.py +1 -2
  197. ansible/module_utils/facts/system/selinux.py +1 -1
  198. ansible/module_utils/facts/system/service_mgr.py +1 -2
  199. ansible/module_utils/facts/system/ssh_pub_keys.py +1 -1
  200. ansible/module_utils/facts/system/systemd.py +1 -1
  201. ansible/module_utils/facts/system/user.py +1 -2
  202. ansible/module_utils/facts/utils.py +3 -3
  203. ansible/module_utils/facts/virtual/base.py +1 -1
  204. ansible/module_utils/facts/virtual/sunos.py +3 -15
  205. ansible/module_utils/facts/virtual/sysctl.py +3 -16
  206. ansible/module_utils/json_utils.py +2 -2
  207. ansible/module_utils/parsing/convert_bool.py +1 -1
  208. ansible/module_utils/service.py +18 -21
  209. ansible/module_utils/splitter.py +7 -7
  210. ansible/module_utils/testing.py +31 -0
  211. ansible/module_utils/urls.py +60 -31
  212. ansible/modules/add_host.py +4 -4
  213. ansible/modules/apt.py +60 -46
  214. ansible/modules/apt_key.py +19 -12
  215. ansible/modules/apt_repository.py +19 -16
  216. ansible/modules/assemble.py +6 -6
  217. ansible/modules/assert.py +4 -4
  218. ansible/modules/async_status.py +10 -12
  219. ansible/modules/async_wrapper.py +8 -3
  220. ansible/modules/blockinfile.py +6 -7
  221. ansible/modules/command.py +10 -17
  222. ansible/modules/copy.py +57 -144
  223. ansible/modules/cron.py +20 -15
  224. ansible/modules/deb822_repository.py +8 -9
  225. ansible/modules/debconf.py +5 -5
  226. ansible/modules/debug.py +4 -4
  227. ansible/modules/dnf.py +8 -8
  228. ansible/modules/dnf5.py +39 -13
  229. ansible/modules/dpkg_selections.py +4 -4
  230. ansible/modules/expect.py +8 -10
  231. ansible/modules/fail.py +4 -4
  232. ansible/modules/fetch.py +4 -4
  233. ansible/modules/file.py +174 -133
  234. ansible/modules/find.py +19 -17
  235. ansible/modules/gather_facts.py +3 -3
  236. ansible/modules/get_url.py +59 -53
  237. ansible/modules/getent.py +7 -9
  238. ansible/modules/git.py +28 -25
  239. ansible/modules/group.py +6 -6
  240. ansible/modules/group_by.py +4 -4
  241. ansible/modules/hostname.py +13 -29
  242. ansible/modules/import_playbook.py +6 -6
  243. ansible/modules/import_role.py +6 -6
  244. ansible/modules/import_tasks.py +6 -6
  245. ansible/modules/include_role.py +6 -6
  246. ansible/modules/include_tasks.py +6 -6
  247. ansible/modules/include_vars.py +6 -6
  248. ansible/modules/iptables.py +86 -73
  249. ansible/modules/known_hosts.py +10 -10
  250. ansible/modules/lineinfile.py +5 -5
  251. ansible/modules/meta.py +4 -4
  252. ansible/modules/mount_facts.py +2 -2
  253. ansible/modules/package.py +4 -4
  254. ansible/modules/package_facts.py +22 -10
  255. ansible/modules/pause.py +6 -6
  256. ansible/modules/ping.py +6 -6
  257. ansible/modules/pip.py +10 -11
  258. ansible/modules/raw.py +4 -4
  259. ansible/modules/reboot.py +6 -6
  260. ansible/modules/replace.py +9 -13
  261. ansible/modules/rpm_key.py +7 -8
  262. ansible/modules/script.py +4 -4
  263. ansible/modules/service.py +7 -8
  264. ansible/modules/service_facts.py +87 -10
  265. ansible/modules/set_fact.py +5 -5
  266. ansible/modules/set_stats.py +4 -4
  267. ansible/modules/setup.py +2 -2
  268. ansible/modules/shell.py +6 -6
  269. ansible/modules/slurp.py +6 -6
  270. ansible/modules/stat.py +9 -23
  271. ansible/modules/subversion.py +15 -15
  272. ansible/modules/systemd.py +6 -6
  273. ansible/modules/systemd_service.py +6 -6
  274. ansible/modules/sysvinit.py +6 -6
  275. ansible/modules/tempfile.py +5 -6
  276. ansible/modules/template.py +6 -6
  277. ansible/modules/unarchive.py +32 -11
  278. ansible/modules/uri.py +33 -26
  279. ansible/modules/user.py +53 -34
  280. ansible/modules/validate_argument_spec.py +10 -7
  281. ansible/modules/wait_for.py +32 -27
  282. ansible/modules/wait_for_connection.py +6 -6
  283. ansible/modules/yum_repository.py +6 -6
  284. ansible/parsing/ajson.py +14 -32
  285. ansible/parsing/dataloader.py +99 -54
  286. ansible/parsing/mod_args.py +28 -44
  287. ansible/parsing/plugin_docs.py +21 -86
  288. ansible/parsing/quoting.py +1 -1
  289. ansible/parsing/splitter.py +27 -12
  290. ansible/parsing/utils/addresses.py +24 -24
  291. ansible/parsing/utils/yaml.py +32 -61
  292. ansible/parsing/vault/__init__.py +319 -87
  293. ansible/parsing/yaml/__init__.py +0 -18
  294. ansible/parsing/yaml/dumper.py +6 -120
  295. ansible/parsing/yaml/loader.py +6 -39
  296. ansible/parsing/yaml/objects.py +36 -339
  297. ansible/playbook/__init__.py +1 -1
  298. ansible/playbook/attribute.py +8 -3
  299. ansible/playbook/base.py +182 -132
  300. ansible/playbook/block.py +26 -24
  301. ansible/playbook/collectionsearch.py +1 -15
  302. ansible/playbook/conditional.py +3 -77
  303. ansible/playbook/handler.py +8 -2
  304. ansible/playbook/helpers.py +41 -53
  305. ansible/playbook/included_file.py +6 -15
  306. ansible/playbook/loop_control.py +2 -2
  307. ansible/playbook/play.py +85 -44
  308. ansible/playbook/play_context.py +12 -17
  309. ansible/playbook/playbook_include.py +14 -15
  310. ansible/playbook/role/__init__.py +24 -26
  311. ansible/playbook/role/definition.py +15 -17
  312. ansible/playbook/role/include.py +2 -4
  313. ansible/playbook/role/metadata.py +10 -11
  314. ansible/playbook/role_include.py +3 -3
  315. ansible/playbook/taggable.py +13 -8
  316. ansible/playbook/task.py +188 -118
  317. ansible/playbook/task_include.py +5 -5
  318. ansible/plugins/__init__.py +68 -21
  319. ansible/plugins/action/__init__.py +209 -176
  320. ansible/plugins/action/add_host.py +1 -1
  321. ansible/plugins/action/assemble.py +1 -1
  322. ansible/plugins/action/assert.py +54 -66
  323. ansible/plugins/action/copy.py +7 -11
  324. ansible/plugins/action/debug.py +37 -31
  325. ansible/plugins/action/dnf.py +3 -4
  326. ansible/plugins/action/fail.py +1 -1
  327. ansible/plugins/action/fetch.py +4 -5
  328. ansible/plugins/action/gather_facts.py +7 -6
  329. ansible/plugins/action/group_by.py +1 -1
  330. ansible/plugins/action/include_vars.py +10 -11
  331. ansible/plugins/action/package.py +3 -6
  332. ansible/plugins/action/pause.py +2 -2
  333. ansible/plugins/action/script.py +15 -8
  334. ansible/plugins/action/service.py +6 -11
  335. ansible/plugins/action/set_fact.py +3 -12
  336. ansible/plugins/action/set_stats.py +3 -8
  337. ansible/plugins/action/template.py +35 -59
  338. ansible/plugins/action/unarchive.py +1 -1
  339. ansible/plugins/action/validate_argument_spec.py +5 -5
  340. ansible/plugins/action/wait_for_connection.py +1 -1
  341. ansible/plugins/become/__init__.py +31 -8
  342. ansible/plugins/become/runas.py +71 -0
  343. ansible/plugins/become/su.py +13 -8
  344. ansible/plugins/become/sudo.py +19 -0
  345. ansible/plugins/cache/__init__.py +35 -44
  346. ansible/plugins/cache/base.py +8 -0
  347. ansible/plugins/cache/jsonfile.py +10 -16
  348. ansible/plugins/cache/memory.py +6 -12
  349. ansible/plugins/callback/__init__.py +141 -123
  350. ansible/plugins/callback/default.py +30 -23
  351. ansible/plugins/callback/junit.py +28 -24
  352. ansible/plugins/callback/minimal.py +17 -14
  353. ansible/plugins/callback/oneline.py +13 -7
  354. ansible/plugins/callback/tree.py +10 -6
  355. ansible/plugins/connection/__init__.py +47 -34
  356. ansible/plugins/connection/local.py +150 -54
  357. ansible/plugins/connection/paramiko_ssh.py +21 -18
  358. ansible/plugins/connection/psrp.py +76 -165
  359. ansible/plugins/connection/ssh.py +301 -78
  360. ansible/plugins/connection/winrm.py +58 -140
  361. ansible/plugins/doc_fragments/action_common_attributes.py +14 -14
  362. ansible/plugins/doc_fragments/action_core.py +6 -6
  363. ansible/plugins/doc_fragments/backup.py +2 -2
  364. ansible/plugins/doc_fragments/checksum_common.py +27 -0
  365. ansible/plugins/doc_fragments/constructed.py +6 -2
  366. ansible/plugins/doc_fragments/decrypt.py +2 -2
  367. ansible/plugins/doc_fragments/default_callback.py +2 -2
  368. ansible/plugins/doc_fragments/files.py +2 -2
  369. ansible/plugins/doc_fragments/inventory_cache.py +2 -2
  370. ansible/plugins/doc_fragments/result_format_callback.py +2 -2
  371. ansible/plugins/doc_fragments/return_common.py +2 -2
  372. ansible/plugins/doc_fragments/template_common.py +4 -4
  373. ansible/plugins/doc_fragments/url.py +17 -1
  374. ansible/plugins/doc_fragments/url_windows.py +2 -2
  375. ansible/plugins/doc_fragments/validate.py +2 -2
  376. ansible/plugins/doc_fragments/vars_plugin_staging.py +2 -2
  377. ansible/plugins/filter/__init__.py +6 -2
  378. ansible/plugins/filter/b64decode.yml +22 -0
  379. ansible/plugins/filter/b64encode.yml +22 -0
  380. ansible/plugins/filter/bool.yml +11 -4
  381. ansible/plugins/filter/core.py +218 -108
  382. ansible/plugins/filter/encryption.py +32 -32
  383. ansible/plugins/filter/flatten.yml +3 -2
  384. ansible/plugins/filter/human_to_bytes.yml +1 -1
  385. ansible/plugins/filter/mathstuff.py +30 -37
  386. ansible/plugins/filter/password_hash.yml +8 -0
  387. ansible/plugins/filter/regex_search.yml +1 -4
  388. ansible/plugins/filter/split.yml +1 -1
  389. ansible/plugins/filter/to_nice_yaml.yml +0 -4
  390. ansible/plugins/filter/to_yaml.yml +0 -4
  391. ansible/plugins/filter/unvault.yml +1 -1
  392. ansible/plugins/filter/urls.py +1 -1
  393. ansible/plugins/filter/urlsplit.py +8 -9
  394. ansible/plugins/filter/vault.yml +14 -9
  395. ansible/plugins/filter/win_basename.yml +6 -1
  396. ansible/plugins/filter/win_dirname.yml +5 -0
  397. ansible/plugins/inventory/__init__.py +97 -77
  398. ansible/plugins/inventory/advanced_host_list.py +7 -5
  399. ansible/plugins/inventory/auto.py +11 -4
  400. ansible/plugins/inventory/constructed.py +21 -24
  401. ansible/plugins/inventory/generator.py +16 -11
  402. ansible/plugins/inventory/host_list.py +7 -5
  403. ansible/plugins/inventory/ini.py +78 -44
  404. ansible/plugins/inventory/script.py +189 -119
  405. ansible/plugins/inventory/toml.py +16 -126
  406. ansible/plugins/inventory/yaml.py +10 -8
  407. ansible/plugins/list.py +3 -3
  408. ansible/plugins/loader.py +197 -82
  409. ansible/plugins/lookup/__init__.py +21 -4
  410. ansible/plugins/lookup/config.py +21 -35
  411. ansible/plugins/lookup/csvfile.py +3 -2
  412. ansible/plugins/lookup/dict.py +1 -6
  413. ansible/plugins/lookup/env.py +12 -9
  414. ansible/plugins/lookup/file.py +5 -8
  415. ansible/plugins/lookup/first_found.py +86 -55
  416. ansible/plugins/lookup/indexed_items.py +1 -10
  417. ansible/plugins/lookup/ini.py +14 -13
  418. ansible/plugins/lookup/items.py +1 -1
  419. ansible/plugins/lookup/lines.py +8 -1
  420. ansible/plugins/lookup/list.py +1 -1
  421. ansible/plugins/lookup/nested.py +2 -18
  422. ansible/plugins/lookup/password.py +5 -5
  423. ansible/plugins/lookup/pipe.py +5 -7
  424. ansible/plugins/lookup/sequence.py +18 -8
  425. ansible/plugins/lookup/subelements.py +1 -4
  426. ansible/plugins/lookup/template.py +42 -36
  427. ansible/plugins/lookup/together.py +0 -12
  428. ansible/plugins/lookup/unvault.py +1 -5
  429. ansible/plugins/lookup/url.py +2 -8
  430. ansible/plugins/lookup/vars.py +16 -24
  431. ansible/plugins/shell/__init__.py +2 -2
  432. ansible/plugins/shell/cmd.py +2 -2
  433. ansible/plugins/shell/powershell.py +39 -22
  434. ansible/plugins/shell/sh.py +3 -2
  435. ansible/plugins/strategy/__init__.py +94 -113
  436. ansible/plugins/strategy/debug.py +2 -2
  437. ansible/plugins/strategy/free.py +13 -28
  438. ansible/plugins/strategy/host_pinned.py +2 -2
  439. ansible/plugins/strategy/linear.py +31 -33
  440. ansible/plugins/terminal/__init__.py +4 -4
  441. ansible/plugins/test/__init__.py +7 -2
  442. ansible/plugins/test/core.py +54 -20
  443. ansible/plugins/test/files.py +1 -1
  444. ansible/plugins/test/mathstuff.py +3 -3
  445. ansible/plugins/test/uri.py +3 -3
  446. ansible/plugins/vars/host_group_vars.py +7 -14
  447. ansible/release.py +2 -2
  448. ansible/template/__init__.py +368 -944
  449. ansible/utils/__init__.py +0 -18
  450. ansible/utils/_ssh_agent.py +657 -0
  451. ansible/utils/collection_loader/__init__.py +52 -5
  452. ansible/utils/collection_loader/_collection_config.py +5 -6
  453. ansible/utils/collection_loader/_collection_finder.py +79 -93
  454. ansible/utils/collection_loader/_collection_meta.py +13 -8
  455. ansible/utils/display.py +428 -58
  456. ansible/utils/encrypt.py +27 -19
  457. ansible/utils/fqcn.py +2 -2
  458. ansible/utils/hashing.py +2 -2
  459. ansible/utils/helpers.py +2 -2
  460. ansible/utils/listify.py +8 -8
  461. ansible/utils/lock.py +2 -2
  462. ansible/utils/path.py +4 -4
  463. ansible/utils/plugin_docs.py +14 -13
  464. ansible/utils/sentinel.py +4 -62
  465. ansible/utils/singleton.py +2 -0
  466. ansible/utils/ssh_functions.py +1 -1
  467. ansible/utils/unsafe_proxy.py +23 -332
  468. ansible/utils/vars.py +28 -8
  469. ansible/utils/version.py +2 -2
  470. ansible/vars/clean.py +4 -4
  471. ansible/vars/hostvars.py +60 -90
  472. ansible/vars/manager.py +205 -264
  473. ansible/vars/reserved.py +8 -9
  474. ansible_core-2.19.0b1.dist-info/BSD-3-Clause.txt +28 -0
  475. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/METADATA +5 -4
  476. ansible_core-2.19.0b1.dist-info/RECORD +1070 -0
  477. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/WHEEL +1 -1
  478. ansible_test/_data/completion/docker.txt +7 -7
  479. ansible_test/_data/completion/remote.txt +6 -6
  480. ansible_test/_data/completion/windows.txt +1 -0
  481. ansible_test/_data/requirements/ansible.txt +2 -2
  482. ansible_test/_data/requirements/sanity.ansible-doc.txt +3 -3
  483. ansible_test/_data/requirements/sanity.changelog.txt +1 -1
  484. ansible_test/_data/requirements/sanity.import.plugin.txt +2 -2
  485. ansible_test/_data/requirements/sanity.pylint.txt +4 -4
  486. ansible_test/_data/requirements/sanity.validate-modules.txt +2 -2
  487. ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
  488. ansible_test/_data/requirements/units.txt +1 -0
  489. ansible_test/_internal/__init__.py +1 -0
  490. ansible_test/_internal/ansible_util.py +2 -0
  491. ansible_test/_internal/become.py +1 -0
  492. ansible_test/_internal/bootstrap.py +1 -0
  493. ansible_test/_internal/cache.py +1 -0
  494. ansible_test/_internal/cgroup.py +1 -0
  495. ansible_test/_internal/ci/__init__.py +1 -0
  496. ansible_test/_internal/ci/azp.py +1 -0
  497. ansible_test/_internal/ci/local.py +1 -0
  498. ansible_test/_internal/classification/__init__.py +1 -0
  499. ansible_test/_internal/classification/common.py +1 -0
  500. ansible_test/_internal/classification/csharp.py +1 -0
  501. ansible_test/_internal/classification/powershell.py +1 -0
  502. ansible_test/_internal/classification/python.py +1 -0
  503. ansible_test/_internal/cli/__init__.py +1 -0
  504. ansible_test/_internal/cli/actions.py +1 -0
  505. ansible_test/_internal/cli/argparsing/__init__.py +1 -0
  506. ansible_test/_internal/cli/argparsing/actions.py +1 -0
  507. ansible_test/_internal/cli/argparsing/argcompletion.py +1 -0
  508. ansible_test/_internal/cli/argparsing/parsers.py +1 -0
  509. ansible_test/_internal/cli/commands/__init__.py +11 -0
  510. ansible_test/_internal/cli/commands/coverage/__init__.py +1 -0
  511. ansible_test/_internal/cli/commands/coverage/analyze/__init__.py +1 -0
  512. ansible_test/_internal/cli/commands/coverage/analyze/targets/__init__.py +1 -0
  513. ansible_test/_internal/cli/commands/coverage/analyze/targets/combine.py +1 -0
  514. ansible_test/_internal/cli/commands/coverage/analyze/targets/expand.py +1 -0
  515. ansible_test/_internal/cli/commands/coverage/analyze/targets/filter.py +1 -0
  516. ansible_test/_internal/cli/commands/coverage/analyze/targets/generate.py +1 -0
  517. ansible_test/_internal/cli/commands/coverage/analyze/targets/missing.py +1 -0
  518. ansible_test/_internal/cli/commands/coverage/combine.py +1 -0
  519. ansible_test/_internal/cli/commands/coverage/erase.py +1 -0
  520. ansible_test/_internal/cli/commands/coverage/html.py +1 -0
  521. ansible_test/_internal/cli/commands/coverage/report.py +1 -0
  522. ansible_test/_internal/cli/commands/coverage/xml.py +1 -0
  523. ansible_test/_internal/cli/commands/env.py +1 -0
  524. ansible_test/_internal/cli/commands/integration/__init__.py +1 -0
  525. ansible_test/_internal/cli/commands/integration/network.py +1 -0
  526. ansible_test/_internal/cli/commands/integration/posix.py +1 -0
  527. ansible_test/_internal/cli/commands/integration/windows.py +1 -0
  528. ansible_test/_internal/cli/commands/sanity.py +9 -0
  529. ansible_test/_internal/cli/commands/shell.py +1 -0
  530. ansible_test/_internal/cli/commands/units.py +1 -0
  531. ansible_test/_internal/cli/compat.py +1 -0
  532. ansible_test/_internal/cli/completers.py +1 -0
  533. ansible_test/_internal/cli/converters.py +1 -0
  534. ansible_test/_internal/cli/environments.py +1 -0
  535. ansible_test/_internal/cli/epilog.py +1 -0
  536. ansible_test/_internal/cli/parsers/__init__.py +1 -0
  537. ansible_test/_internal/cli/parsers/base_argument_parsers.py +1 -0
  538. ansible_test/_internal/cli/parsers/helpers.py +1 -0
  539. ansible_test/_internal/cli/parsers/host_config_parsers.py +1 -0
  540. ansible_test/_internal/cli/parsers/key_value_parsers.py +1 -0
  541. ansible_test/_internal/cli/parsers/value_parsers.py +1 -0
  542. ansible_test/_internal/commands/__init__.py +1 -0
  543. ansible_test/_internal/commands/coverage/__init__.py +2 -1
  544. ansible_test/_internal/commands/coverage/analyze/__init__.py +1 -0
  545. ansible_test/_internal/commands/coverage/analyze/targets/__init__.py +1 -0
  546. ansible_test/_internal/commands/coverage/analyze/targets/combine.py +1 -0
  547. ansible_test/_internal/commands/coverage/analyze/targets/expand.py +1 -0
  548. ansible_test/_internal/commands/coverage/analyze/targets/filter.py +1 -0
  549. ansible_test/_internal/commands/coverage/analyze/targets/generate.py +1 -0
  550. ansible_test/_internal/commands/coverage/analyze/targets/missing.py +1 -0
  551. ansible_test/_internal/commands/coverage/combine.py +2 -1
  552. ansible_test/_internal/commands/coverage/erase.py +1 -0
  553. ansible_test/_internal/commands/coverage/html.py +1 -0
  554. ansible_test/_internal/commands/coverage/report.py +1 -0
  555. ansible_test/_internal/commands/coverage/xml.py +1 -0
  556. ansible_test/_internal/commands/env/__init__.py +2 -0
  557. ansible_test/_internal/commands/integration/__init__.py +4 -0
  558. ansible_test/_internal/commands/integration/cloud/__init__.py +1 -0
  559. ansible_test/_internal/commands/integration/cloud/acme.py +2 -1
  560. ansible_test/_internal/commands/integration/cloud/aws.py +1 -0
  561. ansible_test/_internal/commands/integration/cloud/azure.py +1 -0
  562. ansible_test/_internal/commands/integration/cloud/cs.py +1 -0
  563. ansible_test/_internal/commands/integration/cloud/digitalocean.py +1 -0
  564. ansible_test/_internal/commands/integration/cloud/galaxy.py +3 -2
  565. ansible_test/_internal/commands/integration/cloud/hcloud.py +1 -0
  566. ansible_test/_internal/commands/integration/cloud/httptester.py +2 -1
  567. ansible_test/_internal/commands/integration/cloud/nios.py +2 -1
  568. ansible_test/_internal/commands/integration/cloud/opennebula.py +1 -0
  569. ansible_test/_internal/commands/integration/cloud/openshift.py +1 -0
  570. ansible_test/_internal/commands/integration/cloud/scaleway.py +1 -0
  571. ansible_test/_internal/commands/integration/cloud/vcenter.py +1 -0
  572. ansible_test/_internal/commands/integration/cloud/vultr.py +1 -0
  573. ansible_test/_internal/commands/integration/coverage.py +1 -0
  574. ansible_test/_internal/commands/integration/filters.py +1 -0
  575. ansible_test/_internal/commands/integration/network.py +1 -0
  576. ansible_test/_internal/commands/integration/posix.py +1 -0
  577. ansible_test/_internal/commands/integration/windows.py +1 -0
  578. ansible_test/_internal/commands/sanity/__init__.py +16 -1
  579. ansible_test/_internal/commands/sanity/ansible_doc.py +1 -0
  580. ansible_test/_internal/commands/sanity/bin_symlinks.py +1 -0
  581. ansible_test/_internal/commands/sanity/compile.py +1 -0
  582. ansible_test/_internal/commands/sanity/ignores.py +1 -0
  583. ansible_test/_internal/commands/sanity/import.py +1 -0
  584. ansible_test/_internal/commands/sanity/integration_aliases.py +1 -0
  585. ansible_test/_internal/commands/sanity/pep8.py +1 -0
  586. ansible_test/_internal/commands/sanity/pslint.py +1 -0
  587. ansible_test/_internal/commands/sanity/pylint.py +24 -26
  588. ansible_test/_internal/commands/sanity/shellcheck.py +1 -0
  589. ansible_test/_internal/commands/sanity/validate_modules.py +1 -0
  590. ansible_test/_internal/commands/sanity/yamllint.py +1 -0
  591. ansible_test/_internal/commands/shell/__init__.py +1 -0
  592. ansible_test/_internal/commands/units/__init__.py +1 -0
  593. ansible_test/_internal/compat/__init__.py +1 -0
  594. ansible_test/_internal/compat/packaging.py +1 -0
  595. ansible_test/_internal/compat/yaml.py +1 -0
  596. ansible_test/_internal/completion.py +1 -0
  597. ansible_test/_internal/config.py +2 -0
  598. ansible_test/_internal/connections.py +1 -0
  599. ansible_test/_internal/constants.py +1 -0
  600. ansible_test/_internal/containers.py +1 -0
  601. ansible_test/_internal/content_config.py +1 -0
  602. ansible_test/_internal/core_ci.py +1 -0
  603. ansible_test/_internal/coverage_util.py +11 -10
  604. ansible_test/_internal/data.py +1 -0
  605. ansible_test/_internal/delegation.py +1 -0
  606. ansible_test/_internal/dev/__init__.py +1 -0
  607. ansible_test/_internal/dev/container_probe.py +1 -0
  608. ansible_test/_internal/diff.py +3 -2
  609. ansible_test/_internal/docker_util.py +2 -1
  610. ansible_test/_internal/encoding.py +1 -0
  611. ansible_test/_internal/executor.py +1 -0
  612. ansible_test/_internal/git.py +1 -0
  613. ansible_test/_internal/host_configs.py +1 -0
  614. ansible_test/_internal/host_profiles.py +1 -0
  615. ansible_test/_internal/http.py +1 -0
  616. ansible_test/_internal/init.py +1 -0
  617. ansible_test/_internal/inventory.py +35 -3
  618. ansible_test/_internal/io.py +1 -0
  619. ansible_test/_internal/metadata.py +1 -0
  620. ansible_test/_internal/payload.py +1 -0
  621. ansible_test/_internal/provider/__init__.py +1 -0
  622. ansible_test/_internal/provider/layout/__init__.py +1 -0
  623. ansible_test/_internal/provider/layout/ansible.py +1 -0
  624. ansible_test/_internal/provider/layout/collection.py +1 -0
  625. ansible_test/_internal/provider/layout/unsupported.py +1 -0
  626. ansible_test/_internal/provider/source/__init__.py +1 -0
  627. ansible_test/_internal/provider/source/git.py +1 -0
  628. ansible_test/_internal/provider/source/installed.py +1 -0
  629. ansible_test/_internal/provider/source/unsupported.py +1 -0
  630. ansible_test/_internal/provider/source/unversioned.py +1 -0
  631. ansible_test/_internal/provisioning.py +1 -0
  632. ansible_test/_internal/pypi_proxy.py +6 -5
  633. ansible_test/_internal/python_requirements.py +1 -0
  634. ansible_test/_internal/ssh.py +1 -0
  635. ansible_test/_internal/target.py +1 -0
  636. ansible_test/_internal/test.py +3 -2
  637. ansible_test/_internal/thread.py +1 -0
  638. ansible_test/_internal/timeout.py +1 -0
  639. ansible_test/_internal/util.py +1 -0
  640. ansible_test/_internal/util_common.py +5 -2
  641. ansible_test/_internal/venv.py +1 -0
  642. ansible_test/_util/controller/sanity/code-smell/action-plugin-docs.py +1 -0
  643. ansible_test/_util/controller/sanity/code-smell/changelog/sphinx.py +1 -0
  644. ansible_test/_util/controller/sanity/code-smell/changelog.py +1 -0
  645. ansible_test/_util/controller/sanity/code-smell/empty-init.py +1 -0
  646. ansible_test/_util/controller/sanity/code-smell/line-endings.py +1 -0
  647. ansible_test/_util/controller/sanity/code-smell/no-assert.py +1 -0
  648. ansible_test/_util/controller/sanity/code-smell/no-get-exception.py +1 -0
  649. ansible_test/_util/controller/sanity/code-smell/no-illegal-filenames.py +1 -0
  650. ansible_test/_util/controller/sanity/code-smell/no-smart-quotes.py +1 -0
  651. ansible_test/_util/controller/sanity/code-smell/replace-urlopen.py +1 -0
  652. ansible_test/_util/controller/sanity/code-smell/runtime-metadata.py +28 -1
  653. ansible_test/_util/controller/sanity/code-smell/shebang.py +1 -0
  654. ansible_test/_util/controller/sanity/code-smell/symlinks.py +1 -0
  655. ansible_test/_util/controller/sanity/code-smell/use-argspec-type-path.py +1 -0
  656. ansible_test/_util/controller/sanity/code-smell/use-compat-six.py +1 -0
  657. ansible_test/_util/controller/sanity/integration-aliases/yaml_to_json.py +2 -1
  658. ansible_test/_util/controller/sanity/pep8/current-ignore.txt +4 -0
  659. ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +7 -5
  660. ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +7 -5
  661. ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +7 -5
  662. ansible_test/_util/controller/sanity/pylint/config/collection.cfg +3 -5
  663. ansible_test/_util/controller/sanity/pylint/config/default.cfg +7 -7
  664. ansible_test/_util/controller/sanity/pylint/plugins/deprecated.py +1 -13
  665. ansible_test/_util/controller/sanity/pylint/plugins/hide_unraisable.py +1 -0
  666. ansible_test/_util/controller/sanity/pylint/plugins/string_format.py +1 -8
  667. ansible_test/_util/controller/sanity/pylint/plugins/unwanted.py +1 -8
  668. ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py +55 -28
  669. ansible_test/_util/controller/sanity/validate-modules/validate_modules/module_args.py +12 -5
  670. ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py +13 -2
  671. ansible_test/_util/controller/sanity/validate-modules/validate_modules/utils.py +1 -0
  672. ansible_test/_util/controller/sanity/yamllint/yamllinter.py +35 -17
  673. ansible_test/_util/controller/tools/collection_detail.py +1 -0
  674. ansible_test/_util/controller/tools/yaml_to_json.py +2 -1
  675. ansible_test/_util/target/pytest/plugins/ansible_forked.py +6 -1
  676. ansible_test/_util/target/pytest/plugins/ansible_pytest_collections.py +2 -1
  677. ansible_test/_util/target/pytest/plugins/ansible_pytest_coverage.py +1 -0
  678. ansible_test/_util/target/sanity/compile/compile.py +1 -0
  679. ansible_test/_util/target/sanity/import/importer.py +15 -16
  680. ansible_test/_util/target/setup/bootstrap.sh +9 -20
  681. ansible_test/_util/target/setup/probe_cgroups.py +1 -0
  682. ansible_test/_util/target/setup/quiet_pip.py +1 -0
  683. ansible_test/_util/target/setup/requirements.py +35 -27
  684. ansible_test/_util/target/tools/virtualenvcheck.py +2 -1
  685. ansible_test/_util/target/tools/yamlcheck.py +2 -1
  686. ansible/compat/selectors.py +0 -32
  687. ansible/errors/yaml_strings.py +0 -138
  688. ansible/executor/action_write_locks.py +0 -44
  689. ansible/executor/discovery/python_target.py +0 -47
  690. ansible/executor/powershell/module_powershell_wrapper.ps1 +0 -86
  691. ansible/executor/powershell/module_script_wrapper.ps1 +0 -22
  692. ansible/module_utils/compat/importlib.py +0 -26
  693. ansible/module_utils/compat/selectors.py +0 -32
  694. ansible/module_utils/pycompat24.py +0 -73
  695. ansible/parsing/utils/jsonify.py +0 -36
  696. ansible/parsing/yaml/constructor.py +0 -178
  697. ansible/template/native_helpers.py +0 -251
  698. ansible/template/template.py +0 -43
  699. ansible/template/vars.py +0 -77
  700. ansible/utils/native_jinja.py +0 -11
  701. ansible/vars/fact_cache.py +0 -71
  702. ansible_core-2.18.5rc1.dist-info/RECORD +0 -992
  703. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/Apache-License.txt +0 -0
  704. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/COPYING +0 -0
  705. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/MIT-license.txt +0 -0
  706. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/PSF-license.txt +0 -0
  707. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/entry_points.txt +0 -0
  708. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.dist-info}/simplified_bsd.txt +0 -0
  709. {ansible_core-2.18.5rc1.dist-info → ansible_core-2.19.0b1.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,33 @@
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.errors import AnsibleError, AnsibleOptionsError
33
34
  from ansible.inventory.data import InventoryData
34
35
  from ansible.module_utils.six import string_types
35
36
  from ansible.module_utils.common.text.converters import to_bytes, to_text
36
37
  from ansible.parsing.utils.addresses import parse_address
37
38
  from ansible.plugins.loader import inventory_loader
39
+ from ansible._internal._datatag._tags import Origin
38
40
  from ansible.utils.helpers import deduplicate_list
39
41
  from ansible.utils.path import unfrackpath
40
42
  from ansible.utils.display import Display
41
43
  from ansible.utils.vars import combine_vars
42
44
  from ansible.vars.plugins import get_vars_from_inventory_sources
43
45
 
46
+ if t.TYPE_CHECKING:
47
+ from ansible.plugins.inventory import BaseInventoryPlugin
48
+
44
49
  display = Display()
45
50
 
46
51
  IGNORED_ALWAYS = [br"^\.", b"^host_vars$", b"^group_vars$", b"^vars_plugins$"]
@@ -50,7 +55,7 @@ IGNORED_EXTS = [b'%s$' % to_bytes(re.escape(x)) for x in C.INVENTORY_IGNORE_EXTS
50
55
  IGNORED = re.compile(b'|'.join(IGNORED_ALWAYS + IGNORED_PATTERNS + IGNORED_EXTS))
51
56
 
52
57
  PATTERN_WITH_SUBSCRIPT = re.compile(
53
- r'''^
58
+ r"""^
54
59
  (.+) # A pattern expression ending with...
55
60
  \[(?: # A [subscript] expression comprising:
56
61
  (-?[0-9]+)| # A single positive or negative number
@@ -58,12 +63,12 @@ PATTERN_WITH_SUBSCRIPT = re.compile(
58
63
  ([0-9]*)
59
64
  )\]
60
65
  $
61
- ''', re.X
66
+ """, re.X
62
67
  )
63
68
 
64
69
 
65
70
  def order_patterns(patterns):
66
- ''' takes a list of patterns and reorders them by modifier to apply them consistently '''
71
+ """ takes a list of patterns and reorders them by modifier to apply them consistently """
67
72
 
68
73
  # FIXME: this goes away if we apply patterns incrementally or by groups
69
74
  pattern_regular = []
@@ -125,19 +130,19 @@ def split_host_pattern(pattern):
125
130
  # This mishandles IPv6 addresses, and is retained only for backwards
126
131
  # compatibility.
127
132
  patterns = re.findall(
128
- to_text(r'''(?: # We want to match something comprising:
133
+ to_text(r"""(?: # We want to match something comprising:
129
134
  [^\s:\[\]] # (anything other than whitespace or ':[]'
130
135
  | # ...or...
131
136
  \[[^\]]*\] # a single complete bracketed expression)
132
137
  )+ # occurring once or more
133
- '''), pattern, re.X
138
+ """), pattern, re.X
134
139
  )
135
140
 
136
141
  return [p.strip() for p in patterns if p.strip()]
137
142
 
138
143
 
139
144
  class InventoryManager(object):
140
- ''' Creates and manages inventory '''
145
+ """ Creates and manages inventory """
141
146
 
142
147
  def __init__(self, loader, sources=None, parse=True, cache=True):
143
148
 
@@ -196,12 +201,12 @@ class InventoryManager(object):
196
201
  def get_host(self, hostname):
197
202
  return self._inventory.get_host(hostname)
198
203
 
199
- def _fetch_inventory_plugins(self):
200
- ''' sets up loaded inventory plugins for usage '''
204
+ def _fetch_inventory_plugins(self) -> list[BaseInventoryPlugin]:
205
+ """ sets up loaded inventory plugins for usage """
201
206
 
202
207
  display.vvvv('setting up inventory plugins')
203
208
 
204
- plugins = []
209
+ plugins: list[BaseInventoryPlugin] = []
205
210
  for name in C.INVENTORY_ENABLED:
206
211
  plugin = inventory_loader.get(name)
207
212
  if plugin:
@@ -215,7 +220,7 @@ class InventoryManager(object):
215
220
  return plugins
216
221
 
217
222
  def parse_sources(self, cache=False):
218
- ''' iterate over inventory sources and parse each one to populate it'''
223
+ """ iterate over inventory sources and parse each one to populate it"""
219
224
 
220
225
  parsed = False
221
226
  # allow for multiple inventory parsing
@@ -243,7 +248,7 @@ class InventoryManager(object):
243
248
  host.vars = combine_vars(host.vars, get_vars_from_inventory_sources(self._loader, self._sources, [host], 'inventory'))
244
249
 
245
250
  def parse_source(self, source, cache=False):
246
- ''' Generate or update inventory for the source provided '''
251
+ """ Generate or update inventory for the source provided """
247
252
 
248
253
  parsed = False
249
254
  failures = []
@@ -276,7 +281,6 @@ class InventoryManager(object):
276
281
 
277
282
  # try source with each plugin
278
283
  for plugin in self._fetch_inventory_plugins():
279
-
280
284
  plugin_name = to_text(getattr(plugin, '_load_name', getattr(plugin, '_original_path', '')))
281
285
  display.debug(u'Attempting to use plugin %s (%s)' % (plugin_name, plugin._original_path))
282
286
 
@@ -287,9 +291,14 @@ class InventoryManager(object):
287
291
  plugin_wants = False
288
292
 
289
293
  if plugin_wants:
294
+ # have this tag ready to apply to errors or output; str-ify source since it is often tagged by the CLI
295
+ origin = Origin(description=f'<inventory plugin {plugin_name!r} with source {str(source)!r}>')
290
296
  try:
291
- # FIXME in case plugin fails 1/2 way we have partial inventory
292
- plugin.parse(self._inventory, self._loader, source, cache=cache)
297
+ inventory_wrapper = _InventoryDataWrapper(self._inventory, target_plugin=plugin, origin=origin)
298
+
299
+ # FUTURE: now that we have a wrapper around inventory, we can have it use ChainMaps to preview the in-progress inventory,
300
+ # but be able to roll back partial inventory failures by discarding the outermost layer
301
+ plugin.parse(inventory_wrapper, self._loader, source, cache=cache)
293
302
  try:
294
303
  plugin.update_cache_if_changed()
295
304
  except AttributeError:
@@ -298,14 +307,16 @@ class InventoryManager(object):
298
307
  parsed = True
299
308
  display.vvv('Parsed %s inventory source with %s plugin' % (source, plugin_name))
300
309
  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})
310
+ except AnsibleError as ex:
311
+ if not ex.obj:
312
+ ex.obj = origin
313
+ failures.append({'src': source, 'plugin': plugin_name, 'exc': ex})
314
+ except Exception as ex:
315
+ try:
316
+ # omit line number to prevent contextual display of script or possibly sensitive info
317
+ raise AnsibleError(str(ex), obj=origin) from ex
318
+ except AnsibleError as ex:
319
+ failures.append({'src': source, 'plugin': plugin_name, 'exc': ex})
309
320
  else:
310
321
  display.vvv("%s declined parsing %s as it did not pass its verify_file() method" % (plugin_name, source))
311
322
 
@@ -319,9 +330,8 @@ class InventoryManager(object):
319
330
  if failures:
320
331
  # only if no plugin processed files should we show errors.
321
332
  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']))
333
+ # `obj` should always be set
334
+ display.error_as_warning(msg=f'Failed to parse inventory with {fail["plugin"]!r} plugin.', exception=fail['exc'])
325
335
 
326
336
  # final error/warning on inventory source failure
327
337
  if C.INVENTORY_ANY_UNPARSED_IS_FAILED:
@@ -335,12 +345,12 @@ class InventoryManager(object):
335
345
  return parsed
336
346
 
337
347
  def clear_caches(self):
338
- ''' clear all caches '''
348
+ """ clear all caches """
339
349
  self._hosts_patterns_cache = {}
340
350
  self._pattern_cache = {}
341
351
 
342
352
  def refresh_inventory(self):
343
- ''' recalculate inventory '''
353
+ """ recalculate inventory """
344
354
 
345
355
  self.clear_caches()
346
356
  self._inventory = InventoryData()
@@ -657,9 +667,9 @@ class InventoryManager(object):
657
667
  self._pattern_cache = {}
658
668
 
659
669
  def add_dynamic_host(self, host_info, result_item):
660
- '''
670
+ """
661
671
  Helper function to add a new host to inventory based on a task result.
662
- '''
672
+ """
663
673
 
664
674
  changed = False
665
675
  if not result_item.get('refresh'):
@@ -697,10 +707,10 @@ class InventoryManager(object):
697
707
  result_item['changed'] = changed
698
708
 
699
709
  def add_dynamic_group(self, host, result_item):
700
- '''
710
+ """
701
711
  Helper function to add a group (if it does not exist), and to assign the
702
712
  specified host to that group.
703
- '''
713
+ """
704
714
 
705
715
  changed = False
706
716
 
@@ -749,3 +759,36 @@ class InventoryManager(object):
749
759
  self.reconcile_inventory()
750
760
 
751
761
  result_item['changed'] = changed
762
+
763
+
764
+ class _InventoryDataWrapper(_wrapt.ObjectProxy):
765
+ """
766
+ Proxy wrapper around InventoryData.
767
+ Allows `set_variable` calls to automatically apply template trust for plugins that don't know how.
768
+ """
769
+
770
+ # declared as class attrs to signal to ObjectProxy that we want them stored on the proxy, not the wrapped value
771
+ _target_plugin = None
772
+ _default_origin = None
773
+
774
+ def __init__(self, referent: InventoryData, target_plugin: BaseInventoryPlugin, origin: Origin) -> None:
775
+ super().__init__(referent)
776
+ self._target_plugin = target_plugin
777
+ # fallback origin to ensure that vars are tagged with at least the file they came from
778
+ self._default_origin = origin
779
+
780
+ @functools.cached_property
781
+ def _inspector(self) -> _json.AnsibleVariableVisitor:
782
+ """
783
+ Inventory plugins can delegate to other plugins (e.g. `auto`).
784
+ This hack defers sampling the target plugin's `trusted_by_default` attr until `set_variable` is called, typically inside `parse`.
785
+ Trust is then optionally applied based on the plugin's declared intent via `trusted_by_default`.
786
+ """
787
+ return _json.AnsibleVariableVisitor(
788
+ trusted_as_template=self._target_plugin.trusted_by_default,
789
+ origin=self._default_origin,
790
+ allow_encrypted_string=True,
791
+ )
792
+
793
+ def set_variable(self, entity: str, varname: str, value: t.Any) -> None:
794
+ 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