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
@@ -4,13 +4,24 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- from ansible import constants as C
8
- from ansible.parsing.dataloader import DataLoader
7
+ import collections.abc as _c
8
+ import dataclasses
9
+ import functools
10
+ import typing as t
11
+
12
+ from ansible import constants
13
+ from ansible.utils import vars as _vars
9
14
  from ansible.vars.clean import module_response_deepcopy, strip_internal_keys
15
+ from ansible.module_utils.common import messages as _messages
16
+ from ansible._internal import _collection_proxy
17
+
18
+ if t.TYPE_CHECKING:
19
+ from ansible.inventory.host import Host
20
+ from ansible.playbook.task import Task
10
21
 
11
22
  _IGNORE = ('failed', 'skipped')
12
- _PRESERVE = ('attempts', 'changed', 'retries')
13
- _SUB_PRESERVE = {'_ansible_delegated_vars': ('ansible_host', 'ansible_port', 'ansible_user', 'ansible_connection')}
23
+ _PRESERVE = {'attempts', 'changed', 'retries', '_ansible_no_log'}
24
+ _SUB_PRESERVE = {'_ansible_delegated_vars': {'ansible_host', 'ansible_port', 'ansible_user', 'ansible_connection'}}
14
25
 
15
26
  # stuff callbacks need
