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
@@ -0,0 +1,928 @@
1
+ from __future__ import annotations
2
+
3
+ import abc
4
+ import collections.abc as c
5
+ import copy
6
+ import dataclasses
7
+ import datetime
8
+ import inspect
9
+ import sys
10
+
11
+ from itertools import chain
12
+
13
+ # deprecated: description='typing.Self exists in Python 3.11+' python_version='3.10'
14
+ from ansible.module_utils.compat import typing as t
15
+
16
+ from ansible.module_utils._internal import _dataclass_validation
17
+ from ansible.module_utils._internal._patches import _sys_intern_patch, _socket_patch
18
+
19
+ _sys_intern_patch.SysInternPatch.patch()
20
+ _socket_patch.GetAddrInfoPatch.patch() # DTFIX-FUTURE: consider replacing this with a socket import shim that installs the patch
21
+
22
+ if sys.version_info >= (3, 10):
23
+ # Using slots for reduced memory usage and improved performance.
24
+ _tag_dataclass_kwargs = dict(frozen=True, repr=False, kw_only=True, slots=True)
25
+ else:
26
+ # deprecated: description='always use dataclass slots and keyword-only args' python_version='3.9'
27
+ _tag_dataclass_kwargs = dict(frozen=True, repr=False)
28
+
29
+ _T = t.TypeVar('_T')
30
+ _TAnsibleSerializable = t.TypeVar('_TAnsibleSerializable', bound='AnsibleSerializable')
31
+ _TAnsibleDatatagBase = t.TypeVar('_TAnsibleDatatagBase', bound='AnsibleDatatagBase')
32
+ _TAnsibleTaggedObject = t.TypeVar('_TAnsibleTaggedObject', bound='AnsibleTaggedObject')
33
+
34
+ _NO_INSTANCE_STORAGE = t.cast(t.Tuple[str], tuple())
35
+ _ANSIBLE_TAGGED_OBJECT_SLOTS = tuple(('_ansible_tags_mapping',))
36
+
37
+ # shared empty frozenset for default values
38
+ _empty_frozenset: t.FrozenSet = frozenset()
39
+
40
+
41
+ class AnsibleTagHelper:
42
+ """Utility methods for working with Ansible data tags."""
43
+
44
+ # DTFIX-RELEASE: bikeshed the name and location of this class, also, related, how much more of it should be exposed as public API?
45
+ # it may make sense to move this into another module, but the implementations should remain here (so they can be used without circular imports here)
46
+ # if they're in a separate module, is a class even needed, or should they be globals?
47
+ # DTFIX-RELEASE: add docstrings to all non-override methods in this class
48
+
49
+ @staticmethod
50
+ def untag(value: _T, *tag_types: t.Type[AnsibleDatatagBase]) -> _T:
51
+ """
52
+ If tags matching any of `tag_types` are present on `value`, return a copy with those tags removed.
53
+ If no `tag_types` are specified and the object has tags, return a copy with all tags removed.
54
+ Otherwise, the original `value` is returned.
55
+ """
56
+ tag_set = AnsibleTagHelper.tags(value)
57
+
58
+ if not tag_set:
59
+ return value
60
+
61
+ if tag_types:
62
+ tags_mapping = _AnsibleTagsMapping((type(tag), tag) for tag in tag_set if type(tag) not in tag_types) # pylint: disable=unidiomatic-typecheck
63
+
64
+ if len(tags_mapping) == len(tag_set):
65
+ return value # if no tags were removed, return the original instance
66
+ else:
67
+ tags_mapping = None
68
+
69
+ if not tags_mapping:
70
+ if t.cast(AnsibleTaggedObject, value)._empty_tags_as_native:
71
+ return t.cast(AnsibleTaggedObject, value)._native_copy()
72
+
73
+ tags_mapping = _EMPTY_INTERNAL_TAGS_MAPPING
74
+
75
+ tagged_type = AnsibleTaggedObject._get_tagged_type(type(value))
76
+
77
+ return t.cast(_T, tagged_type._instance_factory(value, tags_mapping))
78
+
79
+ @staticmethod
80
+ def tags(value: t.Any) -> t.FrozenSet[AnsibleDatatagBase]:
81
+ tags = _try_get_internal_tags_mapping(value)
82
+
83
+ if tags is _EMPTY_INTERNAL_TAGS_MAPPING:
84
+ return _empty_frozenset
85
+
86
+ return frozenset(tags.values())
87
+
88
+ @staticmethod
89
+ def tag_types(value: t.Any) -> t.FrozenSet[t.Type[AnsibleDatatagBase]]:
90
+ tags = _try_get_internal_tags_mapping(value)
91
+
92
+ if tags is _EMPTY_INTERNAL_TAGS_MAPPING:
93
+ return _empty_frozenset
94
+
95
+ return frozenset(tags)
96
+
97
+ @staticmethod
98
+ def base_type(type_or_value: t.Any, /) -> type:
99
+ """Return the friendly type of the given type or value. If the type is an AnsibleTaggedObject, the native type will be used."""
100
+ if isinstance(type_or_value, type):
101
+ the_type = type_or_value
102
+ else:
103
+ the_type = type(type_or_value)
104
+
105
+ if issubclass(the_type, AnsibleTaggedObject):
106
+ the_type = type_or_value._native_type
107
+
108
+ # DTFIX-RELEASE: provide a way to report the real type for debugging purposes
109
+ return the_type
110
+
111
+ @staticmethod
112
+ def as_native_type(value: _T) -> _T:
113
+ """
114
+ Returns an untagged native data type matching the input value, or the original input if the value was not a tagged type.
115
+ Containers are not recursively processed.
116
+ """
117
+ if isinstance(value, AnsibleTaggedObject):
118
+ value = value._native_copy()
119
+
120
+ return value
121
+
122
+ @staticmethod
123
+ @t.overload
124
+ def tag_copy(src: t.Any, value: _T) -> _T: ... # pragma: nocover
125
+
126
+ @staticmethod
127
+ @t.overload
128
+ def tag_copy(src: t.Any, value: t.Any, *, value_type: type[_T]) -> _T: ... # pragma: nocover
129
+
130
+ @staticmethod
131
+ @t.overload
132
+ def tag_copy(src: t.Any, value: _T, *, value_type: None = None) -> _T: ... # pragma: nocover
133
+
134
+ @staticmethod
135
+ def tag_copy(src: t.Any, value: _T, *, value_type: t.Optional[type] = None) -> _T:
136
+ """Return a copy of `value`, with tags copied from `src`, overwriting any existing tags of the same types."""
137
+ src_tags = AnsibleTagHelper.tags(src)
138
+ value_tags = [(tag, tag._get_tag_to_propagate(src, value, value_type=value_type)) for tag in src_tags]
139
+ tags = [tag[1] for tag in value_tags if tag[1] is not None]
140
+ tag_types_to_remove = [type(tag[0]) for tag in value_tags if tag[1] is None]
141
+
142
+ if tag_types_to_remove:
143
+ value = AnsibleTagHelper.untag(value, *tag_types_to_remove)
144
+
145
+ return AnsibleTagHelper.tag(value, tags, value_type=value_type)
146
+
147
+ @staticmethod
148
+ @t.overload
149
+ def tag(value: _T, tags: t.Union[AnsibleDatatagBase, t.Iterable[AnsibleDatatagBase]]) -> _T: ... # pragma: nocover
150
+
151
+ @staticmethod
152
+ @t.overload
153
+ def tag(value: t.Any, tags: t.Union[AnsibleDatatagBase, t.Iterable[AnsibleDatatagBase]], *, value_type: type[_T]) -> _T: ... # pragma: nocover
154
+
155
+ @staticmethod
156
+ @t.overload
157
+ def tag(value: _T, tags: t.Union[AnsibleDatatagBase, t.Iterable[AnsibleDatatagBase]], *, value_type: None = None) -> _T: ... # pragma: nocover
158
+
159
+ @staticmethod
160
+ def tag(value: _T, tags: t.Union[AnsibleDatatagBase, t.Iterable[AnsibleDatatagBase]], *, value_type: t.Optional[type] = None) -> _T:
161
+ """
162
+ Return a copy of `value`, with `tags` applied, overwriting any existing tags of the same types.
163
+ If `value` is an ignored type, or `tags` is empty, the original `value` will be returned.
164
+ If `value` is not taggable, a `NotTaggableError` exception will be raised.
165
+ If `value_type` was given, that type will be returned instead.
166
+ """
167
+ if value_type is None:
168
+ value_type_specified = False
169
+ value_type = type(value)
170
+ else:
171
+ value_type_specified = True
172
+
173
+ # if no tags to apply, just return what we got
174
+ # NB: this only works because the untaggable types are singletons (and thus direct type comparison works)
175
+ if not tags or value_type in _untaggable_types:
176
+ if value_type_specified:
177
+ return value_type(value)
178
+
179
+ return value
180
+
181
+ tag_list: list[AnsibleDatatagBase]
182
+
183
+ # noinspection PyProtectedMember
184
+ if type(tags) in _known_tag_types:
185
+ tag_list = [tags] # type: ignore[list-item]
186
+ else:
187
+ tag_list = list(tags) # type: ignore[arg-type]
188
+
189
+ for idx, tag in enumerate(tag_list):
190
+ # noinspection PyProtectedMember
191
+ if type(tag) not in _known_tag_types:
192
+ # noinspection PyProtectedMember
193
+ raise TypeError(f'tags[{idx}] of type {type(tag)} is not one of {_known_tag_types}')
194
+
195
+ existing_internal_tags_mapping = _try_get_internal_tags_mapping(value)
196
+
197
+ if existing_internal_tags_mapping is not _EMPTY_INTERNAL_TAGS_MAPPING:
198
+ # include the existing tags first so new tags of the same type will overwrite
199
+ tag_list = list(chain(existing_internal_tags_mapping.values(), tag_list))
200
+
201
+ tags_mapping = _AnsibleTagsMapping((type(tag), tag) for tag in tag_list)
202
+ tagged_type = AnsibleTaggedObject._get_tagged_type(value_type)
203
+
204
+ return t.cast(_T, tagged_type._instance_factory(value, tags_mapping))
205
+
206
+ @staticmethod
207
+ def try_tag(value: _T, tags: t.Union[AnsibleDatatagBase, t.Iterable[AnsibleDatatagBase]]) -> _T:
208
+ """
209
+ Return a copy of `value`, with `tags` applied, overwriting any existing tags of the same types.
210
+ If `value` is not taggable or `tags` is empty, the original `value` will be returned.
211
+ """
212
+ try:
213
+ return AnsibleTagHelper.tag(value, tags)
214
+ except NotTaggableError:
215
+ return value
216
+
217
+
218
+ class AnsibleSerializable(metaclass=abc.ABCMeta):
219
+ __slots__ = _NO_INSTANCE_STORAGE
220
+
221
+ _known_type_map: t.ClassVar[t.Dict[str, t.Type['AnsibleSerializable']]] = {}
222
+ _TYPE_KEY: t.ClassVar[str] = '__ansible_type'
223
+
224
+ _type_key: t.ClassVar[str]
225
+
226
+ def __init_subclass__(cls, **kwargs) -> None:
227
+ # this is needed to call __init__subclass__ on mixins for derived types
228
+ super().__init_subclass__(**kwargs)
229
+
230
+ cls._type_key = cls.__name__
231
+
232
+ # DTFIX-FUTURE: is there a better way to exclude non-abstract types which are base classes?
233
+ if not inspect.isabstract(cls) and not cls.__name__.endswith('Base') and cls.__name__ != 'AnsibleTaggedObject':
234
+ AnsibleSerializable._known_type_map[cls._type_key] = cls
235
+
236
+ @classmethod
237
+ @abc.abstractmethod
238
+ def _from_dict(cls: t.Type[_TAnsibleSerializable], d: t.Dict[str, t.Any]) -> object:
239
+ """Return an instance of this type, created from the given dictionary."""
240
+
241
+ @abc.abstractmethod
242
+ def _as_dict(self) -> t.Dict[str, t.Any]:
243
+ """
244
+ Return a serialized version of this instance as a dictionary.
245
+ This operation is *NOT* recursive - the returned dictionary may still include custom types.
246
+ It is the responsibility of the caller to handle recursion of the returned dict.
247
+ """
248
+
249
+ def _serialize(self) -> t.Dict[str, t.Any]:
250
+ value = self._as_dict()
251
+ value.update({AnsibleSerializable._TYPE_KEY: self._type_key})
252
+
253
+ return value
254
+
255
+ @staticmethod
256
+ def _deserialize(data: t.Dict[str, t.Any]) -> object:
257
+ """Deserialize an object from the supplied data dict, which will be mutated if it contains a type key."""
258
+ type_name = data.pop(AnsibleSerializable._TYPE_KEY, ...) # common usage assumes `data` is an intermediate dict provided by a deserializer
259
+
260
+ if type_name is ...:
261
+ return None
262
+
263
+ type_value = AnsibleSerializable._known_type_map.get(type_name)
264
+
265
+ if not type_value:
266
+ raise ValueError(f'An unknown {AnsibleSerializable._TYPE_KEY!r} value {type_name!r} was encountered during deserialization.')
267
+
268
+ return type_value._from_dict(data)
269
+
270
+ def _repr(self, name: str) -> str:
271
+ args = self._as_dict()
272
+ arg_string = ', '.join((f'{k}={v!r}' for k, v in args.items()))
273
+ return f'{name}({arg_string})'
274
+
275
+
276
+ class AnsibleSerializableWrapper(AnsibleSerializable, t.Generic[_T], metaclass=abc.ABCMeta):
277
+ __slots__ = ('_value',)
278
+
279
+ _wrapped_types: t.ClassVar[dict[type, type[AnsibleSerializable]]] = {}
280
+ _wrapped_type: t.ClassVar[type] = type(None)
281
+
282
+ def __init__(self, value: _T) -> None:
283
+ self._value: _T = value
284
+
285
+ def __init_subclass__(cls, **kwargs):
286
+ super().__init_subclass__(**kwargs)
287
+
288
+ cls._wrapped_type = t.get_args(cls.__orig_bases__[0])[0]
289
+ cls._wrapped_types[cls._wrapped_type] = cls
290
+
291
+
292
+ class AnsibleSerializableDate(AnsibleSerializableWrapper[datetime.date]):
293
+ __slots__ = _NO_INSTANCE_STORAGE
294
+
295
+ @classmethod
296
+ def _from_dict(cls: t.Type[_TAnsibleSerializable], d: t.Dict[str, t.Any]) -> datetime.date:
297
+ return datetime.date.fromisoformat(d['iso8601'])
298
+
299
+ def _as_dict(self) -> t.Dict[str, t.Any]:
300
+ return dict(
301
+ iso8601=self._value.isoformat(),
302
+ )
303
+
304
+
305
+ class AnsibleSerializableTime(AnsibleSerializableWrapper[datetime.time]):
306
+ __slots__ = _NO_INSTANCE_STORAGE
307
+
308
+ @classmethod
309
+ def _from_dict(cls: t.Type[_TAnsibleSerializable], d: t.Dict[str, t.Any]) -> datetime.time:
310
+ value = datetime.time.fromisoformat(d['iso8601'])
311
+ value.replace(fold=d['fold'])
312
+
313
+ return value
314
+
315
+ def _as_dict(self) -> t.Dict[str, t.Any]:
316
+ return dict(
317
+ iso8601=self._value.isoformat(),
318
+ fold=self._value.fold,
319
+ )
320
+
321
+
322
+ class AnsibleSerializableDateTime(AnsibleSerializableWrapper[datetime.datetime]):
323
+ __slots__ = _NO_INSTANCE_STORAGE
324
+
325
+ @classmethod
326
+ def _from_dict(cls: t.Type[_TAnsibleSerializable], d: t.Dict[str, t.Any]) -> datetime.datetime:
327
+ value = datetime.datetime.fromisoformat(d['iso8601'])
328
+ value.replace(fold=d['fold'])
329
+
330
+ return value
331
+
332
+ def _as_dict(self) -> t.Dict[str, t.Any]:
333
+ return dict(
334
+ iso8601=self._value.isoformat(),
335
+ fold=self._value.fold,
336
+ )
337
+
338
+
339
+ @dataclasses.dataclass(**_tag_dataclass_kwargs)
340
+ class AnsibleSerializableDataclass(AnsibleSerializable, metaclass=abc.ABCMeta):
341
+ _validation_allow_subclasses = True
342
+
343
+ def _as_dict(self) -> t.Dict[str, t.Any]:
344
+ # omit None values when None is the field default
345
+ # DTFIX-RELEASE: this implementation means we can never change the default on fields which have None for their default
346
+ # other defaults can be changed -- but there's no way to override this behavior either way for other default types
347
+ # it's a trip hazard to have the default logic here, rather than per field (or not at all)
348
+ # consider either removing the filtering or requiring it to be explicitly set per field using dataclass metadata
349
+ fields = ((field, getattr(self, field.name)) for field in dataclasses.fields(self))
350
+ return {field.name: value for field, value in fields if value is not None or field.default is not None}
351
+
352
+ @classmethod
353
+ def _from_dict(cls, d: t.Dict[str, t.Any]) -> t.Self:
354
+ # DTFIX-RELEASE: optimize this to avoid the dataclasses fields metadata and get_origin stuff at runtime
355
+ type_hints = t.get_type_hints(cls)
356
+ mutated_dict: dict[str, t.Any] | None = None
357
+
358
+ for field in dataclasses.fields(cls):
359
+ if t.get_origin(type_hints[field.name]) is tuple: # NOTE: only supports bare tuples, not optional or inside a union
360
+ if type(field_value := d.get(field.name)) is list: # pylint: disable=unidiomatic-typecheck
361
+ if mutated_dict is None:
362
+ mutated_dict = d.copy()
363
+
364
+ mutated_dict[field.name] = tuple(field_value)
365
+
366
+ return cls(**(mutated_dict or d))
367
+
368
+ def __init_subclass__(cls, **kwargs) -> None:
369
+ super(AnsibleSerializableDataclass, cls).__init_subclass__(**kwargs) # cannot use super() without arguments when using slots
370
+
371
+ _dataclass_validation.inject_post_init_validation(cls, cls._validation_allow_subclasses) # code gen a real __post_init__ method
372
+
373
+
374
+ class Tripwire:
375
+ """Marker mixin for types that should raise an error when encountered."""
376
+
377
+ __slots__ = _NO_INSTANCE_STORAGE
378
+
379
+ def trip(self) -> t.NoReturn:
380
+ """Derived types should implement a failure behavior."""
381
+ raise NotImplementedError()
382
+
383
+
384
+ @dataclasses.dataclass(**_tag_dataclass_kwargs)
385
+ class AnsibleDatatagBase(AnsibleSerializableDataclass, metaclass=abc.ABCMeta):
386
+ """
387
+ Base class for data tagging tag types.
388
+ New tag types need to be considered very carefully; e.g.: which serialization/runtime contexts they're allowed in, fallback behavior, propagation.
389
+ """
390
+
391
+ _validation_allow_subclasses = False
392
+
393
+ def __init_subclass__(cls, **kwargs) -> None:
394
+ # NOTE: This method is called twice when the datatag type is a dataclass.
395
+ super(AnsibleDatatagBase, cls).__init_subclass__(**kwargs) # cannot use super() without arguments when using slots
396
+
397
+ # DTFIX-FUTURE: "freeze" this after module init has completed to discourage custom external tag subclasses
398
+
399
+ # DTFIX-FUTURE: is there a better way to exclude non-abstract types which are base classes?
400
+ if not inspect.isabstract(cls) and not cls.__name__.endswith('Base'):
401
+ existing = _known_tag_type_map.get(cls.__name__)
402
+
403
+ if existing:
404
+ # When the datatag type is a dataclass, the first instance will be the non-dataclass type.
405
+ # It must be removed from the known tag types before adding the dataclass version.
406
+ _known_tag_types.remove(existing)
407
+
408
+ _known_tag_type_map[cls.__name__] = cls
409
+ _known_tag_types.add(cls)
410
+
411
+ @classmethod
412
+ def is_tagged_on(cls, value: t.Any) -> bool:
413
+ return cls in _try_get_internal_tags_mapping(value)
414
+
415
+ @classmethod
416
+ def first_tagged_on(cls, *values: t.Any) -> t.Any | None:
417
+ """Return the first value which is tagged with this type, or None if no match is found."""
418
+ for value in values:
419
+ if cls.is_tagged_on(value):
420
+ return value
421
+
422
+ return None
423
+
424
+ @classmethod
425
+ def get_tag(cls, value: t.Any) -> t.Optional[t.Self]:
426
+ return _try_get_internal_tags_mapping(value).get(cls)
427
+
428
+ @classmethod
429
+ def get_required_tag(cls, value: t.Any) -> t.Self:
430
+ if (tag := cls.get_tag(value)) is None:
431
+ # DTFIX-FUTURE: we really should have a way to use AnsibleError with obj in module_utils when it's controller-side
432
+ raise ValueError(f'The type {type(value).__name__!r} is not tagged with {cls.__name__!r}.')
433
+
434
+ return tag
435
+
436
+ @classmethod
437
+ def untag(cls, value: _T) -> _T:
438
+ """
439
+ If this tag type is present on `value`, return a copy with that tag removed.
440
+ Otherwise, the original `value` is returned.
441
+ """
442
+ return AnsibleTagHelper.untag(value, cls)
443
+
444
+ def tag(self, value: _T) -> _T:
445
+ """
446
+ Return a copy of `value` with this tag applied, overwriting any existing tag of the same type.
447
+ If `value` is an ignored type, the original `value` will be returned.
448
+ If `value` is not taggable, a `NotTaggableError` exception will be raised.
449
+ """
450
+ return AnsibleTagHelper.tag(value, self)
451
+
452
+ def try_tag(self, value: _T) -> _T:
453
+ """
454
+ Return a copy of `value` with this tag applied, overwriting any existing tag of the same type.
455
+ If `value` is not taggable, the original `value` will be returned.
456
+ """
457
+ return AnsibleTagHelper.try_tag(value, self)
458
+
459
+ def _get_tag_to_propagate(self, src: t.Any, value: object, *, value_type: t.Optional[type] = None) -> t.Self | None:
460
+ """
461
+ Called by `AnsibleTagHelper.tag_copy` during tag propagation.
462
+ Returns an instance of this tag appropriate for propagation to `value`, or `None` if the tag should not be propagated.
463
+ Derived implementations may consult the arguments relayed from `tag_copy` to determine if and how the tag should be propagated.
464
+ """
465
+ return self
466
+
467
+ def __repr__(self) -> str:
468
+ return AnsibleSerializable._repr(self, self.__class__.__name__)
469
+
470
+
471
+ # used by the datatag Ansible/Jinja test plugin to find tags by name
472
+ _known_tag_type_map: t.Dict[str, t.Type[AnsibleDatatagBase]] = {}
473
+ _known_tag_types: t.Set[t.Type[AnsibleDatatagBase]] = set()
474
+
475
+ if sys.version_info >= (3, 9):
476
+ # Include the key and value types in the type hints on Python 3.9 and later.
477
+ # Earlier versions do not support subscriptable dict.
478
+ # deprecated: description='always use subscriptable dict' python_version='3.8'
479
+ class _AnsibleTagsMapping(dict[type[_TAnsibleDatatagBase], _TAnsibleDatatagBase]):
480
+ __slots__ = _NO_INSTANCE_STORAGE
481
+
482
+ else:
483
+
484
+ class _AnsibleTagsMapping(dict):
485
+ __slots__ = _NO_INSTANCE_STORAGE
486
+
487
+
488
+ class _EmptyROInternalTagsMapping(dict):
489
+ """
490
+ Optimizes empty tag mapping by using a shared singleton read-only dict.
491
+ Since mappingproxy is not pickle-able and causes other problems, we had to roll our own.
492
+ """
493
+
494
+ def __new__(cls):
495
+ try:
496
+ # noinspection PyUnresolvedReferences
497
+ return cls._instance
498
+ except AttributeError:
499
+ cls._instance = dict.__new__(cls)
500
+
501
+ # noinspection PyUnresolvedReferences
502
+ return cls._instance
503
+
504
+ def __setitem__(self, key, value):
505
+ raise NotImplementedError()
506
+
507
+ def setdefault(self, __key, __default=None):
508
+ raise NotImplementedError()
509
+
510
+ def update(self, __m, **kwargs):
511
+ raise NotImplementedError()
512
+
513
+
514
+ _EMPTY_INTERNAL_TAGS_MAPPING = t.cast(_AnsibleTagsMapping, _EmptyROInternalTagsMapping())
515
+ """
516
+ An empty read-only mapping of tags.
517
+ Also used as a sentinel to cheaply determine that a type is not tagged by using a reference equality check.
518
+ """
519
+
520
+
521
+ class CollectionWithMro(c.Collection, t.Protocol):
522
+ """Used to represent a Collection with __mro__ in a TypeGuard for tools that don't include __mro__ in Collection."""
523
+
524
+ __mro__: tuple[type, ...]
525
+
526
+
527
+ # DTFIX-RELEASE: This should probably reside elsewhere.
528
+ def is_non_scalar_collection_type(value: type) -> t.TypeGuard[type[CollectionWithMro]]:
529
+ """Returns True if the value is a non-scalar collection type, otherwise returns False."""
530
+ return issubclass(value, c.Collection) and not issubclass(value, str) and not issubclass(value, bytes)
531
+
532
+
533
+ def _try_get_internal_tags_mapping(value: t.Any) -> _AnsibleTagsMapping:
534
+ """Return the internal tag mapping of the given value, or a sentinel value if it is not tagged."""
535
+ # noinspection PyBroadException
536
+ try:
537
+ # noinspection PyProtectedMember
538
+ tags = value._ansible_tags_mapping
539
+ except Exception:
540
+ # try/except is a cheap way to determine if this is a tagged object without using isinstance
541
+ # handling Exception accounts for types that may raise something other than AttributeError
542
+ return _EMPTY_INTERNAL_TAGS_MAPPING
543
+
544
+ # handle cases where the instance always returns something, such as Marker or MagicMock
545
+ if type(tags) is not _AnsibleTagsMapping: # pylint: disable=unidiomatic-typecheck
546
+ return _EMPTY_INTERNAL_TAGS_MAPPING
547
+
548
+ return tags
549
+
550
+
551
+ class NotTaggableError(TypeError):
552
+ def __init__(self, value):
553
+ super(NotTaggableError, self).__init__('{} is not taggable'.format(value))
554
+
555
+
556
+ @dataclasses.dataclass(**_tag_dataclass_kwargs)
557
+ class AnsibleSingletonTagBase(AnsibleDatatagBase):
558
+ def __new__(cls):
559
+ try:
560
+ # noinspection PyUnresolvedReferences
561
+ return cls._instance
562
+ except AttributeError:
563
+ cls._instance = AnsibleDatatagBase.__new__(cls)
564
+
565
+ # noinspection PyUnresolvedReferences
566
+ return cls._instance
567
+
568
+ def _as_dict(self) -> t.Dict[str, t.Any]:
569
+ return {}
570
+
571
+
572
+ class AnsibleTaggedObject(AnsibleSerializable):
573
+ __slots__ = _NO_INSTANCE_STORAGE
574
+
575
+ _native_type: t.ClassVar[type]
576
+ _item_source: t.ClassVar[t.Optional[t.Callable]] = None
577
+
578
+ _tagged_type_map: t.ClassVar[t.Dict[type, t.Type['AnsibleTaggedObject']]] = {}
579
+ _tagged_collection_types: t.ClassVar[t.Set[t.Type[c.Collection]]] = set()
580
+ _collection_types: t.ClassVar[t.Set[t.Type[c.Collection]]] = set()
581
+
582
+ _empty_tags_as_native: t.ClassVar[bool] = True # by default, untag will revert to the native type when no tags remain
583
+ _subclasses_native_type: t.ClassVar[bool] = True # by default, tagged types are assumed to subclass the type they augment
584
+
585
+ _ansible_tags_mapping: _AnsibleTagsMapping | _EmptyROInternalTagsMapping = _EMPTY_INTERNAL_TAGS_MAPPING
586
+ """
587
+ Efficient internal storage of tags, indexed by tag type.
588
+ Contains no more than one instance of each tag type.
589
+ This is defined as a class attribute to support type hinting and documentation.
590
+ It is overwritten with an instance attribute during instance creation.
591
+ The instance attribute slot is provided by the derived type.
592
+ """
593
+
594
+ def __init_subclass__(cls, **kwargs) -> None:
595
+ super().__init_subclass__(**kwargs)
596
+
597
+ try:
598
+ init_class = cls._init_class # type: ignore[attr-defined]
599
+ except AttributeError:
600
+ pass
601
+ else:
602
+ init_class()
603
+
604
+ if not cls._subclasses_native_type:
605
+ return # NOTE: When not subclassing a native type, the derived type must set cls._native_type itself and cls._empty_tags_as_native to False.
606
+
607
+ try:
608
+ # Subclasses of tagged types will already have a native type set and won't need to detect it.
609
+ # Special types which do not subclass a native type can also have their native type already set.
610
+ # Automatic item source selection is only implemented for types that don't set _native_type.
611
+ cls._native_type
612
+ except AttributeError:
613
+ # Direct subclasses of native types won't have cls._native_type set, so detect the native type.
614
+ cls._native_type = cls.__bases__[0]
615
+
616
+ # Detect the item source if not already set.
617
+ if cls._item_source is None and is_non_scalar_collection_type(cls._native_type):
618
+ cls._item_source = cls._native_type.__iter__ # type: ignore[attr-defined]
619
+
620
+ # Use a collection specific factory for types with item sources.
621
+ if cls._item_source:
622
+ cls._instance_factory = cls._instance_factory_collection # type: ignore[method-assign]
623
+
624
+ new_type_direct_subclass = cls.__mro__[1]
625
+
626
+ conflicting_impl = AnsibleTaggedObject._tagged_type_map.get(new_type_direct_subclass)
627
+
628
+ if conflicting_impl:
629
+ raise TypeError(f'Cannot define type {cls.__name__!r} since {conflicting_impl.__name__!r} already extends {new_type_direct_subclass.__name__!r}.')
630
+
631
+ AnsibleTaggedObject._tagged_type_map[new_type_direct_subclass] = cls
632
+
633
+ if is_non_scalar_collection_type(cls):
634
+ AnsibleTaggedObject._tagged_collection_types.add(cls)
635
+ AnsibleTaggedObject._collection_types.update({cls, new_type_direct_subclass})
636
+
637
+ def _native_copy(self) -> t.Any:
638
+ """
639
+ Returns a copy of the current instance as its native Python type.
640
+ Any dynamic access behaviors that apply to this instance will be used during creation of the copy.
641
+ In the case of a container type, this is a shallow copy.
642
+ Recursive calls to native_copy are the responsibility of the caller.
643
+ """
644
+ return self._native_type(self) # pylint: disable=abstract-class-instantiated
645
+
646
+ @classmethod
647
+ def _instance_factory(cls, value: t.Any, tags_mapping: _AnsibleTagsMapping) -> t.Self:
648
+ # There's no way to indicate cls is callable with a single arg without defining a useless __init__.
649
+ instance = cls(value) # type: ignore[call-arg]
650
+ instance._ansible_tags_mapping = tags_mapping
651
+
652
+ return instance
653
+
654
+ @staticmethod
655
+ def _get_tagged_type(value_type: type) -> type[AnsibleTaggedObject]:
656
+ tagged_type: t.Optional[type[AnsibleTaggedObject]]
657
+
658
+ if issubclass(value_type, AnsibleTaggedObject):
659
+ tagged_type = value_type
660
+ else:
661
+ tagged_type = AnsibleTaggedObject._tagged_type_map.get(value_type)
662
+
663
+ if not tagged_type:
664
+ raise NotTaggableError(value_type)
665
+
666
+ return tagged_type
667
+
668
+ def _as_dict(self) -> t.Dict[str, t.Any]:
669
+ return dict(
670
+ value=self._native_copy(),
671
+ tags=list(self._ansible_tags_mapping.values()),
672
+ )
673
+
674
+ @classmethod
675
+ def _from_dict(cls: t.Type[_TAnsibleTaggedObject], d: t.Dict[str, t.Any]) -> _TAnsibleTaggedObject:
676
+ return AnsibleTagHelper.tag(**d)
677
+
678
+ @classmethod
679
+ def _instance_factory_collection(
680
+ cls,
681
+ value: t.Any,
682
+ tags_mapping: _AnsibleTagsMapping,
683
+ ) -> t.Self:
684
+ if type(value) in AnsibleTaggedObject._collection_types:
685
+ # use the underlying iterator to avoid access/iteration side effects (e.g. templating/wrapping on Lazy subclasses)
686
+ instance = cls(cls._item_source(value)) # type: ignore[call-arg,misc]
687
+ else:
688
+ # this is used when the value is a generator
689
+ instance = cls(value) # type: ignore[call-arg]
690
+
691
+ instance._ansible_tags_mapping = tags_mapping
692
+
693
+ return instance
694
+
695
+ def _copy_collection(self) -> AnsibleTaggedObject:
696
+ """
697
+ Return a shallow copy of this instance, which must be a collection.
698
+ This uses the underlying iterator to avoid access/iteration side effects (e.g. templating/wrapping on Lazy subclasses).
699
+ """
700
+ return AnsibleTagHelper.tag_copy(self, type(self)._item_source(self), value_type=type(self)) # type: ignore[misc]
701
+
702
+ @classmethod
703
+ def _new(cls, value: t.Any, *args, **kwargs) -> t.Self:
704
+ if type(value) is _AnsibleTagsMapping: # pylint: disable=unidiomatic-typecheck
705
+ self = cls._native_type.__new__(cls, *args, **kwargs)
706
+ self._ansible_tags_mapping = value
707
+ return self
708
+
709
+ return cls._native_type.__new__(cls, value, *args, **kwargs)
710
+
711
+ def _reduce(self, reduced: t.Union[str, tuple[t.Any, ...]]) -> tuple:
712
+ if type(reduced) is not tuple: # pylint: disable=unidiomatic-typecheck
713
+ raise TypeError()
714
+
715
+ updated: list[t.Any] = list(reduced)
716
+ updated[1] = (self._ansible_tags_mapping,) + updated[1]
717
+
718
+ return tuple(updated)
719
+
720
+
721
+ class _AnsibleTaggedStr(str, AnsibleTaggedObject):
722
+ __slots__ = _ANSIBLE_TAGGED_OBJECT_SLOTS
723
+
724
+
725
+ class _AnsibleTaggedBytes(bytes, AnsibleTaggedObject):
726
+ # nonempty __slots__ not supported for subtype of 'bytes'
727
+ pass
728
+
729
+
730
+ class _AnsibleTaggedInt(int, AnsibleTaggedObject):
731
+ # nonempty __slots__ not supported for subtype of 'int'
732
+ pass
733
+
734
+
735
+ class _AnsibleTaggedFloat(float, AnsibleTaggedObject):
736
+ __slots__ = _ANSIBLE_TAGGED_OBJECT_SLOTS
737
+
738
+
739
+ class _AnsibleTaggedDateTime(datetime.datetime, AnsibleTaggedObject):
740
+ __slots__ = _ANSIBLE_TAGGED_OBJECT_SLOTS
741
+
742
+ @classmethod
743
+ def _instance_factory(cls, value: datetime.datetime, tags_mapping: _AnsibleTagsMapping) -> _AnsibleTaggedDateTime:
744
+ instance = cls(
745
+ year=value.year,
746
+ month=value.month,
747
+ day=value.day,
748
+ hour=value.hour,
749
+ minute=value.minute,
750
+ second=value.second,
751
+ microsecond=value.microsecond,
752
+ tzinfo=value.tzinfo,
753
+ fold=value.fold,
754
+ )
755
+
756
+ instance._ansible_tags_mapping = tags_mapping
757
+
758
+ return instance
759
+
760
+ def _native_copy(self) -> datetime.datetime:
761
+ return datetime.datetime(
762
+ year=self.year,
763
+ month=self.month,
764
+ day=self.day,
765
+ hour=self.hour,
766
+ minute=self.minute,
767
+ second=self.second,
768
+ microsecond=self.microsecond,
769
+ tzinfo=self.tzinfo,
770
+ fold=self.fold,
771
+ )
772
+
773
+ def __new__(cls, year, *args, **kwargs):
774
+ return super()._new(year, *args, **kwargs)
775
+
776
+ def __reduce_ex__(self, protocol: t.SupportsIndex) -> tuple:
777
+ return super()._reduce(super().__reduce_ex__(protocol))
778
+
779
+ def __repr__(self) -> str:
780
+ return self._native_copy().__repr__()
781
+
782
+
783
+ class _AnsibleTaggedDate(datetime.date, AnsibleTaggedObject):
784
+ __slots__ = _ANSIBLE_TAGGED_OBJECT_SLOTS
785
+
786
+ @classmethod
787
+ def _instance_factory(cls, value: datetime.date, tags_mapping: _AnsibleTagsMapping) -> _AnsibleTaggedDate:
788
+ instance = cls(
789
+ year=value.year,
790
+ month=value.month,
791
+ day=value.day,
792
+ )
793
+
794
+ instance._ansible_tags_mapping = tags_mapping
795
+
796
+ return instance
797
+
798
+ def _native_copy(self) -> datetime.date:
799
+ return datetime.date(
800
+ year=self.year,
801
+ month=self.month,
802
+ day=self.day,
803
+ )
804
+
805
+ def __new__(cls, year, *args, **kwargs):
806
+ return super()._new(year, *args, **kwargs)
807
+
808
+ def __reduce__(self) -> tuple:
809
+ return super()._reduce(super().__reduce__())
810
+
811
+ def __repr__(self) -> str:
812
+ return self._native_copy().__repr__()
813
+
814
+
815
+ class _AnsibleTaggedTime(datetime.time, AnsibleTaggedObject):
816
+ __slots__ = _ANSIBLE_TAGGED_OBJECT_SLOTS
817
+
818
+ @classmethod
819
+ def _instance_factory(cls, value: datetime.time, tags_mapping: _AnsibleTagsMapping) -> _AnsibleTaggedTime:
820
+ instance = cls(
821
+ hour=value.hour,
822
+ minute=value.minute,
823
+ second=value.second,
824
+ microsecond=value.microsecond,
825
+ tzinfo=value.tzinfo,
826
+ fold=value.fold,
827
+ )
828
+
829
+ instance._ansible_tags_mapping = tags_mapping
830
+
831
+ return instance
832
+
833
+ def _native_copy(self) -> datetime.time:
834
+ return datetime.time(
835
+ hour=self.hour,
836
+ minute=self.minute,
837
+ second=self.second,
838
+ microsecond=self.microsecond,
839
+ tzinfo=self.tzinfo,
840
+ fold=self.fold,
841
+ )
842
+
843
+ def __new__(cls, hour, *args, **kwargs):
844
+ return super()._new(hour, *args, **kwargs)
845
+
846
+ def __reduce_ex__(self, protocol: t.SupportsIndex) -> tuple:
847
+ return super()._reduce(super().__reduce_ex__(protocol))
848
+
849
+ def __repr__(self) -> str:
850
+ return self._native_copy().__repr__()
851
+
852
+
853
+ class _AnsibleTaggedDict(dict, AnsibleTaggedObject):
854
+ __slots__ = _ANSIBLE_TAGGED_OBJECT_SLOTS
855
+
856
+ _item_source: t.ClassVar[t.Optional[t.Callable]] = dict.items
857
+
858
+ def __copy__(self):
859
+ return super()._copy_collection()
860
+
861
+ def copy(self) -> _AnsibleTaggedDict:
862
+ return copy.copy(self)
863
+
864
+ # NB: Tags are intentionally not preserved for operator methods that return a new instance. In-place operators ignore tags from the `other` instance.
865
+ # Propagation of tags in these cases is left to the caller, based on needs specific to their use case.
866
+
867
+
868
+ class _AnsibleTaggedList(list, AnsibleTaggedObject):
869
+ __slots__ = _ANSIBLE_TAGGED_OBJECT_SLOTS
870
+
871
+ def __copy__(self):
872
+ return super()._copy_collection()
873
+
874
+ def copy(self) -> _AnsibleTaggedList:
875
+ return copy.copy(self)
876
+
877
+ # NB: Tags are intentionally not preserved for operator methods that return a new instance. In-place operators ignore tags from the `other` instance.
878
+ # Propagation of tags in these cases is left to the caller, based on needs specific to their use case.
879
+
880
+
881
+ # DTFIX-RELEASE: do we want frozenset too?
882
+ class _AnsibleTaggedSet(set, AnsibleTaggedObject):
883
+ __slots__ = _ANSIBLE_TAGGED_OBJECT_SLOTS
884
+
885
+ def __copy__(self):
886
+ return super()._copy_collection()
887
+
888
+ def copy(self):
889
+ return copy.copy(self)
890
+
891
+ def __init__(self, value=None, *args, **kwargs):
892
+ if type(value) is _AnsibleTagsMapping: # pylint: disable=unidiomatic-typecheck
893
+ super().__init__(*args, **kwargs)
894
+ else:
895
+ super().__init__(value, *args, **kwargs)
896
+
897
+ def __new__(cls, value=None, *args, **kwargs):
898
+ return super()._new(value, *args, **kwargs)
899
+
900
+ def __reduce_ex__(self, protocol: t.SupportsIndex) -> tuple:
901
+ return super()._reduce(super().__reduce_ex__(protocol))
902
+
903
+ def __str__(self) -> str:
904
+ return self._native_copy().__str__()
905
+
906
+ def __repr__(self) -> str:
907
+ return self._native_copy().__repr__()
908
+
909
+
910
+ class _AnsibleTaggedTuple(tuple, AnsibleTaggedObject):
911
+ # nonempty __slots__ not supported for subtype of 'tuple'
912
+
913
+ def __copy__(self):
914
+ return super()._copy_collection()
915
+
916
+
917
+ # This set gets augmented with additional types when some controller-only types are imported.
918
+ # While we could proxy or subclass builtin singletons, they're idiomatically compared with "is" reference
919
+ # equality, which we can't customize.
920
+ _untaggable_types = {type(None), bool}
921
+
922
+ # noinspection PyProtectedMember
923
+ _ANSIBLE_ALLOWED_VAR_TYPES = frozenset({type(None), bool}) | set(AnsibleTaggedObject._tagged_type_map) | set(AnsibleTaggedObject._tagged_type_map.values())
924
+ """These are the only types supported by Ansible's variable storage. Subclasses are not permitted."""
925
+
926
+ _ANSIBLE_ALLOWED_NON_SCALAR_COLLECTION_VAR_TYPES = frozenset(item for item in _ANSIBLE_ALLOWED_VAR_TYPES if is_non_scalar_collection_type(item))
927
+ _ANSIBLE_ALLOWED_MAPPING_VAR_TYPES = frozenset(item for item in _ANSIBLE_ALLOWED_VAR_TYPES if issubclass(item, c.Mapping))
928
+ _ANSIBLE_ALLOWED_SCALAR_VAR_TYPES = _ANSIBLE_ALLOWED_VAR_TYPES - _ANSIBLE_ALLOWED_NON_SCALAR_COLLECTION_VAR_TYPES