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,38 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import datetime
5
+ import typing as t
6
+
7
+ from ansible.module_utils.common import messages as _messages
8
+ from ansible.module_utils._internal import _datatag
9
+
10
+
11
+ @dataclasses.dataclass(**_datatag._tag_dataclass_kwargs)
12
+ class Deprecated(_datatag.AnsibleDatatagBase):
13
+ msg: str
14
+ help_text: t.Optional[str] = None
15
+ removal_date: t.Optional[datetime.date] = None
16
+ removal_version: t.Optional[str] = None
17
+ plugin: t.Optional[_messages.PluginInfo] = None
18
+
19
+ @classmethod
20
+ def _from_dict(cls, d: t.Dict[str, t.Any]) -> Deprecated:
21
+ source = d
22
+ removal_date = source.get('removal_date')
23
+
24
+ if removal_date is not None:
25
+ source = source.copy()
26
+ source['removal_date'] = datetime.date.fromisoformat(removal_date)
27
+
28
+ return cls(**source)
29
+
30
+ def _as_dict(self) -> t.Dict[str, t.Any]:
31
+ # deprecated: description='no-args super() with slotted dataclass requires 3.14+' python_version='3.13'
32
+ # see: https://github.com/python/cpython/pull/124455
33
+ value = super(Deprecated, self)._as_dict()
34
+
35
+ if self.removal_date is not None:
36
+ value['removal_date'] = self.removal_date.isoformat()
37
+
38
+ return value
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import pathlib
5
+ import sys
6
+
7
+
8
+ def load_params() -> tuple[bytes, str]:
9
+ """Load module arguments and profile when debugging an Ansible module."""
10
+ parser = argparse.ArgumentParser(description="Directly invoke an Ansible module for debugging.")
11
+ parser.add_argument('args', nargs='?', help='module args JSON (file path or inline string)')
12
+ parser.add_argument('--profile', default='legacy', help='profile for JSON decoding/encoding of args/response')
13
+
14
+ parsed_args = parser.parse_args()
15
+
16
+ args: str | None = parsed_args.args
17
+ profile: str = parsed_args.profile
18
+
19
+ if args:
20
+ if (args_path := pathlib.Path(args)).is_file():
21
+ buffer = args_path.read_bytes()
22
+ else:
23
+ buffer = args.encode(errors='surrogateescape')
24
+ else:
25
+ if sys.stdin.isatty():
26
+ sys.stderr.write('Waiting for Ansible module JSON on STDIN...\n')
27
+ sys.stderr.flush()
28
+
29
+ buffer = sys.stdin.buffer.read()
30
+
31
+ return buffer, profile
@@ -0,0 +1,30 @@
1
+ # Copyright (c) 2024 Ansible Project
2
+ # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
3
+
4
+ """Internal error handling logic for targets. Not for use on the controller."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from . import _traceback
9
+ from ..common.messages import Detail, ErrorSummary
10
+
11
+
12
+ def create_error_summary(exception: BaseException) -> ErrorSummary:
13
+ """Return an `ErrorDetail` created from the given exception."""
14
+ return ErrorSummary(
15
+ details=_create_error_details(exception),
16
+ formatted_traceback=_traceback.maybe_extract_traceback(exception, _traceback.TracebackEvent.ERROR),
17
+ )
18
+
19
+
20
+ def _create_error_details(exception: BaseException) -> tuple[Detail, ...]:
21
+ """Return an `ErrorMessage` tuple created from the given exception."""
22
+ target_exception: BaseException | None = exception
23
+ error_details: list[Detail] = []
24
+
25
+ while target_exception:
26
+ error_details.append(Detail(msg=str(target_exception).strip()))
27
+
28
+ target_exception = target_exception.__cause__
29
+
30
+ return tuple(error_details)
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import importlib.util
5
+ import types
6
+
7
+ import typing as t
8
+
9
+ from ansible.module_utils._internal._json._profiles import AnsibleProfileJSONEncoder, AnsibleProfileJSONDecoder, _JSONSerializationProfile
10
+ from ansible.module_utils import _internal
11
+
12
+ _T = t.TypeVar('_T', AnsibleProfileJSONEncoder, AnsibleProfileJSONDecoder)
13
+
14
+
15
+ def get_encoder_decoder(profile: str | types.ModuleType, return_type: type[_T]) -> type[_T]:
16
+ class_name = 'Encoder' if return_type is AnsibleProfileJSONEncoder else 'Decoder'
17
+
18
+ return getattr(get_serialization_module(profile), class_name)
19
+
20
+
21
+ def get_module_serialization_profile_name(name: str, controller_to_module: bool) -> str:
22
+ if controller_to_module:
23
+ name = f'module_{name}_c2m'
24
+ else:
25
+ name = f'module_{name}_m2c'
26
+
27
+ return name
28
+
29
+
30
+ def get_module_serialization_profile_module_name(name: str, controller_to_module: bool) -> str:
31
+ return get_serialization_module_name(get_module_serialization_profile_name(name, controller_to_module))
32
+
33
+
34
+ def get_serialization_profile(name: str | types.ModuleType) -> _JSONSerializationProfile:
35
+ return getattr(get_serialization_module(name), '_Profile')
36
+
37
+
38
+ def get_serialization_module(name: str | types.ModuleType) -> types.ModuleType:
39
+ return importlib.import_module(get_serialization_module_name(name))
40
+
41
+
42
+ def get_serialization_module_name(name: str | types.ModuleType) -> str:
43
+ if isinstance(name, str):
44
+ if '.' in name:
45
+ return name # name is already fully qualified
46
+
47
+ target_name = f'{__name__}._profiles._{name}'
48
+ elif isinstance(name, types.ModuleType):
49
+ return name.__name__
50
+ else:
51
+ raise TypeError(f'Name is {type(name)} instead of {str} or {types.ModuleType}.')
52
+
53
+ if importlib.util.find_spec(target_name):
54
+ return target_name
55
+
56
+ # the value of is_controller can change after import; always pick it up from the module
57
+ if _internal.is_controller:
58
+ controller_name = f'ansible._internal._json._profiles._{name}'
59
+
60
+ if importlib.util.find_spec(controller_name):
61
+ return controller_name
62
+
63
+ raise ValueError(f'Unknown profile name {name!r}.')
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from ansible.module_utils._internal._json import _profiles
4
+ from ansible.module_utils._internal._json._profiles import _tagless
5
+
6
+
7
+ class LegacyTargetJSONEncoder(_tagless.Encoder):
8
+ """Compatibility wrapper over `legacy` profile JSON encoder to support trust stripping and vault value plaintext conversion."""
9
+
10
+ def __init__(self, preprocess_unsafe: bool = False, vault_to_text: bool = False, _decode_bytes: bool = False, **kwargs) -> None:
11
+ self._decode_bytes = _decode_bytes
12
+
13
+ # NOTE: The preprocess_unsafe and vault_to_text arguments are features of LegacyControllerJSONEncoder.
14
+ # They are implemented here to allow callers to pass them without raising an error, but they have no effect.
15
+
16
+ super().__init__(**kwargs)
17
+
18
+ def default(self, o: object) -> object:
19
+ if self._decode_bytes:
20
+ if type(o) is _profiles._WrappedValue: # pylint: disable=unidiomatic-typecheck
21
+ o = o.wrapped
22
+
23
+ if isinstance(o, bytes):
24
+ return o.decode(errors='surrogateescape') # backward compatibility with `ansible.module_utils.basic.jsonify`
25
+
26
+ return super().default(o)
@@ -0,0 +1,410 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+ import functools
5
+ import json
6
+ import typing as t
7
+
8
+ from ansible.module_utils import _internal
9
+ from ansible.module_utils.common import messages as _messages
10
+ from ansible.module_utils._internal._datatag import (
11
+ AnsibleSerializable,
12
+ AnsibleSerializableWrapper,
13
+ AnsibleTaggedObject,
14
+ Tripwire,
15
+ _AnsibleTaggedBytes,
16
+ _AnsibleTaggedDate,
17
+ _AnsibleTaggedDateTime,
18
+ _AnsibleTaggedDict,
19
+ _AnsibleTaggedFloat,
20
+ _AnsibleTaggedInt,
21
+ _AnsibleTaggedList,
22
+ _AnsibleTaggedSet,
23
+ _AnsibleTaggedStr,
24
+ _AnsibleTaggedTime,
25
+ _AnsibleTaggedTuple,
26
+ AnsibleTagHelper,
27
+ _tags,
28
+ )
29
+
30
+ # transformations to "final" JSON representations can only use:
31
+ # str, float, int, bool, None, dict, list
32
+ # NOT SUPPORTED: tuple, set -- the representation of these in JSON varies by profile (can raise an error, may be converted to list, etc.)
33
+ # This means that any special handling required on JSON types that are not wrapped/tagged must be done in a pre-pass before serialization.
34
+ # The final type map cannot contain any JSON types other than tuple or set.
35
+
36
+
37
+ _NoneType: t.Final[type] = type(None)
38
+
39
+ _json_subclassable_scalar_types: t.Final[tuple[type, ...]] = (str, float, int)
40
+ """Scalar types understood by JSONEncoder which can also be subclassed."""
41
+
42
+ _json_scalar_types: t.Final[tuple[type, ...]] = (str, float, int, bool, _NoneType)
43
+ """Scalar types understood by JSONEncoder."""
44
+
45
+ _json_container_types: t.Final[tuple[type, ...]] = (dict, list, tuple)
46
+ """Container types understood by JSONEncoder."""
47
+
48
+ _json_types: t.Final[tuple[type, ...]] = _json_scalar_types + _json_container_types
49
+ """Types understood by JSONEncoder."""
50
+
51
+ _intercept_containers = frozenset(
52
+ {
53
+ dict,
54
+ list,
55
+ tuple,
56
+ _AnsibleTaggedDict,
57
+ _AnsibleTaggedList,
58
+ _AnsibleTaggedTuple,
59
+ }
60
+ )
61
+ """Container types to intercept in support of scalar interception."""
62
+
63
+ _common_module_types: frozenset[type[AnsibleSerializable]] = frozenset(
64
+ {
65
+ _AnsibleTaggedBytes,
66
+ _AnsibleTaggedDate,
67
+ _AnsibleTaggedDateTime,
68
+ _AnsibleTaggedDict,
69
+ _AnsibleTaggedFloat,
70
+ _AnsibleTaggedInt,
71
+ _AnsibleTaggedList,
72
+ _AnsibleTaggedSet,
73
+ _AnsibleTaggedStr,
74
+ _AnsibleTaggedTime,
75
+ _AnsibleTaggedTuple,
76
+ }
77
+ )
78
+ """
79
+ Types that must be supported for all Ansible module serialization profiles.
80
+
81
+ For module-to-controller, all types should support full fidelity serialization.
82
+ This allows infrastructure and library code to use these features even when a module does not.
83
+
84
+ For controller-to-module, type behavior is profile dependent.
85
+ """
86
+
87
+ _common_module_response_types: frozenset[type[AnsibleSerializable]] = frozenset(
88
+ {
89
+ _messages.PluginInfo,
90
+ _messages.Detail,
91
+ _messages.ErrorSummary,
92
+ _messages.WarningSummary,
93
+ _messages.DeprecationSummary,
94
+ _tags.Deprecated,
95
+ }
96
+ )
97
+ """Types that must be supported for all Ansible module-to-controller serialization profiles."""
98
+
99
+ _T_encoder = t.TypeVar('_T_encoder', bound="AnsibleProfileJSONEncoder")
100
+ _T_decoder = t.TypeVar('_T_decoder', bound="AnsibleProfileJSONDecoder")
101
+
102
+
103
+ class _JSONSerializationProfile(t.Generic[_T_encoder, _T_decoder]):
104
+ serialize_map: t.ClassVar[dict[type, t.Callable]]
105
+ """
106
+ Each concrete non-JSON type must be included in this mapping to support serialization.
107
+ Including a JSON type in the mapping allows for overriding or disabling of serialization of that type.
108
+ """
109
+
110
+ deserialize_map: t.ClassVar[dict[str, t.Callable]]
111
+ """A mapping of type keys to type dispatchers for deserialization."""
112
+
113
+ allowed_ansible_serializable_types: t.ClassVar[frozenset[type[AnsibleSerializable]]] = frozenset()
114
+ """Each concrete AnsibleSerialiable derived type must be included in this set to support serialization."""
115
+
116
+ _common_discard_tags: t.ClassVar[dict[type, t.Callable]]
117
+ """
118
+ Serialize map for tagged types to have their tags discarded.
119
+ This is generated by __init_subclass__ and should not be manually updated.
120
+ """
121
+
122
+ _allowed_type_keys: t.ClassVar[frozenset[str]]
123
+ """
124
+ The set of type keys allowed during deserialization.
125
+ This is generated by __init_subclass__ and should not be manually updated.
126
+ """
127
+
128
+ _unwrapped_json_types: t.ClassVar[frozenset[type]]
129
+ """
130
+ The set of types that do not need to be wrapped during serialization.
131
+ This is generated by __init_subclass__ and should not be manually updated.
132
+ """
133
+
134
+ profile_name: t.ClassVar[str]
135
+ """
136
+ The user-facing name of the profile, derived from the module name in which the profile resides.
137
+ Used to load the profile dynamically at runtime.
138
+ This is generated by __init_subclass__ and should not be manually updated.
139
+ """
140
+
141
+ encode_strings_as_utf8: t.ClassVar[bool] = False
142
+ r"""
143
+ When enabled, JSON encoding will result in UTF8 strings being emitted.
144
+ Otherwise, non-ASCII strings will be escaped with `\uXXXX` escape sequences.`
145
+ """
146
+
147
+ @classmethod
148
+ def pre_serialize(cls, encoder: _T_encoder, o: t.Any) -> t.Any:
149
+ return o
150
+
151
+ @classmethod
152
+ def post_deserialize(cls, decoder: _T_decoder, o: t.Any) -> t.Any:
153
+ return o
154
+
155
+ @classmethod
156
+ def cannot_serialize_error(cls, target: t.Any, /) -> t.NoReturn:
157
+ raise TypeError(f'Object of type {type(target).__name__!r} is not JSON serializable by the {cls.profile_name!r} profile.')
158
+
159
+ @classmethod
160
+ def cannot_deserialize_error(cls, target_type_name: str, /) -> t.NoReturn:
161
+ raise TypeError(f'Object of type {target_type_name!r} is not JSON deserializable by the {cls.profile_name!r} profile.')
162
+
163
+ @classmethod
164
+ def unsupported_target_type_error(cls, target_type_name: str, _value: dict) -> t.NoReturn:
165
+ cls.cannot_deserialize_error(target_type_name)
166
+
167
+ @classmethod
168
+ def discard_tags(cls, value: AnsibleTaggedObject) -> object:
169
+ return value._native_copy()
170
+
171
+ @classmethod
172
+ def deserialize_serializable(cls, value: dict[str, t.Any]) -> object:
173
+ type_key = value[AnsibleSerializable._TYPE_KEY]
174
+
175
+ if type_key not in cls._allowed_type_keys:
176
+ cls.cannot_deserialize_error(type_key)
177
+
178
+ return AnsibleSerializable._deserialize(value)
179
+
180
+ @classmethod
181
+ def serialize_as_list(cls, value: t.Iterable) -> list:
182
+ # DTFIX-FUTURE: once we have separate control/data channels for module-to-controller (and back), warn about this conversion
183
+ return AnsibleTagHelper.tag_copy(value, (item for item in value), value_type=list)
184
+
185
+ @classmethod
186
+ def serialize_as_isoformat(cls, value: datetime.date | datetime.time | datetime.datetime) -> str:
187
+ return value.isoformat()
188
+
189
+ @classmethod
190
+ def serialize_serializable_object(cls, value: AnsibleSerializable) -> t.Any:
191
+ return value._serialize()
192
+
193
+ @classmethod
194
+ def post_init(cls) -> None:
195
+ pass
196
+
197
+ @classmethod
198
+ def maybe_wrap(cls, o: t.Any) -> t.Any:
199
+ if type(o) in cls._unwrapped_json_types:
200
+ return o
201
+
202
+ return _WrappedValue(o)
203
+
204
+ @classmethod
205
+ def handle_key(cls, k: t.Any) -> t.Any:
206
+ if not isinstance(k, str): # DTFIX-FUTURE: optimize this to use all known str-derived types in type map / allowed types
207
+ raise TypeError(f'Key of type {type(k).__name__!r} is not JSON serializable by the {cls.profile_name!r} profile.')
208
+
209
+ return k
210
+
211
+ @classmethod
212
+ def default(cls, o: t.Any) -> t.Any:
213
+ # Preserve the built-in JSON encoder support for subclasses of scalar types.
214
+
215
+ if isinstance(o, _json_subclassable_scalar_types):
216
+ return o
217
+
218
+ # Preserve the built-in JSON encoder support for subclasses of dict and list.
219
+ # Additionally, add universal support for mappings and sequences/sets by converting them to dict and list, respectively.
220
+
221
+ if _internal.is_intermediate_mapping(o):
222
+ return {cls.handle_key(k): cls.maybe_wrap(v) for k, v in o.items()}
223
+
224
+ if _internal.is_intermediate_iterable(o):
225
+ return [cls.maybe_wrap(v) for v in o]
226
+
227
+ return cls.last_chance(o)
228
+
229
+ @classmethod
230
+ def last_chance(cls, o: t.Any) -> t.Any:
231
+ if isinstance(o, Tripwire):
232
+ o.trip()
233
+
234
+ cls.cannot_serialize_error(o)
235
+
236
+ def __init_subclass__(cls, **kwargs) -> None:
237
+ cls.deserialize_map = {}
238
+ cls._common_discard_tags = {obj: cls.discard_tags for obj in _common_module_types if issubclass(obj, AnsibleTaggedObject)}
239
+
240
+ cls.post_init()
241
+
242
+ cls.profile_name = cls.__module__.rsplit('.', maxsplit=1)[-1].lstrip('_')
243
+
244
+ wrapper_types = set(obj for obj in cls.serialize_map.values() if isinstance(obj, type) and issubclass(obj, AnsibleSerializableWrapper))
245
+
246
+ cls.allowed_ansible_serializable_types |= wrapper_types
247
+
248
+ # no current need to preserve tags on controller-only types or custom behavior for anything in `allowed_serializable_types`
249
+ cls.serialize_map.update({obj: cls.serialize_serializable_object for obj in cls.allowed_ansible_serializable_types})
250
+ cls.serialize_map.update({obj: func for obj, func in _internal.get_controller_serialize_map().items() if obj not in cls.serialize_map})
251
+
252
+ cls.deserialize_map[AnsibleSerializable._TYPE_KEY] = cls.deserialize_serializable # always recognize tagged types
253
+
254
+ cls._allowed_type_keys = frozenset(obj._type_key for obj in cls.allowed_ansible_serializable_types)
255
+
256
+ cls._unwrapped_json_types = frozenset(
257
+ {obj for obj in cls.serialize_map if not issubclass(obj, _json_types)} # custom types that do not extend JSON-native types
258
+ | {obj for obj in _json_scalar_types if obj not in cls.serialize_map} # JSON-native scalars lacking custom handling
259
+ )
260
+
261
+
262
+ class _WrappedValue:
263
+ __slots__ = ('wrapped',)
264
+
265
+ def __init__(self, wrapped: t.Any) -> None:
266
+ self.wrapped = wrapped
267
+
268
+
269
+ class AnsibleProfileJSONEncoder(json.JSONEncoder):
270
+ """Profile based JSON encoder capable of handling Ansible internal types."""
271
+
272
+ _wrap_container_types = (list, set, tuple, dict)
273
+ _profile: type[_JSONSerializationProfile]
274
+
275
+ profile_name: str
276
+
277
+ def __init__(self, **kwargs):
278
+ self._wrap_types = self._wrap_container_types + (AnsibleSerializable,)
279
+
280
+ if self._profile.encode_strings_as_utf8:
281
+ kwargs.update(ensure_ascii=False)
282
+
283
+ super().__init__(**kwargs)
284
+
285
+ def __init_subclass__(cls, **kwargs) -> None:
286
+ cls.profile_name = cls._profile.profile_name
287
+
288
+ def encode(self, o):
289
+ o = self._profile.maybe_wrap(self._profile.pre_serialize(self, o))
290
+
291
+ return super().encode(o)
292
+
293
+ def default(self, o: t.Any) -> t.Any:
294
+ o_type = type(o)
295
+
296
+ if o_type is _WrappedValue: # pylint: disable=unidiomatic-typecheck
297
+ o = o.wrapped
298
+ o_type = type(o)
299
+
300
+ if mapped_callable := self._profile.serialize_map.get(o_type):
301
+ return self._profile.maybe_wrap(mapped_callable(o))
302
+
303
+ # This is our last chance to intercept the values in containers, so they must be wrapped here.
304
+ # Only containers natively understood by the built-in JSONEncoder are recognized, since any other container types must be present in serialize_map.
305
+
306
+ if o_type is dict: # pylint: disable=unidiomatic-typecheck
307
+ return {self._profile.handle_key(k): self._profile.maybe_wrap(v) for k, v in o.items()}
308
+
309
+ if o_type is list or o_type is tuple: # pylint: disable=unidiomatic-typecheck
310
+ return [self._profile.maybe_wrap(v) for v in o] # JSONEncoder converts tuple to a list, so just make it a list now
311
+
312
+ # Any value here is a type not explicitly handled by this encoder.
313
+ # The profile default handler is responsible for generating an error or converting the value to a supported type.
314
+
315
+ return self._profile.default(o)
316
+
317
+
318
+ class AnsibleProfileJSONDecoder(json.JSONDecoder):
319
+ """Profile based JSON decoder capable of handling Ansible internal types."""
320
+
321
+ _profile: type[_JSONSerializationProfile]
322
+
323
+ profile_name: str
324
+
325
+ def __init__(self, **kwargs):
326
+ kwargs.update(object_hook=self.object_hook)
327
+
328
+ super().__init__(**kwargs)
329
+
330
+ def __init_subclass__(cls, **kwargs) -> None:
331
+ cls.profile_name = cls._profile.profile_name
332
+
333
+ def raw_decode(self, s: str, idx: int = 0) -> tuple[t.Any, int]:
334
+ obj, end = super().raw_decode(s, idx)
335
+
336
+ if _string_encoding_check_enabled():
337
+ try:
338
+ _recursively_check_string_encoding(obj)
339
+ except UnicodeEncodeError as ex:
340
+ raise _create_encoding_check_error() from ex
341
+
342
+ obj = self._profile.post_deserialize(self, obj)
343
+
344
+ return obj, end
345
+
346
+ def object_hook(self, pairs: dict[str, object]) -> object:
347
+ if _string_encoding_check_enabled():
348
+ try:
349
+ for key, value in pairs.items():
350
+ key.encode()
351
+ _recursively_check_string_encoding(value)
352
+ except UnicodeEncodeError as ex:
353
+ raise _create_encoding_check_error() from ex
354
+
355
+ for mapped_key, mapped_callable in self._profile.deserialize_map.items():
356
+ if mapped_key in pairs:
357
+ return mapped_callable(pairs)
358
+
359
+ return pairs
360
+
361
+
362
+ _check_encoding_setting = 'MODULE_STRICT_UTF8_RESPONSE'
363
+ r"""
364
+ The setting to control whether strings are checked to verify they can be encoded as valid UTF8.
365
+ This is currently only used during deserialization, to prevent string values from entering the controller which will later fail to be encoded as bytes.
366
+
367
+ The encoding failure can occur when the string represents one of two kinds of values:
368
+ 1) It was created through decoding bytes with the `surrogateescape` error handler, and that handler is not being used when encoding.
369
+ 2) It represents an invalid UTF8 value, such as `"\ud8f3"` in a JSON payload. This cannot be encoded, even using the `surrogateescape` error handler.
370
+
371
+ Although this becomes an error during deserialization, there are other opportunities for these values to become strings within Ansible.
372
+ Future code changes should further restrict bytes to string conversions to eliminate use of `surrogateescape` where appropriate.
373
+ Additional warnings at other boundaries may be needed to give users an opportunity to resolve the issues before they become errors.
374
+ """
375
+ # DTFIX-FUTURE: add strict UTF8 string encoding checking to serialization profiles (to match the checks performed during deserialization)
376
+ # DTFIX-RELEASE: the surrogateescape note above isn't quite right, for encoding use surrogatepass, which does work
377
+ # DTFIX-RELEASE: this config setting should probably be deprecated
378
+
379
+
380
+ def _create_encoding_check_error() -> Exception:
381
+ """
382
+ Return an AnsibleError for use when a UTF8 string encoding check has failed.
383
+ These checks are only performed in the controller context, but since this is module_utils code, dynamic loading of the `errors` module is required.
384
+ """
385
+ errors = _internal.import_controller_module('ansible.errors') # bypass AnsiballZ import scanning
386
+
387
+ return errors.AnsibleRuntimeError(
388
+ message='Refusing to deserialize an invalid UTF8 string value.',
389
+ help_text=f'This check can be disabled with the `{_check_encoding_setting}` setting.',
390
+ )
391
+
392
+
393
+ @functools.lru_cache
394
+ def _string_encoding_check_enabled() -> bool:
395
+ """Return True if JSON deserialization should verify strings can be encoded as valid UTF8."""
396
+ if constants := _internal.import_controller_module('ansible.constants'): # bypass AnsiballZ import scanning
397
+ return constants.config.get_config_value(_check_encoding_setting) # covers all profile-based deserializers, not just modules
398
+
399
+ return False
400
+
401
+
402
+ def _recursively_check_string_encoding(value: t.Any) -> None:
403
+ """Recursively check the given object to ensure all strings can be encoded as valid UTF8."""
404
+ value_type = type(value)
405
+
406
+ if value_type is str:
407
+ value.encode()
408
+ elif value_type is list: # dict is handled by the JSON deserializer
409
+ for item in value:
410
+ _recursively_check_string_encoding(item)
@@ -0,0 +1,73 @@
1
+ """
2
+ Lossy best-effort serialization for Ansible variables; used primarily for callback JSON display.
3
+ Any type which is not supported by JSON will be converted to a string.
4
+ The string representation of any type that is not native to JSON is subject to change and should not be considered stable.
5
+ The decoder provides no special behavior.
6
+ """
7
+
8
+ from __future__ import annotations as _annotations
9
+
10
+ import datetime as _datetime
11
+ import typing as _t
12
+
13
+ from json import dumps as _dumps
14
+
15
+ from ... import _datatag
16
+ from .. import _profiles
17
+
18
+
19
+ class _Profile(_profiles._JSONSerializationProfile["Encoder", "Decoder"]):
20
+ serialize_map: _t.ClassVar[dict[type, _t.Callable]]
21
+
22
+ @classmethod
23
+ def post_init(cls) -> None:
24
+ cls.serialize_map = {
25
+ bytes: cls.serialize_bytes_as_str,
26
+ set: cls.serialize_as_list,
27
+ tuple: cls.serialize_as_list,
28
+ _datetime.date: cls.serialize_as_isoformat,
29
+ _datetime.time: cls.serialize_as_isoformat,
30
+ _datetime.datetime: cls.serialize_as_isoformat,
31
+ _datatag._AnsibleTaggedDate: cls.discard_tags,
32
+ _datatag._AnsibleTaggedTime: cls.discard_tags,
33
+ _datatag._AnsibleTaggedDateTime: cls.discard_tags,
34
+ _datatag._AnsibleTaggedStr: cls.discard_tags,
35
+ _datatag._AnsibleTaggedInt: cls.discard_tags,
36
+ _datatag._AnsibleTaggedFloat: cls.discard_tags,
37
+ _datatag._AnsibleTaggedSet: cls.discard_tags,
38
+ _datatag._AnsibleTaggedList: cls.discard_tags,
39
+ _datatag._AnsibleTaggedTuple: cls.discard_tags,
40
+ _datatag._AnsibleTaggedDict: cls.discard_tags,
41
+ _datatag._AnsibleTaggedBytes: cls.discard_tags,
42
+ }
43
+
44
+ @classmethod
45
+ def serialize_bytes_as_str(cls, value: bytes) -> str:
46
+ return value.decode(errors='surrogateescape')
47
+
48
+ @classmethod
49
+ def handle_key(cls, k: _t.Any) -> _t.Any:
50
+ while mapped_callable := cls.serialize_map.get(type(k)):
51
+ k = mapped_callable(k)
52
+
53
+ k = cls.default(k)
54
+
55
+ if not isinstance(k, str):
56
+ k = _dumps(k, cls=Encoder)
57
+
58
+ return k
59
+
60
+ @classmethod
61
+ def last_chance(cls, o: _t.Any) -> _t.Any:
62
+ try:
63
+ return str(o)
64
+ except Exception as ex:
65
+ return str(ex)
66
+
67
+
68
+ class Encoder(_profiles.AnsibleProfileJSONEncoder):
69
+ _profile = _Profile
70
+
71
+
72
+ class Decoder(_profiles.AnsibleProfileJSONDecoder):
73
+ _profile = _Profile