16
27
  CLEAN_EXCEPTIONS = (
@@ -21,61 +32,120 @@ CLEAN_EXCEPTIONS = (
21
32
  )
22
33
 
23
34
 
24
- class TaskResult:
25
- '''
35
+ @t.final
36
+ @dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
37
+ class _WireTaskResult:
38
+ """A thin version of `_RawTaskResult` which can be sent over the worker queue."""
39
+
40
+ host_name: str
41
+ task_uuid: str
42
+ return_data: _c.MutableMapping[str, object]
43
+ task_fields: _c.Mapping[str, object]
44
+
45
+
46
+ class _BaseTaskResult:
47
+ """
26
48
  This class is responsible for interpreting the resulting data
27
49
  from an executed task, and provides helper methods for determining
28
50
  the result of a given task.
29
- '''
51
+ """
30
52
 
31
- def __init__(self, host, task, return_data, task_fields=None):
32
- self._host = host
33
- self._task = task
53
+ def __init__(self, host: Host, task: Task, return_data: _c.MutableMapping[str, t.Any], task_fields: _c.Mapping[str, t.Any]) -> None:
54
+ self.__host = host
55
+ self.__task = task
56
+ self._return_data = return_data # FIXME: this should be immutable, but strategy result processing mutates it in some corner cases
57
+ self.__task_fields = task_fields
34
58
 
35
- if isinstance(return_data, dict):
36
- self._result = return_data.copy()
37
- else:
38
- self._result = DataLoader().load(return_data)
59
+ @property
60
+ def host(self) -> Host:
61
+ """The host associated with this result."""
62
+ return self.__host
39
63
 
40
- if task_fields is None:
41
- self._task_fields = dict()
42
- else:
43
- self._task_fields = task_fields
64
+ @property
65
+ def _host(self) -> Host:
66
+ """Use the `host` property when supporting only ansible-core 2.19 or later."""
67
+ # deprecated: description='Deprecate `_host` in favor of `host`' core_version='2.23'
68
+ return self.__host
69
+
70
+ @property
71
+ def task(self) -> Task:
72
+ """The task associated with this result."""
73
+ return self.__task
74
+
75
+ @property
76
+ def _task(self) -> Task:
77
+ """Use the `task` property when supporting only ansible-core 2.19 or later."""
78
+ # deprecated: description='Deprecate `_task` in favor of `task`' core_version='2.23'
79
+ return self.__task
80
+
81
+ @property
82
+ def task_fields(self) -> _c.Mapping[str, t.Any]:
83
+ """The task fields associated with this result."""
84
+ return self.__task_fields
85
+
86
+ @property
87
+ def _task_fields(self) -> _c.Mapping[str, t.Any]:
88
+ """Use the `task_fields` property when supporting only ansible-core 2.19 or later."""
89
+ # deprecated: description='Deprecate `_task_fields` in favor of `task`' core_version='2.23'
90
+ return self.__task_fields
91
+
92
+ @property
93
+ def exception(self) -> _messages.ErrorSummary | None:
94
+ """The error from this task result, if any."""
95
+ return self._return_data.get('exception')
96
+
97
+ @property
98
+ def warnings(self) -> _c.Sequence[_messages.WarningSummary]:
99
+ """The warnings for this task, if any."""
100
+ return _collection_proxy.SequenceProxy(self._return_data.get('warnings') or [])
101
+
102
+ @property
103
+ def deprecations(self) -> _c.Sequence[_messages.DeprecationSummary]:
104
+ """The deprecation warnings for this task, if any."""
105
+ return _collection_proxy.SequenceProxy(self._return_data.get('deprecations') or [])
106
+
107
+ @property
108
+ def _loop_results(self) -> list[_c.MutableMapping[str, t.Any]]:
109
+ """Return a list of loop results. If no loop results are present, an empty list is returned."""
110
+ results = self._return_data.get('results')
111
+
112
+ if not isinstance(results, list):
113
+ return []
114
+
115
+ return results
44
116
 
45
117
  @property
46
- def task_name(self):
47
- return self._task_fields.get('name', None) or self._task.get_name()
118
+ def task_name(self) -> str:
119
+ return str(self.task_fields.get('name', '')) or self.task.get_name()
48
120
 
49
- def is_changed(self):
121
+ def is_changed(self) -> bool:
50
122
  return self._check_key('changed')
51
123
 
52
- def is_skipped(self):
53
- # loop results
54
- if 'results' in self._result:
55
- results = self._result['results']
124
+ def is_skipped(self) -> bool:
125
+ if self._loop_results:
56
126
  # Loop tasks are only considered skipped if all items were skipped.
57
127
  # some squashed results (eg, dnf) are not dicts and can't be skipped individually
58
- if results and all(isinstance(res, dict) and res.get('skipped', False) for res in results):
128
+ if all(isinstance(loop_res, dict) and loop_res.get('skipped', False) for loop_res in self._loop_results):
59
129
  return True
60
130
 
61
131
  # regular tasks and squashed non-dict results
62
- return self._result.get('skipped', False)
132
+ return bool(self._return_data.get('skipped', False))
63
133
 
64
- def is_failed(self):
65
- if 'failed_when_result' in self._result or \
66
- 'results' in self._result and True in [True for x in self._result['results'] if 'failed_when_result' in x]:
134
+ def is_failed(self) -> bool:
135
+ if 'failed_when_result' in self._return_data or any(isinstance(loop_res, dict) and 'failed_when_result' in loop_res for loop_res in self._loop_results):
67
136
  return self._check_key('failed_when_result')
68
- else:
69
- return self._check_key('failed')
70
137
 
71
- def is_unreachable(self):
138
+ return self._check_key('failed')
139
+
140
+ def is_unreachable(self) -> bool:
72
141
  return self._check_key('unreachable')
73
142
 
74
- def needs_debugger(self, globally_enabled=False):
75
- _debugger = self._task_fields.get('debugger')
76
- _ignore_errors = C.TASK_DEBUGGER_IGNORE_ERRORS and self._task_fields.get('ignore_errors')
143
+ def needs_debugger(self, globally_enabled: bool = False) -> bool:
144
+ _debugger = self.task_fields.get('debugger')
145
+ _ignore_errors = constants.TASK_DEBUGGER_IGNORE_ERRORS and self.task_fields.get('ignore_errors')
77
146
 
78
147
  ret = False
148
+
79
149
  if globally_enabled and ((self.is_failed() and not _ignore_errors) or self.is_unreachable()):
80
150
  ret = True
81
151
 
@@ -92,62 +162,97 @@ class TaskResult:
92
162
 
93
163
  return ret
94
164
 
95
- def _check_key(self, key):
96
- '''get a specific key from the result or its items'''
165
+ def _check_key(self, key: str) -> bool:
166
+ """Fetch a specific named boolean value from the result; if missing, a logical OR of the value from nested loop results; False for non-loop results."""
167
+ if (value := self._return_data.get(key, ...)) is not ...:
168
+ return bool(value)
97
169
 
98
- if isinstance(self._result, dict) and key in self._result:
99
- return self._result.get(key, False)
100
- else:
101
- flag = False
102
- for res in self._result.get('results', []):
103
- if isinstance(res, dict):
104
- flag |= res.get(key, False)
105
- return flag
170
+ return any(isinstance(result, dict) and result.get(key) for result in self._loop_results)
106
171
 
107
- def clean_copy(self):
108
172
 
109
- ''' returns 'clean' taskresult object '''
173
+ @t.final
174
+ class _RawTaskResult(_BaseTaskResult):
175
+ def as_wire_task_result(self) -> _WireTaskResult:
176
+ """Return a `_WireTaskResult` from this instance."""
177
+ return _WireTaskResult(
178
+ host_name=self.host.name,
179
+ task_uuid=self.task._uuid,
180
+ return_data=self._return_data,
181
+ task_fields=self.task_fields,
182
+ )
110
183
 
111
- # FIXME: clean task_fields, _task and _host copies
112
- result = TaskResult(self._host, self._task, {}, self._task_fields)
184
+ def as_callback_task_result(self) -> CallbackTaskResult:
185
+ """Return a `CallbackTaskResult` from this instance."""
186
+ ignore: tuple[str, ...]
113
187
 
114
188
  # statuses are already reflected on the event type
115
- if result._task and result._task.action in C._ACTION_DEBUG:
189
+ if self.task and self.task.action in constants._ACTION_DEBUG:
116
190
  # debug is verbose by default to display vars, no need to add invocation
117
191
  ignore = _IGNORE + ('invocation',)
118
192
  else:
119
193
  ignore = _IGNORE
120
194
 
121
- subset = {}
195
+ subset: dict[str, dict[str, object]] = {}
196
+
122
197
  # preserve subset for later
123
- for sub in _SUB_PRESERVE:
124
- if sub in self._result:
125
- subset[sub] = {}
126
- for key in _SUB_PRESERVE[sub]:
127
- if key in self._result[sub]:
128
- subset[sub][key] = self._result[sub][key]
129
-
130
- if isinstance(self._task.no_log, bool) and self._task.no_log or self._result.get('_ansible_no_log', False):
131
- x = {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result"}
132
-
133
- # preserve full
134
- for preserve in _PRESERVE:
135
- if preserve in self._result:
136
- x[preserve] = self._result[preserve]
137
-
138
- result._result = x
139
- elif self._result:
140
- result._result = module_response_deepcopy(self._result)
141
-
142
- # actually remove
143
- for remove_key in ignore:
144
- if remove_key in result._result:
145
- del result._result[remove_key]
198
+ for sub, sub_keys in _SUB_PRESERVE.items():
199
+ sub_data = self._return_data.get(sub)
200
+
201
+ if isinstance(sub_data, dict):
202
+ subset[sub] = {key: value for key, value in sub_data.items() if key in sub_keys}
203
+
204
+ # DTFIX-FUTURE: is checking no_log here redundant now that we use _ansible_no_log everywhere?
205
+ if isinstance(self.task.no_log, bool) and self.task.no_log or self._return_data.get('_ansible_no_log'):
206
+ censored_result = censor_result(self._return_data)
207
+
208
+ if self._loop_results:
209
+ # maintain shape for loop results so callback behavior recognizes a loop was performed
210
+ censored_result.update(results=[
211
+ censor_result(loop_res) if isinstance(loop_res, dict) and loop_res.get('_ansible_no_log') else loop_res for loop_res in self._loop_results
212
+ ])
213
+
214
+ return_data = censored_result
215
+ elif self._return_data:
216
+ return_data = {k: v for k, v in module_response_deepcopy(self._return_data).items() if k not in ignore}
146
217
 
147
218
  # remove almost ALL internal keys, keep ones relevant to callback
148
- strip_internal_keys(result._result, exceptions=CLEAN_EXCEPTIONS)
219
+ strip_internal_keys(return_data, exceptions=CLEAN_EXCEPTIONS)
220
+ else:
221
+ return_data = {}
149
222
 
150
223
  # keep subset
151
- result._result.update(subset)
224
+ return_data.update(subset)
225
+
226
+ return CallbackTaskResult(self.host, self.task, return_data, self.task_fields)
227
+
228
+
229
+ @t.final
230
+ class CallbackTaskResult(_BaseTaskResult):
231
+ """Public contract of TaskResult """
232
+
233
+ # DTFIX-RELEASE: find a better home for this since it's public API
234
+
235
+ @property
236
+ def _result(self) -> _c.MutableMapping[str, t.Any]:
237
+ """Use the `result` property when supporting only ansible-core 2.19 or later."""
238
+ # deprecated: description='Deprecate `_result` in favor of `result`' core_version='2.23'
239
+ return self.result
240
+
241
+ @functools.cached_property
242
+ def result(self) -> _c.MutableMapping[str, t.Any]:
243
+ """
244
+ Returns a cached copy of the task result dictionary for consumption by callbacks.
245
+ Internal custom types are transformed to native Python types to facilitate access and serialization.
246
+ """
247
+ return t.cast(_c.MutableMapping[str, t.Any], _vars.transform_to_native_types(self._return_data))
248
+
249
+
250
+ TaskResult = CallbackTaskResult
251
+ """Compatibility name for the pre-2.19 callback-shaped TaskResult passed to callbacks."""
252
+
253
+
254
+ def censor_result(result: _c.Mapping[str, t.Any]) -> dict[str, t.Any]:
255
+ censored_result = {key: value for key in _PRESERVE if (value := result.get(key, ...)) is not ...}
256
+ censored_result.update(censored="the output has been hidden due to the fact that 'no_log: true' was specified for this result")
152
257
 
153
- return result
258
+ return censored_result
@@ -18,7 +18,7 @@
18
18
  # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
19
19
  #
20
20
  ########################################################################
21
- ''' This manages remote shared Ansible objects, mainly roles'''
21
+ """ This manages remote shared Ansible objects, mainly roles"""
22
22
 
23
23
  from __future__ import annotations
24
24
 
@@ -40,7 +40,7 @@ def get_collections_galaxy_meta_info():
40
40
 
41
41
 
42
42
  class Galaxy(object):
43
- ''' Keeps global galaxy info '''
43
+ """ Keeps global galaxy info """
44
44
 
45
45
  def __init__(self):
46
46
  # TODO: eventually remove this as it contains a mismash of properties that aren't really global
ansible/galaxy/api.py CHANGED
@@ -57,13 +57,13 @@ def should_retry_error(exception):
57
57
  if isinstance(exception, GalaxyError) and exception.http_code in RETRY_HTTP_ERROR_CODES:
58
58
  return True
59
59
 
60
- if isinstance(exception, AnsibleError) and (orig_exc := getattr(exception, 'orig_exc', None)):
60
+ if isinstance(exception, AnsibleError) and (cause := exception.__cause__):
61
61
  # URLError is often a proxy for an underlying error, handle wrapped exceptions
62
- if isinstance(orig_exc, URLError):
63
- orig_exc = orig_exc.reason
62
+ if isinstance(cause, URLError):
63
+ cause = cause.reason
64
64
 
65
65
  # Handle common URL related errors
66
- if isinstance(orig_exc, (TimeoutError, BadStatusLine, IncompleteRead)):
66
+ if isinstance(cause, (TimeoutError, BadStatusLine, IncompleteRead)):
67
67
  return True
68
68
 
69
69
  return False
@@ -408,11 +408,8 @@ class GalaxyAPI:
408
408
  method=method, timeout=self._server_timeout, http_agent=user_agent(), follow_redirects='safe')
409
409
  except HTTPError as e:
410
410
  raise GalaxyError(e, error_context_msg)
411
- except Exception as e:
412
- raise AnsibleError(
413
- "Unknown error when attempting to call Galaxy at '%s': %s" % (url, to_native(e)),
414
- orig_exc=e
415
- )
411
+ except Exception as ex:
412
+ raise AnsibleError(f"Unknown error when attempting to call Galaxy at {url!r}.") from ex
416
413
 
417
414
  resp_data = to_text(resp.read(), errors='surrogate_or_strict')
418
415
  try:
@@ -471,8 +468,8 @@ class GalaxyAPI:
471
468
  resp = open_url(url, data=args, validate_certs=self.validate_certs, method="POST", http_agent=user_agent(), timeout=self._server_timeout)
472
469
  except HTTPError as e:
473
470
  raise GalaxyError(e, 'Attempting to authenticate to galaxy')
474
- except Exception as e:
475
- raise AnsibleError('Unable to authenticate to galaxy: %s' % to_native(e), orig_exc=e)
471
+ except Exception as ex:
472
+ raise AnsibleError('Unable to authenticate to galaxy.') from ex
476
473
 
477
474
  data = json.loads(to_text(resp.read(), errors='surrogate_or_strict'))
478
475
  return data
@@ -817,8 +814,17 @@ class GalaxyAPI:
817
814
 
818
815
  signatures = data.get('signatures') or []
819
816
 
817
+ download_url_info = urlparse(data['download_url'])
818
+ if not download_url_info.scheme and not download_url_info.path.startswith('/'):
819
+ # galaxy does a lot of redirects, with much more complex pathing than we use
820
+ # within this codebase, without updating _call_galaxy to be able to return
821
+ # the final URL, we can't reliably build a relative URL.
822
+ raise AnsibleError(f'Invalid non absolute download_url: {data["download_url"]}')
823
+
824
+ download_url = urljoin(self.api_server, data['download_url'])
825
+
820
826
  return CollectionVersionMetadata(data['namespace']['name'], data['collection']['name'], data['version'],
821
- data['download_url'], data['artifact']['sha256'],
827
+ download_url, data['artifact']['sha256'],
822
828
  data['metadata']['dependencies'], data['href'], signatures)
823
829
 
824
830
  @g_connect(['v2', 'v3'])
@@ -896,12 +902,10 @@ class GalaxyAPI:
896
902
  if not next_link:
897
903
  break
898
904
  elif relative_link:
899
- # TODO: This assumes the pagination result is relative to the root server. Will need to be verified
900
- # with someone who knows the AH API.
901
-
902
- # Remove the query string from the versions_url to use the next_link's query
903
- versions_url = urljoin(versions_url, urlparse(versions_url).path)
904
- next_link = versions_url.replace(versions_url_info.path, next_link)
905
+ next_link_info = urlparse(next_link)
906
+ if not next_link_info.scheme and not next_link_info.path.startswith('/'):
907
+ raise AnsibleError(f'Invalid non absolute pagination link: {next_link}')
908
+ next_link = urljoin(self.api_server, next_link)
905
909
 
906
910
  data = self._call_galaxy(to_native(next_link, errors='surrogate_or_strict'),
907
911
  error_context_msg=error_context_msg, cache=True, cache_key=cache_key)
@@ -126,13 +126,13 @@ from ansible.galaxy.dependency_resolution.dataclasses import (
126
126
  from ansible.galaxy.dependency_resolution.versioning import meets_requirements
127
127
  from ansible.plugins.loader import get_all_plugin_loaders
128
128
  from ansible.module_utils.common.file import S_IRWU_RG_RO, S_IRWXU_RXG_RXO, S_IXANY
129
+ from ansible.module_utils.common.sentinel import Sentinel
129
130
  from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
130
131
  from ansible.module_utils.common.collections import is_sequence
131
132
  from ansible.module_utils.common.yaml import yaml_dump
132
133
  from ansible.utils.collection_loader import AnsibleCollectionRef
133
134
  from ansible.utils.display import Display
134
135
  from ansible.utils.hashing import secure_hash, secure_hash_s
135
- from ansible.utils.sentinel import Sentinel
136
136
 
137
137
 
138
138
  display = Display()
@@ -35,10 +35,10 @@ from ansible.module_utils.common.text.converters import to_bytes, to_native, to_
35
35
  from ansible.module_utils.api import retry_with_delays_and_condition
36
36
  from ansible.module_utils.api import generate_jittered_backoff
37
37
  from ansible.module_utils.common.process import get_bin_path
38
+ from ansible.module_utils.common.sentinel import Sentinel
38
39
  from ansible.module_utils.common.yaml import yaml_load
39
40
  from ansible.module_utils.urls import open_url
40
41
  from ansible.utils.display import Display
41
- from ansible.utils.sentinel import Sentinel
42
42
 
43
43
  import ansible.constants as C
44
44
 
@@ -485,16 +485,13 @@ def _download_file(url, b_path, expected_hash, validate_certs, token=None, timeo
485
485
  display.display("Downloading %s to %s" % (url, to_text(b_tarball_dir)))
486
486
  # NOTE: Galaxy redirects downloads to S3 which rejects the request
487
487
  # NOTE: if an Authorization header is attached so don't redirect it
488
- try:
489
- resp = open_url(
490
- to_native(url, errors='surrogate_or_strict'),
491
- validate_certs=validate_certs,
492
- headers=None if token is None else token.headers(),
493
- unredirected_headers=['Authorization'], http_agent=user_agent(),
494
- timeout=timeout
495
- )
496
- except Exception as err:
497
- raise AnsibleError(to_native(err), orig_exc=err)
488
+ resp = open_url(
489
+ to_native(url, errors='surrogate_or_strict'),
490
+ validate_certs=validate_certs,
491
+ headers=None if token is None else token.headers(),
492
+ unredirected_headers=['Authorization'], http_agent=user_agent(),
493
+ timeout=timeout
494
+ )
498
495
 
499
496
  with open(b_file_path, 'wb') as download_file: # type: t.BinaryIO
500
497
  actual_hash = _consume_file(resp, write_to=download_file)
@@ -7,6 +7,7 @@
7
7
  from __future__ import annotations
8
8
 
9
9
  import os
10
+ import pathlib
10
11
  import typing as t
11
12
 
12
13
  from collections import namedtuple
@@ -25,6 +26,8 @@ if t.TYPE_CHECKING:
25
26
  '_ComputedReqKindsMixin',
26
27
  )
27
28
 
29
+ import ansible
30
+ import ansible.release
28
31
 
29
32
  from ansible.errors import AnsibleError, AnsibleAssertionError
30
33
  from ansible.galaxy.api import GalaxyAPI
@@ -39,6 +42,7 @@ _ALLOW_CONCRETE_POINTER_IN_SOURCE = False # NOTE: This is a feature flag
39
42
  _GALAXY_YAML = b'galaxy.yml'
40
43
  _MANIFEST_JSON = b'MANIFEST.json'
41
44
  _SOURCE_METADATA_FILE = b'GALAXY.yml'
45
+ _ANSIBLE_PACKAGE_PATH = pathlib.Path(ansible.__file__).parent
42
46
 
43
47
  display = Display()
44
48
 
@@ -224,6 +228,13 @@ class _ComputedReqKindsMixin:
224
228
  if dir_path.endswith(to_bytes(os.path.sep)):
225
229
  dir_path = dir_path.rstrip(to_bytes(os.path.sep))
226
230
  if not _is_collection_dir(dir_path):
231
+ dir_pathlib = pathlib.Path(to_text(dir_path))
232
+
233
+ # special handling for bundled collections without manifests, e.g., ansible._protomatter
234
+ if dir_pathlib.is_relative_to(_ANSIBLE_PACKAGE_PATH):
235
+ req_name = f'{dir_pathlib.parent.name}.{dir_pathlib.name}'
236
+ return cls(req_name, ansible.release.__version__, dir_path, 'dir', None)
237
+
227
238
  display.warning(
228
239
  u"Collection at '{path!s}' does not have a {manifest_json!s} "
229
240
  u'file, nor has it {galaxy_yml!s}: cannot detect version.'.
@@ -578,10 +589,9 @@ class _ComputedReqKindsMixin:
578
589
 
579
590
  See https://github.com/ansible/ansible/pull/81606 for extra context.
580
591
  """
581
- version_string = self.ver[0]
582
- return version_string.isdigit() or not (
583
- version_string == '*' or
584
- version_string.startswith(('<', '>', '!='))
592
+ version_spec_start_char = self.ver[0]
593
+ return version_spec_start_char.isdigit() or not (
594
+ version_spec_start_char.startswith(('<', '>', '!', '*'))
585
595
  )
586
596
 
587
597
  @property
@@ -39,7 +39,7 @@ except ImportError:
39
39
 
40
40
  # TODO: add python requirements to ansible-test's ansible-core distribution info and remove the hardcoded lowerbound/upperbound fallback
41
41
  RESOLVELIB_LOWERBOUND = SemanticVersion("0.5.3")
42
- RESOLVELIB_UPPERBOUND = SemanticVersion("1.1.0")
42
+ RESOLVELIB_UPPERBOUND = SemanticVersion("2.0.0")
43
43
  RESOLVELIB_VERSION = SemanticVersion.from_loose_version(LooseVersion(resolvelib_version))
44
44
 
45
45
 
@@ -5,12 +5,46 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ from collections import defaultdict
9
+
8
10
  try:
9
11
  from resolvelib import BaseReporter
10
12
  except ImportError:
11
13
  class BaseReporter: # type: ignore[no-redef]
12
14
  pass
13
15
 
16
+ try:
17
+ from resolvelib.resolvers import Criterion
18
+ except ImportError:
19
+ class Criterion: # type: ignore[no-redef]
20
+ pass
21
+
22
+ from ansible.utils.display import Display
23
+ from .dataclasses import Candidate, Requirement
24
+
25
+
26
+ display = Display()
27
+
28
+
29
+ _CLI_APP_NAME = 'ansible-galaxy'
30
+ _MESSAGES_AT_REJECT_COUNT = {
31
+ 1: (
32
+ f'{_CLI_APP_NAME} is looking at multiple versions of {{fqcn}} to '
33
+ 'determine which version is compatible with other '
34
+ 'requirements. This could take a while.'
35
+ ),
36
+ 8: (
37
+ f'{_CLI_APP_NAME} is looking at multiple versions of {{fqcn}} to '
38
+ 'determine which version is compatible with other '
39
+ 'requirements. This could take a while.'
40
+ ),
41
+ 13: (
42
+ 'This is taking longer than usual. You might need to provide '
43
+ 'the dependency resolver with stricter constraints to reduce '
44
+ 'runtime. If you want to abort this run, press Ctrl + C.'
45
+ ),
46
+ }
47
+
14
48
 
15
49
  class CollectionDependencyReporter(BaseReporter):
16
50
  """A dependency reporter for Ansible Collections.
@@ -18,3 +52,50 @@ class CollectionDependencyReporter(BaseReporter):
18
52
  This is a proxy class allowing us to abstract away importing resolvelib
19
53
  outside of the `ansible.galaxy.dependency_resolution` Python package.
20
54
  """
55
+
56
+ def __init__(self) -> None:
57
+ """Initialize the collection rejection counter."""
58
+ super().__init__()
59
+
60
+ self.reject_count_by_fqcn: defaultdict[str, int] = defaultdict(int)
61
+
62
+ def _maybe_log_rejection_message(self, candidate: Candidate) -> bool:
63
+ """Print out rejection messages on pre-defined limit hits."""
64
+ # Inspired by https://github.com/pypa/pip/commit/9731131
65
+ self.reject_count_by_fqcn[candidate.fqcn] += 1
66
+
67
+ collection_rejections_count = self.reject_count_by_fqcn[candidate.fqcn]
68
+
69
+ if collection_rejections_count not in _MESSAGES_AT_REJECT_COUNT:
70
+ return False
71
+
72
+ collection_rejection_message = _MESSAGES_AT_REJECT_COUNT[
73
+ collection_rejections_count
74
+ ]
75
+ display.display(collection_rejection_message.format(fqcn=candidate.fqcn))
76
+
77
+ return True
78
+
79
+ def rejecting_candidate( # resolvelib >= 0.9.0
80
+ self,
81
+ criterion: Criterion[Candidate, Requirement],
82
+ candidate: Candidate,
83
+ ) -> None:
84
+ """Augment rejection messages with conflict details."""
85
+ if not self._maybe_log_rejection_message(candidate):
86
+ return
87
+
88
+ msg = 'Will try a different candidate, due to conflict:'
89
+ for req_info in criterion.information:
90
+ req, parent = req_info.requirement, req_info.parent
91
+ msg += '\n '
92
+ if parent:
93
+ msg += f'{parent !s} depends on '
94
+ else:
95
+ msg += 'The user requested '
96
+ msg += str(req)
97
+ display.v(msg)
98
+
99
+ def backtracking(self, candidate: Candidate) -> None: # resolvelib < 0.9.0
100
+ """Print out rejection messages on pre-defined limit hits."""
101
+ self._maybe_log_rejection_message(candidate)
ansible/galaxy/role.py CHANGED
@@ -185,13 +185,11 @@ class GalaxyRole(object):
185
185
  info_path = os.path.join(self.path, self.META_INSTALL)
186
186
  if os.path.isfile(info_path):
187
187
  try:
188
- f = open(info_path, 'r')
189
- self._install_info = yaml_load(f)
188
+ with open(info_path, 'r') as f:
189
+ self._install_info = yaml_load(f)
190
190
  except Exception:
191
191
  display.vvvvv("Unable to load Galaxy install info for %s" % self.name)
192
192
  return False
193
- finally:
194
- f.close()
195
193
  return self._install_info
196
194
 
197
195
  @property
@@ -470,12 +468,10 @@ class GalaxyRole(object):
470
468
  meta_path = os.path.join(self.path, meta_requirements)
471
469
  if os.path.isfile(meta_path):
472
470
  try:
473
- f = open(meta_path, 'r')
474
- self._requirements = yaml_load(f)
471
+ with open(meta_path, 'r') as f:
472
+ self._requirements = yaml_load(f)
475
473
  except Exception:
476
474
  display.vvvvv("Unable to load requirements for %s" % self.name)
477
- finally:
478
- f.close()
479
475
 
480
476
  break
481
477