From 4ab675394d8a1d8124e7c0b5ab4210c4645c2d47 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Tue, 28 Jul 2026 11:58:55 +0200 Subject: [PATCH 01/19] feat: docs_bundle --- BUILD | 10 + MODULE.bazel.lock | 120 +++++++ bzl/basics.bzl | 45 +++ bzl/bundle_rules.bzl | 306 +++++++++++++++++ bzl/mount_rules.bzl | 44 +++ docs.bzl | 249 +++++++------- docs/concepts/bidirectional_traceability.rst | 11 +- docs/how-to/other_modules.rst | 38 ++- docs/internals/extensions/sync_toml.rst | 22 +- docs/reference/bazel_macros.rst | 80 ++++- docs/reference/commands.md | 2 - src/BUILD | 1 + src/extensions/score_mounts/BUILD | 55 +++ src/extensions/score_mounts/__init__.py | 142 ++++++++ src/extensions/score_mounts/_resolver.py | 95 ++++++ src/extensions/score_mounts/docs/BUILD | 30 ++ .../score_mounts/docs/concept/index.rst | 263 ++++++++++++++ .../score_mounts/docs/howto/index.rst | 322 ++++++++++++++++++ src/extensions/score_mounts/tests/__init__.py | 0 .../score_mounts/tests/test_resolver.py | 182 ++++++++++ .../score_source_code_linker/helpers.py | 2 +- .../score_source_code_linker/needlinks.py | 3 +- src/extensions/score_sphinx_bundle/BUILD | 1 + .../score_sphinx_bundle/__init__.py | 5 + src/extensions/score_sync_toml/BUILD | 10 + src/extensions/score_sync_toml/__init__.py | 24 +- src/extensions/score_sync_toml/_mounts.py | 70 ++++ src/extensions/score_sync_toml/test_mounts.py | 65 ++++ src/incremental.py | 13 +- src/requirements.in | 3 + src/requirements.txt | 121 +++++++ src/tests/docs_e2e/BUILD | 46 +++ src/tests/docs_e2e/host_docs/conf.py | 16 + src/tests/docs_e2e/host_docs/index.rst | 16 + src/tests/docs_e2e/support.py | 66 ++++ src/tests/docs_e2e/test_docs_e2e.py | 30 ++ src/tests/mounts_conflict/BUILD | 102 ++++++ src/tests/mounts_conflict/child/a.rst | 18 + src/tests/mounts_contract/BUILD | 93 +++++ src/tests/mounts_contract/README.md | 83 +++++ src/tests/mounts_contract/child/example.py | 14 + src/tests/mounts_contract/child/index.rst | 16 + .../host_docs/concepts/index.rst | 19 ++ src/tests/mounts_contract/host_docs/conf.py | 17 + src/tests/mounts_contract/host_docs/index.rst | 21 ++ src/tests/mounts_contract/other/index.rst | 16 + src/tests/mounts_contract/parent/index.rst | 16 + .../mounts_contract/test_mount_docs_e2e.py | 69 ++++ src/tests/mounts_external/BUILD | 25 ++ src/tests/mounts_external/host_docs/conf.py | 17 + src/tests/mounts_external/host_docs/index.rst | 19 ++ 51 files changed, 2888 insertions(+), 165 deletions(-) create mode 100644 bzl/basics.bzl create mode 100644 bzl/bundle_rules.bzl create mode 100644 bzl/mount_rules.bzl create mode 100644 src/extensions/score_mounts/BUILD create mode 100644 src/extensions/score_mounts/__init__.py create mode 100644 src/extensions/score_mounts/_resolver.py create mode 100644 src/extensions/score_mounts/docs/BUILD create mode 100644 src/extensions/score_mounts/docs/concept/index.rst create mode 100644 src/extensions/score_mounts/docs/howto/index.rst create mode 100644 src/extensions/score_mounts/tests/__init__.py create mode 100644 src/extensions/score_mounts/tests/test_resolver.py create mode 100644 src/extensions/score_sync_toml/_mounts.py create mode 100644 src/extensions/score_sync_toml/test_mounts.py create mode 100644 src/tests/docs_e2e/BUILD create mode 100644 src/tests/docs_e2e/host_docs/conf.py create mode 100644 src/tests/docs_e2e/host_docs/index.rst create mode 100644 src/tests/docs_e2e/support.py create mode 100644 src/tests/docs_e2e/test_docs_e2e.py create mode 100644 src/tests/mounts_conflict/BUILD create mode 100644 src/tests/mounts_conflict/child/a.rst create mode 100644 src/tests/mounts_contract/BUILD create mode 100644 src/tests/mounts_contract/README.md create mode 100644 src/tests/mounts_contract/child/example.py create mode 100644 src/tests/mounts_contract/child/index.rst create mode 100644 src/tests/mounts_contract/host_docs/concepts/index.rst create mode 100644 src/tests/mounts_contract/host_docs/conf.py create mode 100644 src/tests/mounts_contract/host_docs/index.rst create mode 100644 src/tests/mounts_contract/other/index.rst create mode 100644 src/tests/mounts_contract/parent/index.rst create mode 100644 src/tests/mounts_contract/test_mount_docs_e2e.py create mode 100644 src/tests/mounts_external/BUILD create mode 100644 src/tests/mounts_external/host_docs/conf.py create mode 100644 src/tests/mounts_external/host_docs/index.rst diff --git a/BUILD b/BUILD index 80284fe40..20a0fef50 100644 --- a/BUILD +++ b/BUILD @@ -20,6 +20,16 @@ docs( data = [ "@score_process//:needs_json", ], + bundles = [ + { + "bundle": "//src/extensions/score_mounts/docs:concept", + "mount_at": "concepts/mounts", + }, + { + "bundle": "//src/extensions/score_mounts/docs:howto", + "mount_at": "how-to/mounts", + }, + ], scan_code = [ "//scripts_bazel:sources", "//src:all_sources", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 1570a3bb6..d8cd028da 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1153,6 +1153,122 @@ "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz": "ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl": "048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8" }, + "ignore-python": { + "https://files.pythonhosted.org/packages/00/19/64cc90ed114d1ec38cf20e7e1825c4885bf4d66b2ce9ca1208ca13540c64/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "9bccb48b57b7a85677c1022afbaaf86e5cda8c1ecff00ad96877e10166d1eddc", + "https://files.pythonhosted.org/packages/03/76/2b0cf84c80d973b285831ae59f795635eb518342e00aab1d96a0c918bbe6/ignore_python-0.3.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "a999ef004caa048e5ecccb5f3383d857105baa37b8895a0a2b7cd66f9cb0b0b4", + "https://files.pythonhosted.org/packages/06/a2/6db321813eba49f04f680af7b83dddda117f09e5a33e8fa2de7cbbe26f36/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "4f8c85a6738c632abd477c217297f8929ec1cafb261b94fa05a7fcf28095d70f", + "https://files.pythonhosted.org/packages/0c/f8/8ae1f5dc1e9b5fd3b4460ac685bb84c22a327f14f76daeebcd7eeeaba9ff/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl": "22c216e3130077060eb4cce99a8bf79074826655bbea6c594d36d8a0735fac6c", + "https://files.pythonhosted.org/packages/0d/93/2a28bd91ab75f22e21448e47b2972670ea5320fb37bf5d3f3f0167be8743/ignore_python-0.3.3-cp311-cp311-win_amd64.whl": "0a6b2d7900ce82acd61ab6714b10103c37d0a1c16ee7af46fb6e94d14a2e4dca", + "https://files.pythonhosted.org/packages/0e/00/aee2481903dc3578998df4edf94dc0460644f7f8a9fbee6e84189a24d278/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "68f393318292a6346c6d72c2b8ee301a081bff778dfb0e6ef6f0c36da4053374", + "https://files.pythonhosted.org/packages/0f/e4/e260e5cb1b289230a6be99de15db5eb0697e6334830cb1ff3907568414ab/ignore_python-0.3.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl": "df78545bc5d54abf875a70a70db7e1e12e606377d1c9f4e68b6763e8c701a748", + "https://files.pythonhosted.org/packages/12/30/976ce0f8e1002fc19781ea48b6d5519200f734b19be03d769eefeb0ca749/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "71dc7505c0520e066c5d567f49d7173703c34192af1b8f89ce401a34098391f5", + "https://files.pythonhosted.org/packages/13/96/1b66b3be8862b771dc6eae3c522609ee22309c91276a48cfee1f17b469b5/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "f93a3425169961aa7f0c7193dc12f01414700f86f4fb99ec078c18780432b77a", + "https://files.pythonhosted.org/packages/1c/0d/f0b3e6a35b66570d6212850711067a719604034b72ee0c8cec312e5be13f/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "2b608a30d3505720f9aceaba7d6d26769867d71c212fc5c8a80fae1a8afec663", + "https://files.pythonhosted.org/packages/1c/76/ea5c9a892f9f10eefda168b0ad8e894b838dc0128317e5f965d7b546205d/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "875fdfa0e3e9102164a540509ca2d5ad959f1f53858cf11a4174a1845e3c575c", + "https://files.pythonhosted.org/packages/1c/b7/41b9bea87bdab57c2b705767bdd9c930417e5f1f14502aac9ac6044d86ee/ignore_python-0.3.3-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl": "a30070520aa114133feffc2413ba62bfcd8ef2f9826ebe7616de123f73b57977", + "https://files.pythonhosted.org/packages/1e/ce/f82e5156b64c3f8e8835c36ef1cb046d280af8294192672ec99a5f0efb6d/ignore_python-0.3.3-cp314-cp314-macosx_11_0_arm64.whl": "c3430b73a99af300b0b1203da2cd30f1831f504466d94343b065b1ef0435802c", + "https://files.pythonhosted.org/packages/23/2f/509eeeb1d61e6e66518601274c4fe1c1ebb45493ae5704033c4a01f2a721/ignore_python-0.3.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl": "c68dc5db03a22aada43f23f55e359109e00afe3886824d6fecfbd6821dcdbd6f", + "https://files.pythonhosted.org/packages/28/d3/91b086d42cd2e9654e8d532975d068fdfe64dda176a56642608f3627002b/ignore_python-0.3.3-cp310-cp310-win_amd64.whl": "be6a4e3244c33f133d3c0bc43f9725c61dc9a11f7100c615639ce1daee064766", + "https://files.pythonhosted.org/packages/29/94/17a644d95f13c08845bc6be9750f16e19e7d0650a6f43e6ce63de57556b0/ignore_python-0.3.3-cp314-cp314t-musllinux_1_2_i686.whl": "8dd30b865dfd3206756212796cb13686b6c45befa5cc495ccc9866108215f7c1", + "https://files.pythonhosted.org/packages/2a/fd/dbb1ed9af9bc50d53a2edcb71e0e236d32d28fdb60a62ccc965f3cad8811/ignore_python-0.3.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "0f0edb622f5a8b7f735e14a7ca13d4cb7ca04b6fd7e844f5fa0fd9f86622b986", + "https://files.pythonhosted.org/packages/2d/bd/8db6909e5ab266b8827fc5659fe98ea905d15bad818fa088d923da46643e/ignore_python-0.3.3-cp39-cp39-musllinux_1_2_x86_64.whl": "5055772fa6f09148a09e6958770c4e4f4435f6e3de33476f815af9c5db13e29c", + "https://files.pythonhosted.org/packages/30/60/d0c7be4619a9d8537e0e930b26d358b870f14820ec89eaba80e97d00481d/ignore_python-0.3.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "1206189e1c988a3d0fb7de3298e5f7dd5284b4a23781878fb8f9f6224659b270", + "https://files.pythonhosted.org/packages/34/36/eb0337f76ea09ffe68246228f9cb1d14d8c84deb6bda8dcbbedf16bdb373/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "8d26f91ac1fea52abd16dab224b4ac016d8914f28db02e582f4e589ee2b5faa5", + "https://files.pythonhosted.org/packages/37/46/3c93a7bcbd21d23faa9eb0c90613b711e6afb218d09b6122325cac4f3ae0/ignore_python-0.3.3-cp311-cp311-musllinux_1_2_x86_64.whl": "3b6536698628af08b6db260d338b41e7c48b2a6c5c93b12de4d1ecafc1bb86ae", + "https://files.pythonhosted.org/packages/3a/17/f0d111f0d90ae0d2322ba0c4a2002b2b6634dd64555ad9f42eaae20b633b/ignore_python-0.3.3-cp314-cp314-musllinux_1_2_x86_64.whl": "dda677506171a0c4f27925e13f9f8b4b652941969f0e0e6b555ce795e18b7467", + "https://files.pythonhosted.org/packages/3d/00/176044e51c351465221f6b98ced3639d7660330bb12d89fb99e13423803c/ignore_python-0.3.3-cp313-cp313-win_amd64.whl": "2af502d988282cc360094dc7b2733a7b68e54a8e3cf0128178ab1ba84f9ec290", + "https://files.pythonhosted.org/packages/40/dc/76b1fc8e4d51680656c963338d5ca2f17b6bc04ad1c4425c2cc498630908/ignore_python-0.3.3-cp311-cp311-macosx_11_0_arm64.whl": "c478ee58fd2d6f5f7b75b32288d8a099904f710e83dd878f40058a309a0f6060", + "https://files.pythonhosted.org/packages/40/f0/3ad575b0a10d4d69efe87e745c7e94a4bbc824963707b24272d49a150bda/ignore_python-0.3.3-cp312-cp312-win_arm64.whl": "3623ce12eb96976c0db36a0ad99c65c669fdafece71e7221a538f12fa6fa41ef", + "https://files.pythonhosted.org/packages/43/d7/7ad0916a3aa863f26f8654dfe095adf65e720a201043093e1f8614dc0e16/ignore_python-0.3.3-cp310-cp310-musllinux_1_2_i686.whl": "275f5b3e4c5b25fcb58ad1eedd983a346866939a6140e564b9c56ee9f8fc7760", + "https://files.pythonhosted.org/packages/43/ee/611f7c7d8973d288e2908a14aaf48b8e65ee0d3e11ba59c7b62e8b07ef69/ignore_python-0.3.3-cp312-cp312-musllinux_1_2_x86_64.whl": "e9c95f8c3a68449f7d3bd5860ea832b5827a04803337796da72d8340786e9268", + "https://files.pythonhosted.org/packages/49/14/32e3a2c4313367a38b9c8b564785444bb3ed31317386fb81efd80496a5af/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "0f50dd3c3f0ef982e256d9e702b44c1d81cbe0da2d98746d5b189bc1fd5191cd", + "https://files.pythonhosted.org/packages/4d/45/fcb27a731e98ff8d71ff7559f5558091efdb107eafd4ee7aef6ac91f5dad/ignore_python-0.3.3-cp311-cp311-musllinux_1_2_armv7l.whl": "901a862196bf610745e164d29c518e0cbf727eaaedc83d5058a7895721be2173", + "https://files.pythonhosted.org/packages/4e/4b/c5abae65901dce53a197c6e37178821b259b0cc40b8ff99183e409946a71/ignore_python-0.3.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl": "931744cdcbb84d77159daf6b54e3b459e9f0a0ba52ea6b99d1d228e32556c2b9", + "https://files.pythonhosted.org/packages/58/fc/f7cfe4a5ea6be67bbaa42afb6fe3d8fbaca6d3a71aa63bf86c22a7fac53b/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "44bf617535f5ead500a6f83178426f7c607e015bcf9e614e18ae88aa3de1a340", + "https://files.pythonhosted.org/packages/5c/83/ba162a3b82eca12ddbb7f229a3d05b79ec5be66bde44007eab3de7abed7b/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl": "0c9408949156bf4ae07e60d5c8f356114be1482dc8f708ecd5785151e9ae21fa", + "https://files.pythonhosted.org/packages/5e/e9/b8e55bd15b231ff44028b7ad5ffd231cf038b8181bf288b58f8ce2324072/ignore_python-0.3.3-cp313-cp313-macosx_10_12_x86_64.whl": "593679d7714b4228d7e8c3bda0005badb9f0d4cb37cd8dda8385ef734e675e23", + "https://files.pythonhosted.org/packages/5f/f3/b6aa2183016cefb19175308a5b65ba72dddc2f9ff2dce6ef9bb9f2e61b1e/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl": "d94d9b91eece76104077a7e0b3276bb14728a4bff00ba23b0f0bea2a10c0e6e8", + "https://files.pythonhosted.org/packages/61/2c/fe119eea2eb12c3fa3cf2793b0a8664e396c9b158967b37f2ed3e959f6d2/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "82fecbeb7fa309245aaaa7e3aaa09c744f1059fc238e2f7acd889d803f1a0be7", + "https://files.pythonhosted.org/packages/62/d0/ac58193a1ec6c8ec06da209fcbead510938a8dde89fc6478cace59cb77b7/ignore_python-0.3.3-cp314-cp314-musllinux_1_2_i686.whl": "4252f803f3cdae6c8775f1e905f5babd6e77860781f4c29cf798452ee5fd6936", + "https://files.pythonhosted.org/packages/63/03/c1890e428c05445eb14523e76fe87ba71151bae370874e05bb0f32276700/ignore_python-0.3.3-cp313-cp313t-musllinux_1_2_i686.whl": "7e9bea86436a59eb3f24e8e15bb3b3a36cdb98aec950c70aa619e33f35eb6beb", + "https://files.pythonhosted.org/packages/63/63/31483b9985ca2c4b1d828457ecccc1e60b02f3887d548ecbaa565176618a/ignore_python-0.3.3-cp314-cp314t-musllinux_1_2_armv7l.whl": "356b783877c7e88eba69c99bcc5e2d9fb07c2fe54f2c64e03897b7ae2adce2a7", + "https://files.pythonhosted.org/packages/6a/b8/7ef6326e648aacbc34ee31f2c4c9db073473f85d1f649cd5bda799f47d60/ignore_python-0.3.3-cp314-cp314-musllinux_1_2_armv7l.whl": "b0f0bc9eab99a36f54e0a4e3043768e529107565bc67a7f420b4febff8006f32", + "https://files.pythonhosted.org/packages/6b/37/57e38f4bc0f7584b578751dbfbb9081cf5af38848893aec69a814db24d03/ignore_python-0.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl": "b123951f9befe6052a7397778fa64ade18983345d79fd9477160b11dfd736df0", + "https://files.pythonhosted.org/packages/71/81/f2dd6e0ab7aa9b860d82c1b3ce195ed5d7284e92cde2cbe5a6915241a8c4/ignore_python-0.3.3-cp314-cp314-win_amd64.whl": "fabb25c4352d3d4f1a2a197d6fb403848026344aab73e1674bcb31e4a7f54914", + "https://files.pythonhosted.org/packages/74/ef/767d321b08e9b21d8d7a5b34d3f9d4b4eaf4d3c33b4761dcd0b6c0d87560/ignore_python-0.3.3-cp311-cp311-musllinux_1_2_i686.whl": "477bb090189b79b8a81753c74a59ccb6c0ebf91985f30e926177f062c8616d05", + "https://files.pythonhosted.org/packages/75/be/c220b77b9997f2ae81d30af3562bf7dee8e4c68a4de0a0404098b98fb49f/ignore_python-0.3.3-cp313-cp313-musllinux_1_2_armv7l.whl": "0dfa531bbf65f45f9a233e2809f8b0a49aa9078686dbb0e4f3e30848e09cc208", + "https://files.pythonhosted.org/packages/77/d2/be1a05941346a51384a5a71e1aa9933cfdc8a4508ed6f03ae925bb37dffc/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl": "8e9085cec8d730b43ac8c86ef5da0c902f0f57da8da2ee009045e7006fe4860f", + "https://files.pythonhosted.org/packages/7a/dc/a90ba9f5eab933e8bfd65396ee88c14221641e8fb48d93ce45454083e50d/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl": "7d63688dc696b72d54623dd55f269d5203fcd5de5eeeba7bc276864300a8d790", + "https://files.pythonhosted.org/packages/7b/8f/083356b171a87168b281a0af3da5d558e55fde13a792ee7b079a99ad01ee/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "5756229d699ec5ab8caf4cec3ce9827d02145059c79a2416f363e2df4406d378", + "https://files.pythonhosted.org/packages/7c/b4/aadf114682a3495361f12b827a38d1f4ee8f452166747513cfbc18818121/ignore_python-0.3.3-cp312-cp312-musllinux_1_2_i686.whl": "de10b31770a8978e381c8f0c05479e18d6ea1defa18a0aa825b0487acd1f082c", + "https://files.pythonhosted.org/packages/7e/ea/4bd00b6848a5c93a7c9f179709ab1e09edea7728287177145f878c68630d/ignore_python-0.3.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl": "8f0e8379d4eff6842b61c01c7f03dc7415afac994b96e4a7d24f80b1078d5c1d", + "https://files.pythonhosted.org/packages/80/b8/167a424011bfe0af21304de7c8df8114e7e680e290f5e1a7aa1318179420/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl": "1b23770925db422fe9c94920da82e0606516aca09fd851880bc8c8ed68fe6455", + "https://files.pythonhosted.org/packages/83/2b/f52307ec26252690f6a4f31df7d727a53807a39c2e70727342b3ae4343e7/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl": "4ba31985a790af71bc45c16643d4773b829b8233acf4175bba2af466a2bbc959", + "https://files.pythonhosted.org/packages/86/62/7989a0a1e3d133b4d70727732020cd690969e693227615e45e275b6af46c/ignore_python-0.3.3-cp310-cp310-musllinux_1_2_aarch64.whl": "7e3b89f96cda6df85687b9532eac4faffdf15d2d918b239399cab6c78dd5282b", + "https://files.pythonhosted.org/packages/89/7e/908bf9ee614a5e3c4cc695e620c07c3a1449508a1ebdd54ab688a6c71e8b/ignore_python-0.3.3-cp310-cp310-musllinux_1_2_armv7l.whl": "1225e6210e302e0725265504a11a367f0b8cb882e3e2e231748559d5b05179c8", + "https://files.pythonhosted.org/packages/8b/b2/24102dc6c85b3fcfb29c9928d69e8d8656557e14ec50d1dd5caf437a6e6f/ignore_python-0.3.3-cp312-cp312-win_amd64.whl": "152c5aecf42e709138c8e3da2ac733d00f5efdcd004c240a95193e62b6bd024e", + "https://files.pythonhosted.org/packages/8c/00/f3f82228067b68f0f66594261a1f51e090fbc32616e1494fbe32339a13fd/ignore_python-0.3.3-cp313-cp313-musllinux_1_2_aarch64.whl": "1b2ff29dbe59bbbd370feacb99e5bec7791abe730dd535b5db848de5b5210d7e", + "https://files.pythonhosted.org/packages/8c/90/7d1d5ddca496189b61d8ccccbc0fbb579a7c2c5b2dbd5e41eec944068dc8/ignore_python-0.3.3-cp313-cp313t-musllinux_1_2_armv7l.whl": "cc49302f8fdbd3426eba4b99671ba10f0d150d90ea4f344a0204e4ac8e4fcb66", + "https://files.pythonhosted.org/packages/90/db/d28201d52730132f63ecc3bebcc4ba8694a2764157c65425ffd86ba05903/ignore_python-0.3.3-cp314-cp314-musllinux_1_2_aarch64.whl": "1ac4491082df61d370f7fc087d5c0b16bc84b647e126e3f45a97d31cf3b2f514", + "https://files.pythonhosted.org/packages/94/09/a89778efcd14cc4d7c332133a58ce2cb3933ee0dc066aeeb7f749637c3b9/ignore_python-0.3.3-cp312-cp312-macosx_10_12_x86_64.whl": "429a9b792afdca7dd9cff59f5ace44c2f55d01fc30b0ce3d28d63bee223116ed", + "https://files.pythonhosted.org/packages/94/8e/360c8ec57ec2b7be346609571b3fa070882c0b20cc60b0c0cb1dd8c11f5f/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "d9778479aaeac4f000d2b9c0f6da76149926011588b08a6b22892ae921c4f563", + "https://files.pythonhosted.org/packages/94/c1/28ad4b5e4e4108407542f7adebccd98d554bfa5f6fcd99f959d6aea9057b/ignore_python-0.3.3-cp38-cp38-musllinux_1_2_x86_64.whl": "f65475871a57413dee3094cc5d479ed202af85c38bc90bda5d7ee4435ff96819", + "https://files.pythonhosted.org/packages/95/cf/1417966361c38b3acb80e2a427cf34883b9c1dc4274dc1097cd9c3c6af2d/ignore_python-0.3.3-cp313-cp313-musllinux_1_2_i686.whl": "e4786eac47d2e9dab245fc376157bdc458f4b3269b81006b80a74d514b37efb7", + "https://files.pythonhosted.org/packages/97/ff/c3707e735606b0197484c4be4ac8e6a32a8899c95d0db25a9bedc301776c/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "c8b112dd1906487fe56525361cbeca57b07adcc9496b641560327654aced7e8f", + "https://files.pythonhosted.org/packages/a2/67/13e76d9a9bf4484b212645631884f48503cdf37ca1f8a0cd761d63faf8ac/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "b4f2df2bbca999f9749430e54dcd13bcc35289520b63e09ed4d2e1877a524260", + "https://files.pythonhosted.org/packages/a4/12/60370837ef24be9ae16ee5189b8c2070d429e45a616106303acece8d9d0e/ignore_python-0.3.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl": "56e8f00aef89a19b0305cb233fb59736ad14aa8c6db05720b13ac0bca811b5e7", + "https://files.pythonhosted.org/packages/a4/bc/d510141c02207147c6ca5eb16412cfbbab556abd8ec2fc4cba061cd67981/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "e397f034d675ef14980f2df0a074dec3218963a912b7011d0e8e94f9a3d87d41", + "https://files.pythonhosted.org/packages/a6/1b/10bc2fce9d9f7d669755ea6b22f463f1950eb5538806cbf873f25092b9d3/ignore_python-0.3.3-cp311-cp311-macosx_10_12_x86_64.whl": "5134be429fb954ec589857dbc6152dc09014902313e3d2293af9338a4b7799ae", + "https://files.pythonhosted.org/packages/a8/c6/53f2cff5be0b05f153aaa1e4f72e14f43c5b3980d4453672d9d718e62ff9/ignore_python-0.3.3-cp312-cp312-musllinux_1_2_armv7l.whl": "b730d11384a02bc4fb195b8a73555cb450e325d2676b39c8b5d20a0786102f37", + "https://files.pythonhosted.org/packages/aa/8e/97ed261d173b12101c590682b812759d6e787cd1161fb49921707399149b/ignore_python-0.3.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "c73743c4c8622ebed7998990c5f18a2c2a4fae915288798aeb8fef6d5411743b", + "https://files.pythonhosted.org/packages/aa/b1/c3d851fbf68d1a70cbe49e66b17d10f43af831ef06c34d7d6d385b59499e/ignore_python-0.3.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl": "d8be9b991c63fd8c76a6a9deac24ed164533824da1bdb2356a33511bfa446d54", + "https://files.pythonhosted.org/packages/ab/c6/d9daf9b3919a8d60d1128507d1ef96992563c6d4158f3a0409255542f19f/ignore_python-0.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl": "7ad2cc34fb600ab4aa22014bc8cc9b8bae2d467f772074d3a4929deb4adf64d6", + "https://files.pythonhosted.org/packages/ac/83/1d9de21d59d37ebf984b4caa40c745cfebb10f0d10400b1783e4231b5283/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "8fc3b2fcb6ee1c2b1512a0b29d26bb9b9e945d0d50c0a85673a675622fbfb0f4", + "https://files.pythonhosted.org/packages/af/93/c2aef57d2a83cc33fae854d5a8a33f13bd1d4e8a5accbee52bb2c8ef41d6/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "59e26bf7bbbd5937a196f01480050d3d80418ade8933164b28e3e36734baecd7", + "https://files.pythonhosted.org/packages/b2/30/2f337d260e0169a3a8ee1425c59093b08e055735110c9b62dab5a785f4bf/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "05ccc5bdf2ad1f840a0a2296efff5f4761a26f015185ed0bb835ad1901f08ee8", + "https://files.pythonhosted.org/packages/be/44/0e091481938ffff7bbef9f1964a49249d0a098f11a3b459f05c279d15429/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "85f3ec2d15ba134e6159ddebdb1a0af89f99431d93336f8b3cb71aa2de9f3324", + "https://files.pythonhosted.org/packages/bf/89/abbd4c122ccc82cff70ccec42c9f2f4a096e5e3c9946cd76436b28b1d6c3/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "2d637f1a6b2ec7bcb74b2ac11f75e15eaf6092535b8dc31415305a73a3762e3e", + "https://files.pythonhosted.org/packages/c0/1b/90b799876f7129bc045811ee80025f5f1a2cfed300fed972bb5790466b35/ignore_python-0.3.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "17749af58a6fe6aaa3198b285c874fbf0f036ef2cb684225ef62288b62948d26", + "https://files.pythonhosted.org/packages/c2/a9/a326f1a295e3ecbcadf90699c01b7389276f6405fa4da67472bcc9423ada/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "e61a305384a59997fa1971a6c886db9b8e2ff59fca3ee9bb4e1fe191b6d02593", + "https://files.pythonhosted.org/packages/c6/bd/a37cd31e6d4c6eaabe6ceb22defdb278246973d4f50f9deffea12b1ea037/ignore_python-0.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl": "3c40627f3bde32a37e75950b97733b586e9167fd545c958195dbf1bb73d96a81", + "https://files.pythonhosted.org/packages/c7/64/dbbbe3d4bc43207772aba5ea9aee8c24444e578790088bca4bb7acd8572e/ignore_python-0.3.3-cp312-cp312-musllinux_1_2_aarch64.whl": "af35c6b8c3a9a27721e5c2a849e2ee21973e14b8b7d2e76e15940475a8ace443", + "https://files.pythonhosted.org/packages/c7/89/c1a25074ee04c2db36ec1a1638a72dc91f0056674924783ed3f227fb2346/ignore_python-0.3.3-cp313-cp313-win32.whl": "97db61620be5c56a78115967d05e0d7a130a27a68f401eb98bf0753d3f770cb5", + "https://files.pythonhosted.org/packages/c8/c9/8929fdfbc3c0464ed4f5c5d6ad4b31a8c003dec1bca4446fcf9b8ba44866/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "dab6441f4403483a420de9481987e68ab75a28ade5bac8f1d82a3533ed83a6eb", + "https://files.pythonhosted.org/packages/c8/d7/545c0aabd0c51a08f2245062dfa4be2ea50285148ae78a5fcf013841a37a/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "1ed9c8c858dfe2ba91bc4ab60ebd9d12dd4602a4d0d555e0fee9c8621f9ca292", + "https://files.pythonhosted.org/packages/cb/2a/86804702f3d0820b75c67fa19180812a6cb7c945a720d7054862823c3246/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "39e7b9d976c12c68b09b9944328a000b012725f1f7c4655510eb890761016fb3", + "https://files.pythonhosted.org/packages/cc/32/3e44ceceb0111c9d6cdcb24aaf7d531c4dc0037c51cdd22a75fd2d71de9d/ignore_python-0.3.3-cp313-cp313-musllinux_1_2_x86_64.whl": "4f88aa2ee7335399c823aa050edec118e28ddafe7859e0db4093de0ca815467e", + "https://files.pythonhosted.org/packages/cc/cb/30c8862795c88c8c249a34e584ee95e27748ca890466ab9044e94f32b717/ignore_python-0.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl": "4aac18bf9346f06fd17412d5bcfca53090a72456f53f3ffdf3b1e896f15cc356", + "https://files.pythonhosted.org/packages/cc/dd/9b22e9d958261346fffc5f5062b03af44c7cb77f9ec9265c25ec607e4c39/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "48cf09c7668b5b8bb7dcaf185618130b8ea7839df0eb0f6ae4fae7b7162a0c37", + "https://files.pythonhosted.org/packages/cd/92/02801ce20abad2103c8e634582d1b45cca21985a226d29870ee9ebde0338/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "0c9ea5d81e6b1a1284f5463203f57cffb3c7e391c0d8ce3fe0e4d838621367fb", + "https://files.pythonhosted.org/packages/cd/b5/aaba0c76d2694873a1dbf31886575c15b11bf7e250a1958deac7221c502a/ignore_python-0.3.3-cp39-cp39-musllinux_1_2_i686.whl": "b2c072a4615c9610bd43cde816882ede0c910599702e54cbc07371874d1ca95b", + "https://files.pythonhosted.org/packages/d2/a8/84baf48e05b603480b963f655e73b97230f7296433d3cc8206b742c167fd/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "ebb00b54e5a01d8b10a51a7de7d2f884359375f4abb5352378f6f7f2c8878a58", + "https://files.pythonhosted.org/packages/d2/b6/2f093454e0c4390128eeeec265181b2b770214ecf1dba920664cdef56756/ignore_python-0.3.3-cp38-cp38-musllinux_1_2_armv7l.whl": "3001adecf33250dfa90c6e727affd559b31ad3b32b8519920c14bef53410444f", + "https://files.pythonhosted.org/packages/d5/d8/b868b289dc2466f3339e72602861379905d4ba1ef32d5c89c0874bf5a1e0/ignore_python-0.3.3-cp313-cp313-win_arm64.whl": "842572b228382c9bb6283428f14ff4481b3822cb7488ce4388281a8c6c465a81", + "https://files.pythonhosted.org/packages/d6/31/71cc339d9a76585f0d3dedf9ea47e070278e792ba87ab88779d02e3765a2/ignore_python-0.3.3-cp313-cp313-macosx_11_0_arm64.whl": "000ae12f6187791bc4748e40e7073b9769ca6ddb0cabfc11d1c682ca0e6a3953", + "https://files.pythonhosted.org/packages/d7/2c/ca2816e41d182f1b60d859906414e831b0cb6182006eb76f7cb4ade0630b/ignore_python-0.3.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl": "f24c6af8fd78b5c58e0f2683004e0b6fac146ee047b2d8fc8d7b16c69e0a3aaa", + "https://files.pythonhosted.org/packages/d7/cf/ae857c74b643ad0cda78501c3fd9ef4107cc4551d0dce04b0a3907f616f5/ignore_python-0.3.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "89b2efb712c5bc0c57cc5a9deabfbcb2196504139d2d108a4afedd140c02063f", + "https://files.pythonhosted.org/packages/d9/3a/33afae6b7102e957506dd702ae74346fe368a541d4675e9aba5725381997/ignore_python-0.3.3-cp312-cp312-macosx_11_0_arm64.whl": "365ab0bf94b64f3def2264fdca58f6e9811763830ad1a48a41d70574a496e5b9", + "https://files.pythonhosted.org/packages/d9/4b/2a07a65878f256effc14bce8afd8d1245549da231c364e4489a6b0cf773d/ignore_python-0.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl": "62afd51edaf5634e21206a65e9e244e038b747e39ba969ebcc9361b63825a4f9", + "https://files.pythonhosted.org/packages/db/20/54bfd6688b19fcd7978163e39078c43f397c9e1730a3ae6149c9573b6401/ignore_python-0.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl": "7e49780796e39812ade8001b0c7d2a2f1a9aeac90964e8e757c2013d30dfbe4e", + "https://files.pythonhosted.org/packages/db/de/aa50c31cdabf694b4e76e42f0a771ba6527e5d9e2b0d1c2bdc9e2a3a6b2b/ignore_python-0.3.3-cp314-cp314-win_arm64.whl": "c545cfd062a463c6d8e90b2738ec93fb9228f12c0aa80866fdc80f64fbc8f1a3", + "https://files.pythonhosted.org/packages/de/d4/2e9a778269b535c17ef016f6e4300e4811688119beaf0367df9fee909690/ignore_python-0.3.3-cp311-cp311-musllinux_1_2_aarch64.whl": "e571e0e3de9bca0b70afc4261aa2df6a305bfff94b092942ccd081fa07b8a148", + "https://files.pythonhosted.org/packages/e3/da/299d98fdb31e2a51d9f814fb0a341efbf3febed212fe69c8796aad8d3811/ignore_python-0.3.3-cp39-cp39-musllinux_1_2_armv7l.whl": "a894e6bf85988edee94474ff1399b61685d5fa7399d1c25a40efa28f7a2799ea", + "https://files.pythonhosted.org/packages/e5/e7/8df35dc4e6e162306c07c9912e8bf4e4aca2bc3e41dbbaf4feb953089d2f/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "da0ebc45da1d4e918fba53a2bc059339d00a1f666b3b72b9acd4fe4eeaec8343", + "https://files.pythonhosted.org/packages/e6/ae/9cbd37a23b4f7367369fb289d7729b0995c68e651f1b06b720e24a5169f3/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "5f3d88554e779f03567c05286f31d2ce21f6103892c7412bdf350ef2fb50184a", + "https://files.pythonhosted.org/packages/e8/cb/f246ba7e4b6ea4c819eb6f6469bcb68d5abc372068117f305849d04f8be6/ignore_python-0.3.3-cp38-cp38-musllinux_1_2_i686.whl": "fb712f94825c04fa605b989d6f5889195b51c927f5045c34b9ff7a448cd0a6f0", + "https://files.pythonhosted.org/packages/eb/ea/f30078aa0359d777056bf83efc38463258056ae6e904246588200f55157d/ignore_python-0.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl": "a2cdfbd3c9df9e98dd067858fce7d6ab919f2fb038f6b3124fc5f05b8825b546", + "https://files.pythonhosted.org/packages/ec/66/70338e7d59df3050658b3e34512f572f1865849520352b5002b7723025aa/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl": "3da9e102800f162468ddb5d2d392b79e2481d3709e886f875037ef7066df5481", + "https://files.pythonhosted.org/packages/f0/01/6c3396e839ddc339bff4245015a259496986afc0ffa1f0aec28957829502/ignore_python-0.3.3-cp39-cp39-musllinux_1_2_aarch64.whl": "e3754a2529b0c163bece327be6c5e92fe885cd01d066bc4a3fc13224eae5d221", + "https://files.pythonhosted.org/packages/f0/63/9c4665a4ea2894eb269bd69043f2004c571024a5699cc231a11219aa00a9/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl": "130d9ace07988b69669e871ed84dc77088d7b5ce35265efbb0b0d425085e2a99", + "https://files.pythonhosted.org/packages/f0/65/4730b11443d4da0fef9ed7525879529d452d862001c5287f1383d9c2a9ac/ignore_python-0.3.3-cp314-cp314-macosx_10_12_x86_64.whl": "d01b0578252d0df17b64400103ffbfea5b8332483a3da01d116f4919467128de", + "https://files.pythonhosted.org/packages/f3/7f/ab5f380507de1e1d1e1e20f8b64a241581a538d234ce3b66d3f74d4ed66a/ignore_python-0.3.3-cp310-cp310-musllinux_1_2_x86_64.whl": "12827fc970d57865b6f31a82f79838bea24b63d85d9f61f1821b7cf8bd01128c", + "https://files.pythonhosted.org/packages/f3/a1/e907e02bc252f8c7efdab0d317019741cb243dc5eb00bf07e6680f66096f/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "8528c819c151ccabbd1bb9e591dd99495c2d0423b10ccdef47d48fe25da2b2d6", + "https://files.pythonhosted.org/packages/f4/4a/37928a560a345c6efb207452cf81d3c14f25a6d83df0fa5a00752c0c912b/ignore_python-0.3.3.tar.gz": "dc80ac80ace112da6d02f44681b6beb2ccecb68d6ac2b5e1b82d7f84347e1cf6", + "https://files.pythonhosted.org/packages/f9/a5/2aee7c1997f904aa0fdf5e34471c6e3c5cd6254a55b9aa8c3f2e0a158353/ignore_python-0.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl": "49ccb834be168a7e72b104ffc02024d4eb4d91840bc30e2948787551f5242ff0", + "https://files.pythonhosted.org/packages/fc/15/728225102093748271ef402e8d9c1ad1cce823289de79e236ab436d64979/ignore_python-0.3.3-cp38-cp38-musllinux_1_2_aarch64.whl": "558f79f48d2cd7bd42ce3e6747752be9483504d3f2f84c1d39c39bcf1961bac3", + "https://files.pythonhosted.org/packages/fd/34/af959f5525f98f93167a2a06e70373a1532734f5f1fc716d9f885c9a6164/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "d4f8b4137b0153c95ea7e4e3acff4c5e6f6c25206d3e3b86dbe879658b00c927", + "https://files.pythonhosted.org/packages/ff/67/bd846385bc059e58cc2be198de8953fdfcf7417cced6bc9686b62a523338/ignore_python-0.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl": "cb82264552ae789f2d8c2543b3b1eb2b3e091977c22f180ab268f5b677825279" + }, "imagesize": { "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl": "5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz": "8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3" @@ -1876,6 +1992,10 @@ "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz": "d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl": "f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282" }, + "sphinx-mounts": { + "https://files.pythonhosted.org/packages/50/43/3ed3fafb632fd168a62f8b9a1b5edc00e000802961f81dd4e922df946e60/sphinx_mounts-0.1.0.tar.gz": "d1818e3a0b7e0b327c6fa265afcf4d43f5bb7147276e69a9b35cd046deeb9439", + "https://files.pythonhosted.org/packages/5e/a2/1318ed0af240e580d1b3c3f1e33708b8bb4fba91dfc0b3c9754b08ab337a/sphinx_mounts-0.1.0-py3-none-any.whl": "aff3450756727d0ca43538ea72b67fddb8b8799c52110f41de8871c59d3d1540" + }, "sphinx-needs": { "https://files.pythonhosted.org/packages/b9/dd/69c5d151e1b4d98e85371fdf8ce2c1fe497f4345f32e0429cec20442d4de/sphinx_needs-8.0.0-py3-none-any.whl": "540c380c074d4088a557ea353e91513bfc1cb7712b10925c13ac9e5ebb7be091", "https://files.pythonhosted.org/packages/e4/77/909f87ae766363e8a0bb3907f54af4a7a963538a7cdc7fe6d331e7098ed1/sphinx_needs-8.0.0.tar.gz": "c4336ee0e3c949eff9eb11a14910f7b6b68cb8284d731cfddf97694037337674" diff --git a/bzl/basics.bzl b/bzl/basics.bzl new file mode 100644 index 000000000..96734a310 --- /dev/null +++ b/bzl/basics.bzl @@ -0,0 +1,45 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +def join_path(prefix, rest): + """Compose two docname segments with `/`. + + Args: + prefix: Leading docname segment, possibly empty. + rest: Trailing docname segment, possibly empty. + + Returns: + The combined docname. + """ + if not prefix or prefix == ".": + return rest + if not rest: + return prefix + return prefix + "/" + rest + +def dirname(path): + idx = path.rfind("/") + return "" if idx < 0 else path[:idx] + +def glob_doc_sources(prefix): + """Return glob patterns for documentation sources below ``prefix``.""" + extensions = [ + "png", "svg", "md", "rst", "html", "css", + "puml", "need", "yaml", "json", "csv", "inc", + ] + if prefix == ".": + prefix = "" + elif prefix and not prefix.endswith("/"): + prefix += "/" + param = [prefix + "**/*." + ext for ext in extensions] + srcs = native.glob(param, allow_empty = True) + return srcs diff --git a/bzl/bundle_rules.bzl b/bzl/bundle_rules.bzl new file mode 100644 index 000000000..670a57039 --- /dev/null +++ b/bzl/bundle_rules.bzl @@ -0,0 +1,306 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Internal Bazel support for composing reusable documentation bundles.""" + +# `docs_bundle` and `sphinx_docs_library` operate at a similar architectural level: +# both describe reusable, transitively composable collections of documentation sources +# that are later assembled into a Sphinx source tree. + +# However, their data models and responsibilities differ significantly. + +# `sphinx_docs_library` primarily models file placement. Each library contributes files +# together with a `strip_prefix` and a `prefix`, allowing the final Sphinx rule to map +# every source file to a new location in the generated source tree. + +# `docs_bundle` instead models documentation structure at the bundle level. In +# addition to the source files, it propagates information such as: + +# * where a bundle is mounted, * which document it is attached to, * which document acts +# as its entry point, * which repository owns its sources, * whether it is an internal +# or external bundle, * and how nested +# bundles are rebased when composed. + +# It also performs bundle-specific validation and conflict detection. The propagated unit +# is therefore not just a set of files with path transformations, but a structured +# documentation component with composition semantics. + +# Using `sphinx_docs_library` directly would not preserve the metadata required by this +# model. We would need a second provider alongside it and would still have to implement +# most of the bundle traversal, rebasing, validation, and composition logic ourselves. + +# Extending `sphinx_docs_library` is also not a good fit. Its provider represents +# individual file mappings, while our provider represents complete mounted bundles. Adding +# the required metadata would therefore not be a small extension of the existing +# abstraction; it would change its propagated unit and its semantics. It would also couple +# SCORE-specific composition rules to the generic `rules_sphinxdocs` implementation. + +# We therefore reimplement the relatively small overlapping part—transitive source +# collection—while keeping the richer bundle model explicit and independent. + +# The name `docs_bundle` reflects that relationship: it fills the same general role +# as `sphinx_docs_library`, but uses a SCORE-specific data model for composing structured +# documentation bundles. + + + +load("@score_docs_as_code//:bzl/basics.bzl", "join_path") + +# Internal data passed between bundle targets and eventually consumed by an +# adapter such as the Sphinx mounts manifest. Users configure bundles through +# `docs_bundle()` and `docs()`; they do not need to reference this provider. +DocsBundleInfo = provider( + doc = "A documentation bundle with its source and placement metadata.", + fields = { + "entries": "Ordered entries, one per source directory, including its final documentation-tree location.", + "sourcelinks": "Source-code-link JSON files together with their owning repository.", + }, +) + +def _parent_index_docname(mount_at): + """Choose the page that links to a bundled subtree by default.""" + parent = mount_at.rsplit("/", 1)[0] if "/" in mount_at else "" + return join_path(parent, "index") + +def _validate_and_deduplicate_entries(entries): + """Keep one entry per source directory and reject conflicting metadata.""" + seen = {} + out = [] + for entry in entries: + key = entry.runtime_path + if key in seen: + differing_fields = [ + field + for field in ["mount_at", "attach_to", "entry_doc", "src_root", "external", "repository"] + if getattr(seen[key], field) != getattr(entry, field) + ] + if differing_fields: + fail(("bundle conflict: source directory %r has conflicting %s; " + + "a bundle must resolve to one complete placement declaration") % + (key, differing_fields)) + continue + seen[key] = entry + out.append(entry) + return out + +def _bundle_runtime_path(ctx): + """Return this bundle source directory's Bazel runtime path.""" + source_file = ctx.files.srcs[0].short_path + external_prefix = "" + if source_file.startswith("../"): + path_parts = source_file.split("/") + external_prefix = path_parts[0] + "/" + path_parts[1] + "/" + return external_prefix + ctx.attr.strip_prefix.rstrip("/") + +def _bundle_execroot_path(runtime_path): + """Return the execroot-relative spelling of an external runtime path.""" + if runtime_path.startswith("../"): + return "external/" + runtime_path[3:] + return runtime_path + +def _rebase_bundle_entry(entry, mount_at, attach_to, entry_doc): + """Place a bundle entry below a requested documentation-tree location.""" + is_bundle_root = not entry.mount_at + if is_bundle_root: + rebased_attach_to = attach_to or _parent_index_docname(mount_at) + else: + rebased_attach_to = join_path(mount_at, entry.attach_to) + + return struct( + runtime_path = entry.runtime_path, + src_root = entry.src_root, + mount_at = join_path(mount_at, entry.mount_at), + attach_to = rebased_attach_to, + entry_doc = entry_doc if is_bundle_root else entry.entry_doc, + external = entry.external, + repository = entry.repository, + ) + +def _entries_visible_through(ctx, child): + """Keep an external module's own docs, but not its foreign mounts.""" + entries = child[DocsBundleInfo].entries + child_repository = child.label.workspace_name + if child_repository == ctx.label.workspace_name: + return entries + return [entry for entry in entries if entry.repository == child_repository] + +def _sourcelinks_visible_through(ctx, child): + """Apply the same external-module boundary to traceability metadata.""" + sourcelinks = child[DocsBundleInfo].sourcelinks + child_repository = child.label.workspace_name + if child_repository == ctx.label.workspace_name: + return sourcelinks + return [link for link in sourcelinks if link.repository == child_repository] + +def _validate_docname(value, field_name, allow_empty = False): + """Validate a relative documentation docname used in a bundle declaration.""" + if type(value) != "string": + fail("%s must be a string, got %r" % (field_name, value)) + if not value: + if allow_empty: + return + fail("%s must not be empty" % field_name) + invalid_segments = [segment for segment in value.split("/") if not segment or segment in [".", ".."]] + if invalid_segments: + fail("%s must be a relative docname without empty, '.' or '..' segments; got %r" % + (field_name, value)) + +def _parse_bundle_declaration(bundle): + """Read one nested-bundle declaration and fill in optional values.""" + if type(bundle) != "dict": + fail("each bundle declaration must be a dict, got %r" % bundle) + + allowed_keys = ["bundle", "mount_at", "attach_to", "entry_doc"] + unknown = [key for key in bundle if key not in allowed_keys] + if unknown: + fail("unknown key(s) %r in %r; allowed keys: %r" % + (unknown, bundle, allowed_keys)) + if "bundle" not in bundle or "mount_at" not in bundle: + fail("each entry needs 'bundle' and 'mount_at'; got %r" % bundle) + + mount_at = bundle["mount_at"] + attach_to = bundle.get("attach_to", "") + entry_doc = bundle.get("entry_doc", "index") + _validate_docname(mount_at, "mount_at", allow_empty = True) + _validate_docname(attach_to, "attach_to", allow_empty = True) + _validate_docname(entry_doc, "entry_doc") + + return struct( + bundle = bundle["bundle"], + mount_at = mount_at, + attach_to = attach_to, + entry_doc = entry_doc, + ) + +def _docs_bundle_impl(ctx): + """Compose source files and nested bundles into a reusable bundle.""" + entries = [] + direct_files = [] + + if ctx.files.srcs: + runtime_path = _bundle_runtime_path(ctx) + external = runtime_path.startswith("../") + entries.append(struct( + runtime_path = runtime_path, + # The execution root and runfiles tree spell external repositories + # differently. Keep both locations so every public docs() target can + # resolve them in its own context. + src_root = _bundle_execroot_path(runtime_path), + mount_at = "", + attach_to = "", + entry_doc = "index", + external = external, + repository = ctx.label.workspace_name, + )) + direct_files.extend(ctx.files.srcs) + + transitive_files = [] + sourcelinks = [ + struct(file = source_link, repository = ctx.label.workspace_name) + for source_link in ctx.files.sourcelinks + ] + for index, child in enumerate(ctx.attr.bundles): + entries.extend([ + _rebase_bundle_entry( + entry, + ctx.attr.bundle_mount_ats[index], + ctx.attr.bundle_attach_tos[index], + ctx.attr.bundle_entry_docs[index], + ) + for entry in _entries_visible_through(ctx, child) + ]) + transitive_files.append(child[DefaultInfo].files) + sourcelinks.extend(_sourcelinks_visible_through(ctx, child)) + + deduplicated_entries = _validate_and_deduplicate_entries(entries) + files = depset(direct = direct_files, transitive = transitive_files) + return [ + DefaultInfo(files = files), + DocsBundleInfo( + entries = deduplicated_entries, + sourcelinks = sourcelinks, + ), + ] + +_docs_bundle = rule( + implementation = _docs_bundle_impl, + attrs = { + "srcs": attr.label_list(allow_files = True), + "sourcelinks": attr.label_list(allow_files = True), + "strip_prefix": attr.string(default = ""), + "bundles": attr.label_list(providers = [DocsBundleInfo]), + "bundle_mount_ats": attr.string_list(), + "bundle_attach_tos": attr.string_list(), + "bundle_entry_docs": attr.string_list(), + }, + doc = "Internal rule that carries bundle files and their documentation-tree locations.", +) + +def create_bundle(name, bundles, srcs = [], sourcelinks = [], strip_prefix = "", visibility = None, **kwargs): + """Create a reusable documentation bundle from files and child declarations.""" + parsed_bundles = [_parse_bundle_declaration(declaration) for declaration in bundles] + _docs_bundle( + name = name, + srcs = srcs, + sourcelinks = sourcelinks, + strip_prefix = strip_prefix, + bundles = [bundle.bundle for bundle in parsed_bundles], + bundle_mount_ats = [bundle.mount_at for bundle in parsed_bundles], + bundle_attach_tos = [bundle.attach_to for bundle in parsed_bundles], + bundle_entry_docs = [bundle.entry_doc for bundle in parsed_bundles], + visibility = visibility, + **kwargs + ) + return ":" + name + +def _merge_bundle_sourcelinks_impl(ctx): + """Merge source-code links propagated by a documentation bundle.""" + sourcelinks = [link.file for link in ctx.attr.bundle[DocsBundleInfo].sourcelinks] + out = ctx.actions.declare_file(ctx.label.name + ".json") + args = ctx.actions.args() + args.add("--output", out.path) + if ctx.file.known_good: + args.add("--known_good", ctx.file.known_good.path) + args.add_all(sourcelinks) + inputs = [depset(sourcelinks)] + if ctx.file.known_good: + inputs.append(depset([ctx.file.known_good])) + ctx.actions.run( + executable = ctx.executable._merge_sourcelinks, + arguments = [args], + inputs = depset(transitive = inputs), + outputs = [out], + mnemonic = "MergeBundleSourcelinks", + ) + return [DefaultInfo(files = depset([out]))] + +_merge_bundle_sourcelinks = rule( + implementation = _merge_bundle_sourcelinks_impl, + attrs = { + "bundle": attr.label(providers = [DocsBundleInfo]), + "known_good": attr.label(allow_single_file = True), + "_merge_sourcelinks": attr.label( + default = Label("//scripts_bazel:merge_sourcelinks"), + cfg = "exec", + executable = True, + ), + }, +) + +def merge_bundle_sourcelinks(name, bundle, known_good = None, visibility = None): + """Create one source-code-link JSON file for a complete docs bundle.""" + _merge_bundle_sourcelinks( + name = name, + bundle = bundle, + known_good = known_good, + visibility = visibility, + ) diff --git a/bzl/mount_rules.bzl b/bzl/mount_rules.bzl new file mode 100644 index 000000000..50e51dde5 --- /dev/null +++ b/bzl/mount_rules.bzl @@ -0,0 +1,44 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +""" +Conversion of documentation bundles from Bazel into mount metadata. +""" + +load("@score_docs_as_code//:bzl/bundle_rules.bzl", "DocsBundleInfo") + +def _mounts_manifest_impl(ctx): + """Generate the canonical Sphinx mount manifest.""" + entries = ctx.attr.bundle[DocsBundleInfo].entries + + json_mounts = [] + for entry in entries: + json_mounts.append({ + "src_root": entry.src_root, + "runtime_path": entry.runtime_path, + "mount_at": entry.mount_at, + "attach_to": entry.attach_to, + "entry_doc": entry.entry_doc, + "external": entry.external, + }) + + out = ctx.actions.declare_file(ctx.label.name + ".json") + ctx.actions.write(out, json.encode({"mounts": json_mounts})) + return [DefaultInfo(files = depset([out]))] + +create_mounts_manifest = rule( + implementation = _mounts_manifest_impl, + attrs = { + "bundle": attr.label(providers = [DocsBundleInfo]), + }, + doc = "Writes a Sphinx mount manifest from reusable documentation bundles.", +) diff --git a/docs.bzl b/docs.bzl index 30c0cdaa3..2468fe28e 100644 --- a/docs.bzl +++ b/docs.bzl @@ -44,56 +44,62 @@ Easy streamlined way for S-CORE docs-as-code. load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_venv") load("@docs_as_code_hub_env//:requirements.bzl", "all_requirements") load("@sphinxdocs//sphinxdocs:sphinx.bzl", "sphinx_build_binary", "sphinx_docs") - -def _rewrite_needs_json_to_docs_sources(labels): - """Replace '@repo//:needs_json' -> '@repo//:docs_sources' for every item.""" - out = [] - for x in labels: - s = str(x) - if s.endswith(":needs_json"): - out.append(s.replace(":needs_json", ":docs_sources")) - else: - out.append(s) - return out - -def _rewrite_needs_json_to_sourcelinks(labels): - """Replace '@repo//:needs_json' -> '@repo//:sourcelinks_json' for every item.""" - out = [] - for x in labels: - s = str(x) - if s.endswith(":needs_json"): - out.append(s.replace(":needs_json", ":sourcelinks_json")) - #Items which do not end up with ':needs_json' shall not be appended to 'out'. - #They are treated separately and are not related to source code linking. - return out - -def _merge_sourcelinks(name, sourcelinks, known_good = None): - """Merge multiple sourcelinks JSON files into a single file. +load( + "@score_docs_as_code//:bzl/basics.bzl", + "glob_doc_sources", + "join_path", +) +load( + "@score_docs_as_code//:bzl/bundle_rules.bzl", + "create_bundle", + "merge_bundle_sourcelinks", +) +load( + "@score_docs_as_code//:bzl/mount_rules.bzl", + "create_mounts_manifest", +) + +def docs_bundle(name, source_dir = None, bundles = [], scan_code = [], visibility = None, **kwargs): + """A docs bundle, optionally composed of others. Args: - name: Name for the merged sourcelinks target - sourcelinks: List of sourcelinks JSON file targets + name: target name. + source_dir: optional directory holding this bundle's own doc sources. It is + globbed like `docs()` (same file kinds) and the contents are stored after + stripping the `source_dir` prefix. Leave it unset for a pure aggregator. + bundles: nested bundles to compose, each a dict + { + "bundle": , + "mount_at": , + "attach_to": + }. + scan_code: Source-code targets to scan for source-code links owned by this + bundle. + visibility: Target visibility. + **kwargs: Additional attributes forwarded to the underlying rule. """ - extra_srcs = [] - known_good_arg = "" - if known_good != None: - extra_srcs = [known_good] - known_good_arg = "--known_good $(location %s)" % known_good + srcs = glob_doc_sources(source_dir) if source_dir != None else [] + sourcelinks = [] + if scan_code: + sourcelinks_name = name + "_sourcelinks_json" + _sourcelinks_json(name = sourcelinks_name, srcs = scan_code) + sourcelinks = [":" + sourcelinks_name] - merge_sourcelinks_tool = Label("//scripts_bazel:merge_sourcelinks") + # Store the source directory relative to the workspace so bundle consumers + # can locate the original files without copying them. + pkg = native.package_name() + strip_prefix = join_path(pkg, source_dir) if source_dir != None else "" - native.genrule( + # The helper validates child declarations and creates the internal target. + create_bundle( name = name, - srcs = sourcelinks + extra_srcs, - outs = [name + ".json"], - cmd = """ - $(location {merge_sourcelinks_tool}) \ - --output $@ \ - {known_good_arg} \ - $(SRCS) - """.format(known_good_arg = known_good_arg, merge_sourcelinks_tool = merge_sourcelinks_tool), - tools = [merge_sourcelinks_tool], + srcs = srcs, + sourcelinks = sourcelinks, + strip_prefix = strip_prefix, + bundles = bundles, + visibility = visibility, + **kwargs ) def _missing_requirements(deps): @@ -127,7 +133,16 @@ def _missing_requirements(deps): fail(msg) fail("This case should be unreachable?!") -def docs(source_dir = "docs", data = [], deps = [], scan_code = [], test_sources = [], known_good = None, metamodel = None): +def docs( + source_dir = "docs", + data = [], + deps = [], + scan_code = [], + test_sources = [], + known_good = None, + metamodel = None, + bundles = [], + ): """Creates all targets related to documentation. By using this function, you'll get any and all updates for documentation targets in one place. @@ -142,17 +157,36 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = [], test_sources known_good: Optional label to a "known good" JSON file for source links. metamodel: Optional label to a metamodel.yaml file. When set, the extension loads this file instead of the default metamodel shipped with score_metamodel. + bundles: List of placement dicts describing documentation bundles to overlay + into this documentation's source tree. Each entry is a dict + { + "bundle": , + "mount_at": , + "attach_to": , + "entry_doc": , + }. + Note: a bundle label may also point at another module's auto-exposed + bundle, e.g. "@score_process//:docs_bundle". """ - metamodel_data = [] - metamodel_env = {} - metamodel_opts = [] - if metamodel != None: - metamodel_data = [metamodel] - metamodel_env = {"SCORE_METAMODEL_YAML": "$(location " + str(metamodel) + ")"} - metamodel_opts = ["--define=score_metamodel_yaml=$(location " + str(metamodel) + ")"] - - module_deps = deps + source_config = ":" + ("" if source_dir == "." else source_dir + "/") + "conf.py" + + # Convention in this macro: an optional Bazel label is named ``*_label`` + # but represented as a 0/1 list. This lets it be appended directly to + # list-valued attributes such as ``data`` and ``tools``. + metamodel_label = [metamodel] if metamodel else [] + + mounts_manifest_label = [] + if bundles: + create_mounts_manifest( + name = "_mounts_manifest", + bundle = create_bundle( + name = "_docs_mounts", + bundles = bundles, + visibility = ["//visibility:private"], + ), + ) + mounts_manifest_label = [":_mounts_manifest"] deps = deps + _missing_requirements(deps) deps = deps + [ Label("//src:plantuml_for_python"), @@ -164,64 +198,46 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = [], test_sources sphinx_build_binary( name = "sphinx_build", visibility = ["//visibility:private"], - data = data + metamodel_data, + data = data + metamodel_label + [":docs_bundle"], deps = deps, ) - # If the source directory is the root (".") we must omit it, otherwise: - # > invalid glob pattern './**/*.png': segment '.' not permitted - if source_dir == ".": - source_prefix = "" - else: - source_prefix = source_dir + "/" - - native.filegroup( - name = "docs_sources", - srcs = native.glob([ - source_prefix + "**/*.png", - source_prefix + "**/*.svg", - source_prefix + "**/*.md", - source_prefix + "**/*.rst", - source_prefix + "**/*.html", - source_prefix + "**/*.css", - source_prefix + "**/*.puml", - source_prefix + "**/*.need", - source_prefix + "**/*.yaml", - source_prefix + "**/*.json", - source_prefix + "**/*.csv", - source_prefix + "**/*.inc", - ], allow_empty = True), + known_good_label = [known_good] if known_good else [] + # The public bundle carries both the complete source tree and the + # transitive source-code links of every nested bundle. + docs_bundle( + name = "docs_bundle", + source_dir = source_dir, + bundles = bundles, + scan_code = scan_code, visibility = ["//visibility:public"], ) + merge_bundle_sourcelinks( + name = "sourcelinks_json", + bundle = ":docs_bundle", + known_good = known_good, + ) - _sourcelinks_json(name = "sourcelinks_json", srcs = scan_code) - - data_with_docs_sources = _rewrite_needs_json_to_docs_sources(data) - additional_combo_sourcelinks = _rewrite_needs_json_to_sourcelinks(data) - _merge_sourcelinks(name = "merged_sourcelinks", sourcelinks = [":sourcelinks_json"] + additional_combo_sourcelinks, known_good = known_good) - docs_data = data + metamodel_data + [":sourcelinks_json"] - combo_data = data_with_docs_sources + metamodel_data + [":merged_sourcelinks"] + docs_data = data + metamodel_label + [":sourcelinks_json", ":docs_bundle"] + mounts_manifest_label docs_env = { "SOURCE_DIRECTORY": source_dir, "PACKAGE_DIR": native.package_name(), - "DATA": str(data), "TEST_SOURCES": str(test_sources), + "DATA": str(data), + # `bazel run` starts from a runfiles tree, so this logical path is + # resolved by score_mounts through ``RUNFILES_DIR``. + "MOUNTS_MANIFEST": "$(rlocationpath :_mounts_manifest)" if bundles else "", "SCORE_SOURCELINKS": "$(location :sourcelinks_json)", - } | metamodel_env - docs_sources_env = { - "SOURCE_DIRECTORY": source_dir, - "PACKAGE_DIR": native.package_name(), - "DATA": str(data_with_docs_sources), - "TEST_SOURCES": str(test_sources), - "SCORE_SOURCELINKS": "$(location :merged_sourcelinks)", - } | metamodel_env - if known_good: - known_good_str = str(known_good) + } + if metamodel: + # The interactive ``py_binary`` targets run from a runfiles tree. + # incremental.py resolves this logical path through ``RUNFILES_DIR``. + docs_env["SCORE_METAMODEL_YAML"] = "$(rlocationpath " + str(metamodel) + ")" + if known_good_label: + known_good_str = str(known_good_label[0]) docs_env["KNOWN_GOOD_JSON"] = "$(location " + known_good_str + ")" - docs_sources_env["KNOWN_GOOD_JSON"] = "$(location " + known_good_str + ")" - docs_data.append(known_good) - combo_data.append(known_good) + docs_data += known_good_label docs_env["ACTION"] = "incremental" @@ -234,22 +250,6 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = [], test_sources env = docs_env ) - docs_sources_env["ACTION"] = "incremental" - py_binary( - name = "docs_combo", - tags = ["cli_help=Build full documentation with all dependencies:\nbazel run //:docs_combo"], - srcs = [incremental_src], - data = combo_data, - deps = deps, - env = docs_sources_env - ) - - native.alias( - name = "docs_combo_experimental", - actual = ":docs_combo", - deprecation = "Target '//:docs_combo_experimental' is deprecated. Use '//:docs_combo' instead.", - ) - docs_env["ACTION"] = "linkcheck" py_binary( name = "docs_link_check", @@ -280,16 +280,6 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = [], test_sources env = docs_env ) - docs_sources_env["ACTION"] = "live_preview" - py_binary( - name = "live_preview_combo_experimental", - tags = ["cli_help=Live preview full documentation with all dependencies in the browser:\nbazel run //:live_preview_combo_experimental"], - srcs = [incremental_src], - data = combo_data, - deps = deps, - env = docs_sources_env - ) - py_venv( name = "ide_support", tags = ["cli_help=Create virtual environment (.venv_docs) for documentation support:\nbazel run //:ide_support"], @@ -301,8 +291,8 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = [], test_sources sphinx_docs( name = "needs_json", - srcs = [":docs_sources"], - config = ":" + source_prefix + "conf.py", + srcs = [":docs_bundle"], + config = source_config, extra_opts = [ "-W", "--keep-going", @@ -310,12 +300,15 @@ def docs(source_dir = "docs", data = [], deps = [], scan_code = [], test_sources "--jobs", "auto", "--define=external_needs_source=" + str(data), + # ``sphinx_docs`` is a sandboxed build action, so it needs the + # action-input path rather than the runfiles-relative spelling. + "--define=mounts_manifest=" + ("$(location :_mounts_manifest)" if bundles else ""), "--define=score_sourcelinks_json=$(location :sourcelinks_json)", "--define=score_source_code_linker_plain_links=1", - ], + ] + (["--define=score_metamodel_yaml=$(location " + str(metamodel) + ")"] if metamodel else []), formats = ["needs"], sphinx = ":sphinx_build", - tools = data + [":sourcelinks_json"], + tools = data + metamodel_label + [":sourcelinks_json", ":docs_bundle"] + mounts_manifest_label, visibility = ["//visibility:public"], # Persistent workers cause stale symlinks after dependency version # changes, corrupting the Bazel cache. diff --git a/docs/concepts/bidirectional_traceability.rst b/docs/concepts/bidirectional_traceability.rst index 1df0065c3..ddd45b483 100644 --- a/docs/concepts/bidirectional_traceability.rst +++ b/docs/concepts/bidirectional_traceability.rst @@ -48,17 +48,18 @@ That other module's need elements will not have backlinks. At least not immediately. In a later revision they can update their dependency on the first module and then the references are updated in their documentation. -Build with copies +Build with mounts ~~~~~~~~~~~~~~~~~ .. code-block:: sh - bazel run //:docs_combo + bazel run //:docs -The documentation build does not depend on the needs.json but on whole documentation source code. +The documentation build mounts the whole documentation source tree declared through +``bundles`` instead of depending on a copied ``needs.json``. -Using `sphinx_collections `_ -not just the current module is built but all referenced modules are included. +Using `sphinx-mounts `_, the current +documentation source tree is extended with the declared bundles. The advantage is that the produced documentation is consistent and stays that way. There is no outwards hyperlink which could break or be outdated. diff --git a/docs/how-to/other_modules.rst b/docs/how-to/other_modules.rst index a64c65568..07f40902b 100644 --- a/docs/how-to/other_modules.rst +++ b/docs/how-to/other_modules.rst @@ -19,8 +19,9 @@ This document explains how to enable cross-module (bi-directional) linking betwe In short: 1. Make the other module available to Bazel via the `MODULE` (aka `MODULE.bazel`) file. -2. Add the external module's documentation targets to your `docs(data=[...])` target so Sphinx can see the other module's built inventory. -3. Reference remote needs using the normal Sphinx-Needs referencing syntax. +2. Choose one integration mode: import its built ``needs.json`` **or** mount its + documentation sources. +3. Reference Needs using the normal Sphinx-Needs referencing syntax. Details and Example ------------------- @@ -37,8 +38,8 @@ A minimal example (add or extend the existing `bazel_deps` stanza): bazel_dep(name = "score_process", version = "1.5.3") -2) Extend your `docs` rule so Sphinx picks up the other module's inventory -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2a) Import the other module's built inventory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The documentation build in this project is exposed via a Bazel macro/rule that accepts a `data` parameter. Add the external module's ``:needs_json`` target to that list @@ -58,6 +59,35 @@ Example `BUILD` snippet (consumer module): More details in :ref:`docs_bidirectional_traceability`. + +2b) Mount the external module's documentation bundle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The documentation build in this project is exposed via a Bazel macro that accepts +a ``bundles`` parameter. Mount the external module's auto-exposed +``:docs_bundle`` bundle. The mounted sources define their needs in the host +build, so do **not** also add that module's ``:needs_json`` to ``data`` — doing +so would create duplicate need IDs. + +Example `BUILD` snippet (consumer module): + +.. code-block:: starlark + + load("@score_docs_as_code//:docs.bzl", "docs") + docs( + bundles = [ + { + "bundle": "@score_process//:docs_bundle", + "mount_at": "process", + "attach_to": "index", + }, + ], + source_dir = "docs", + ) + +See :ref:`howto_mount_external_sources` for the full mount reference, and +:ref:`docs_bidirectional_traceability` for more on cross-module linking. + 3) Reference needs across modules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/internals/extensions/sync_toml.rst b/docs/internals/extensions/sync_toml.rst index 46ad26b91..9fddd890d 100644 --- a/docs/internals/extensions/sync_toml.rst +++ b/docs/internals/extensions/sync_toml.rst @@ -31,20 +31,18 @@ is needed to extract this information and make it available to the IDE extension The basic idea is to stay with the programmed configuration system for Sphinx and Sphinx-Needs as it exists in S-CORE, but use it to generate the ``ubproject.toml`` file. -The ``ubproject.toml`` file is generated into the directory holding the ``conf.py`` file -(called ``confdir`` in Sphinx) and should be checked into the version control system -alongside ``conf.py``. +A single ``ubproject.toml`` is generated at the **git repo root** — the one directory +that is an ancestor of every source tree, so IDE tooling walking up from any open file +(including files in mounted bundles, see :ref:`howto_mount_external_sources`) reaches it. +The output location is resolved via ``find_git_root()``, which works under ``bazel run`` +and esbonio / direct Sphinx alike; a sandboxed ``bazel build`` has no git root and its +copy is discarded. The ``ubproject.toml`` file is generated on each Sphinx build, so any changes to the -Sphinx-Needs configuration are automatically reflected in the generated file. -If changes are detected, a warning is emitted during the Sphinx build to remind the user -to commit the updated ``ubproject.toml`` file. Changes may occur because docs-as-code -updated a configuration or a new Sphinx-Needs version added configuration. - -Committing the generated ``ubproject.toml`` file allows the IDE extension to work -without requiring any Sphinx build to be performed first. For a fully complete network -of need items, required external or imported ``needs.json`` files must first be -generated by Bazel. +Sphinx-Needs configuration are automatically reflected in the generated file. Because it +is regenerated every build, it is currently gitignored rather than committed. For a +fully complete network of need items, required external or imported ``needs.json`` files +must first be generated by Bazel. The command line tool ``ubc`` uses the same configuration as ``ubCode`` and can be used to lint and format all RST files in any of the S-CORE documentations. diff --git a/docs/reference/bazel_macros.rst b/docs/reference/bazel_macros.rst index 09554b505..de03e231d 100644 --- a/docs/reference/bazel_macros.rst +++ b/docs/reference/bazel_macros.rst @@ -38,6 +38,10 @@ Minimal example (root ``BUILD``) # labels to any extra tools or data you want included # e.g. "//:needs_json" or other tool targets ], + bundles = [ + # dicts describing bundles to mount into this project + # e.g. {"bundle": "@score_process//:docs_bundle", "mount_at": "process"} + ], deps = [ # additional bazel labels providing Python deps or other runfiles ], @@ -49,9 +53,17 @@ Minimal example (root ``BUILD``) - ``data`` (list of bazel labels) Extra runfiles / data targets that should be made available to the documentation targets. - Typical entries are targets that generate or provide external data used by the docs, for - example a ``:needs_json`` producer. The items in ``data`` are added to the py_binaries and - to the Sphinx tooling so they are available at build time. + The items in ``data`` are added to the py_binaries and to the Sphinx tooling so they are + available at build time. + + .. note:: + + To pull in another module's needs for cross-referencing, add its + ``:needs_json`` target here. + +- ``bundles`` (list of placement dicts) + Documentation bundles to overlay into this project's documentation tree, each with its + placement (``mount_at``). See :ref:`howto_mount_external_sources` for the full reference. - ``deps`` (list of bazel labels) Additional Bazel dependencies to add to the Python binaries and the virtual environment @@ -85,10 +97,68 @@ Minimal example (root ``BUILD``) for extension processing. When ``metamodel`` is omitted the default metamodel is used unchanged. +.. _docs_bundle_macro: + +Bazel macro: ``docs_bundle`` +---------------------------- + +``docs_bundle`` (also from ``docs.bzl``) declares a **mountable documentation +bundle**: a chunk of RST/Markdown content that can be overlaid into a host +documentation project. A bundle carries only *content* — it has **no placement of its +own**. Where it appears is decided by whoever mounts it (a composing +``docs_bundle`` or the :ref:`docs(bundles=[...]) ` call +site). + +.. code-block:: python + + load("//:docs.bzl", "docs_bundle") + + docs_bundle( + name = "docs_dir", + source_dir = "docs", + bundles = [], + visibility = ["//visibility:public"], + ) + +Signature: ``docs_bundle(name, source_dir = None, bundles = [], scan_code = [], visibility = None)``. + +- ``source_dir`` (string, optional) + Directory holding the bundle's own doc sources. It is globbed the same way as + ``docs()`` (RST, Markdown, images, and the other doc file kinds). The + ``source_dir`` itself is the mount root, so the files mount relative to it (so + ``concept/index.rst`` with ``source_dir = "concept"`` becomes ``index.rst``). + The bundle exposes those files as a Bazel depset (via the ``DocsBundleInfo`` + provider) and records the ``source_dir`` path; sphinx-mounts walks that original + directory directly — no copy is made. Leave it unset for a pure aggregator that only + composes ``bundles``. + +- ``bundles`` (list of composition dicts, optional) + Nested bundles to compose into this one, so a bundle can aggregate other + bundles transitively. Each entry is a dict: + + - ``bundle`` — label of another ``docs_bundle`` target. + - ``mount_at`` — the docname prefix at which the child bundle appears *inside* + this bundle. + - ``attach_to`` (optional) — a docname (relative to this bundle) whose toctree + receives the child's entry document. + - ``entry_doc`` (optional, default ``"index"``) — the child-relative docname of + the entry document, used together with ``attach_to``. + + A child's ``mount_at``/``attach_to`` **prefix-stack** with the placement this + bundle later receives, so composition is fully transitive. The same underlying + bundle resolving to two different final ``mount_at`` values is a hard build + error. See :ref:`howto_mount_external_sources` for a worked example and + :ref:`docs_concept_mounts` for the composition and transitivity semantics. + +.. note:: + + A bundle is **placement-free**: its ``mount_at`` / ``attach_to`` / + ``entry_doc`` are assigned by the mounter, never by the bundle itself. This is + what lets the same bundle be mounted at different locations by different + consumers. + Edge cases ---------- - If your Sphinx ``conf.py`` expects files generated by other Bazel targets, make sure those targets are included in the ``data`` list so they are available to the build driver. -- The experimental "combo" targets rewrite some ``data`` labels for combined builds; those - are intended for advanced use and are optional for normal doc workflows. diff --git a/docs/reference/commands.md b/docs/reference/commands.md index 7ec765f4c..99026c8a7 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -23,10 +23,8 @@ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------- | | `bazel run //:docs` | Builds documentation (also writes `metrics.json`) | | `bazel run //:docs_check` | Verifies documentation correctness | -| `bazel run //:docs_combo` | Builds combined documentation with all external dependencies included | | `bazel run //:traceability_gate -- --metrics-json bazel-bin/needs_json/_build/needs/metrics.json --min-req-code 70 --min-req-test 70 --min-req-fully-linked 60 --min-tests-linked 70` | Reads the pre-computed metrics.json and fails if coverage thresholds are not met | | `bazel run //:live_preview` | Creates a live_preview of the documentation viewable in a local server | -| `bazel run //:live_preview_combo_experimental` | Creates a live_preview of the full documentation with all dependencies viewable in a local server | | `bazel run //:ide_support` | Sets up a Python venv for esbonio (Remember to restart VS Code!) | ## Internal targets (do not use directly) diff --git a/src/BUILD b/src/BUILD index 5f8dd1bef..62a531a12 100644 --- a/src/BUILD +++ b/src/BUILD @@ -50,6 +50,7 @@ filegroup( "//src/extensions/score_draw_uml_funcs:all_sources", "//src/extensions/score_layout:all_sources", "//src/extensions/score_metamodel:all_sources", + "//src/extensions/score_mounts:all_sources", "//src/extensions/score_source_code_linker:all_sources", "//src/extensions/score_sphinx_bundle:all_sources", "//src/extensions/score_sync_toml:all_sources", diff --git a/src/extensions/score_mounts/BUILD b/src/extensions/score_mounts/BUILD new file mode 100644 index 000000000..7033526d7 --- /dev/null +++ b/src/extensions/score_mounts/BUILD @@ -0,0 +1,55 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@aspect_rules_py//py:defs.bzl", "py_library") +load("@docs_as_code_hub_env//:requirements.bzl", "requirement") +load("//:score_pytest.bzl", "score_pytest") + +filegroup( + name = "sources", + srcs = glob(["*.py"]), +) + +filegroup( + name = "tests", + srcs = glob(["tests/*.py"]), +) + +filegroup( + name = "all_sources", + srcs = [ + ":sources", + ":tests", + ], + visibility = ["//visibility:public"], +) + +py_library( + name = "score_mounts", + srcs = [":sources"], + imports = ["."], + visibility = ["//visibility:public"], + deps = [ + requirement("sphinx"), + requirement("sphinx-mounts"), + "//src/helper_lib", + ], +) + +score_pytest( + name = "score_mounts_tests", + size = "small", + srcs = glob(["tests/*.py"]), + deps = [":score_mounts"], + pytest_config = "//:pyproject.toml", +) diff --git a/src/extensions/score_mounts/__init__.py b/src/extensions/score_mounts/__init__.py new file mode 100644 index 000000000..b380c92c4 --- /dev/null +++ b/src/extensions/score_mounts/__init__.py @@ -0,0 +1,142 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +""" +Bridge extension: consume the mounts manifest authored by Bazel rules and feed it to +``sphinx_mounts``. + +All mount paths originate from Bazel; this extension performs no path computation. It: + +* sets ``config.mounts`` so ``sphinx_mounts`` can build the documentation; +``score_sync_toml`` reads the resulting ``config.mounts`` directly to write the +generated ``ubproject.toml``. +""" + +from __future__ import annotations + +import os +from pathlib import Path + +from sphinx.application import Sphinx +from sphinx.config import Config +from sphinx.util import logging + +from src.extensions.score_mounts._resolver import ( + MountsManifest, + MountSpec, + load_mounts_manifest, +) +from src.helper_lib import find_ws_root, get_runfiles_dir + +logger = logging.getLogger(__name__) + + +def _read_manifest(config: Config): + """Locate and load the mounts manifest, or return ``None`` when unset. + + The manifest path is passed by Bazel either via the ``mounts_manifest`` config + value or the ``MOUNTS`` env var. Its interpretation depends on the build + context: under ``bazel run`` it is a runfiles-relative path + (``$(rlocationpath)``) resolved against the runfiles dir; in a sandbox build + it is relative to the exec root (``$(location)``). Resolving the path here + keeps that context branch out of the pure ``_resolver`` module. + """ + raw = getattr(config, "mounts_manifest", None) or os.environ.get( + "MOUNTS_MANIFEST", None + ) + if not raw or not raw.strip() or not isinstance(raw, str): + return None + + # ``bazel run`` passes an rlocation-relative path; ``sphinx_docs`` in a + # sandbox passes its execroot-relative ``$(location)`` path directly. + manifest_path = get_runfiles_dir() / raw if find_ws_root() else Path(raw) + + return load_mounts_manifest(manifest_path) + + +def _resolve_walk_dir( + manifest: MountsManifest, spec: MountSpec, ws_root: Path | None +) -> Path: + """Resolve one mount consistently for runfiles and sandboxed builds.""" + if spec.external and ws_root is not None: + return manifest.runtime_dir(spec) + if ws_root is not None: + return ws_root / spec.src_root + return Path(os.path.abspath(spec.src_root)) + + +def _on_config_inited(app: Sphinx, config: Config) -> None: + """Translate the Bazel manifest into ``sphinx_mounts`` runtime config. + + Runs on Sphinx's ``config-inited`` event (before ``sphinx_mounts``, see the + priority in ``setup``). For each mount it resolves the directory + ``sphinx_mounts`` should walk and writes the assembled list to + ``config.mounts``. A missing or empty manifest is a no-op. + """ + manifest = _read_manifest(config) + if manifest is None or not manifest.mounts: + return + + # In every context sphinx_mounts walks the bundle's original files (no copy is + # made); only where those files are staged differs: + # * external bundle: use its runfiles-relative location under ``bazel run`` + # and its execroot-relative location in a sandboxed Bazel build. + # * in-tree bundle under `bazel run`: use the live workspace source + # (ws_root/src_root) -- editable, best for live preview / jump-to-def. + # * in-tree bundle in a sandbox build: the bundle's source files are staged + # as inputs at their exec-root-relative path. The manifest lives under + # bazel-out/ and is NOT colocated with them, so src_root is resolved + # against the exec root (the sphinx action's cwd), not the manifest. + ws_root = find_ws_root() + + runtime_mounts: list[dict[str, object]] = [] + for spec in manifest.mounts: + walk_dir = _resolve_walk_dir(manifest, spec, ws_root) + if not walk_dir.is_dir(): + raise ValueError( + "score_mounts: resolved mount dir does not exist: " + f"{walk_dir} (mount_at={spec.mount_at})" + ) + + runtime_mounts.append( + { + "dir": str(walk_dir), + "mount_at": spec.mount_at, + "attach_to": spec.attach_to, + "entry_doc": spec.entry_doc, + } + ) + + config.mounts = runtime_mounts + # Prevent sphinx_mounts._on_load_toml from overwriting our config with a + # possibly-stale docs/ubproject.toml entry. + config.mounts_from_toml = None + + logger.info("score_mounts: registered %d mount(s)", len(runtime_mounts)) + + +def setup(app: Sphinx) -> dict[str, object]: + """Sphinx extension entry point: register the config value and event hook. + + ``mounts_manifest`` carries the Bazel-resolved manifest path. The + ``config-inited`` handler is connected at priority 300 (< 400) so it runs + before ``sphinx_mounts._on_load_toml`` and can override the mount config the + latter would otherwise load from ``ubproject.toml``. + """ + app.add_config_value("mounts_manifest", default="", rebuild="env", types=(str,)) + app.connect("config-inited", _on_config_inited, priority=300) + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/src/extensions/score_mounts/_resolver.py b/src/extensions/score_mounts/_resolver.py new file mode 100644 index 000000000..d01e114f1 --- /dev/null +++ b/src/extensions/score_mounts/_resolver.py @@ -0,0 +1,95 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +"""Load the mounts manifest JSON emitted by the ``_mounts_manifest`` Bazel rule. + +All mount paths are authored by Bazel (where ``File`` objects have real paths) +and shipped in a small JSON manifest. This module only *reads* that manifest — it +performs no label-to-path reconstruction. Every path stored in the manifest is a +Bazel ``short_path`` and therefore resolves relative to the manifest's own +directory.""" + +from __future__ import annotations + +import json +import os +from dataclasses import dataclass +from pathlib import Path + + +@dataclass(frozen=True) +class MountSpec: + src_root: str + runtime_path: str + mount_at: str + attach_to: str | None = None + entry_doc: str = "index" + external: bool = False + + +@dataclass(frozen=True) +class MountsManifest: + manifest_path: Path + mounts: list[MountSpec] + + @property + def root(self) -> Path: + """Directory the manifest lives in — the base for its short_path entries.""" + return self.manifest_path.parent + + def runtime_dir(self, spec: MountSpec) -> Path: + """Absolute path of a bundle's staged mount-root directory. + + ``runtime_path`` is a Bazel ``short_path`` (the bundle's source + directory, staged in place — not a copy) and resolves relative to the + manifest's own directory. Main-repo sources resolve the same way inside a + sandbox build. External sources (``../+/X``) are only ever walked + under ``bazel run`` with an external bundle, where ``../`` steps out + of ``_main`` to the sibling repo dir in the runfiles tree — so no + context-dependent mapping is needed here. + """ + return Path(os.path.abspath(str(self.root / spec.runtime_path))) + + +def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: + """Read the manifest JSON at ``manifest_path`` (an already-resolved path). + + Context-dependent path resolution (runfiles under ``bazel run`` vs. the + exec root in a sandbox) is the caller's responsibility. + """ + manifest_path = Path(manifest_path) + data = json.loads(manifest_path.read_text(encoding="utf-8")) + if not isinstance(data, dict): + raise ValueError( + f"mounts manifest must be a JSON object, got {type(data).__name__}: {data!r}" + ) + mounts: list[MountSpec] = [] + for entry in data.get("mounts", []): + if "src_root" not in entry or "mount_at" not in entry: + raise ValueError( + f"mounts manifest entry missing 'src_root'/'mount_at': {entry!r}" + ) + mounts.append( + MountSpec( + src_root=entry["src_root"], + runtime_path=entry.get("runtime_path", ""), + mount_at=entry["mount_at"], + attach_to=entry.get("attach_to") or None, + entry_doc=entry.get("entry_doc") or "index", + external=entry.get("external", False), + ) + ) + return MountsManifest( + manifest_path=manifest_path, + mounts=mounts, + ) diff --git a/src/extensions/score_mounts/docs/BUILD b/src/extensions/score_mounts/docs/BUILD new file mode 100644 index 000000000..561b235de --- /dev/null +++ b/src/extensions/score_mounts/docs/BUILD @@ -0,0 +1,30 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("//:docs.bzl", "docs_bundle") + +# The mount feature's own documentation, mounted back into the site via the +# mount feature itself (dogfooding). One subdirectory per bundle: mounts are +# directory-scoped, so co-locating both files in one dir would graft both at +# each mount point. +docs_bundle( + name = "concept", + source_dir = "concept", + visibility = ["//visibility:public"], +) + +docs_bundle( + name = "howto", + source_dir = "howto", + visibility = ["//visibility:public"], +) diff --git a/src/extensions/score_mounts/docs/concept/index.rst b/src/extensions/score_mounts/docs/concept/index.rst new file mode 100644 index 000000000..afabbe947 --- /dev/null +++ b/src/extensions/score_mounts/docs/concept/index.rst @@ -0,0 +1,263 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +.. _docs_concept_mounts: + +====================================== +Mounts: which directory Sphinx walks +====================================== + +Mounting another documentation bundle into this project's Sphinx tree grafts the +*contents of one directory* into the doc tree. This page explains which directory +that is — and why it is always the bundle's **original files**, never a copy. + +For the user-facing "how do I mount another module" guide, see +:ref:`howto_mount_external_sources`. + +Mounting overlays a directory +============================= + +**Mounting** is the placement/overlay operation: at runtime ``sphinx_mounts`` +takes the contents of one directory and grafts them into the doc tree at the +bundle's ``mount_at`` (optionally attached under ``attach_to``). A ``docs_bundle`` +with a ``source_dir`` contributes exactly that directory. + +A ``source_dir`` bundle **is already a mount-ready directory**: its files sit on +disk under ``source_dir`` in the layout they should mount in. So the bundle makes +no copy — it only records the *mount root* (the ``source_dir`` path as a Bazel +``short_path``) and lets ``sphinx_mounts`` walk the real files. Sphinx therefore +always operates on the originals, which keeps live preview and jump-to-definition +pointing at editable source. + +Transitive composition +======================= + +A ``docs_bundle`` exposes its content through the ``DocsBundleInfo`` provider, +whose ``entries`` field is an **ordered list** of placed content entries. +When a bundle composes children (via ``bundles = [...]``), each child's entries +are appended in declaration order and re-based under the child's ``mount_at``. +A mounter therefore sees one flat, ordered list regardless of how deeply the +graph nests. + +**Placement composes by prefix-stacking.** A bundle is placement-free; its own +root takes the placement it is mounted with, and every nested entry gets the +enclosing ``mount_at`` (and ``attach_to``) prefixed onto its own. A child mounted +at ``mount_at = "child"`` inside a parent that is later mounted at +``internals/code_docs`` resolves to ``internals/code_docs/child``. Resolution is +independent of nesting depth. + +**One bundle, one placement.** After the graph is flattened, the same underlying +bundle directory resolving to two different final ``mount_at`` values is a hard +build error (``mount conflict … a bundle must resolve to a single mount_at``). +The same directory at the *same* ``mount_at`` is simply deduplicated. + +**Composition stops at external module boundaries.** A module may mount another +module for its own documentation build. When a third module mounts the first, +it receives the first module's source tree and in-repo bundles, but not foreign +modules the first one mounted. Consumers opt in to every external module +explicitly, keeping ownership and collision handling predictable. + +For data-only integration, **needs stay one ``needs.json`` per module**. A +consumer that imports another module's ``needs.json`` does not mount its sources; +cross-module references then use the external-needs mechanism. A consumer that +mounts a bundle instead builds the mounted sources and Need directives as part +of its own Sphinx project. + +Which directory gets walked +=========================== + +The runtime resolver (``score_mounts``) picks the directory per mount — always an +original source directory, differing only in *where* that directory is staged: + +.. code-block:: python + + if spec.external and ws_root: # bazel run: sibling repo in the runfiles tree + walk_dir = manifest.runtime_dir(spec) + elif ws_root is not None: # bazel run, in-tree: the live workspace source + walk_dir = ws_root / spec.src_root + else: # sandbox, in-tree: source staged at the exec root + walk_dir = Path(os.path.abspath(spec.src_root)) + +* ``ws_root`` is only set under ``bazel run`` (it points at + ``BUILD_WORKSPACE_DIRECTORY``); in a sandboxed ``bazel build`` it is ``None``. +* ``external`` marks a bundle whose sources come from another Bazel module. +* The manifest (a ``bazel-out`` artifact) is colocated with the sources only in the + runfiles tree; in a sandbox the in-tree sources are resolved against the exec + root instead, which is why the branch splits three ways. + +So the mount walks: + +* the **live workspace source** under ``bazel run`` with an in-tree bundle (edits + show up immediately — best for live preview and jump-to-definition); +* the **in-place staged inputs** in a sandbox build (``needs_json``), where only + the bundle's globbed files are present at their ``source_dir`` path; +* the **staged sibling-repo directory** for an external bundle under + ``bazel run`` (``../+/source_dir`` in the runfiles tree), or its + ``external/+/source_dir`` execroot path in a sandboxed build. + +In all three cases the payload is the untouched source directory; a stray file +that is not doc source (e.g. a ``conf.py``) is simply ignored by Sphinx. + +Without an external bundle declaration, a dependency is referenced via its +prebuilt ``needs.json`` and its sources are not walked at all. + +.. _docs_concept_mounts_rematerialize: + +Re-introducing materialization later +==================================== + +Earlier versions copied each bundle into a normalized ``declare_directory`` at +build time ("materialization"). That was dropped because a ``source_dir`` bundle +is *already* a mount-ready directory — the copy was a byte-for-byte duplicate of a +directory Bazel stages anyway. This section records how to bring materialization +back should the bundle model grow beyond whole-``source_dir`` inputs. + +.. note:: + + Materialization should be a **last resort**. Before re-adding a build-time + copy, investigate whether ``sphinx_mounts`` can already express the need + directly — e.g. a **file/glob mode** or **include/exclude filtering** on the + mount itself. Filtering a subset or picking individual files at the mount layer + avoids duplicating the tree and keeps Sphinx pointed at the originals; prefer + that over reintroducing a copy action. + +**When it becomes necessary.** Materialization earns its keep only when a bundle +is *not* already one ready-to-mount directory on disk *and* ``sphinx_mounts`` +cannot select the payload itself: + +* **File globs / a filtered subset** — the bundle is an explicit list of files + rather than a whole directory, so no single existing directory holds exactly the + payload. +* **A custom ``strip_prefix``** that differs from the files' on-disk layout — the + mount-relative paths then exist only in a rewritten tree. +* **Provider-supplying rules** — a rule that emits ``DocsBundleInfo`` for content + it *generates* (no stable on-disk source directory to point at). + +**Sketch of the mechanism** (intentionally high level — flesh out when needed): + +#. In the bundle rule, ``ctx.actions.declare_directory(name)`` and a copy action + assemble the normalized, mount-relative tree from ``ctx.files.srcs``. +#. The content entry then carries that directory ``File`` (alongside, or instead + of, ``runtime_path``); the emitted ``runtime_path`` points at it. +#. A materialized directory is a ``bazel-out`` artifact colocated with the manifest + in every context, so it resolves via ``manifest.runtime_dir`` — the same + manifest-relative rule the external branch already uses. That sidesteps the + exec-root vs. runfiles split the in-tree source walk has to handle, which is + the resolver-simplicity that materialization used to buy. + +**Provider contract.** A content entry must let the runtime resolve a directory to +walk. Today that is ``runtime_path`` (a source-directory ``short_path``) plus +``src_root`` (the live in-tree path, empty for external). A materialized entry +would instead (or additionally) carry a directory ``File`` whose ``short_path`` +serves as ``runtime_path``. Either representation is valid as long as +the mount-entry deduplication has a stable identity key and ``_mounts_manifest`` can emit a +``runtime_path``. + + +The problem +----------- + +The S-CORE documentation toolchain has historically assumed that every +RST/Markdown file under a Sphinx project lives under its source +directory (``docs/`` in this repository). Two situations break that +assumption: + +* **Generated content** — RST produced by a Bazel rule lands under + ``bazel-bin/...`` and is therefore outside ``docs/`` by construction. + Examples: API reference tables generated from code, requirement + catalogues exported from upstream modules, traceability matrices. + +* **In-repo content owned by another tree** — for example, README-style + documentation that lives next to its source code under ``src/`` and + must remain there for code-ownership reasons but should still appear + in the rendered docs site. + +Historical workarounds either (a) copied or symlinked the files into +``docs/`` — which loses the original source location for IDE +navigation, complicates ``git blame``, and risks stale copies — or +(b) materialized an entire merged source tree at build time and +pointed Sphinx at that. The latter solves the build-side problem but +keeps Sphinx on the IDE critical path. Useful editing in an IDE +requires validation **as you type**, and that is hard to achieve from +any tool without live knowledge of every file and dependency in the +project. Sphinx is built for batch document processing, not for the +millisecond-latency feedback an editor needs; routing IDE feedback +through it therefore caps the editing experience at the speed and +scope of the next rebuild. + + +What ``sphinx-mounts`` does +--------------------------- + +`sphinx-mounts`_ is a Sphinx extension that registers external source +trees with Sphinx's project map by **absolute path**, without copying +or staging. The original files stay exactly where they live; Sphinx +reads them from there. Configuration is declarative TOML in +``ubproject.toml``, the file already shared with Sphinx-Needs, +sphinx-codelinks, and ubCode. + +.. _sphinx-mounts: https://sphinx-mounts.useblocks.com/ + +The key consequence: **every consumer reads the same file**. ubCode, +language servers, indexers, and CI gates can all parse +``ubproject.toml`` to discover where a project's RST sources live — +including the mounted ones — without ever invoking Sphinx. That +preserves the IDE editing experience (real-time validation, jump-to- +definition pointing at the real source, schema-aware autocomplete) +while still letting Sphinx produce the published HTML. + + +Why this matters for IDE support +-------------------------------- + +ubCode (and similar tooling) walks **up** the directory tree from an +open ``.rst`` / ``.md`` file to find the nearest ``ubproject.toml``, +treats that directory as the project root, and reads the file to +learn the type system, link types, layouts, and field defaults the +project uses. A file inside ``docs/`` and a file inside a **mounted** +bundle (for example, ``src/docs/overview.rst``) live in different +subtrees, so no single ``ubproject.toml`` placed *inside* either tree +is visible from the other. + +To close this gap, the toolchain emits a **single** ``ubproject.toml`` +at the **git repo root** — the one directory that is an ancestor of +both ``docs/`` and every in-repo bundle. The walk-up from any source +file therefore reaches it. Because it is the only config file, it +carries the host's full type system *and* the ``[[mounts]]`` entries; +there are no sanitized per-bundle copies to keep in sync. + + +Comparison with the materialization approach +-------------------------------------------- + ++---------------------------------------+---------------------------------------+---------------------------------------+ +| Concern | Materialize-then-Sphinx | sphinx-mounts (this approach) | ++=======================================+=======================================+=======================================+ +| IDE feedback latency | bounded by next Sphinx rebuild | direct file access via TOML | ++---------------------------------------+---------------------------------------+---------------------------------------+ +| As-you-type validation | not feasible (Sphinx is a batch tool) | works on real files directly | ++---------------------------------------+---------------------------------------+---------------------------------------+ +| Live preview | autobuild-based | ``sphinx-autobuild`` works as-is | ++---------------------------------------+---------------------------------------+---------------------------------------+ +| "Go to definition" lands in | the materialized copy under bazel-bin | the real source file | ++---------------------------------------+---------------------------------------+---------------------------------------+ +| ``conf.py`` execution required for IDE| yes | no — TOML is enough | ++---------------------------------------+---------------------------------------+---------------------------------------+ +| Sandbox-friendly Bazel build | yes | yes | ++---------------------------------------+---------------------------------------+---------------------------------------+ + +The two approaches are not mutually exclusive — a materialized-tree +rule can coexist if a downstream consumer needs it. But sphinx-mounts +is the lighter-weight surface and the primary entry point for new +bundles. diff --git a/src/extensions/score_mounts/docs/howto/index.rst b/src/extensions/score_mounts/docs/howto/index.rst new file mode 100644 index 000000000..4c1b47283 --- /dev/null +++ b/src/extensions/score_mounts/docs/howto/index.rst @@ -0,0 +1,322 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +.. _howto_mount_external_sources: + +Mounting external source bundles +================================ + +This guide explains how to surface RST or Markdown content that lives +**outside** ``docs/`` into the docs-as-code build. + +.. contents:: + :local: + :depth: 2 + +Declaring a bundle with ``docs_bundle`` +--------------------------------------- + +A mountable bundle carries only **content**: the source files. It is a +Bazel target created with the ``docs_bundle`` rule from ``docs.bzl``, +declared next to the bundle's sources: + +.. code-block:: starlark + + # src/BUILD + load("//:docs.bzl", "docs_bundle") + + docs_bundle( + name = "docs_dir", + source_dir = "docs", + visibility = ["//visibility:public"], + ) + +Each attribute: + +* ``source_dir`` — directory holding the bundle's own doc sources. It is + globbed the same way as ``docs()`` (RST, Markdown, images, and the + other doc file kinds). The ``source_dir`` itself *is* the mount root, so + the files mount relative to it (``docs/index.rst`` becomes ``index.rst``). + The bundle exposes those files as a Bazel depset (via the + ``DocsBundleInfo`` provider) and records the ``source_dir`` path; + sphinx-mounts walks that original directory directly — no copy is made. + +The bundle carries **no placement** — where it appears in a host project +is decided by the *consumer*, so the same bundle can be mounted at +different locations by different consumers (see the next section). + +Placement at the consumer: ``docs(bundles=[...])`` +--------------------------------------------------- + +The ``docs()`` macro's ``bundles`` argument is a list of placement dicts. +Each dict pairs a bundle label with where it goes in *this* project: + +.. code-block:: starlark + + load("//:docs.bzl", "docs") + + docs( + bundles = [ + { + "bundle": "//src:docs_dir", + "mount_at": "internals/code_docs", + "attach_to": "internals/index", + }, + ], + source_dir = "docs", + ) + +Each placement key: + +* ``bundle`` — label of a ``docs_bundle`` target (in-repo or from another + module, see :ref:`cross-repo mounts `). + +* ``mount_at`` — the docname prefix at which the bundle appears in the + host project. With ``mount_at = "internals/code_docs"``, a bundle file + ``overview.rst`` is reachable in the host as the docname + ``internals/code_docs/overview``. + +* ``attach_to`` (optional) — a host docname whose toctree should + automatically receive the bundle's entry document. With + ``attach_to = "internals/index"``, the bundle's ``index`` doc is + appended to the first toctree in ``docs/internals/index.rst`` at build + time; that host doc does not need a manual entry. + +* ``entry_doc`` (optional, default ``"index"``) — the mount-relative + docname of the bundle's entry document, used together with + ``attach_to``. + +The bundle's ``dir`` in the generated ``ubproject.toml`` is **derived +automatically** from the source file paths — there is no ``src_root`` +attribute. + +Every project that uses ``docs()`` also **auto-exposes its own** +``source_dir`` as a public bundle named ``docs_bundle``. No extra +wiring is needed: because a consumer's ``docs()`` call *is* this macro, +``@//:docs_bundle`` exists for free and can be mounted elsewhere. + +Composing bundles +----------------- + +A ``docs_bundle`` may itself mount other bundles through its ``bundles`` +argument, so one bundle can aggregate a whole sub-tree of content. Each entry +uses the same placement keys as ``docs(bundles=[...])`` (``bundle``, ``mount_at``, +optional ``attach_to`` / ``entry_doc``), but the placement is *relative to the +composing bundle* rather than to a host project: + +.. code-block:: starlark + + docs_bundle( + name = "guide", + source_dir = "guide", + bundles = [ + {"bundle": "//some/pkg:api_docs", "mount_at": "reference", "attach_to": "index"}, + ], + ) + +Here ``guide`` bundles ``api_docs`` under ``reference`` and attaches its entry +doc to ``guide``'s own ``index``. ``guide`` is still **placement-free**: when a +consumer mounts ``guide`` at, say, ``mount_at = "internals/code_docs"``, the two +placements **prefix-stack**. The nested ``api_docs`` then resolves to +``internals/code_docs/reference`` in the host, and its ``attach_to`` resolves to +``internals/code_docs/index``. Composition is therefore fully transitive: a +bundle nested any number of levels deep lands at the concatenation of every +``mount_at`` above it. + +If the **same** underlying bundle resolves to two different final ``mount_at`` +values (for example, mounted both directly and again via a composing bundle), the +build fails hard with a ``mount conflict … a bundle must resolve to a single +mount_at`` error — duplicating a bundle's pages would collide docnames and need +IDs. See :ref:`docs_concept_mounts` for the composition semantics. + +.. _cross_repo_mounts: + +Cross-repo mounts +----------------- + +A bundle label in ``bundles`` may point at another Bazel module's +auto-exposed bundle. For example, this repository mounts the process +description — already a dependency and itself a ``docs()`` user — with: + +.. code-block:: starlark + + bundles = [ + {"bundle": "@score_process//:docs_bundle", "mount_at": "process", "attach_to": "index"}, + ] + +Mounting an external bundle also mounts the Need directives authored in that +module's sources. Do not add the same module's ``:needs_json`` to ``data``: +that would import a second copy of every Need and Sphinx-Needs rejects the +duplicate IDs. Use ``data = ["@module//:needs_json"]`` only for a JSON-only +dependency whose documentation sources are not mounted. If that module mounts +further external modules for its own site, those are not re-exported; mount each +such module explicitly when it is wanted in this project. + +For an **external** bundle the sources do not exist in the consumer's +git tree; they live under ``bazel-*/external/+/…``. The ``dir`` +in ``ubproject.toml`` therefore points at the staged source directory under +``bazel-bin/external/+/…`` — the same pattern already used for +external ``needs.json`` (``json_path = "bazel-bin/external/…"``). +"Go to definition" for such a mount lands in that read-only staged module +tree, not in an editable source file. In-repo bundles keep pointing at their +real, editable sources. + + +How the wiring works +-------------------- + +The pieces fit together like this: + +.. code-block:: text + + docs_bundle(...) in a BUILD ← bundle: files depset + mount root + │ (DocsBundleInfo provider, content only) + ▼ + docs(bundles = [{"bundle": "//src:docs_dir", "mount_at": ...}]) + │ placement lives at the call site + ▼ + docs.bzl: _mounts_manifest rule ← reads the providers + placement, + derives each bundle's source dir from + the file paths, and emits ONE canonical + JSON manifest + │ + ▼ + sphinx-build (mounts_manifest = manifest path) + │ + ▼ + score_mounts extension ← reads the manifest and configures runtime + mounts plus neutral metadata: + │ + ┌───┴────┐ + ▼ ▼ + sphinx_mounts score_sync_toml + walks the dir derives TOML paths, serializes and merges /ubproject.toml + +The key inversion from earlier iterations: **Bazel is the single +source of truth for mount paths**. All paths are computed in the rule, +where ``File`` objects have real paths, instead of being reconstructed +from label strings at Sphinx runtime. + +After a successful ``bazel run //:docs_check``, the repo-root +``ubproject.toml`` contains a mount entry like: + +.. code-block:: toml + + mounts = [ + { dir = "src/docs", mount_at = "internals/code_docs", attach_to = "internals/index" }, + ] + +The ``dir`` value points at the bundle's **real source location** +(here, ``src/docs/`` — derived automatically from the bundle's source +files), not at any bazel-bin path. It is relative to the +``ubproject.toml`` location (the git root). ubCode and similar tools +that follow this mount entry therefore navigate to the original files; +jump-to-definition and ``git blame`` work as the author wrote them. + +This block is what every external consumer of the project (ubCode, +sphinx-build, CI) reads to discover the bundle. + +``score_sync_toml`` serializes the structured entries to TOML and passes that +temporary fragment to ``needs-config-writer``, the same writer that emits the +rest of the project's type system. Bazel never emits TOML. + +Building from Bazel +~~~~~~~~~~~~~~~~~~~ + +Relevant targets wired by the ``docs()`` macro: + +* ``bazel run //:docs`` — incremental HTML build for day-to-day + editing; outputs to ``_build/``. Resolves mounts via runfiles + (fast, dev-local). + +* ``bazel run //:docs_check`` — same as above but with the ``check`` + action; also regenerates the repo-root ``ubproject.toml``. Run this + after editing the mount list or to refresh the IDE-facing TOML. + +* ``bazel build //:needs_json`` — sandboxed needs-only build. Verifies + that mounted bundles resolve correctly without ``bazel run``. + + +A single ``ubproject.toml`` at the git root +-------------------------------------------- + +The whole project — host and every mounted bundle — is described by +**one** ``ubproject.toml`` at the git repo root. ``needs-config-writer`` +relativizes every path field against the output file's directory, so +anchoring the file at the root makes ``external_needs`` JSON paths, the +mount ``dir`` values, and schema paths all root-relative and valid for +any consumer that reads them from there. + +The output location is set by ``score_sync_toml`` to +``find_git_root() / "ubproject.toml"``. ``find_git_root()`` resolves the +repo root both under ``bazel run`` and under esbonio / direct Sphinx +(via a working-directory fallback), so the file lands at the root in +every IDE-facing context. In a sandboxed ``bazel build`` there is no +git root; the writer falls back to the confdir default and that copy is +discarded with the sandbox. + +The file is gitignored (``/ubproject.toml``): it is regenerated on +every build and is not a source artifact. + + +Caveats and known limitations +----------------------------- + +* **External-repository bundles read the staged bazel-bin tree.** Bundles + from another Bazel module are supported (see :ref:`cross_repo_mounts`), but + their ``dir`` points at the staged ``bazel-bin/external/…`` source tree + rather than at editable sources, and that tree only exists after a + build. + +* **One source directory per bundle.** A bundle's content is a single + ``source_dir``; that directory is exactly the mount-relative tree + sphinx-mounts walks, so there is no cross-directory layout to reconcile. + +* **Standard confdir assumption.** Anchoring at the git root assumes + the git root is an ancestor of every source tree (host and bundles), + which holds for the standard layout. A repo whose sources live + outside its git tree would need a different anchor. + + +Cross-bundle references work +---------------------------- + +A need authored inside a mounted bundle can be linked from anywhere in +the host project, just like a need authored in ``docs/`` itself. This +page *is* a mounted bundle, so we dogfood it directly: the stakeholder +requirement below is authored right here, in the mounted score_mounts +how-to bundle, yet the host-side ``tool_req__docs_mount_traceability`` +(in ``docs/internals/requirements/``) carries a ``:satisfies:`` link +straight to it. + +The link resolves at host build time with no copy or materialisation, +and is enforced by ``sphinx-needs`` schema validation. That +cross-boundary link uses only stock relations from +``score_metamodel`` (``tool_req`` may satisfy ``stkh_req`` without any +metamodel extension): the bundle owns its own ``.rst`` and lives next +to its code, but its needs participate in the host's traceability +graph as first-class citizens. + + +Further reading +--------------- + +* `sphinx-mounts documentation`_ — full configuration reference, + TOML schema, behaviour of ``attach_to`` and ``entry_doc``. +* `ubCode`_ — the IDE extension that reads ``ubproject.toml``. +* :doc:`add_extensions` — how to plug other Sphinx extensions into + the docs-as-code build. + +.. _sphinx-mounts documentation: https://sphinx-mounts.useblocks.com/ +.. _ubCode: https://ubcode.useblocks.com/ diff --git a/src/extensions/score_mounts/tests/__init__.py b/src/extensions/score_mounts/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/extensions/score_mounts/tests/test_resolver.py b/src/extensions/score_mounts/tests/test_resolver.py new file mode 100644 index 000000000..a1c3357d5 --- /dev/null +++ b/src/extensions/score_mounts/tests/test_resolver.py @@ -0,0 +1,182 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Unit tests for the mounts manifest loader (``_resolver``). + +These cover the pure parsing layer only: reading the JSON manifest into +``MountSpec`` objects, applying defaults, resolving ``runtime_dir`` relative +to the manifest, and rejecting malformed +input. Context-dependent path resolution (runfiles vs. exec root) lives in the +extension's ``__init__`` and is exercised via the consumer tests instead.""" + +import json +import os +from pathlib import Path + +import pytest + +from src.extensions.score_mounts.__init__ import _resolve_walk_dir +from src.extensions.score_mounts._resolver import ( + MountSpec, + load_mounts_manifest, +) + + +def _write_manifest(tmp_path: Path, payload: dict) -> Path: + manifest = tmp_path / "_mounts_manifest.json" + manifest.write_text(json.dumps(payload), encoding="utf-8") + return manifest + + +def test_load_single_entry(tmp_path: Path): + manifest = _write_manifest( + tmp_path, + { + "mounts": [ + { + "src_root": "src/docs", + "runtime_path": "src/docs_dir", + "mount_at": "internals/code_docs", + } + ], + }, + ) + result = load_mounts_manifest(str(manifest)) + assert result is not None + assert result.mounts == [ + MountSpec( + src_root="src/docs", + runtime_path="src/docs_dir", + mount_at="internals/code_docs", + ) + ] + + +def test_load_entry_with_attach_to_and_entry_doc(tmp_path: Path): + manifest = _write_manifest( + tmp_path, + { + "mounts": [ + { + "src_root": "src/docs", + "runtime_path": "src/docs_dir", + "mount_at": "x", + "attach_to": "internals/index", + "entry_doc": "start", + } + ], + }, + ) + spec = load_mounts_manifest(str(manifest)).mounts[0] + assert spec.attach_to == "internals/index" + assert spec.entry_doc == "start" + + +def test_external_mount_keeps_execroot_and_runfiles_locations(tmp_path: Path): + manifest = _write_manifest( + tmp_path, + { + "mounts": [ + { + "src_root": "src/docs", + "runtime_path": "src/docs_dir", + "mount_at": "x", + }, + { + "src_root": "external/score_process+/docs_as_mount", + "runtime_path": "../score_process+/docs_as_mount", + "mount_at": "process", + "external": True, + }, + ], + }, + ) + specs = load_mounts_manifest(str(manifest)).mounts + assert specs[0].src_root == "src/docs" + assert specs[1].src_root == "external/score_process+/docs_as_mount" + assert specs[1].external is True + + +def test_runtime_dir_resolves_next_to_manifest(tmp_path: Path): + manifest = _write_manifest( + tmp_path, + { + "mounts": [ + { + "src_root": "src/docs", + "runtime_path": "src/docs_dir", + "mount_at": "x", + } + ], + }, + ) + result = load_mounts_manifest(str(manifest)) + assert result.runtime_dir(result.mounts[0]) == tmp_path / "src" / "docs_dir" + + +def test_load_missing_required_key_raises(tmp_path: Path): + manifest = _write_manifest(tmp_path, {"mounts": [{"runtime_path": "src/docs_dir"}]}) + with pytest.raises(ValueError, match="missing 'src_root'/'mount_at'"): + load_mounts_manifest(str(manifest)) + + +def test_load_non_object_raises(tmp_path: Path): + manifest = tmp_path / "_mounts_manifest.json" + manifest.write_text('["not", "an", "object"]', encoding="utf-8") + with pytest.raises(ValueError, match="must be a JSON object"): + load_mounts_manifest(str(manifest)) + + +def test_runtime_dir_external_path_resolves_relative_to_manifest(tmp_path: Path): + # Under `bazel run`, the '../+/...' short_path resolves natively in + # the runfiles tree. runtime_dir must NOT remap '../' to 'external/'. + manifest = _write_manifest( + tmp_path, + { + "mounts": [ + { + "src_root": "external/score_process+/docs_as_mount", + "runtime_path": "../score_process+/docs_as_mount", + "mount_at": "process", + "external": True, + } + ], + }, + ) + result = load_mounts_manifest(str(manifest)) + # runtime_dir uses os.path.abspath (lexical, no symlink resolution); mirror + # that here so the assertion never diverges on a symlinked tmp dir. + expected = Path( + os.path.abspath(tmp_path / ".." / "score_process+" / "docs_as_mount") + ) + assert result.runtime_dir(result.mounts[0]) == expected + + +def test_external_mount_uses_execroot_path_in_sandbox(tmp_path: Path, monkeypatch): + monkeypatch.chdir(tmp_path) + manifest = _write_manifest( + tmp_path, + { + "mounts": [ + { + "src_root": "external/score_process+/docs_as_mount", + "runtime_path": "../score_process+/docs_as_mount", + "mount_at": "process", + "external": True, + } + ] + }, + ) + spec = load_mounts_manifest(manifest).mounts[0] + assert _resolve_walk_dir(load_mounts_manifest(manifest), spec, None) == ( + tmp_path / "external" / "score_process+" / "docs_as_mount" + ) diff --git a/src/extensions/score_source_code_linker/helpers.py b/src/extensions/score_source_code_linker/helpers.py index 9ab9dbfe0..b6cd7cc46 100644 --- a/src/extensions/score_source_code_linker/helpers.py +++ b/src/extensions/score_source_code_linker/helpers.py @@ -45,7 +45,7 @@ def get_github_link( git_root = find_git_root() or Path() # Local path (//:docs) return get_github_link_from_git(git_root, link) - # Ref-Integration path (//:docs_combo..) + # Reference-integration path (mounted external documentation) return get_github_link_from_json(metadata, link) diff --git a/src/extensions/score_source_code_linker/needlinks.py b/src/extensions/score_source_code_linker/needlinks.py index fb099e14a..2998240fc 100644 --- a/src/extensions/score_source_code_linker/needlinks.py +++ b/src/extensions/score_source_code_linker/needlinks.py @@ -217,8 +217,7 @@ def load_source_code_links_json(file: Path) -> list[NeedLink]: Returns: [NeedLink, NeedLink, ...] - This normally should be the one called in combo builds - => :docs_combo_experimental target + This is used when mounted external documentation contributes source links. """ if not file.is_absolute(): # use env variable set by Bazel diff --git a/src/extensions/score_sphinx_bundle/BUILD b/src/extensions/score_sphinx_bundle/BUILD index c34d2907e..9058f698f 100644 --- a/src/extensions/score_sphinx_bundle/BUILD +++ b/src/extensions/score_sphinx_bundle/BUILD @@ -28,6 +28,7 @@ py_library( "@score_docs_as_code//src/extensions/score_draw_uml_funcs", "@score_docs_as_code//src/extensions/score_layout", "@score_docs_as_code//src/extensions/score_metamodel", + "@score_docs_as_code//src/extensions/score_mounts", "@score_docs_as_code//src/extensions/score_source_code_linker", "@score_docs_as_code//src/extensions/score_metrics", "@score_docs_as_code//src/extensions/score_sync_toml", diff --git a/src/extensions/score_sphinx_bundle/__init__.py b/src/extensions/score_sphinx_bundle/__init__.py index ded7ea095..0c2296ade 100644 --- a/src/extensions/score_sphinx_bundle/__init__.py +++ b/src/extensions/score_sphinx_bundle/__init__.py @@ -24,6 +24,11 @@ "score_metamodel", "sphinx_design", "myst_parser", + # sphinx_mounts provides the mount machinery; score_mounts feeds it the + # Bazel-emitted mount config and must be loaded right after it (its + # config-inited hook runs before sphinx_mounts' own, see score_mounts.setup). + "sphinx_mounts", + "score_mounts", "score_source_code_linker", "score_draw_uml_funcs", "score_layout", diff --git a/src/extensions/score_sync_toml/BUILD b/src/extensions/score_sync_toml/BUILD index 34ba6584d..ffbbd38c4 100644 --- a/src/extensions/score_sync_toml/BUILD +++ b/src/extensions/score_sync_toml/BUILD @@ -12,6 +12,7 @@ # ******************************************************************************* load("@aspect_rules_py//py:defs.bzl", "py_library") +load("//:score_pytest.bzl", "score_pytest") load("@docs_as_code_hub_env//:requirements.bzl", "requirement") filegroup( @@ -31,5 +32,14 @@ py_library( deps = [ requirement("sphinx"), requirement("needs-config-writer"), + "//src/helper_lib", ], ) + +score_pytest( + name = "score_sync_toml_tests", + size = "small", + srcs = ["test_mounts.py"], + deps = [":score_sync_toml"], + pytest_config = "//:pyproject.toml", +) diff --git a/src/extensions/score_sync_toml/__init__.py b/src/extensions/score_sync_toml/__init__.py index 42709b218..610f0e8d2 100644 --- a/src/extensions/score_sync_toml/__init__.py +++ b/src/extensions/score_sync_toml/__init__.py @@ -14,7 +14,8 @@ from sphinx.application import Sphinx -from src.helper_lib import config_setdefault +from src.extensions.score_sync_toml._mounts import register_mounts +from src.helper_lib import config_setdefault, find_git_root def setup(app: Sphinx) -> dict[str, str | bool]: @@ -24,8 +25,16 @@ def setup(app: Sphinx) -> dict[str, str | bool]: See https://needs-config-writer.useblocks.com """ - config_setdefault(app.config, "needscfg_outpath", "ubproject.toml") - """Write to the confdir directory.""" + # Emit a single ubproject.toml at the git repo root, where UI extensions + # (ubCode / esbonio) look for it. needs-config-writer relativizes every path + # field against the output file's directory, so anchoring the file at the + # root yields root-relative paths automatically. find_git_root() resolves the + # root under `bazel run` and esbonio alike; in a sandbox build it returns None + # and we fall back to the confdir default (that copy is ephemeral / discarded). + git_root = find_git_root() + outpath = str(git_root / "ubproject.toml") if git_root else "ubproject.toml" + config_setdefault(app.config, "needscfg_outpath", outpath) + """Write a single ubproject.toml at the git repo root.""" config_setdefault(app.config, "needscfg_overwrite", True) """Any changes to the shared/local configuration updates the generated config.""" @@ -33,7 +42,7 @@ def setup(app: Sphinx) -> dict[str, str | bool]: config_setdefault(app.config, "needscfg_write_all", True) """Write full config, so the final configuration is visible in one file.""" - config_setdefault(app.config, "needscfg_exclude_defaults", True) + config_setdefault(app.config, "needscfg_exclude_defaults", False) """Exclude default values from the generated configuration.""" # This is disabled for right now as it causes a lot of issues @@ -46,6 +55,13 @@ def setup(app: Sphinx) -> dict[str, str | bool]: ) """Merge the static TOML file into the generated configuration.""" + # score_mounts resolves Bazel's JSON manifest during ``config-inited``. Run + # afterwards and serialize its structured entries here, alongside the rest + # of the needs-config-writer configuration. + app.connect( + "config-inited", lambda app, config: register_mounts(config), priority=500 + ) + app.config.needscfg_relative_path_fields.extend( [ "needs_external_needs[*].json_path", diff --git a/src/extensions/score_sync_toml/_mounts.py b/src/extensions/score_sync_toml/_mounts.py new file mode 100644 index 000000000..d84cf3412 --- /dev/null +++ b/src/extensions/score_sync_toml/_mounts.py @@ -0,0 +1,70 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Serialize Bazel-derived mount metadata for ``needs-config-writer``.""" + +from __future__ import annotations + +import tempfile +from pathlib import Path +from typing import Any + +from sphinx.config import Config + +from src.helper_lib import find_git_root, get_runfiles_dir + + +def _toml_string(value: str) -> str: + return '"' + value.replace("\\", "\\\\").replace('"', '\\"') + '"' + + +def _toml_dir(entry: dict[str, Any]) -> str: + """Derive a stable TOML path from a resolved ``config.mounts`` entry.""" + walk_dir = Path(entry["dir"]).resolve() + git_root = find_git_root() + if git_root is not None: + try: + return str(walk_dir.relative_to(git_root)) + except ValueError: + pass + + try: + external_path = walk_dir.relative_to(get_runfiles_dir()) + except ValueError: + return str(walk_dir) + if external_path.parts[0] == "_main": + return str(Path(*external_path.parts[1:])) + return "bazel-bin/external/" + str(external_path) + + +def materialize_mounts(entries: list[dict[str, Any]]) -> Path | None: + """Write resolved mounts as a temporary, Git-root-relative TOML merge file.""" + if not entries: + return None + lines: list[str] = [] + for entry in entries: + lines.extend( + [ + "[[mounts]]", + f"dir = {_toml_string(_toml_dir(entry))}", + f"mount_at = {_toml_string(entry['mount_at'])}", + ] + ) + if entry.get("attach_to"): + lines.append(f"attach_to = {_toml_string(entry['attach_to'])}") + if entry.get("entry_doc", "index") != "index": + lines.append(f"entry_doc = {_toml_string(entry['entry_doc'])}") + lines.append("") + outdir = Path(tempfile.mkdtemp(prefix="score_sync_toml_")) + fragment = outdir / "score_mounts.toml" + fragment.write_text("\n".join(lines), encoding="utf-8") + return fragment + + +def register_mounts(config: Config) -> None: + """Merge configured mount entries into the generated ``ubproject.toml``.""" + fragment = materialize_mounts(config.mounts) + if fragment is not None: + config.needscfg_merge_toml_files.append(str(fragment)) diff --git a/src/extensions/score_sync_toml/test_mounts.py b/src/extensions/score_sync_toml/test_mounts.py new file mode 100644 index 000000000..f057e2aa8 --- /dev/null +++ b/src/extensions/score_sync_toml/test_mounts.py @@ -0,0 +1,65 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +from src.extensions.score_sync_toml import _mounts +from src.extensions.score_sync_toml._mounts import materialize_mounts + + +def test_materialize_mounts_serializes_structured_entries(): + fragment = materialize_mounts( + [ + { + "dir": 'docs/with "quotes"', + "mount_at": "guide", + "attach_to": "index", + "entry_doc": "start", + } + ] + ) + + assert fragment is not None + assert fragment.read_text(encoding="utf-8") == ( + "[[mounts]]\n" + 'dir = "docs/with \\"quotes\\""\n' + 'mount_at = "guide"\n' + 'attach_to = "index"\n' + 'entry_doc = "start"\n' + ) + + +def test_materialize_mounts_omits_default_fields(): + fragment = materialize_mounts( + [{"dir": "docs", "mount_at": "guide", "attach_to": None, "entry_doc": "index"}] + ) + + assert fragment is not None + assert ( + fragment.read_text(encoding="utf-8") + == '[[mounts]]\ndir = "docs"\nmount_at = "guide"\n' + ) + + +def test_materialize_mounts_maps_external_runfiles_path_to_bazel_bin( + tmp_path, monkeypatch +): + runfiles_dir = tmp_path / "runfiles" + walk_dir = runfiles_dir / "score_process+" / "process" + walk_dir.mkdir(parents=True) + monkeypatch.setattr(_mounts, "find_git_root", lambda: None) + monkeypatch.setattr(_mounts, "get_runfiles_dir", lambda: runfiles_dir) + + fragment = materialize_mounts( + [ + { + "dir": str(walk_dir), + "mount_at": "process", + } + ] + ) + + assert fragment is not None + assert fragment.read_text(encoding="utf-8") == ( + '[[mounts]]\ndir = "bazel-bin/external/score_process+/process"\nmount_at = "process"\n' + ) diff --git a/src/incremental.py b/src/incremental.py index 0519dff63..fb2d5c505 100644 --- a/src/incremental.py +++ b/src/incremental.py @@ -124,13 +124,22 @@ def update_module_hash(build_dir: Path, sentinel_files: list[Path]) -> None: "auto", f"--define=external_needs_source={get_env('DATA')}", f"--define=testcase_source_dirs={os.environ.get('TEST_SOURCES', '[]')}", + # Path to the Bazel-emitted mounts manifest (empty when no mounts are + # configured); consumed by the score_mounts extension. + f"--define=mounts_manifest={os.environ.get('MOUNTS_MANIFEST', '')}", ] metamodel_yaml = os.environ.get("SCORE_METAMODEL_YAML", "") if metamodel_yaml: - # Normalize to absolute path so it resolves correctly after Sphinx changes cwd + # ``docs`` passes a runfiles-relative path under ``bazel run``. Keep + # the workspace-relative fallback for direct invocations. if not os.path.isabs(metamodel_yaml): - metamodel_yaml = str(ws_root / metamodel_yaml) + runfiles_dir = os.environ.get("RUNFILES_DIR", "") + metamodel_yaml = str( + (Path(runfiles_dir) / metamodel_yaml) + if runfiles_dir + else (ws_root / metamodel_yaml) + ) metamodel_yaml = os.path.abspath(metamodel_yaml) base_arguments.append(f"--define=score_metamodel_yaml={metamodel_yaml}") diff --git a/src/requirements.in b/src/requirements.in index 04317ea12..09ec79ae2 100644 --- a/src/requirements.in +++ b/src/requirements.in @@ -30,6 +30,9 @@ needs-config-writer == 0.2.4 # use this for a specific commit for fast development iterations # needs-config-writer @ https://github.com/useblocks/needs-config-writer/archive/032a5f8.zip +# Mount external source trees into the Sphinx project without copying. +sphinx-mounts + # Need this to enable non bazel execution bazel-runfiles diff --git a/src/requirements.txt b/src/requirements.txt index 191ba1bf6..b309a1d45 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -520,6 +520,122 @@ idna==3.15 \ # via # anyio # requests +ignore-python==0.3.3 \ + --hash=sha256:000ae12f6187791bc4748e40e7073b9769ca6ddb0cabfc11d1c682ca0e6a3953 \ + --hash=sha256:05ccc5bdf2ad1f840a0a2296efff5f4761a26f015185ed0bb835ad1901f08ee8 \ + --hash=sha256:0a6b2d7900ce82acd61ab6714b10103c37d0a1c16ee7af46fb6e94d14a2e4dca \ + --hash=sha256:0c9408949156bf4ae07e60d5c8f356114be1482dc8f708ecd5785151e9ae21fa \ + --hash=sha256:0c9ea5d81e6b1a1284f5463203f57cffb3c7e391c0d8ce3fe0e4d838621367fb \ + --hash=sha256:0dfa531bbf65f45f9a233e2809f8b0a49aa9078686dbb0e4f3e30848e09cc208 \ + --hash=sha256:0f0edb622f5a8b7f735e14a7ca13d4cb7ca04b6fd7e844f5fa0fd9f86622b986 \ + --hash=sha256:0f50dd3c3f0ef982e256d9e702b44c1d81cbe0da2d98746d5b189bc1fd5191cd \ + --hash=sha256:1206189e1c988a3d0fb7de3298e5f7dd5284b4a23781878fb8f9f6224659b270 \ + --hash=sha256:1225e6210e302e0725265504a11a367f0b8cb882e3e2e231748559d5b05179c8 \ + --hash=sha256:12827fc970d57865b6f31a82f79838bea24b63d85d9f61f1821b7cf8bd01128c \ + --hash=sha256:130d9ace07988b69669e871ed84dc77088d7b5ce35265efbb0b0d425085e2a99 \ + --hash=sha256:152c5aecf42e709138c8e3da2ac733d00f5efdcd004c240a95193e62b6bd024e \ + --hash=sha256:17749af58a6fe6aaa3198b285c874fbf0f036ef2cb684225ef62288b62948d26 \ + --hash=sha256:1ac4491082df61d370f7fc087d5c0b16bc84b647e126e3f45a97d31cf3b2f514 \ + --hash=sha256:1b23770925db422fe9c94920da82e0606516aca09fd851880bc8c8ed68fe6455 \ + --hash=sha256:1b2ff29dbe59bbbd370feacb99e5bec7791abe730dd535b5db848de5b5210d7e \ + --hash=sha256:1ed9c8c858dfe2ba91bc4ab60ebd9d12dd4602a4d0d555e0fee9c8621f9ca292 \ + --hash=sha256:22c216e3130077060eb4cce99a8bf79074826655bbea6c594d36d8a0735fac6c \ + --hash=sha256:275f5b3e4c5b25fcb58ad1eedd983a346866939a6140e564b9c56ee9f8fc7760 \ + --hash=sha256:2af502d988282cc360094dc7b2733a7b68e54a8e3cf0128178ab1ba84f9ec290 \ + --hash=sha256:2b608a30d3505720f9aceaba7d6d26769867d71c212fc5c8a80fae1a8afec663 \ + --hash=sha256:2d637f1a6b2ec7bcb74b2ac11f75e15eaf6092535b8dc31415305a73a3762e3e \ + --hash=sha256:3001adecf33250dfa90c6e727affd559b31ad3b32b8519920c14bef53410444f \ + --hash=sha256:356b783877c7e88eba69c99bcc5e2d9fb07c2fe54f2c64e03897b7ae2adce2a7 \ + --hash=sha256:3623ce12eb96976c0db36a0ad99c65c669fdafece71e7221a538f12fa6fa41ef \ + --hash=sha256:365ab0bf94b64f3def2264fdca58f6e9811763830ad1a48a41d70574a496e5b9 \ + --hash=sha256:39e7b9d976c12c68b09b9944328a000b012725f1f7c4655510eb890761016fb3 \ + --hash=sha256:3b6536698628af08b6db260d338b41e7c48b2a6c5c93b12de4d1ecafc1bb86ae \ + --hash=sha256:3c40627f3bde32a37e75950b97733b586e9167fd545c958195dbf1bb73d96a81 \ + --hash=sha256:3da9e102800f162468ddb5d2d392b79e2481d3709e886f875037ef7066df5481 \ + --hash=sha256:4252f803f3cdae6c8775f1e905f5babd6e77860781f4c29cf798452ee5fd6936 \ + --hash=sha256:429a9b792afdca7dd9cff59f5ace44c2f55d01fc30b0ce3d28d63bee223116ed \ + --hash=sha256:44bf617535f5ead500a6f83178426f7c607e015bcf9e614e18ae88aa3de1a340 \ + --hash=sha256:477bb090189b79b8a81753c74a59ccb6c0ebf91985f30e926177f062c8616d05 \ + --hash=sha256:48cf09c7668b5b8bb7dcaf185618130b8ea7839df0eb0f6ae4fae7b7162a0c37 \ + --hash=sha256:49ccb834be168a7e72b104ffc02024d4eb4d91840bc30e2948787551f5242ff0 \ + --hash=sha256:4aac18bf9346f06fd17412d5bcfca53090a72456f53f3ffdf3b1e896f15cc356 \ + --hash=sha256:4ba31985a790af71bc45c16643d4773b829b8233acf4175bba2af466a2bbc959 \ + --hash=sha256:4f88aa2ee7335399c823aa050edec118e28ddafe7859e0db4093de0ca815467e \ + --hash=sha256:4f8c85a6738c632abd477c217297f8929ec1cafb261b94fa05a7fcf28095d70f \ + --hash=sha256:5055772fa6f09148a09e6958770c4e4f4435f6e3de33476f815af9c5db13e29c \ + --hash=sha256:5134be429fb954ec589857dbc6152dc09014902313e3d2293af9338a4b7799ae \ + --hash=sha256:558f79f48d2cd7bd42ce3e6747752be9483504d3f2f84c1d39c39bcf1961bac3 \ + --hash=sha256:56e8f00aef89a19b0305cb233fb59736ad14aa8c6db05720b13ac0bca811b5e7 \ + --hash=sha256:5756229d699ec5ab8caf4cec3ce9827d02145059c79a2416f363e2df4406d378 \ + --hash=sha256:593679d7714b4228d7e8c3bda0005badb9f0d4cb37cd8dda8385ef734e675e23 \ + --hash=sha256:59e26bf7bbbd5937a196f01480050d3d80418ade8933164b28e3e36734baecd7 \ + --hash=sha256:5f3d88554e779f03567c05286f31d2ce21f6103892c7412bdf350ef2fb50184a \ + --hash=sha256:62afd51edaf5634e21206a65e9e244e038b747e39ba969ebcc9361b63825a4f9 \ + --hash=sha256:68f393318292a6346c6d72c2b8ee301a081bff778dfb0e6ef6f0c36da4053374 \ + --hash=sha256:71dc7505c0520e066c5d567f49d7173703c34192af1b8f89ce401a34098391f5 \ + --hash=sha256:7ad2cc34fb600ab4aa22014bc8cc9b8bae2d467f772074d3a4929deb4adf64d6 \ + --hash=sha256:7d63688dc696b72d54623dd55f269d5203fcd5de5eeeba7bc276864300a8d790 \ + --hash=sha256:7e3b89f96cda6df85687b9532eac4faffdf15d2d918b239399cab6c78dd5282b \ + --hash=sha256:7e49780796e39812ade8001b0c7d2a2f1a9aeac90964e8e757c2013d30dfbe4e \ + --hash=sha256:7e9bea86436a59eb3f24e8e15bb3b3a36cdb98aec950c70aa619e33f35eb6beb \ + --hash=sha256:82fecbeb7fa309245aaaa7e3aaa09c744f1059fc238e2f7acd889d803f1a0be7 \ + --hash=sha256:842572b228382c9bb6283428f14ff4481b3822cb7488ce4388281a8c6c465a81 \ + --hash=sha256:8528c819c151ccabbd1bb9e591dd99495c2d0423b10ccdef47d48fe25da2b2d6 \ + --hash=sha256:85f3ec2d15ba134e6159ddebdb1a0af89f99431d93336f8b3cb71aa2de9f3324 \ + --hash=sha256:875fdfa0e3e9102164a540509ca2d5ad959f1f53858cf11a4174a1845e3c575c \ + --hash=sha256:89b2efb712c5bc0c57cc5a9deabfbcb2196504139d2d108a4afedd140c02063f \ + --hash=sha256:8d26f91ac1fea52abd16dab224b4ac016d8914f28db02e582f4e589ee2b5faa5 \ + --hash=sha256:8dd30b865dfd3206756212796cb13686b6c45befa5cc495ccc9866108215f7c1 \ + --hash=sha256:8e9085cec8d730b43ac8c86ef5da0c902f0f57da8da2ee009045e7006fe4860f \ + --hash=sha256:8f0e8379d4eff6842b61c01c7f03dc7415afac994b96e4a7d24f80b1078d5c1d \ + --hash=sha256:8fc3b2fcb6ee1c2b1512a0b29d26bb9b9e945d0d50c0a85673a675622fbfb0f4 \ + --hash=sha256:901a862196bf610745e164d29c518e0cbf727eaaedc83d5058a7895721be2173 \ + --hash=sha256:931744cdcbb84d77159daf6b54e3b459e9f0a0ba52ea6b99d1d228e32556c2b9 \ + --hash=sha256:97db61620be5c56a78115967d05e0d7a130a27a68f401eb98bf0753d3f770cb5 \ + --hash=sha256:9bccb48b57b7a85677c1022afbaaf86e5cda8c1ecff00ad96877e10166d1eddc \ + --hash=sha256:a2cdfbd3c9df9e98dd067858fce7d6ab919f2fb038f6b3124fc5f05b8825b546 \ + --hash=sha256:a30070520aa114133feffc2413ba62bfcd8ef2f9826ebe7616de123f73b57977 \ + --hash=sha256:a894e6bf85988edee94474ff1399b61685d5fa7399d1c25a40efa28f7a2799ea \ + --hash=sha256:a999ef004caa048e5ecccb5f3383d857105baa37b8895a0a2b7cd66f9cb0b0b4 \ + --hash=sha256:af35c6b8c3a9a27721e5c2a849e2ee21973e14b8b7d2e76e15940475a8ace443 \ + --hash=sha256:b0f0bc9eab99a36f54e0a4e3043768e529107565bc67a7f420b4febff8006f32 \ + --hash=sha256:b123951f9befe6052a7397778fa64ade18983345d79fd9477160b11dfd736df0 \ + --hash=sha256:b2c072a4615c9610bd43cde816882ede0c910599702e54cbc07371874d1ca95b \ + --hash=sha256:b4f2df2bbca999f9749430e54dcd13bcc35289520b63e09ed4d2e1877a524260 \ + --hash=sha256:b730d11384a02bc4fb195b8a73555cb450e325d2676b39c8b5d20a0786102f37 \ + --hash=sha256:be6a4e3244c33f133d3c0bc43f9725c61dc9a11f7100c615639ce1daee064766 \ + --hash=sha256:c3430b73a99af300b0b1203da2cd30f1831f504466d94343b065b1ef0435802c \ + --hash=sha256:c478ee58fd2d6f5f7b75b32288d8a099904f710e83dd878f40058a309a0f6060 \ + --hash=sha256:c545cfd062a463c6d8e90b2738ec93fb9228f12c0aa80866fdc80f64fbc8f1a3 \ + --hash=sha256:c68dc5db03a22aada43f23f55e359109e00afe3886824d6fecfbd6821dcdbd6f \ + --hash=sha256:c73743c4c8622ebed7998990c5f18a2c2a4fae915288798aeb8fef6d5411743b \ + --hash=sha256:c8b112dd1906487fe56525361cbeca57b07adcc9496b641560327654aced7e8f \ + --hash=sha256:cb82264552ae789f2d8c2543b3b1eb2b3e091977c22f180ab268f5b677825279 \ + --hash=sha256:cc49302f8fdbd3426eba4b99671ba10f0d150d90ea4f344a0204e4ac8e4fcb66 \ + --hash=sha256:d01b0578252d0df17b64400103ffbfea5b8332483a3da01d116f4919467128de \ + --hash=sha256:d4f8b4137b0153c95ea7e4e3acff4c5e6f6c25206d3e3b86dbe879658b00c927 \ + --hash=sha256:d8be9b991c63fd8c76a6a9deac24ed164533824da1bdb2356a33511bfa446d54 \ + --hash=sha256:d94d9b91eece76104077a7e0b3276bb14728a4bff00ba23b0f0bea2a10c0e6e8 \ + --hash=sha256:d9778479aaeac4f000d2b9c0f6da76149926011588b08a6b22892ae921c4f563 \ + --hash=sha256:da0ebc45da1d4e918fba53a2bc059339d00a1f666b3b72b9acd4fe4eeaec8343 \ + --hash=sha256:dab6441f4403483a420de9481987e68ab75a28ade5bac8f1d82a3533ed83a6eb \ + --hash=sha256:dc80ac80ace112da6d02f44681b6beb2ccecb68d6ac2b5e1b82d7f84347e1cf6 \ + --hash=sha256:dda677506171a0c4f27925e13f9f8b4b652941969f0e0e6b555ce795e18b7467 \ + --hash=sha256:de10b31770a8978e381c8f0c05479e18d6ea1defa18a0aa825b0487acd1f082c \ + --hash=sha256:df78545bc5d54abf875a70a70db7e1e12e606377d1c9f4e68b6763e8c701a748 \ + --hash=sha256:e3754a2529b0c163bece327be6c5e92fe885cd01d066bc4a3fc13224eae5d221 \ + --hash=sha256:e397f034d675ef14980f2df0a074dec3218963a912b7011d0e8e94f9a3d87d41 \ + --hash=sha256:e4786eac47d2e9dab245fc376157bdc458f4b3269b81006b80a74d514b37efb7 \ + --hash=sha256:e571e0e3de9bca0b70afc4261aa2df6a305bfff94b092942ccd081fa07b8a148 \ + --hash=sha256:e61a305384a59997fa1971a6c886db9b8e2ff59fca3ee9bb4e1fe191b6d02593 \ + --hash=sha256:e9c95f8c3a68449f7d3bd5860ea832b5827a04803337796da72d8340786e9268 \ + --hash=sha256:ebb00b54e5a01d8b10a51a7de7d2f884359375f4abb5352378f6f7f2c8878a58 \ + --hash=sha256:f24c6af8fd78b5c58e0f2683004e0b6fac146ee047b2d8fc8d7b16c69e0a3aaa \ + --hash=sha256:f65475871a57413dee3094cc5d479ed202af85c38bc90bda5d7ee4435ff96819 \ + --hash=sha256:f93a3425169961aa7f0c7193dc12f01414700f86f4fb99ec078c18780432b77a \ + --hash=sha256:fabb25c4352d3d4f1a2a197d6fb403848026344aab73e1674bcb31e4a7f54914 \ + --hash=sha256:fb712f94825c04fa605b989d6f5889195b51c927f5045c34b9ff7a448cd0a6f0 + # via sphinx-mounts imagesize==2.0.0 \ --hash=sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96 \ --hash=sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3 @@ -1260,6 +1376,7 @@ sphinx==9.1.0 \ # sphinx-collections # sphinx-data-viewer # sphinx-design + # sphinx-mounts # sphinx-needs # sphinxcontrib-jquery # sphinxcontrib-mermaid @@ -1280,6 +1397,10 @@ sphinx-design==0.7.0 \ --hash=sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a \ --hash=sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282 # via -r requirements.in +sphinx-mounts==0.1.0 \ + --hash=sha256:aff3450756727d0ca43538ea72b67fddb8b8799c52110f41de8871c59d3d1540 \ + --hash=sha256:d1818e3a0b7e0b327c6fa265afcf4d43f5bb7147276e69a9b35cd046deeb9439 + # via -r requirements.in sphinx-needs[plotting]==8.0.0 \ --hash=sha256:540c380c074d4088a557ea353e91513bfc1cb7712b10925c13ac9e5ebb7be091 \ --hash=sha256:c4336ee0e3c949eff9eb11a14910f7b6b68cb8284d731cfddf97694037337674 diff --git a/src/tests/docs_e2e/BUILD b/src/tests/docs_e2e/BUILD new file mode 100644 index 000000000..37b21f466 --- /dev/null +++ b/src/tests/docs_e2e/BUILD @@ -0,0 +1,46 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@aspect_rules_py//py:defs.bzl", "py_library") +load("//:docs.bzl", "docs") +load("//:score_pytest.bzl", "score_pytest") + +py_library( + name = "e2e_support", + srcs = ["support.py"], + imports = ["."], + visibility = ["//visibility:public"], +) + +docs( + source_dir = "host_docs", +) + +score_pytest( + name = "docs_e2e_test", + size = "medium", + srcs = ["test_docs_e2e.py"], + data = [ + ":docs", + ":sourcelinks_json", + "host_docs/conf.py", + "host_docs/index.rst", + ], + deps = [":e2e_support"], + env = { + "DOCS_BINARY": "$(rootpath :docs)", + "DOCS_CONF": "$(rootpath host_docs/conf.py)", + "SOURCELINKS": "$(rootpath :sourcelinks_json)", + }, + pytest_config = "//:pyproject.toml", +) diff --git a/src/tests/docs_e2e/host_docs/conf.py b/src/tests/docs_e2e/host_docs/conf.py new file mode 100644 index 000000000..717d335bf --- /dev/null +++ b/src/tests/docs_e2e/host_docs/conf.py @@ -0,0 +1,16 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +project = "Docs E2E fixture" +project_url = "https://example.invalid/docs-e2e" +extensions = ["score_sphinx_bundle"] diff --git a/src/tests/docs_e2e/host_docs/index.rst b/src/tests/docs_e2e/host_docs/index.rst new file mode 100644 index 000000000..cfabcc31f --- /dev/null +++ b/src/tests/docs_e2e/host_docs/index.rst @@ -0,0 +1,16 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Docs E2E fixture +================ diff --git a/src/tests/docs_e2e/support.py b/src/tests/docs_e2e/support.py new file mode 100644 index 000000000..ccead1eb3 --- /dev/null +++ b/src/tests/docs_e2e/support.py @@ -0,0 +1,66 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Shared runner for end-to-end tests of docs() binaries.""" + +import os +import shutil +import subprocess +from pathlib import Path + + +def runfile(path_env: str) -> Path: + """Resolve a runfiles-relative path passed through a test environment variable.""" + return ( + Path(os.environ["TEST_SRCDIR"]) + / os.environ["TEST_WORKSPACE"] + / os.environ[path_env] + ) + + +def run_docs_build( + tmp_path: Path, + *, + docs_binary: Path, + source_dir: Path, + sourcelinks: Path, + mounts_manifest: Path | None = None, +) -> subprocess.CompletedProcess[str]: + """Run a docs() binary in an isolated writable workspace. + + The binary normally runs from a developer's workspace. This helper provides + the small writable equivalent needed by a Bazel test while preserving the + runfiles layout used to resolve in-tree mount sources. + """ + runfiles_workspace = Path(os.environ["TEST_SRCDIR"]) / os.environ["TEST_WORKSPACE"] + (tmp_path / "src").symlink_to(runfiles_workspace / "src", target_is_directory=True) + + copied_source_dir = tmp_path / "docs" + shutil.copytree(source_dir, copied_source_dir) + for name in ["MODULE.bazel", "MODULE.bazel.lock", "BUILD"]: + (tmp_path / name).touch() + + env = os.environ.copy() + env["SOURCE_DIRECTORY"] = str(copied_source_dir) + env["MOUNTS_MANIFEST"] = str(mounts_manifest) if mounts_manifest else "" + env["DATA"] = "[]" + env["ACTION"] = "incremental" + env["SCORE_SOURCELINKS"] = str(sourcelinks) + + return subprocess.run( + [str(docs_binary)], + cwd=tmp_path, + env=env, + capture_output=True, + text=True, + check=False, + ) diff --git a/src/tests/docs_e2e/test_docs_e2e.py b/src/tests/docs_e2e/test_docs_e2e.py new file mode 100644 index 000000000..d570262c3 --- /dev/null +++ b/src/tests/docs_e2e/test_docs_e2e.py @@ -0,0 +1,30 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""End-to-end test for a minimal docs() project without mounts.""" + +from pathlib import Path + +from support import run_docs_build, runfile + + +def test_docs_builds_html(tmp_path: Path): + """docs() builds the host documentation without a mount configuration.""" + result = run_docs_build( + tmp_path, + docs_binary=runfile("DOCS_BINARY"), + source_dir=runfile("DOCS_CONF").parent, + sourcelinks=runfile("SOURCELINKS"), + ) + + assert result.returncode == 0, result.stdout + result.stderr + assert (tmp_path / "_build" / "index.html").is_file() diff --git a/src/tests/mounts_conflict/BUILD b/src/tests/mounts_conflict/BUILD new file mode 100644 index 000000000..9cc1e58df --- /dev/null +++ b/src/tests/mounts_conflict/BUILD @@ -0,0 +1,102 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +load("//:docs.bzl", "docs_bundle") + +docs_bundle( + name = "child", + source_dir = "child", +) + +docs_bundle( + name = "wrapper_x", + source_dir = "_empty", + bundles = [ + {"bundle": ":child", "mount_at": "x"}, + ], +) + +docs_bundle( + name = "wrapper_y", + source_dir = "_empty", + bundles = [ + {"bundle": ":child", "mount_at": "y"}, + ], +) + +# These wrappers have the same final document location, but disagree about +# metadata that the manifest can represent only once. +docs_bundle( + name = "attach_to_a", + source_dir = "_empty", + bundles = [ + {"bundle": ":child", "mount_at": "same", "attach_to": "a/index"}, + ], +) + +docs_bundle( + name = "attach_to_b", + source_dir = "_empty", + bundles = [ + {"bundle": ":child", "mount_at": "same", "attach_to": "b/index"}, + ], +) + +docs_bundle( + name = "entry_doc_index", + source_dir = "_empty", + bundles = [ + {"bundle": ":child", "mount_at": "same", "entry_doc": "index"}, + ], +) + +docs_bundle( + name = "entry_doc_overview", + source_dir = "_empty", + bundles = [ + {"bundle": ":child", "mount_at": "same", "entry_doc": "overview"}, + ], +) + +# The child reaches the final bundle at two different locations, so analysis +# must stop before Sphinx would receive two copies of the same pages. +docs_bundle( + name = "bad", + source_dir = "_empty", + bundles = [ + {"bundle": ":wrapper_x", "mount_at": "root"}, + {"bundle": ":wrapper_y", "mount_at": "root"}, + ], + # Tagged manual so `bazel build //...` never expands to it; + # the test builds it explicitly and asserts the analysis error. + tags = ["manual"], +) + +docs_bundle( + name = "bad_attach_to", + source_dir = "_empty", + bundles = [ + {"bundle": ":attach_to_a", "mount_at": "root"}, + {"bundle": ":attach_to_b", "mount_at": "root"}, + ], + tags = ["manual"], +) + +docs_bundle( + name = "bad_entry_doc", + source_dir = "_empty", + bundles = [ + {"bundle": ":entry_doc_index", "mount_at": "root"}, + {"bundle": ":entry_doc_overview", "mount_at": "root"}, + ], + tags = ["manual"], +) diff --git a/src/tests/mounts_conflict/child/a.rst b/src/tests/mounts_conflict/child/a.rst new file mode 100644 index 000000000..4b7aaaac4 --- /dev/null +++ b/src/tests/mounts_conflict/child/a.rst @@ -0,0 +1,18 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Conflict Child +============== + +Fixture page for the mount-conflict negative test. diff --git a/src/tests/mounts_contract/BUILD b/src/tests/mounts_contract/BUILD new file mode 100644 index 000000000..0ff7323cb --- /dev/null +++ b/src/tests/mounts_contract/BUILD @@ -0,0 +1,93 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("//:bzl/mount_rules.bzl", "create_mounts_manifest") +load("//:docs.bzl", "docs", "docs_bundle") +load("//:score_pytest.bzl", "score_pytest") + +docs_bundle( + name = "child", + source_dir = "child", + scan_code = [":scan_code"], +) + +filegroup( + name = "scan_code", + srcs = ["child/example.py"], +) + +# The child is nested below the parent bundle before the parent itself is +# mounted into the synthetic host tree below. +docs_bundle( + name = "parent", + source_dir = "parent", + bundles = [{ + "bundle": ":child", + "mount_at": "child", + }], +) + +# An aggregator deliberately has no source_dir. It only propagates its child +# and must preserve the declaration order in the generated manifest. +docs_bundle( + name = "ordered_aggregate", + bundles = [ + {"bundle": ":other", "mount_at": "first"}, + {"bundle": ":child", "mount_at": "second"}, + ], +) + +docs_bundle( + name = "other", + source_dir = "other", +) + +create_mounts_manifest( + name = "ordered_aggregate_manifest", + bundle = ":ordered_aggregate", +) + +# A complete docs() invocation is the synthetic host project used by the +# end-to-end test below. +docs( + source_dir = "host_docs", + bundles = [{ + "bundle": ":parent", + "mount_at": "concepts/example_bundle", + }], +) + +score_pytest( + name = "mount_docs_e2e_test", + size = "medium", + srcs = ["test_mount_docs_e2e.py"], + data = [ + ":docs", + ":_mounts_manifest", + ":ordered_aggregate_manifest", + ":sourcelinks_json", + ":parent", + "host_docs/conf.py", + "host_docs/concepts/index.rst", + "host_docs/index.rst", + ], + deps = ["//src/tests/docs_e2e:e2e_support"], + env = { + "FIXTURE_DOCS_BINARY": "$(rootpath :docs)", + "FIXTURE_MOUNTS_MANIFEST": "$(rootpath :_mounts_manifest)", + "ORDERED_AGGREGATE_MANIFEST": "$(rootpath :ordered_aggregate_manifest)", + "FIXTURE_SOURCELINKS": "$(rootpath :sourcelinks_json)", + "HOST_DOCS_CONF": "$(rootpath host_docs/conf.py)", + }, + pytest_config = "//:pyproject.toml", +) diff --git a/src/tests/mounts_contract/README.md b/src/tests/mounts_contract/README.md new file mode 100644 index 000000000..9ac58f981 --- /dev/null +++ b/src/tests/mounts_contract/README.md @@ -0,0 +1,83 @@ + + +# End-to-end test for Bazel documentation mounts + +This directory contains one black-box test for the public `docs_bundle()` and +`docs()` macros. It does not inspect a Starlark provider or the generated +mount manifest. Instead, it runs the same `:docs` executable that a user would +start with `bazel run` and asserts the visible Sphinx result. + +Run the test with: + +```console +bazel test //src/tests/mounts_contract:mount_docs_e2e_test +``` + +## Fixture + +The fixture builds this small documentation site: + +```text +host_docs/ +└── concepts/index + └── parent bundle at concepts/example_bundle + └── child bundle at child +``` + +`docs_bundle()` defines the parent and child bundles. `docs()` defines the +synthetic host project and creates its ordinary `:docs` binary, just as it +does in a real project. + +## Why the test starts a subprocess + +`docs()` is a Starlark macro, not an executable function. During `bazel test` +Bazel evaluates the macro and creates targets, including this fixture's +`:docs` `py_binary`. It does not run that binary merely because the macro was +evaluated. + +The Python test starts that already-built binary in a subprocess: + +```text +docs() macro + -> defines :docs +bazel test + -> builds :docs and starts the pytest test +pytest subprocess + -> executes :docs + -> incremental.py runs Sphinx + -> score_mounts configures sphinx-mounts +``` + +This is deliberately not a nested `bazel run`: a Bazel test must not start +another Bazel process in its sandbox. The subprocess directly executes the +`:docs` file that Bazel placed in the test runfiles. + +## What the test verifies + +`test_docs_build_mounts_bundle_and_extends_toctree` creates a writable +temporary workspace, runs the fixture's `:docs` binary, and checks: + +- Sphinx reports that it extended the `concepts/index` toctree. +- The mounted document is rendered as + `_build/concepts/example_bundle/index.html`. + +Together these assertions cover bundle composition, the mount manifest, +runfiles path resolution, the Sphinx extension, the target Toctree, and HTML +output. A wrong `mount_at` or `attach_to` fails this test as a user would see +it: Sphinx cannot extend the expected Toctree or the mounted page is absent. + +## Related tests + +- `bazel test //src/extensions/score_mounts:score_mounts_tests` contains + Python unit tests for the `score_mounts` extension. +- `bazel build //src/tests/mounts_conflict:bad`, `:bad_attach_to`, and + `:bad_entry_doc` are manually invoked negative fixtures. They must fail + because one source directory cannot produce conflicting mount locations, + Toctree targets, or entry documents. +- `bazel run //:docs` remains the project-wide documentation smoke check. diff --git a/src/tests/mounts_contract/child/example.py b/src/tests/mounts_contract/child/example.py new file mode 100644 index 000000000..ca83634c4 --- /dev/null +++ b/src/tests/mounts_contract/child/example.py @@ -0,0 +1,14 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# req-traceability: REQ_CHILD diff --git a/src/tests/mounts_contract/child/index.rst b/src/tests/mounts_contract/child/index.rst new file mode 100644 index 000000000..5c3b0816e --- /dev/null +++ b/src/tests/mounts_contract/child/index.rst @@ -0,0 +1,16 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Child bundle +============ diff --git a/src/tests/mounts_contract/host_docs/concepts/index.rst b/src/tests/mounts_contract/host_docs/concepts/index.rst new file mode 100644 index 000000000..5da136269 --- /dev/null +++ b/src/tests/mounts_contract/host_docs/concepts/index.rst @@ -0,0 +1,19 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Concepts +======== + +.. toctree:: + :maxdepth: 1 diff --git a/src/tests/mounts_contract/host_docs/conf.py b/src/tests/mounts_contract/host_docs/conf.py new file mode 100644 index 000000000..f87d4a9c2 --- /dev/null +++ b/src/tests/mounts_contract/host_docs/conf.py @@ -0,0 +1,17 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +project = "Mount contract fixture" +project_url = "https://example.invalid/mount-contract" +extensions = ["score_sphinx_bundle"] +suppress_warnings = ["score_source_code_linker"] diff --git a/src/tests/mounts_contract/host_docs/index.rst b/src/tests/mounts_contract/host_docs/index.rst new file mode 100644 index 000000000..a345eb0cf --- /dev/null +++ b/src/tests/mounts_contract/host_docs/index.rst @@ -0,0 +1,21 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Mount contract fixture +====================== + +.. toctree:: + :maxdepth: 1 + + concepts/index diff --git a/src/tests/mounts_contract/other/index.rst b/src/tests/mounts_contract/other/index.rst new file mode 100644 index 000000000..236c2d131 --- /dev/null +++ b/src/tests/mounts_contract/other/index.rst @@ -0,0 +1,16 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Other bundle +============ diff --git a/src/tests/mounts_contract/parent/index.rst b/src/tests/mounts_contract/parent/index.rst new file mode 100644 index 000000000..d14b5c54e --- /dev/null +++ b/src/tests/mounts_contract/parent/index.rst @@ -0,0 +1,16 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Parent bundle +============= diff --git a/src/tests/mounts_contract/test_mount_docs_e2e.py b/src/tests/mounts_contract/test_mount_docs_e2e.py new file mode 100644 index 000000000..14082546c --- /dev/null +++ b/src/tests/mounts_contract/test_mount_docs_e2e.py @@ -0,0 +1,69 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""End-to-end test for a documentation build with a mounted bundle.""" + +import json +from pathlib import Path + +from src.tests.docs_e2e.support import run_docs_build, runfile + + +def test_docs_build_mounts_bundle_and_extends_toctree(tmp_path: Path): + """A nested bundle keeps its placement and becomes part of the host site.""" + mounts_manifest = runfile("FIXTURE_MOUNTS_MANIFEST") + manifest = json.loads(mounts_manifest.read_text(encoding="utf-8")) + assert [mount["mount_at"] for mount in manifest["mounts"]] == [ + "concepts/example_bundle", + "concepts/example_bundle/child", + ] + sourcelinks = json.loads(runfile("FIXTURE_SOURCELINKS").read_text(encoding="utf-8")) + assert sourcelinks == [ + { + "file": "src/tests/mounts_contract/child/example.py", + "line": 14, + "tag": "# req-traceability:", + "need": "REQ_CHILD", + "full_line": "# req-traceability: REQ_CHILD", + "repo_name": "local_repo", + "hash": "", + "url": "", + } + ] + + result = run_docs_build( + tmp_path, + docs_binary=runfile("FIXTURE_DOCS_BINARY"), + source_dir=runfile("HOST_DOCS_CONF").parent, + mounts_manifest=mounts_manifest, + sourcelinks=runfile("FIXTURE_SOURCELINKS"), + ) + + assert result.returncode == 0, result.stdout + result.stderr + assert "extended toctree #0 in 'concepts/index'" in result.stdout + assert ( + tmp_path / "_build" / "concepts" / "example_bundle" / "index.html" + ).is_file() + toml = (tmp_path / "docs" / "ubproject.toml").read_text(encoding="utf-8") + assert 'mount_at = "concepts/example_bundle"' in toml + assert 'mount_at = "concepts/example_bundle/child"' in toml + + +def test_pure_aggregator_keeps_declared_mount_order(): + """An aggregator contributes no implicit package sources or reordered mounts.""" + manifest = json.loads( + runfile("ORDERED_AGGREGATE_MANIFEST").read_text(encoding="utf-8") + ) + assert [mount["mount_at"] for mount in manifest["mounts"]] == [ + "first", + "second", + ] diff --git a/src/tests/mounts_external/BUILD b/src/tests/mounts_external/BUILD new file mode 100644 index 000000000..b9f98c06d --- /dev/null +++ b/src/tests/mounts_external/BUILD @@ -0,0 +1,25 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("//:docs.bzl", "docs") + +# This fixture exercises the public Bzlmod contract. In particular, the +# sandboxed :needs_json target must be able to walk an external bundle rather +# than silently omitting it. +docs( + source_dir = "host_docs", + bundles = [{ + "bundle": "@score_process//:docs_bundle", + "mount_at": "process", + }], +) diff --git a/src/tests/mounts_external/host_docs/conf.py b/src/tests/mounts_external/host_docs/conf.py new file mode 100644 index 000000000..123a2f7da --- /dev/null +++ b/src/tests/mounts_external/host_docs/conf.py @@ -0,0 +1,17 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +project = "External mount fixture" +project_url = "https://example.invalid/external-mount" +extensions = ["score_sphinx_bundle"] +suppress_warnings = ["score_source_code_linker"] diff --git a/src/tests/mounts_external/host_docs/index.rst b/src/tests/mounts_external/host_docs/index.rst new file mode 100644 index 000000000..1535c0c0b --- /dev/null +++ b/src/tests/mounts_external/host_docs/index.rst @@ -0,0 +1,19 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +External mount fixture +====================== + +.. toctree:: + :maxdepth: 1 From 2d531217c99873750def813fb158f81b07ac979c Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Tue, 28 Jul 2026 17:49:57 +0200 Subject: [PATCH 02/19] fix runtime conflict for "data" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Maximilian Sören Pollak --- bzl/bundle_rules.bzl | 51 ++++++++++++++++++++++++++++++++++++++------ bzl/mount_rules.bzl | 10 ++++++++- docs.bzl | 33 ++++++++++++++++++++-------- 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/bzl/bundle_rules.bzl b/bzl/bundle_rules.bzl index 670a57039..b6d5f7e54 100644 --- a/bzl/bundle_rules.bzl +++ b/bzl/bundle_rules.bzl @@ -63,6 +63,7 @@ DocsBundleInfo = provider( fields = { "entries": "Ordered entries, one per source directory, including its final documentation-tree location.", "sourcelinks": "Source-code-link JSON files together with their owning repository.", + "external_runfiles": "Documentation source files from external repositories needed in runfiles.", }, ) @@ -184,7 +185,8 @@ def _parse_bundle_declaration(bundle): def _docs_bundle_impl(ctx): """Compose source files and nested bundles into a reusable bundle.""" entries = [] - direct_files = [] + own_source_files = [] + own_external_runfiles = [] if ctx.files.srcs: runtime_path = _bundle_runtime_path(ctx) @@ -201,9 +203,14 @@ def _docs_bundle_impl(ctx): external = external, repository = ctx.label.workspace_name, )) - direct_files.extend(ctx.files.srcs) - - transitive_files = [] + own_source_files.extend(ctx.files.srcs) + # Local sources are read directly from the workspace by ``bazel run``. + # Only sources from external repositories must be staged in runfiles. + if external: + own_external_runfiles.extend(ctx.files.srcs) + + child_source_files = [] + child_external_runfiles = [] sourcelinks = [ struct(file = source_link, repository = ctx.label.workspace_name) for source_link in ctx.files.sourcelinks @@ -218,16 +225,25 @@ def _docs_bundle_impl(ctx): ) for entry in _entries_visible_through(ctx, child) ]) - transitive_files.append(child[DefaultInfo].files) + child_source_files.append(child[DefaultInfo].files) + child_external_runfiles.append(child[DocsBundleInfo].external_runfiles) sourcelinks.extend(_sourcelinks_visible_through(ctx, child)) deduplicated_entries = _validate_and_deduplicate_entries(entries) - files = depset(direct = direct_files, transitive = transitive_files) + all_source_files = depset( + direct = own_source_files, + transitive = child_source_files, + ) + external_runfiles = depset( + direct = own_external_runfiles, + transitive = child_external_runfiles, + ) return [ - DefaultInfo(files = files), + DefaultInfo(files = all_source_files), DocsBundleInfo( entries = deduplicated_entries, sourcelinks = sourcelinks, + external_runfiles = external_runfiles, ), ] @@ -262,6 +278,27 @@ def create_bundle(name, bundles, srcs = [], sourcelinks = [], strip_prefix = "", ) return ":" + name +def _external_docs_runfiles_impl(ctx): + """Expose external documentation sources needed under ``bazel run``.""" + return [DefaultInfo(files = ctx.attr.bundle[DocsBundleInfo].external_runfiles)] + +_external_docs_runfiles = rule( + implementation = _external_docs_runfiles_impl, + attrs = { + "bundle": attr.label(providers = [DocsBundleInfo]), + }, + doc = "Internal adapter from a docs bundle to its runtime runfiles.", +) + +def external_docs_runfiles(name, bundle, visibility = None): + """Create a target containing only external bundle sources for ``bazel run``.""" + _external_docs_runfiles( + name = name, + bundle = bundle, + visibility = visibility, + ) + return ":" + name + def _merge_bundle_sourcelinks_impl(ctx): """Merge source-code links propagated by a documentation bundle.""" sourcelinks = [link.file for link in ctx.attr.bundle[DocsBundleInfo].sourcelinks] diff --git a/bzl/mount_rules.bzl b/bzl/mount_rules.bzl index 50e51dde5..aa710795d 100644 --- a/bzl/mount_rules.bzl +++ b/bzl/mount_rules.bzl @@ -35,10 +35,18 @@ def _mounts_manifest_impl(ctx): ctx.actions.write(out, json.encode({"mounts": json_mounts})) return [DefaultInfo(files = depset([out]))] -create_mounts_manifest = rule( +_create_mounts_manifest = rule( implementation = _mounts_manifest_impl, attrs = { "bundle": attr.label(providers = [DocsBundleInfo]), }, doc = "Writes a Sphinx mount manifest from reusable documentation bundles.", ) + +def create_mounts_manifest(name, bundle): + """Create a Sphinx mount manifest from reusable documentation bundles.""" + _create_mounts_manifest( + name = name, + bundle = bundle, + ) + return ":" + name diff --git a/docs.bzl b/docs.bzl index 2468fe28e..01f369848 100644 --- a/docs.bzl +++ b/docs.bzl @@ -53,6 +53,7 @@ load( "@score_docs_as_code//:bzl/bundle_rules.bzl", "create_bundle", "merge_bundle_sourcelinks", + "external_docs_runfiles", ) load( "@score_docs_as_code//:bzl/mount_rules.bzl", @@ -178,15 +179,19 @@ def docs( mounts_manifest_label = [] if bundles: - create_mounts_manifest( - name = "_mounts_manifest", - bundle = create_bundle( - name = "_docs_mounts", - bundles = bundles, - visibility = ["//visibility:private"], - ), + mounts_bundle = create_bundle( + name = "_docs_mounts", + bundles = bundles, + visibility = ["//visibility:private"], ) - mounts_manifest_label = [":_mounts_manifest"] + + mounts_manifest_label = [ + create_mounts_manifest( + name = "_mounts_manifest", + bundle = mounts_bundle, + ) + ] + deps = deps + _missing_requirements(deps) deps = deps + [ Label("//src:plantuml_for_python"), @@ -218,7 +223,17 @@ def docs( known_good = known_good, ) - docs_data = data + metamodel_label + [":sourcelinks_json", ":docs_bundle"] + mounts_manifest_label + external_docs_runfiles( + name = "_external_docs_runfiles", + bundle = ":docs_bundle", + visibility = ["//visibility:private"], + ) + + # ``bazel run`` reads local documentation from the workspace. Passing the + # complete bundle here would add those files to runfiles and could collide + # with the executable target name (for example ``docs`` and ``docs/``). + # External bundles do need runfiles, so keep only those sources. + docs_data = data + metamodel_label + [":sourcelinks_json", ":_external_docs_runfiles"] + mounts_manifest_label docs_env = { "SOURCE_DIRECTORY": source_dir, From bc30554b7da86ea076f16dc6170f3f83c24a4134 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Tue, 28 Jul 2026 18:16:59 +0200 Subject: [PATCH 03/19] feat: better error message on missing dot --- src/extensions/score_plantuml.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/extensions/score_plantuml.py b/src/extensions/score_plantuml.py index d3d2854cb..3484f83af 100644 --- a/src/extensions/score_plantuml.py +++ b/src/extensions/score_plantuml.py @@ -24,6 +24,7 @@ In addition it sets common PlantUML options, like output to svg_obj. """ +import subprocess from pathlib import Path from sphinx.application import Sphinx @@ -52,6 +53,30 @@ def find_correct_path(runfiles: Path) -> Path: return runfiles / module / "src" / "plantuml" +def check_graphviz(app: Sphinx) -> None: + """Report a missing Graphviz dependency before rendering any diagrams.""" + + # Ensure plantuml only for HTML builder + if "html" not in app.builder.name: + return + + result = subprocess.run( + [app.config.plantuml, "-version"], + capture_output=True, + check=False, + text=True, + ) + output = (result.stdout + result.stderr).strip() + + if "Dot executable does not exist" in output: + logger.error( + "PlantUML requires Graphviz, but its 'dot' executable is not " + "available on PATH. Install the 'graphviz' package in the " + "development environment.\n\nPlantUML output:\n" + output + ) + raise SystemExit(1) + + def setup(app: Sphinx): # we must overwrite the plantuml path due to Bazel app.config.plantuml = str(find_correct_path(get_runfiles_dir())) @@ -60,6 +85,7 @@ def setup(app: Sphinx): config_setdefault(app.config, "needs_build_needumls", "_plantuml_sources") logger.debug(f"PlantUML binary found at {app.config.plantuml}") + app.connect("builder-inited", check_graphviz) # The extension is not even active at runtime. return {"parallel_read_safe": True, "parallel_write_safe": True} From 53ebaeba8384a0ea52dc1f8b95be6ede8a14aac6 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Tue, 28 Jul 2026 18:17:36 +0200 Subject: [PATCH 04/19] fix docu refs --- docs/how-to/add_extensions.rst | 2 ++ docs/reference/bazel_macros.rst | 2 +- src/extensions/score_mounts/docs/howto/index.rst | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/how-to/add_extensions.rst b/docs/how-to/add_extensions.rst index 54673bda8..6b7cb2790 100644 --- a/docs/how-to/add_extensions.rst +++ b/docs/how-to/add_extensions.rst @@ -12,6 +12,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +.. _howto_add_extensions: + Add Extensions =================== diff --git a/docs/reference/bazel_macros.rst b/docs/reference/bazel_macros.rst index de03e231d..5e15504c8 100644 --- a/docs/reference/bazel_macros.rst +++ b/docs/reference/bazel_macros.rst @@ -106,7 +106,7 @@ Bazel macro: ``docs_bundle`` bundle**: a chunk of RST/Markdown content that can be overlaid into a host documentation project. A bundle carries only *content* — it has **no placement of its own**. Where it appears is decided by whoever mounts it (a composing -``docs_bundle`` or the :ref:`docs(bundles=[...]) ` call +``docs_bundle`` or the :ref:`docs(bundles=[...]) ` call site). .. code-block:: python diff --git a/src/extensions/score_mounts/docs/howto/index.rst b/src/extensions/score_mounts/docs/howto/index.rst index 4c1b47283..649cf4b61 100644 --- a/src/extensions/score_mounts/docs/howto/index.rst +++ b/src/extensions/score_mounts/docs/howto/index.rst @@ -315,7 +315,7 @@ Further reading * `sphinx-mounts documentation`_ — full configuration reference, TOML schema, behaviour of ``attach_to`` and ``entry_doc``. * `ubCode`_ — the IDE extension that reads ``ubproject.toml``. -* :doc:`add_extensions` — how to plug other Sphinx extensions into +* :ref:`howto_add_extensions` — how to plug other Sphinx extensions into the docs-as-code build. .. _sphinx-mounts documentation: https://sphinx-mounts.useblocks.com/ From 00529fc96b8e85a30124e13621e08873ad0fd7f1 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Tue, 28 Jul 2026 18:17:54 +0200 Subject: [PATCH 05/19] fix error message --- src/extensions/score_source_code_linker/xml_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extensions/score_source_code_linker/xml_parser.py b/src/extensions/score_source_code_linker/xml_parser.py index c40443ce6..194c2651f 100644 --- a/src/extensions/score_source_code_linker/xml_parser.py +++ b/src/extensions/score_source_code_linker/xml_parser.py @@ -229,7 +229,7 @@ def read_test_xml_file( testcasename = testcase.get("name", "") testclassname = testcase.get("classname", "") assert testclassname or testcasename, ( - f"Testcase: {testcase} does not have a 'name' or 'classname' attribute." + f"One testcase in {file} does not have a 'name' or 'classname' attribute." "One of which is mandatory. This should not happen, something is wrong." ) if testclassname: From 7e4a5516d9a3d864f3752a03dbe97e03f6ffa0d2 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Tue, 28 Jul 2026 21:34:19 +0200 Subject: [PATCH 06/19] feat: add mounts to live preview --- src/BUILD | 2 +- src/extensions/score_mounts/__init__.py | 16 ++------- src/extensions/score_mounts/_resolver.py | 11 +++++++ .../score_mounts/tests/test_resolver.py | 4 +-- src/incremental.py | 28 ++++++++++++++++ src/incremental_dirty_build_test.py | 33 ++++++++++++++++++- 6 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/BUILD b/src/BUILD index 62a531a12..aa9885ef8 100644 --- a/src/BUILD +++ b/src/BUILD @@ -38,7 +38,7 @@ score_pytest( "incremental_dirty_build_test.py", "incremental.py", ], - deps = all_requirements, + deps = all_requirements + ["//src/extensions/score_sphinx_bundle:score_sphinx_bundle"], pytest_config = "//:pyproject.toml", ) diff --git a/src/extensions/score_mounts/__init__.py b/src/extensions/score_mounts/__init__.py index b380c92c4..c9a3dc3af 100644 --- a/src/extensions/score_mounts/__init__.py +++ b/src/extensions/score_mounts/__init__.py @@ -32,9 +32,8 @@ from sphinx.util import logging from src.extensions.score_mounts._resolver import ( - MountsManifest, - MountSpec, load_mounts_manifest, + resolve_walk_dir, ) from src.helper_lib import find_ws_root, get_runfiles_dir @@ -64,17 +63,6 @@ def _read_manifest(config: Config): return load_mounts_manifest(manifest_path) -def _resolve_walk_dir( - manifest: MountsManifest, spec: MountSpec, ws_root: Path | None -) -> Path: - """Resolve one mount consistently for runfiles and sandboxed builds.""" - if spec.external and ws_root is not None: - return manifest.runtime_dir(spec) - if ws_root is not None: - return ws_root / spec.src_root - return Path(os.path.abspath(spec.src_root)) - - def _on_config_inited(app: Sphinx, config: Config) -> None: """Translate the Bazel manifest into ``sphinx_mounts`` runtime config. @@ -101,7 +89,7 @@ def _on_config_inited(app: Sphinx, config: Config) -> None: runtime_mounts: list[dict[str, object]] = [] for spec in manifest.mounts: - walk_dir = _resolve_walk_dir(manifest, spec, ws_root) + walk_dir = resolve_walk_dir(manifest, spec, ws_root) if not walk_dir.is_dir(): raise ValueError( "score_mounts: resolved mount dir does not exist: " diff --git a/src/extensions/score_mounts/_resolver.py b/src/extensions/score_mounts/_resolver.py index d01e114f1..e4787d407 100644 --- a/src/extensions/score_mounts/_resolver.py +++ b/src/extensions/score_mounts/_resolver.py @@ -93,3 +93,14 @@ def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: manifest_path=manifest_path, mounts=mounts, ) + + +def resolve_walk_dir( + manifest: MountsManifest, spec: MountSpec, ws_root: Path | None +) -> Path: + """Resolve a mount directory for either ``bazel run`` or a sandbox build.""" + if spec.external and ws_root is not None: + return manifest.runtime_dir(spec) + if ws_root is not None: + return ws_root / spec.src_root + return Path(os.path.abspath(spec.src_root)) diff --git a/src/extensions/score_mounts/tests/test_resolver.py b/src/extensions/score_mounts/tests/test_resolver.py index a1c3357d5..969b795d1 100644 --- a/src/extensions/score_mounts/tests/test_resolver.py +++ b/src/extensions/score_mounts/tests/test_resolver.py @@ -24,10 +24,10 @@ import pytest -from src.extensions.score_mounts.__init__ import _resolve_walk_dir from src.extensions.score_mounts._resolver import ( MountSpec, load_mounts_manifest, + resolve_walk_dir, ) @@ -177,6 +177,6 @@ def test_external_mount_uses_execroot_path_in_sandbox(tmp_path: Path, monkeypatc }, ) spec = load_mounts_manifest(manifest).mounts[0] - assert _resolve_walk_dir(load_mounts_manifest(manifest), spec, None) == ( + assert resolve_walk_dir(load_mounts_manifest(manifest), spec, None) == ( tmp_path / "external" / "score_process+" / "docs_as_mount" ) diff --git a/src/incremental.py b/src/incremental.py index fb2d5c505..edcd9c73c 100644 --- a/src/incremental.py +++ b/src/incremental.py @@ -26,6 +26,9 @@ main as sphinx_autobuild_main, # type: ignore[reportUnknownVariableType] # sphinx_autobuild doesn't provide complete type annotations ) +from src.extensions.score_mounts._resolver import load_mounts_manifest, resolve_walk_dir +from src.helper_lib import find_ws_root, get_runfiles_dir + logger = logging.getLogger(__name__) _MODULE_HASH_FILE = ".module_bazel_hash" @@ -71,6 +74,18 @@ def update_module_hash(build_dir: Path, sentinel_files: list[Path]) -> None: (build_dir / _MODULE_HASH_FILE).write_text(_compute_hash(sentinel_files)) +def _mounted_watch_dirs(manifest_path: Path, ws_root: Path | None) -> list[str]: + """Return the directories provided by docs bundles for ``sphinx-autobuild``. + + This deliberately uses the same manifest and path-resolution rules as the + ``score_mounts`` extension. The extension consumes the paths during a + Sphinx build; autobuild needs them separately to notice edits that happen + outside the primary Sphinx source directory. + """ + manifest = load_mounts_manifest(manifest_path) + return [str(resolve_walk_dir(manifest, spec, ws_root)) for spec in manifest.mounts] + + if __name__ == "__main__": parser = argparse.ArgumentParser() # Add debuging functionality @@ -159,6 +174,18 @@ def update_module_hash(build_dir: Path, sentinel_files: list[Path]) -> None: action = get_env("ACTION") if action == "live_preview": (build_dir / "score_source_code_linker_cache.json").unlink(missing_ok=True) + mounts_manifest = os.environ.get("MOUNTS_MANIFEST", "") + watch_arguments: list[str] = [] + if mounts_manifest: + # ``MOUNTS_MANIFEST`` is runfiles-relative under ``bazel run`` and + # an ordinary path for direct invocations, matching score_mounts. + manifest_path = ( + get_runfiles_dir() / mounts_manifest + if find_ws_root() + else Path(mounts_manifest) + ) + for watch_dir in _mounted_watch_dirs(manifest_path, find_ws_root()): + watch_arguments.extend(["--watch", watch_dir]) sphinx_autobuild_main( base_arguments + [ @@ -166,6 +193,7 @@ def update_module_hash(build_dir: Path, sentinel_files: list[Path]) -> None: "--define=skip_rescanning_via_source_code_linker=1", f"--port={args.port}", ] + + watch_arguments ) else: if action == "incremental": diff --git a/src/incremental_dirty_build_test.py b/src/incremental_dirty_build_test.py index 8c3252a9e..d59e6837a 100644 --- a/src/incremental_dirty_build_test.py +++ b/src/incremental_dirty_build_test.py @@ -13,11 +13,12 @@ # Unit Tests of incremental.py +import json from pathlib import Path from pyfakefs.fake_filesystem import FakeFilesystem as FFS -from incremental import clean_builddir_if_stale, update_module_hash +from incremental import _mounted_watch_dirs, clean_builddir_if_stale, update_module_hash _BUILD = Path("/build") _MODULE = Path("/MODULE.bazel") @@ -112,3 +113,33 @@ def test_missing_hash_file_triggers_clean(fs: FFS) -> None: clean_builddir_if_stale(_BUILD, [_MODULE]) assert not _BUILD.exists() + + +def test_mounted_watch_dirs_match_sphinx_mount_paths(tmp_path: Path) -> None: + manifest_path = tmp_path / "_mounts_manifest.json" + manifest_path.write_text( + json.dumps( + { + "mounts": [ + { + "src_root": "extensions/local/docs", + "runtime_path": "extensions/local/docs", + "mount_at": "local", + }, + { + "src_root": "external/vendor+/docs", + "runtime_path": "../vendor+/docs", + "mount_at": "external", + "external": True, + }, + ] + } + ), + encoding="utf-8", + ) + workspace = tmp_path / "workspace" + + assert _mounted_watch_dirs(manifest_path, workspace) == [ + str(workspace / "extensions/local/docs"), + str(manifest_path.parent.parent / "vendor+" / "docs"), + ] From 0edb95c467f3ebf51e8364494f5db98291055a01 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 11:58:01 +0200 Subject: [PATCH 07/19] security --- src/extensions/score_mounts/_resolver.py | 77 +++++++++++++++++-- .../score_mounts/tests/test_resolver.py | 38 +++++++++ 2 files changed, 107 insertions(+), 8 deletions(-) diff --git a/src/extensions/score_mounts/_resolver.py b/src/extensions/score_mounts/_resolver.py index e4787d407..0c4269233 100644 --- a/src/extensions/score_mounts/_resolver.py +++ b/src/extensions/score_mounts/_resolver.py @@ -58,14 +58,64 @@ def runtime_dir(self, spec: MountSpec) -> Path: of ``_main`` to the sibling repo dir in the runfiles tree — so no context-dependent mapping is needed here. """ - return Path(os.path.abspath(str(self.root / spec.runtime_path))) + base = self.root.parent if spec.external else self.root + return _resolve_below(base, spec.runtime_path) + + +def _validate_relative_path(value: object, field_name: str, *, external: bool) -> str: + """Apply a defense-in-depth check before a manifest path reaches the filesystem. + + Internal paths are relative to the manifest directory. External runfiles use + Bazel's ``..//...`` spelling and are relative to its parent. + + This is intentionally not part of the normal bundle semantics: Bazel's + Starlark rules already produce paths in this format. The validation only + protects direct or tampered manifest input from turning into filesystem + traversal when this Python extension reads it. + """ + if not isinstance(value, str) or not value: + raise ValueError(f"{field_name} must be a non-empty string") + + parts = value.split("/") + if external: + if len(parts) < 3 or parts[0] != "..": + raise ValueError( + f"external {field_name} must start with '..//': {value!r}" + ) + parts = parts[1:] + if any(not part or part in {".", ".."} for part in parts): + raise ValueError( + f"{field_name} must not contain empty, '.' or '..' segments: {value!r}" + ) + return value + + +def _resolve_below(base: Path, relative_path: str) -> Path: + """Keep a validated path inside its Bazel-controlled root (security boundary).""" + resolved_base = Path(os.path.abspath(base)) + # External runtime paths begin with '../' but are resolved against the + # manifest's parent, so their repository directory remains below this base. + relative = relative_path.removeprefix("../") + # Do not resolve symlinks here: test runfiles and Bazel workspaces commonly + # expose in-tree sources through symlinks. The manifest validation above + # rejects traversal segments, while ``abspath`` normalizes the lexical path. + resolved_path = Path(os.path.abspath(resolved_base / relative)) + try: + resolved_path.relative_to(resolved_base) + except ValueError as error: + raise ValueError( + f"mount path escapes its expected root: {relative_path!r}" + ) from error + return resolved_path def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: """Read the manifest JSON at ``manifest_path`` (an already-resolved path). Context-dependent path resolution (runfiles under ``bazel run`` vs. the - exec root in a sandbox) is the caller's responsibility. + exec root in a sandbox) is the caller's responsibility. The manifest is + normally generated by Bazel, so its schema checks below are deliberately + defense-in-depth rather than a functional requirement for bundle loading. """ manifest_path = Path(manifest_path) data = json.loads(manifest_path.read_text(encoding="utf-8")) @@ -75,18 +125,29 @@ def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: ) mounts: list[MountSpec] = [] for entry in data.get("mounts", []): - if "src_root" not in entry or "mount_at" not in entry: + if ( + not isinstance(entry, dict) + or "src_root" not in entry + or "mount_at" not in entry + ): raise ValueError( f"mounts manifest entry missing 'src_root'/'mount_at': {entry!r}" ) + external = entry.get("external", False) + if not isinstance(external, bool): + raise ValueError(f"mounts manifest 'external' must be boolean: {entry!r}") mounts.append( MountSpec( - src_root=entry["src_root"], - runtime_path=entry.get("runtime_path", ""), + src_root=_validate_relative_path( + entry["src_root"], "src_root", external=False + ), + runtime_path=_validate_relative_path( + entry.get("runtime_path", ""), "runtime_path", external=external + ), mount_at=entry["mount_at"], attach_to=entry.get("attach_to") or None, entry_doc=entry.get("entry_doc") or "index", - external=entry.get("external", False), + external=external, ) ) return MountsManifest( @@ -102,5 +163,5 @@ def resolve_walk_dir( if spec.external and ws_root is not None: return manifest.runtime_dir(spec) if ws_root is not None: - return ws_root / spec.src_root - return Path(os.path.abspath(spec.src_root)) + return _resolve_below(ws_root, spec.src_root) + return _resolve_below(Path.cwd(), spec.src_root) diff --git a/src/extensions/score_mounts/tests/test_resolver.py b/src/extensions/score_mounts/tests/test_resolver.py index 969b795d1..d148aa59e 100644 --- a/src/extensions/score_mounts/tests/test_resolver.py +++ b/src/extensions/score_mounts/tests/test_resolver.py @@ -136,6 +136,44 @@ def test_load_non_object_raises(tmp_path: Path): load_mounts_manifest(str(manifest)) +@pytest.mark.parametrize( + ("entry", "message"), + [ + ( + { + "src_root": "../outside", + "runtime_path": "docs", + "mount_at": "x", + }, + "src_root must not contain", + ), + ( + { + "src_root": "src/docs", + "runtime_path": "../../outside", + "mount_at": "x", + }, + "runtime_path must not contain", + ), + ( + { + "src_root": "src/docs", + "runtime_path": "docs", + "mount_at": "x", + "external": True, + }, + "external runtime_path must start", + ), + ], +) +def test_load_rejects_paths_that_escape_bazel_roots( + tmp_path: Path, entry: dict[str, object], message: str +): + manifest = _write_manifest(tmp_path, {"mounts": [entry]}) + with pytest.raises(ValueError, match=message): + load_mounts_manifest(manifest) + + def test_runtime_dir_external_path_resolves_relative_to_manifest(tmp_path: Path): # Under `bazel run`, the '../+/...' short_path resolves natively in # the runfiles tree. runtime_dir must NOT remap '../' to 'external/'. From 820b6b7f3f7ccff3969595572deed4a89f286112 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 12:00:27 +0200 Subject: [PATCH 08/19] install dot pre test --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8773ef2dc..4d4bdb97c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,6 +26,12 @@ jobs: with: egress-policy: audit + - name: Install Graphviz + uses: eclipse-score/apt-install@main + with: + packages: graphviz + cache: false + - name: Checkout repository (Handle all events) uses: actions/checkout@v4.2.2 with: From 39c10a463962a57e51cb0267a66b5228b2a8ab32 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 12:11:27 +0200 Subject: [PATCH 09/19] security 2 --- src/extensions/score_mounts/__init__.py | 19 +++++++++++++++- src/extensions/score_mounts/_resolver.py | 17 +++++++------- src/tests/docs_e2e/support.py | 28 ++++++++++++++++++------ 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/extensions/score_mounts/__init__.py b/src/extensions/score_mounts/__init__.py index c9a3dc3af..ce49cc566 100644 --- a/src/extensions/score_mounts/__init__.py +++ b/src/extensions/score_mounts/__init__.py @@ -40,6 +40,22 @@ logger = logging.getLogger(__name__) +def _manifest_path_below(root: Path, raw_path: str) -> Path: + """Normalize a Bazel-provided manifest path inside its expected root. + + ``raw_path`` normally comes from Bazel expansion and is already valid. The + containment check is defense-in-depth for direct configuration or a tampered + environment, and also makes the filesystem trust boundary explicit. + """ + # CodeQL recognizes this ``normpath``/``startswith`` pair as the relevant + # normalization and root-containment guard for path-injection analysis. + normalized_root = os.path.normpath(os.path.abspath(root)) + normalized_path = os.path.normpath(os.path.join(normalized_root, raw_path)) + if not normalized_path.startswith(normalized_root + os.sep): + raise ValueError(f"score_mounts: manifest path escapes its root: {raw_path!r}") + return Path(normalized_path) + + def _read_manifest(config: Config): """Locate and load the mounts manifest, or return ``None`` when unset. @@ -58,7 +74,8 @@ def _read_manifest(config: Config): # ``bazel run`` passes an rlocation-relative path; ``sphinx_docs`` in a # sandbox passes its execroot-relative ``$(location)`` path directly. - manifest_path = get_runfiles_dir() / raw if find_ws_root() else Path(raw) + manifest_root = get_runfiles_dir() if find_ws_root() else Path.cwd() + manifest_path = _manifest_path_below(manifest_root, raw) return load_mounts_manifest(manifest_path) diff --git a/src/extensions/score_mounts/_resolver.py b/src/extensions/score_mounts/_resolver.py index 0c4269233..c161109d2 100644 --- a/src/extensions/score_mounts/_resolver.py +++ b/src/extensions/score_mounts/_resolver.py @@ -92,21 +92,20 @@ def _validate_relative_path(value: object, field_name: str, *, external: bool) - def _resolve_below(base: Path, relative_path: str) -> Path: """Keep a validated path inside its Bazel-controlled root (security boundary).""" - resolved_base = Path(os.path.abspath(base)) + # Keep this as ``os.path.normpath`` plus ``startswith`` instead of the + # equivalent pathlib operations: CodeQL models this pair as a normalized, + # root-confined path for its path-injection analysis. + resolved_base = os.path.normpath(os.path.abspath(base)) # External runtime paths begin with '../' but are resolved against the # manifest's parent, so their repository directory remains below this base. relative = relative_path.removeprefix("../") # Do not resolve symlinks here: test runfiles and Bazel workspaces commonly # expose in-tree sources through symlinks. The manifest validation above # rejects traversal segments, while ``abspath`` normalizes the lexical path. - resolved_path = Path(os.path.abspath(resolved_base / relative)) - try: - resolved_path.relative_to(resolved_base) - except ValueError as error: - raise ValueError( - f"mount path escapes its expected root: {relative_path!r}" - ) from error - return resolved_path + resolved_path = os.path.normpath(os.path.join(resolved_base, relative)) + if not resolved_path.startswith(resolved_base + os.sep): + raise ValueError(f"mount path escapes its expected root: {relative_path!r}") + return Path(resolved_path) def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: diff --git a/src/tests/docs_e2e/support.py b/src/tests/docs_e2e/support.py index ccead1eb3..a84338d17 100644 --- a/src/tests/docs_e2e/support.py +++ b/src/tests/docs_e2e/support.py @@ -19,11 +19,20 @@ def runfile(path_env: str) -> Path: - """Resolve a runfiles-relative path passed through a test environment variable.""" - return ( - Path(os.environ["TEST_SRCDIR"]) - / os.environ["TEST_WORKSPACE"] - / os.environ[path_env] + """Resolve a test runfile without allowing its env value to escape runfiles.""" + runfiles_root = _runfiles_root() + candidate = os.path.normpath(os.path.join(runfiles_root, os.environ[path_env])) + if not candidate.startswith(str(runfiles_root) + os.sep): + raise ValueError(f"runfile path escapes TEST_SRCDIR: {candidate!r}") + return Path(candidate) + + +def _runfiles_root() -> Path: + """Return this test's Bazel-managed runfiles root.""" + return Path( + os.path.normpath( + os.path.join(os.environ["TEST_SRCDIR"], os.environ["TEST_WORKSPACE"]) + ) ) @@ -41,7 +50,7 @@ def run_docs_build( the small writable equivalent needed by a Bazel test while preserving the runfiles layout used to resolve in-tree mount sources. """ - runfiles_workspace = Path(os.environ["TEST_SRCDIR"]) / os.environ["TEST_WORKSPACE"] + runfiles_workspace = _runfiles_root() (tmp_path / "src").symlink_to(runfiles_workspace / "src", target_is_directory=True) copied_source_dir = tmp_path / "docs" @@ -51,7 +60,12 @@ def run_docs_build( env = os.environ.copy() env["SOURCE_DIRECTORY"] = str(copied_source_dir) - env["MOUNTS_MANIFEST"] = str(mounts_manifest) if mounts_manifest else "" + if mounts_manifest: + copied_manifest = tmp_path / "_mounts_manifest.json" + shutil.copyfile(mounts_manifest, copied_manifest) + env["MOUNTS_MANIFEST"] = copied_manifest.name + else: + env["MOUNTS_MANIFEST"] = "" env["DATA"] = "[]" env["ACTION"] = "incremental" env["SCORE_SOURCELINKS"] = str(sourcelinks) From 37e121b388e7ec9f6806eb4b2cebd4c5c252e22b Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 13:05:30 +0200 Subject: [PATCH 10/19] last minute fixes --- .github/workflows/test.yml | 9 ++++ src/extensions/score_mounts/__init__.py | 3 +- src/extensions/score_mounts/_resolver.py | 38 +++++--------- .../score_mounts/docs/concept/index.rst | 23 ++++---- .../score_mounts/docs/howto/index.rst | 7 +-- .../score_mounts/tests/test_resolver.py | 52 ++++++------------- src/incremental.py | 16 ++++-- src/incremental_dirty_build_test.py | 5 +- 8 files changed, 72 insertions(+), 81 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3cbe63f67..6ca6b1299 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,6 +55,15 @@ jobs: - name: Run bazel test targets run: bazel test --lockfile_mode=error //... --build_tests_only + - name: Verify mount conflicts are rejected + run: | + ! bazel build --lockfile_mode=error //src/tests/mounts_conflict:bad + ! bazel build --lockfile_mode=error //src/tests/mounts_conflict:bad_attach_to + ! bazel build --lockfile_mode=error //src/tests/mounts_conflict:bad_entry_doc + + - name: Build an external documentation bundle from runfiles + run: bazel run --lockfile_mode=error //src/tests/mounts_external:docs + - name: Build all targets # Skip tests, since some are non buildable negative tests run: bazel build --lockfile_mode=error //... -- -//src/tests/... diff --git a/src/extensions/score_mounts/__init__.py b/src/extensions/score_mounts/__init__.py index ce49cc566..7a258beeb 100644 --- a/src/extensions/score_mounts/__init__.py +++ b/src/extensions/score_mounts/__init__.py @@ -103,10 +103,11 @@ def _on_config_inited(app: Sphinx, config: Config) -> None: # bazel-out/ and is NOT colocated with them, so src_root is resolved # against the exec root (the sphinx action's cwd), not the manifest. ws_root = find_ws_root() + runfiles_dir = get_runfiles_dir() if ws_root is not None else None runtime_mounts: list[dict[str, object]] = [] for spec in manifest.mounts: - walk_dir = resolve_walk_dir(manifest, spec, ws_root) + walk_dir = resolve_walk_dir(manifest, spec, ws_root, runfiles_dir) if not walk_dir.is_dir(): raise ValueError( "score_mounts: resolved mount dir does not exist: " diff --git a/src/extensions/score_mounts/_resolver.py b/src/extensions/score_mounts/_resolver.py index c161109d2..aeb31f602 100644 --- a/src/extensions/score_mounts/_resolver.py +++ b/src/extensions/score_mounts/_resolver.py @@ -15,9 +15,8 @@ All mount paths are authored by Bazel (where ``File`` objects have real paths) and shipped in a small JSON manifest. This module only *reads* that manifest — it -performs no label-to-path reconstruction. Every path stored in the manifest is a -Bazel ``short_path`` and therefore resolves relative to the manifest's own -directory.""" +performs no label-to-path reconstruction. The caller provides the Bazel execution +context needed to resolve the manifest's ``short_path`` values safely.""" from __future__ import annotations @@ -39,28 +38,8 @@ class MountSpec: @dataclass(frozen=True) class MountsManifest: - manifest_path: Path mounts: list[MountSpec] - @property - def root(self) -> Path: - """Directory the manifest lives in — the base for its short_path entries.""" - return self.manifest_path.parent - - def runtime_dir(self, spec: MountSpec) -> Path: - """Absolute path of a bundle's staged mount-root directory. - - ``runtime_path`` is a Bazel ``short_path`` (the bundle's source - directory, staged in place — not a copy) and resolves relative to the - manifest's own directory. Main-repo sources resolve the same way inside a - sandbox build. External sources (``../+/X``) are only ever walked - under ``bazel run`` with an external bundle, where ``../`` steps out - of ``_main`` to the sibling repo dir in the runfiles tree — so no - context-dependent mapping is needed here. - """ - base = self.root.parent if spec.external else self.root - return _resolve_below(base, spec.runtime_path) - def _validate_relative_path(value: object, field_name: str, *, external: bool) -> str: """Apply a defense-in-depth check before a manifest path reaches the filesystem. @@ -150,17 +129,24 @@ def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: ) ) return MountsManifest( - manifest_path=manifest_path, mounts=mounts, ) def resolve_walk_dir( - manifest: MountsManifest, spec: MountSpec, ws_root: Path | None + manifest: MountsManifest, + spec: MountSpec, + ws_root: Path | None, + runfiles_dir: Path | None = None, ) -> Path: """Resolve a mount directory for either ``bazel run`` or a sandbox build.""" if spec.external and ws_root is not None: - return manifest.runtime_dir(spec) + if runfiles_dir is None: + raise ValueError("external mounts under bazel run require RUNFILES_DIR") + # External short paths begin with ``../+`` relative to the + # runfiles ``_main`` directory, not relative to a manifest nested in a + # Bazel package. Prefixing ``_main`` preserves that Bazel convention. + return _resolve_below(runfiles_dir, "_main/" + spec.runtime_path) if ws_root is not None: return _resolve_below(ws_root, spec.src_root) return _resolve_below(Path.cwd(), spec.src_root) diff --git a/src/extensions/score_mounts/docs/concept/index.rst b/src/extensions/score_mounts/docs/concept/index.rst index afabbe947..57924e01d 100644 --- a/src/extensions/score_mounts/docs/concept/index.rst +++ b/src/extensions/score_mounts/docs/concept/index.rst @@ -83,18 +83,22 @@ original source directory, differing only in *where* that directory is staged: .. code-block:: python if spec.external and ws_root: # bazel run: sibling repo in the runfiles tree - walk_dir = manifest.runtime_dir(spec) + walk_dir = _resolve_below(runfiles_dir, "_main/" + spec.runtime_path) elif ws_root is not None: # bazel run, in-tree: the live workspace source - walk_dir = ws_root / spec.src_root + walk_dir = _resolve_below(ws_root, spec.src_root) else: # sandbox, in-tree: source staged at the exec root - walk_dir = Path(os.path.abspath(spec.src_root)) + walk_dir = _resolve_below(Path.cwd(), spec.src_root) * ``ws_root`` is only set under ``bazel run`` (it points at ``BUILD_WORKSPACE_DIRECTORY``); in a sandboxed ``bazel build`` it is ``None``. * ``external`` marks a bundle whose sources come from another Bazel module. -* The manifest (a ``bazel-out`` artifact) is colocated with the sources only in the - runfiles tree; in a sandbox the in-tree sources are resolved against the exec - root instead, which is why the branch splits three ways. +* External runtime paths are anchored at the runfiles root (their Bazel + ``../+`` prefix starts below ``_main``). In a sandbox the in-tree + sources are instead resolved against the exec root, which is why the branch + splits three ways. +* ``_resolve_below`` normalizes the path and rejects traversal outside that + Bazel-controlled root. It is a runtime security boundary; valid manifests + emitted by the Bazel rule already satisfy it. So the mount walks: @@ -151,10 +155,9 @@ cannot select the payload itself: #. The content entry then carries that directory ``File`` (alongside, or instead of, ``runtime_path``); the emitted ``runtime_path`` points at it. #. A materialized directory is a ``bazel-out`` artifact colocated with the manifest - in every context, so it resolves via ``manifest.runtime_dir`` — the same - manifest-relative rule the external branch already uses. That sidesteps the - exec-root vs. runfiles split the in-tree source walk has to handle, which is - the resolver-simplicity that materialization used to buy. + in every context. A future resolver can therefore resolve it relative to that + manifest without the exec-root versus runfiles split required by the current + source-directory walk. **Provider contract.** A content entry must let the runtime resolve a directory to walk. Today that is ``runtime_path`` (a source-directory ``short_path``) plus diff --git a/src/extensions/score_mounts/docs/howto/index.rst b/src/extensions/score_mounts/docs/howto/index.rst index 649cf4b61..189f1734c 100644 --- a/src/extensions/score_mounts/docs/howto/index.rst +++ b/src/extensions/score_mounts/docs/howto/index.rst @@ -238,11 +238,12 @@ Relevant targets wired by the ``docs()`` macro: * ``bazel run //:docs`` — incremental HTML build for day-to-day editing; outputs to ``_build/``. Resolves mounts via runfiles - (fast, dev-local). + (fast, dev-local) and refreshes the IDE-facing repo-root + ``ubproject.toml``. * ``bazel run //:docs_check`` — same as above but with the ``check`` - action; also regenerates the repo-root ``ubproject.toml``. Run this - after editing the mount list or to refresh the IDE-facing TOML. + action; it also refreshes the repo-root ``ubproject.toml``. Run either + command after editing the mount list to refresh the IDE-facing TOML. * ``bazel build //:needs_json`` — sandboxed needs-only build. Verifies that mounted bundles resolve correctly without ``bazel run``. diff --git a/src/extensions/score_mounts/tests/test_resolver.py b/src/extensions/score_mounts/tests/test_resolver.py index d148aa59e..c09d75bbd 100644 --- a/src/extensions/score_mounts/tests/test_resolver.py +++ b/src/extensions/score_mounts/tests/test_resolver.py @@ -13,13 +13,11 @@ """Unit tests for the mounts manifest loader (``_resolver``). These cover the pure parsing layer only: reading the JSON manifest into -``MountSpec`` objects, applying defaults, resolving ``runtime_dir`` relative -to the manifest, and rejecting malformed +``MountSpec`` objects, applying defaults, and rejecting malformed input. Context-dependent path resolution (runfiles vs. exec root) lives in the extension's ``__init__`` and is exercised via the consumer tests instead.""" import json -import os from pathlib import Path import pytest @@ -32,6 +30,7 @@ def _write_manifest(tmp_path: Path, payload: dict) -> Path: + tmp_path.mkdir(parents=True, exist_ok=True) manifest = tmp_path / "_mounts_manifest.json" manifest.write_text(json.dumps(payload), encoding="utf-8") return manifest @@ -106,23 +105,6 @@ def test_external_mount_keeps_execroot_and_runfiles_locations(tmp_path: Path): assert specs[1].external is True -def test_runtime_dir_resolves_next_to_manifest(tmp_path: Path): - manifest = _write_manifest( - tmp_path, - { - "mounts": [ - { - "src_root": "src/docs", - "runtime_path": "src/docs_dir", - "mount_at": "x", - } - ], - }, - ) - result = load_mounts_manifest(str(manifest)) - assert result.runtime_dir(result.mounts[0]) == tmp_path / "src" / "docs_dir" - - def test_load_missing_required_key_raises(tmp_path: Path): manifest = _write_manifest(tmp_path, {"mounts": [{"runtime_path": "src/docs_dir"}]}) with pytest.raises(ValueError, match="missing 'src_root'/'mount_at'"): @@ -174,9 +156,8 @@ def test_load_rejects_paths_that_escape_bazel_roots( load_mounts_manifest(manifest) -def test_runtime_dir_external_path_resolves_relative_to_manifest(tmp_path: Path): - # Under `bazel run`, the '../+/...' short_path resolves natively in - # the runfiles tree. runtime_dir must NOT remap '../' to 'external/'. +def test_external_mount_uses_execroot_path_in_sandbox(tmp_path: Path, monkeypatch): + monkeypatch.chdir(tmp_path) manifest = _write_manifest( tmp_path, { @@ -187,22 +168,18 @@ def test_runtime_dir_external_path_resolves_relative_to_manifest(tmp_path: Path) "mount_at": "process", "external": True, } - ], + ] }, ) - result = load_mounts_manifest(str(manifest)) - # runtime_dir uses os.path.abspath (lexical, no symlink resolution); mirror - # that here so the assertion never diverges on a symlinked tmp dir. - expected = Path( - os.path.abspath(tmp_path / ".." / "score_process+" / "docs_as_mount") + spec = load_mounts_manifest(manifest).mounts[0] + assert resolve_walk_dir(load_mounts_manifest(manifest), spec, None) == ( + tmp_path / "external" / "score_process+" / "docs_as_mount" ) - assert result.runtime_dir(result.mounts[0]) == expected -def test_external_mount_uses_execroot_path_in_sandbox(tmp_path: Path, monkeypatch): - monkeypatch.chdir(tmp_path) +def test_external_mount_uses_runfiles_root_under_bazel_run(tmp_path: Path): manifest = _write_manifest( - tmp_path, + tmp_path / "_main" / "package", { "mounts": [ { @@ -215,6 +192,9 @@ def test_external_mount_uses_execroot_path_in_sandbox(tmp_path: Path, monkeypatc }, ) spec = load_mounts_manifest(manifest).mounts[0] - assert resolve_walk_dir(load_mounts_manifest(manifest), spec, None) == ( - tmp_path / "external" / "score_process+" / "docs_as_mount" - ) + assert resolve_walk_dir( + load_mounts_manifest(manifest), + spec, + tmp_path / "workspace", + tmp_path, + ) == (tmp_path / "score_process+" / "docs_as_mount") diff --git a/src/incremental.py b/src/incremental.py index edcd9c73c..e52937b03 100644 --- a/src/incremental.py +++ b/src/incremental.py @@ -74,7 +74,9 @@ def update_module_hash(build_dir: Path, sentinel_files: list[Path]) -> None: (build_dir / _MODULE_HASH_FILE).write_text(_compute_hash(sentinel_files)) -def _mounted_watch_dirs(manifest_path: Path, ws_root: Path | None) -> list[str]: +def _mounted_watch_dirs( + manifest_path: Path, ws_root: Path | None, runfiles_dir: Path | None = None +) -> list[str]: """Return the directories provided by docs bundles for ``sphinx-autobuild``. This deliberately uses the same manifest and path-resolution rules as the @@ -83,7 +85,10 @@ def _mounted_watch_dirs(manifest_path: Path, ws_root: Path | None) -> list[str]: outside the primary Sphinx source directory. """ manifest = load_mounts_manifest(manifest_path) - return [str(resolve_walk_dir(manifest, spec, ws_root)) for spec in manifest.mounts] + return [ + str(resolve_walk_dir(manifest, spec, ws_root, runfiles_dir)) + for spec in manifest.mounts + ] if __name__ == "__main__": @@ -184,7 +189,12 @@ def _mounted_watch_dirs(manifest_path: Path, ws_root: Path | None) -> list[str]: if find_ws_root() else Path(mounts_manifest) ) - for watch_dir in _mounted_watch_dirs(manifest_path, find_ws_root()): + ws_root = find_ws_root() + for watch_dir in _mounted_watch_dirs( + manifest_path, + ws_root, + get_runfiles_dir() if ws_root is not None else None, + ): watch_arguments.extend(["--watch", watch_dir]) sphinx_autobuild_main( base_arguments diff --git a/src/incremental_dirty_build_test.py b/src/incremental_dirty_build_test.py index d59e6837a..9e9ecd01a 100644 --- a/src/incremental_dirty_build_test.py +++ b/src/incremental_dirty_build_test.py @@ -138,8 +138,9 @@ def test_mounted_watch_dirs_match_sphinx_mount_paths(tmp_path: Path) -> None: encoding="utf-8", ) workspace = tmp_path / "workspace" + runfiles_dir = tmp_path / "runfiles" - assert _mounted_watch_dirs(manifest_path, workspace) == [ + assert _mounted_watch_dirs(manifest_path, workspace, runfiles_dir) == [ str(workspace / "extensions/local/docs"), - str(manifest_path.parent.parent / "vendor+" / "docs"), + str(runfiles_dir / "vendor+" / "docs"), ] From ef8b35d975034df40e2b2c7e6ade379d434e0c99 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 16:04:38 +0200 Subject: [PATCH 11/19] refine mounts documentation --- BUILD | 4 + src/extensions/score_mounts/__init__.py | 19 +- src/extensions/score_mounts/_resolver.py | 75 +--- src/extensions/score_mounts/docs/BUILD | 6 + .../score_mounts/docs/concept/index.rst | 316 +++++------------ .../score_mounts/docs/howto/index.rst | 321 +++++------------- .../score_mounts/docs/internals/index.rst | 112 ++++++ .../score_mounts/tests/test_resolver.py | 38 --- src/extensions/score_sync_toml/__init__.py | 2 +- src/tests/docs_e2e/support.py | 28 +- 10 files changed, 294 insertions(+), 627 deletions(-) create mode 100644 src/extensions/score_mounts/docs/internals/index.rst diff --git a/BUILD b/BUILD index 20a0fef50..586a4595f 100644 --- a/BUILD +++ b/BUILD @@ -29,6 +29,10 @@ docs( "bundle": "//src/extensions/score_mounts/docs:howto", "mount_at": "how-to/mounts", }, + { + "bundle": "//src/extensions/score_mounts/docs:internals", + "mount_at": "internals/extensions/mounts", + }, ], scan_code = [ "//scripts_bazel:sources", diff --git a/src/extensions/score_mounts/__init__.py b/src/extensions/score_mounts/__init__.py index 7a258beeb..0aa79255b 100644 --- a/src/extensions/score_mounts/__init__.py +++ b/src/extensions/score_mounts/__init__.py @@ -40,22 +40,6 @@ logger = logging.getLogger(__name__) -def _manifest_path_below(root: Path, raw_path: str) -> Path: - """Normalize a Bazel-provided manifest path inside its expected root. - - ``raw_path`` normally comes from Bazel expansion and is already valid. The - containment check is defense-in-depth for direct configuration or a tampered - environment, and also makes the filesystem trust boundary explicit. - """ - # CodeQL recognizes this ``normpath``/``startswith`` pair as the relevant - # normalization and root-containment guard for path-injection analysis. - normalized_root = os.path.normpath(os.path.abspath(root)) - normalized_path = os.path.normpath(os.path.join(normalized_root, raw_path)) - if not normalized_path.startswith(normalized_root + os.sep): - raise ValueError(f"score_mounts: manifest path escapes its root: {raw_path!r}") - return Path(normalized_path) - - def _read_manifest(config: Config): """Locate and load the mounts manifest, or return ``None`` when unset. @@ -74,8 +58,7 @@ def _read_manifest(config: Config): # ``bazel run`` passes an rlocation-relative path; ``sphinx_docs`` in a # sandbox passes its execroot-relative ``$(location)`` path directly. - manifest_root = get_runfiles_dir() if find_ws_root() else Path.cwd() - manifest_path = _manifest_path_below(manifest_root, raw) + manifest_path = get_runfiles_dir() / raw if find_ws_root() else Path(raw) return load_mounts_manifest(manifest_path) diff --git a/src/extensions/score_mounts/_resolver.py b/src/extensions/score_mounts/_resolver.py index aeb31f602..786af1b1b 100644 --- a/src/extensions/score_mounts/_resolver.py +++ b/src/extensions/score_mounts/_resolver.py @@ -41,59 +41,11 @@ class MountsManifest: mounts: list[MountSpec] -def _validate_relative_path(value: object, field_name: str, *, external: bool) -> str: - """Apply a defense-in-depth check before a manifest path reaches the filesystem. - - Internal paths are relative to the manifest directory. External runfiles use - Bazel's ``..//...`` spelling and are relative to its parent. - - This is intentionally not part of the normal bundle semantics: Bazel's - Starlark rules already produce paths in this format. The validation only - protects direct or tampered manifest input from turning into filesystem - traversal when this Python extension reads it. - """ - if not isinstance(value, str) or not value: - raise ValueError(f"{field_name} must be a non-empty string") - - parts = value.split("/") - if external: - if len(parts) < 3 or parts[0] != "..": - raise ValueError( - f"external {field_name} must start with '..//': {value!r}" - ) - parts = parts[1:] - if any(not part or part in {".", ".."} for part in parts): - raise ValueError( - f"{field_name} must not contain empty, '.' or '..' segments: {value!r}" - ) - return value - - -def _resolve_below(base: Path, relative_path: str) -> Path: - """Keep a validated path inside its Bazel-controlled root (security boundary).""" - # Keep this as ``os.path.normpath`` plus ``startswith`` instead of the - # equivalent pathlib operations: CodeQL models this pair as a normalized, - # root-confined path for its path-injection analysis. - resolved_base = os.path.normpath(os.path.abspath(base)) - # External runtime paths begin with '../' but are resolved against the - # manifest's parent, so their repository directory remains below this base. - relative = relative_path.removeprefix("../") - # Do not resolve symlinks here: test runfiles and Bazel workspaces commonly - # expose in-tree sources through symlinks. The manifest validation above - # rejects traversal segments, while ``abspath`` normalizes the lexical path. - resolved_path = os.path.normpath(os.path.join(resolved_base, relative)) - if not resolved_path.startswith(resolved_base + os.sep): - raise ValueError(f"mount path escapes its expected root: {relative_path!r}") - return Path(resolved_path) - - def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: """Read the manifest JSON at ``manifest_path`` (an already-resolved path). Context-dependent path resolution (runfiles under ``bazel run`` vs. the - exec root in a sandbox) is the caller's responsibility. The manifest is - normally generated by Bazel, so its schema checks below are deliberately - defense-in-depth rather than a functional requirement for bundle loading. + exec root in a sandbox) is the caller's responsibility. """ manifest_path = Path(manifest_path) data = json.loads(manifest_path.read_text(encoding="utf-8")) @@ -103,29 +55,18 @@ def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: ) mounts: list[MountSpec] = [] for entry in data.get("mounts", []): - if ( - not isinstance(entry, dict) - or "src_root" not in entry - or "mount_at" not in entry - ): + if "src_root" not in entry or "mount_at" not in entry: raise ValueError( f"mounts manifest entry missing 'src_root'/'mount_at': {entry!r}" ) - external = entry.get("external", False) - if not isinstance(external, bool): - raise ValueError(f"mounts manifest 'external' must be boolean: {entry!r}") mounts.append( MountSpec( - src_root=_validate_relative_path( - entry["src_root"], "src_root", external=False - ), - runtime_path=_validate_relative_path( - entry.get("runtime_path", ""), "runtime_path", external=external - ), + src_root=entry["src_root"], + runtime_path=entry.get("runtime_path", ""), mount_at=entry["mount_at"], attach_to=entry.get("attach_to") or None, entry_doc=entry.get("entry_doc") or "index", - external=external, + external=entry.get("external", False), ) ) return MountsManifest( @@ -146,7 +87,7 @@ def resolve_walk_dir( # External short paths begin with ``../+`` relative to the # runfiles ``_main`` directory, not relative to a manifest nested in a # Bazel package. Prefixing ``_main`` preserves that Bazel convention. - return _resolve_below(runfiles_dir, "_main/" + spec.runtime_path) + return Path(os.path.abspath(runfiles_dir / "_main" / spec.runtime_path)) if ws_root is not None: - return _resolve_below(ws_root, spec.src_root) - return _resolve_below(Path.cwd(), spec.src_root) + return ws_root / spec.src_root + return Path.cwd() / spec.src_root diff --git a/src/extensions/score_mounts/docs/BUILD b/src/extensions/score_mounts/docs/BUILD index 561b235de..6e17c274b 100644 --- a/src/extensions/score_mounts/docs/BUILD +++ b/src/extensions/score_mounts/docs/BUILD @@ -28,3 +28,9 @@ docs_bundle( source_dir = "howto", visibility = ["//visibility:public"], ) + +docs_bundle( + name = "internals", + source_dir = "internals", + visibility = ["//visibility:public"], +) diff --git a/src/extensions/score_mounts/docs/concept/index.rst b/src/extensions/score_mounts/docs/concept/index.rst index 57924e01d..5efa8d76b 100644 --- a/src/extensions/score_mounts/docs/concept/index.rst +++ b/src/extensions/score_mounts/docs/concept/index.rst @@ -14,253 +14,89 @@ .. _docs_concept_mounts: -====================================== -Mounts: which directory Sphinx walks -====================================== +Documentation bundles and mounts +================================ -Mounting another documentation bundle into this project's Sphinx tree grafts the -*contents of one directory* into the doc tree. This page explains which directory -that is — and why it is always the bundle's **original files**, never a copy. +Documentation does not always live in a project's ``docs/`` directory. A +library may keep its reference material next to its source code, and a Bazel +module may want to contribute its own documentation to another module's site. +Bundles and mounts make that content part of one Sphinx project without copying +it. -For the user-facing "how do I mount another module" guide, see +For the commands and BUILD declarations, see :ref:`howto_mount_external_sources`. -Mounting overlays a directory -============================= +Bundle, mount, and placement +---------------------------- -**Mounting** is the placement/overlay operation: at runtime ``sphinx_mounts`` -takes the contents of one directory and grafts them into the doc tree at the -bundle's ``mount_at`` (optionally attached under ``attach_to``). A ``docs_bundle`` -with a ``source_dir`` contributes exactly that directory. +A **bundle** is a named, mountable documentation directory. It owns its +content, but not its position in a consuming documentation site. -A ``source_dir`` bundle **is already a mount-ready directory**: its files sit on -disk under ``source_dir`` in the layout they should mount in. So the bundle makes -no copy — it only records the *mount root* (the ``source_dir`` path as a Bazel -``short_path``) and lets ``sphinx_mounts`` walk the real files. Sphinx therefore -always operates on the originals, which keeps live preview and jump-to-definition -pointing at editable source. +A **mount** makes a bundle visible in a host site. The host chooses the +**placement** with ``mount_at`` and can optionally attach the bundle's entry +document to a host toctree with ``attach_to``. This separation lets different +projects reuse the same bundle at different locations. -Transitive composition -======================= +Bundles are read from their original source directories. Consequently, an +in-repository bundle remains editable and IDE navigation reaches its real +source files rather than generated copies. -A ``docs_bundle`` exposes its content through the ``DocsBundleInfo`` provider, -whose ``entries`` field is an **ordered list** of placed content entries. -When a bundle composes children (via ``bundles = [...]``), each child's entries -are appended in declaration order and re-based under the child's ``mount_at``. -A mounter therefore sees one flat, ordered list regardless of how deeply the -graph nests. - -**Placement composes by prefix-stacking.** A bundle is placement-free; its own -root takes the placement it is mounted with, and every nested entry gets the -enclosing ``mount_at`` (and ``attach_to``) prefixed onto its own. A child mounted -at ``mount_at = "child"`` inside a parent that is later mounted at -``internals/code_docs`` resolves to ``internals/code_docs/child``. Resolution is -independent of nesting depth. - -**One bundle, one placement.** After the graph is flattened, the same underlying -bundle directory resolving to two different final ``mount_at`` values is a hard -build error (``mount conflict … a bundle must resolve to a single mount_at``). -The same directory at the *same* ``mount_at`` is simply deduplicated. - -**Composition stops at external module boundaries.** A module may mount another -module for its own documentation build. When a third module mounts the first, -it receives the first module's source tree and in-repo bundles, but not foreign -modules the first one mounted. Consumers opt in to every external module -explicitly, keeping ownership and collision handling predictable. - -For data-only integration, **needs stay one ``needs.json`` per module**. A -consumer that imports another module's ``needs.json`` does not mount its sources; -cross-module references then use the external-needs mechanism. A consumer that -mounts a bundle instead builds the mounted sources and Need directives as part -of its own Sphinx project. - -Which directory gets walked -=========================== - -The runtime resolver (``score_mounts``) picks the directory per mount — always an -original source directory, differing only in *where* that directory is staged: - -.. code-block:: python - - if spec.external and ws_root: # bazel run: sibling repo in the runfiles tree - walk_dir = _resolve_below(runfiles_dir, "_main/" + spec.runtime_path) - elif ws_root is not None: # bazel run, in-tree: the live workspace source - walk_dir = _resolve_below(ws_root, spec.src_root) - else: # sandbox, in-tree: source staged at the exec root - walk_dir = _resolve_below(Path.cwd(), spec.src_root) - -* ``ws_root`` is only set under ``bazel run`` (it points at - ``BUILD_WORKSPACE_DIRECTORY``); in a sandboxed ``bazel build`` it is ``None``. -* ``external`` marks a bundle whose sources come from another Bazel module. -* External runtime paths are anchored at the runfiles root (their Bazel - ``../+`` prefix starts below ``_main``). In a sandbox the in-tree - sources are instead resolved against the exec root, which is why the branch - splits three ways. -* ``_resolve_below`` normalizes the path and rejects traversal outside that - Bazel-controlled root. It is a runtime security boundary; valid manifests - emitted by the Bazel rule already satisfy it. - -So the mount walks: - -* the **live workspace source** under ``bazel run`` with an in-tree bundle (edits - show up immediately — best for live preview and jump-to-definition); -* the **in-place staged inputs** in a sandbox build (``needs_json``), where only - the bundle's globbed files are present at their ``source_dir`` path; -* the **staged sibling-repo directory** for an external bundle under - ``bazel run`` (``../+/source_dir`` in the runfiles tree), or its - ``external/+/source_dir`` execroot path in a sandboxed build. - -In all three cases the payload is the untouched source directory; a stray file -that is not doc source (e.g. a ``conf.py``) is simply ignored by Sphinx. - -Without an external bundle declaration, a dependency is referenced via its -prebuilt ``needs.json`` and its sources are not walked at all. - -.. _docs_concept_mounts_rematerialize: - -Re-introducing materialization later -==================================== - -Earlier versions copied each bundle into a normalized ``declare_directory`` at -build time ("materialization"). That was dropped because a ``source_dir`` bundle -is *already* a mount-ready directory — the copy was a byte-for-byte duplicate of a -directory Bazel stages anyway. This section records how to bring materialization -back should the bundle model grow beyond whole-``source_dir`` inputs. - -.. note:: - - Materialization should be a **last resort**. Before re-adding a build-time - copy, investigate whether ``sphinx_mounts`` can already express the need - directly — e.g. a **file/glob mode** or **include/exclude filtering** on the - mount itself. Filtering a subset or picking individual files at the mount layer - avoids duplicating the tree and keeps Sphinx pointed at the originals; prefer - that over reintroducing a copy action. - -**When it becomes necessary.** Materialization earns its keep only when a bundle -is *not* already one ready-to-mount directory on disk *and* ``sphinx_mounts`` -cannot select the payload itself: - -* **File globs / a filtered subset** — the bundle is an explicit list of files - rather than a whole directory, so no single existing directory holds exactly the - payload. -* **A custom ``strip_prefix``** that differs from the files' on-disk layout — the - mount-relative paths then exist only in a rewritten tree. -* **Provider-supplying rules** — a rule that emits ``DocsBundleInfo`` for content - it *generates* (no stable on-disk source directory to point at). - -**Sketch of the mechanism** (intentionally high level — flesh out when needed): - -#. In the bundle rule, ``ctx.actions.declare_directory(name)`` and a copy action - assemble the normalized, mount-relative tree from ``ctx.files.srcs``. -#. The content entry then carries that directory ``File`` (alongside, or instead - of, ``runtime_path``); the emitted ``runtime_path`` points at it. -#. A materialized directory is a ``bazel-out`` artifact colocated with the manifest - in every context. A future resolver can therefore resolve it relative to that - manifest without the exec-root versus runfiles split required by the current - source-directory walk. - -**Provider contract.** A content entry must let the runtime resolve a directory to -walk. Today that is ``runtime_path`` (a source-directory ``short_path``) plus -``src_root`` (the live in-tree path, empty for external). A materialized entry -would instead (or additionally) carry a directory ``File`` whose ``short_path`` -serves as ``runtime_path``. Either representation is valid as long as -the mount-entry deduplication has a stable identity key and ``_mounts_manifest`` can emit a -``runtime_path``. - - -The problem +Composition ----------- -The S-CORE documentation toolchain has historically assumed that every -RST/Markdown file under a Sphinx project lives under its source -directory (``docs/`` in this repository). Two situations break that -assumption: - -* **Generated content** — RST produced by a Bazel rule lands under - ``bazel-bin/...`` and is therefore outside ``docs/`` by construction. - Examples: API reference tables generated from code, requirement - catalogues exported from upstream modules, traceability matrices. - -* **In-repo content owned by another tree** — for example, README-style - documentation that lives next to its source code under ``src/`` and - must remain there for code-ownership reasons but should still appear - in the rendered docs site. - -Historical workarounds either (a) copied or symlinked the files into -``docs/`` — which loses the original source location for IDE -navigation, complicates ``git blame``, and risks stale copies — or -(b) materialized an entire merged source tree at build time and -pointed Sphinx at that. The latter solves the build-side problem but -keeps Sphinx on the IDE critical path. Useful editing in an IDE -requires validation **as you type**, and that is hard to achieve from -any tool without live knowledge of every file and dependency in the -project. Sphinx is built for batch document processing, not for the -millisecond-latency feedback an editor needs; routing IDE feedback -through it therefore caps the editing experience at the speed and -scope of the next rebuild. - - -What ``sphinx-mounts`` does ---------------------------- - -`sphinx-mounts`_ is a Sphinx extension that registers external source -trees with Sphinx's project map by **absolute path**, without copying -or staging. The original files stay exactly where they live; Sphinx -reads them from there. Configuration is declarative TOML in -``ubproject.toml``, the file already shared with Sphinx-Needs, -sphinx-codelinks, and ubCode. - -.. _sphinx-mounts: https://sphinx-mounts.useblocks.com/ - -The key consequence: **every consumer reads the same file**. ubCode, -language servers, indexers, and CI gates can all parse -``ubproject.toml`` to discover where a project's RST sources live — -including the mounted ones — without ever invoking Sphinx. That -preserves the IDE editing experience (real-time validation, jump-to- -definition pointing at the real source, schema-aware autocomplete) -while still letting Sphinx produce the published HTML. - - -Why this matters for IDE support --------------------------------- - -ubCode (and similar tooling) walks **up** the directory tree from an -open ``.rst`` / ``.md`` file to find the nearest ``ubproject.toml``, -treats that directory as the project root, and reads the file to -learn the type system, link types, layouts, and field defaults the -project uses. A file inside ``docs/`` and a file inside a **mounted** -bundle (for example, ``src/docs/overview.rst``) live in different -subtrees, so no single ``ubproject.toml`` placed *inside* either tree -is visible from the other. - -To close this gap, the toolchain emits a **single** ``ubproject.toml`` -at the **git repo root** — the one directory that is an ancestor of -both ``docs/`` and every in-repo bundle. The walk-up from any source -file therefore reaches it. Because it is the only config file, it -carries the host's full type system *and* the ``[[mounts]]`` entries; -there are no sanitized per-bundle copies to keep in sync. - - -Comparison with the materialization approach --------------------------------------------- - -+---------------------------------------+---------------------------------------+---------------------------------------+ -| Concern | Materialize-then-Sphinx | sphinx-mounts (this approach) | -+=======================================+=======================================+=======================================+ -| IDE feedback latency | bounded by next Sphinx rebuild | direct file access via TOML | -+---------------------------------------+---------------------------------------+---------------------------------------+ -| As-you-type validation | not feasible (Sphinx is a batch tool) | works on real files directly | -+---------------------------------------+---------------------------------------+---------------------------------------+ -| Live preview | autobuild-based | ``sphinx-autobuild`` works as-is | -+---------------------------------------+---------------------------------------+---------------------------------------+ -| "Go to definition" lands in | the materialized copy under bazel-bin | the real source file | -+---------------------------------------+---------------------------------------+---------------------------------------+ -| ``conf.py`` execution required for IDE| yes | no — TOML is enough | -+---------------------------------------+---------------------------------------+---------------------------------------+ -| Sandbox-friendly Bazel build | yes | yes | -+---------------------------------------+---------------------------------------+---------------------------------------+ - -The two approaches are not mutually exclusive — a materialized-tree -rule can coexist if a downstream consumer needs it. But sphinx-mounts -is the lighter-weight surface and the primary entry point for new -bundles. +A bundle can contain placements for other bundles. Placements compose by +prefixing their paths: a child placed at ``reference`` inside a guide that is +mounted at ``internals/code_docs`` appears at +``internals/code_docs/reference``. + +A bundle must have one final location in a documentation build. If the same +bundle would resolve to two different locations, the build fails instead of +creating colliding docnames or Need IDs. Repeating the same placement is +deduplicated. + +External modules and Needs +-------------------------- + +A bundle from another Bazel module is mounted like an in-repository bundle, but +its files are read from Bazel's staged dependency tree. They are useful for +published documentation but are not editable from the consuming checkout. + +Mounted sources participate in the host's Sphinx-Needs project, including their +Need directives and cross-bundle references. By contrast, importing a module's +``needs.json`` is a data-only integration: its sources are not mounted. Do not +use both mechanisms for the same module in one build, because that would import +the same Need IDs twice. + +Mounting an external module does not implicitly mount the external modules it +uses itself. Consumers select each external documentation dependency explicitly; +this keeps the published structure and ownership clear. + +Editor support and generated configuration +------------------------------------------ + +The build writes one ``ubproject.toml`` at the repository root. It describes +the host and its mounted directories for Sphinx, ubCode, and similar tools. +Because the file is an ancestor of both ``docs/`` and in-repository bundle +directories, editor tooling can discover one consistent project configuration +from either location. + +Run ``bazel run //:docs`` or ``bazel run //:docs_check`` after changing mounts +to refresh this generated, gitignored file. A sandboxed ``needs_json`` build +checks the same mount configuration without producing a persistent local file. + +Design rationale +---------------- + +The bundle model deliberately keeps content ownership separate from site +placement and avoids materializing copies merely to arrange documentation. +That follows the infrastructure direction in +`DR-008-infra `_. +It keeps source navigation useful while allowing Bazel modules to compose their +documentation in a reproducible build. + +Further reading +--------------- + +* :ref:`howto_mount_external_sources` — declare and mount bundles. +* `sphinx-mounts documentation `_ — + configuration reference, including ``attach_to`` and ``entry_doc``. diff --git a/src/extensions/score_mounts/docs/howto/index.rst b/src/extensions/score_mounts/docs/howto/index.rst index 189f1734c..3d51595ac 100644 --- a/src/extensions/score_mounts/docs/howto/index.rst +++ b/src/extensions/score_mounts/docs/howto/index.rst @@ -14,22 +14,21 @@ .. _howto_mount_external_sources: -Mounting external source bundles -================================ +Mount docs bundles +================== -This guide explains how to surface RST or Markdown content that lives -**outside** ``docs/`` into the docs-as-code build. +Use a documentation bundle to publish RST or Markdown that lives outside a +project's ``docs/`` directory. For the terminology and composition rules, see +:ref:`docs_concept_mounts`. .. contents:: :local: :depth: 2 -Declaring a bundle with ``docs_bundle`` ---------------------------------------- +Declare a bundle +---------------- -A mountable bundle carries only **content**: the source files. It is a -Bazel target created with the ``docs_bundle`` rule from ``docs.bzl``, -declared next to the bundle's sources: +Create a ``docs_bundle`` next to the source directory that it exports: .. code-block:: starlark @@ -42,25 +41,14 @@ declared next to the bundle's sources: visibility = ["//visibility:public"], ) -Each attribute: +``source_dir`` is the root of the mounted tree. Its supported documentation +files are collected like those of ``docs()``; for example, +``src/docs/index.rst`` becomes ``index`` within the bundle. -* ``source_dir`` — directory holding the bundle's own doc sources. It is - globbed the same way as ``docs()`` (RST, Markdown, images, and the - other doc file kinds). The ``source_dir`` itself *is* the mount root, so - the files mount relative to it (``docs/index.rst`` becomes ``index.rst``). - The bundle exposes those files as a Bazel depset (via the - ``DocsBundleInfo`` provider) and records the ``source_dir`` path; - sphinx-mounts walks that original directory directly — no copy is made. +Mount a bundle in a project +--------------------------- -The bundle carries **no placement** — where it appears in a host project -is decided by the *consumer*, so the same bundle can be mounted at -different locations by different consumers (see the next section). - -Placement at the consumer: ``docs(bundles=[...])`` ---------------------------------------------------- - -The ``docs()`` macro's ``bundles`` argument is a list of placement dicts. -Each dict pairs a bundle label with where it goes in *this* project: +Pass a placement dictionary to ``docs(bundles = [...])``: .. code-block:: starlark @@ -77,43 +65,32 @@ Each dict pairs a bundle label with where it goes in *this* project: source_dir = "docs", ) -Each placement key: - -* ``bundle`` — label of a ``docs_bundle`` target (in-repo or from another - module, see :ref:`cross-repo mounts `). +``bundle`` identifies the ``docs_bundle`` target and ``mount_at`` selects the +docname prefix in this project. In this example, ``overview.rst`` is available +as ``internals/code_docs/overview``. -* ``mount_at`` — the docname prefix at which the bundle appears in the - host project. With ``mount_at = "internals/code_docs"``, a bundle file - ``overview.rst`` is reachable in the host as the docname - ``internals/code_docs/overview``. +``attach_to`` is optional. When it is set, the bundle entry document (``index`` +by default) is added to the first toctree of the named host document. Set +``entry_doc`` only when the entry document is not named ``index``. -* ``attach_to`` (optional) — a host docname whose toctree should - automatically receive the bundle's entry document. With - ``attach_to = "internals/index"``, the bundle's ``index`` doc is - appended to the first toctree in ``docs/internals/index.rst`` at build - time; that host doc does not need a manual entry. +The configuration has two separate responsibilities: -* ``entry_doc`` (optional, default ``"index"``) — the mount-relative - docname of the bundle's entry document, used together with - ``attach_to``. +.. plantuml:: -The bundle's ``dir`` in the generated ``ubproject.toml`` is **derived -automatically** from the source file paths — there is no ``src_root`` -attribute. + @startuml + left to right direction + rectangle "docs_bundle\ncontent" as bundle + rectangle "docs(bundles = [...])\nplacement" as docs + rectangle "Rendered docs" as output + bundle --> docs : select bundle + docs --> output : mount_at / attach_to + @enduml -Every project that uses ``docs()`` also **auto-exposes its own** -``source_dir`` as a public bundle named ``docs_bundle``. No extra -wiring is needed: because a consumer's ``docs()`` call *is* this macro, -``@//:docs_bundle`` exists for free and can be mounted elsewhere. - -Composing bundles ------------------ +Compose bundles +--------------- -A ``docs_bundle`` may itself mount other bundles through its ``bundles`` -argument, so one bundle can aggregate a whole sub-tree of content. Each entry -uses the same placement keys as ``docs(bundles=[...])`` (``bundle``, ``mount_at``, -optional ``attach_to`` / ``entry_doc``), but the placement is *relative to the -composing bundle* rather than to a host project: +A bundle can include other bundles. The child placement is relative to the +parent bundle, and the consumer chooses where the assembled bundle appears: .. code-block:: starlark @@ -121,203 +98,63 @@ composing bundle* rather than to a host project: name = "guide", source_dir = "guide", bundles = [ - {"bundle": "//some/pkg:api_docs", "mount_at": "reference", "attach_to": "index"}, + { + "bundle": "//some/pkg:api_docs", + "mount_at": "reference", + "attach_to": "index", + }, ], ) -Here ``guide`` bundles ``api_docs`` under ``reference`` and attaches its entry -doc to ``guide``'s own ``index``. ``guide`` is still **placement-free**: when a -consumer mounts ``guide`` at, say, ``mount_at = "internals/code_docs"``, the two -placements **prefix-stack**. The nested ``api_docs`` then resolves to -``internals/code_docs/reference`` in the host, and its ``attach_to`` resolves to -``internals/code_docs/index``. Composition is therefore fully transitive: a -bundle nested any number of levels deep lands at the concatenation of every -``mount_at`` above it. +Mounting ``guide`` at ``internals/code_docs`` makes the child available below +``internals/code_docs/reference``. A build fails if the same bundle would end +up at two different final locations. + +Mount a bundle from another Bazel module +---------------------------------------- + +Every project using ``docs()`` exposes its own source directory as +``:docs_bundle``. Mount such a bundle from another module by using its label: + +.. code-block:: starlark -If the **same** underlying bundle resolves to two different final ``mount_at`` -values (for example, mounted both directly and again via a composing bundle), the -build fails hard with a ``mount conflict … a bundle must resolve to a single -mount_at`` error — duplicating a bundle's pages would collide docnames and need -IDs. See :ref:`docs_concept_mounts` for the composition semantics. + docs( + bundles = [ + { + "bundle": "@score_process//:docs_bundle", + "mount_at": "process", + "attach_to": "index", + }, + ], + source_dir = "docs", + ) -.. _cross_repo_mounts: +Mounted sources bring their Need directives with them. Do not also add that +module's ``:needs_json`` target to ``data``: Sphinx-Needs would receive each +Need twice. Use ``:needs_json`` only for a dependency whose documentation is +not mounted. -Cross-repo mounts ------------------ +An external module's mounted sources are staged by Bazel, so navigation leads +to a read-only staged tree. Mount every external module explicitly; bundles do +not automatically re-export mounts from other external modules. -A bundle label in ``bundles`` may point at another Bazel module's -auto-exposed bundle. For example, this repository mounts the process -description — already a dependency and itself a ``docs()`` user — with: +Refresh the generated project configuration +------------------------------------------- -.. code-block:: starlark +After changing ``bundles``, run either command once: - bundles = [ - {"bundle": "@score_process//:docs_bundle", "mount_at": "process", "attach_to": "index"}, - ] - -Mounting an external bundle also mounts the Need directives authored in that -module's sources. Do not add the same module's ``:needs_json`` to ``data``: -that would import a second copy of every Need and Sphinx-Needs rejects the -duplicate IDs. Use ``data = ["@module//:needs_json"]`` only for a JSON-only -dependency whose documentation sources are not mounted. If that module mounts -further external modules for its own site, those are not re-exported; mount each -such module explicitly when it is wanted in this project. - -For an **external** bundle the sources do not exist in the consumer's -git tree; they live under ``bazel-*/external/+/…``. The ``dir`` -in ``ubproject.toml`` therefore points at the staged source directory under -``bazel-bin/external/+/…`` — the same pattern already used for -external ``needs.json`` (``json_path = "bazel-bin/external/…"``). -"Go to definition" for such a mount lands in that read-only staged module -tree, not in an editable source file. In-repo bundles keep pointing at their -real, editable sources. - - -How the wiring works --------------------- - -The pieces fit together like this: - -.. code-block:: text - - docs_bundle(...) in a BUILD ← bundle: files depset + mount root - │ (DocsBundleInfo provider, content only) - ▼ - docs(bundles = [{"bundle": "//src:docs_dir", "mount_at": ...}]) - │ placement lives at the call site - ▼ - docs.bzl: _mounts_manifest rule ← reads the providers + placement, - derives each bundle's source dir from - the file paths, and emits ONE canonical - JSON manifest - │ - ▼ - sphinx-build (mounts_manifest = manifest path) - │ - ▼ - score_mounts extension ← reads the manifest and configures runtime - mounts plus neutral metadata: - │ - ┌───┴────┐ - ▼ ▼ - sphinx_mounts score_sync_toml - walks the dir derives TOML paths, serializes and merges /ubproject.toml - -The key inversion from earlier iterations: **Bazel is the single -source of truth for mount paths**. All paths are computed in the rule, -where ``File`` objects have real paths, instead of being reconstructed -from label strings at Sphinx runtime. - -After a successful ``bazel run //:docs_check``, the repo-root -``ubproject.toml`` contains a mount entry like: - -.. code-block:: toml - - mounts = [ - { dir = "src/docs", mount_at = "internals/code_docs", attach_to = "internals/index" }, - ] - -The ``dir`` value points at the bundle's **real source location** -(here, ``src/docs/`` — derived automatically from the bundle's source -files), not at any bazel-bin path. It is relative to the -``ubproject.toml`` location (the git root). ubCode and similar tools -that follow this mount entry therefore navigate to the original files; -jump-to-definition and ``git blame`` work as the author wrote them. - -This block is what every external consumer of the project (ubCode, -sphinx-build, CI) reads to discover the bundle. - -``score_sync_toml`` serializes the structured entries to TOML and passes that -temporary fragment to ``needs-config-writer``, the same writer that emits the -rest of the project's type system. Bazel never emits TOML. - -Building from Bazel -~~~~~~~~~~~~~~~~~~~ - -Relevant targets wired by the ``docs()`` macro: - -* ``bazel run //:docs`` — incremental HTML build for day-to-day - editing; outputs to ``_build/``. Resolves mounts via runfiles - (fast, dev-local) and refreshes the IDE-facing repo-root - ``ubproject.toml``. - -* ``bazel run //:docs_check`` — same as above but with the ``check`` - action; it also refreshes the repo-root ``ubproject.toml``. Run either - command after editing the mount list to refresh the IDE-facing TOML. - -* ``bazel build //:needs_json`` — sandboxed needs-only build. Verifies - that mounted bundles resolve correctly without ``bazel run``. - - -A single ``ubproject.toml`` at the git root --------------------------------------------- - -The whole project — host and every mounted bundle — is described by -**one** ``ubproject.toml`` at the git repo root. ``needs-config-writer`` -relativizes every path field against the output file's directory, so -anchoring the file at the root makes ``external_needs`` JSON paths, the -mount ``dir`` values, and schema paths all root-relative and valid for -any consumer that reads them from there. - -The output location is set by ``score_sync_toml`` to -``find_git_root() / "ubproject.toml"``. ``find_git_root()`` resolves the -repo root both under ``bazel run`` and under esbonio / direct Sphinx -(via a working-directory fallback), so the file lands at the root in -every IDE-facing context. In a sandboxed ``bazel build`` there is no -git root; the writer falls back to the confdir default and that copy is -discarded with the sandbox. - -The file is gitignored (``/ubproject.toml``): it is regenerated on -every build and is not a source artifact. - - -Caveats and known limitations ------------------------------ - -* **External-repository bundles read the staged bazel-bin tree.** Bundles - from another Bazel module are supported (see :ref:`cross_repo_mounts`), but - their ``dir`` points at the staged ``bazel-bin/external/…`` source tree - rather than at editable sources, and that tree only exists after a - build. - -* **One source directory per bundle.** A bundle's content is a single - ``source_dir``; that directory is exactly the mount-relative tree - sphinx-mounts walks, so there is no cross-directory layout to reconcile. - -* **Standard confdir assumption.** Anchoring at the git root assumes - the git root is an ancestor of every source tree (host and bundles), - which holds for the standard layout. A repo whose sources live - outside its git tree would need a different anchor. - - -Cross-bundle references work ----------------------------- - -A need authored inside a mounted bundle can be linked from anywhere in -the host project, just like a need authored in ``docs/`` itself. This -page *is* a mounted bundle, so we dogfood it directly: the stakeholder -requirement below is authored right here, in the mounted score_mounts -how-to bundle, yet the host-side ``tool_req__docs_mount_traceability`` -(in ``docs/internals/requirements/``) carries a ``:satisfies:`` link -straight to it. +.. code-block:: console -The link resolves at host build time with no copy or materialisation, -and is enforced by ``sphinx-needs`` schema validation. That -cross-boundary link uses only stock relations from -``score_metamodel`` (``tool_req`` may satisfy ``stkh_req`` without any -metamodel extension): the bundle owns its own ``.rst`` and lives next -to its code, but its needs participate in the host's traceability -graph as first-class citizens. + $ bazel run //:docs + $ bazel run //:docs_check +Both commands refresh the repository-root ``ubproject.toml`` used by IDE tools. +``bazel build //:needs_json`` checks the sandboxed needs build without creating +that developer-facing file. Further reading --------------- -* `sphinx-mounts documentation`_ — full configuration reference, - TOML schema, behaviour of ``attach_to`` and ``entry_doc``. -* `ubCode`_ — the IDE extension that reads ``ubproject.toml``. -* :ref:`howto_add_extensions` — how to plug other Sphinx extensions into - the docs-as-code build. - -.. _sphinx-mounts documentation: https://sphinx-mounts.useblocks.com/ -.. _ubCode: https://ubcode.useblocks.com/ +* :ref:`docs_concept_mounts` — bundle and mount semantics. +* `sphinx-mounts documentation `_ — full + configuration reference. diff --git a/src/extensions/score_mounts/docs/internals/index.rst b/src/extensions/score_mounts/docs/internals/index.rst new file mode 100644 index 000000000..c9f2a3b41 --- /dev/null +++ b/src/extensions/score_mounts/docs/internals/index.rst @@ -0,0 +1,112 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +.. _score_mounts_internals: + +Mounts extension internals +========================== + +This page is for maintainers of ``score_mounts``. User-facing bundle and mount +semantics are documented in :ref:`docs_concept_mounts`; BUILD usage is in +:ref:`howto_mount_external_sources`. + +Architecture and manifest contract +---------------------------------- + +``docs.bzl`` owns bundle graph traversal. Its ``_mounts_manifest`` rule turns +the ``DocsBundleInfo`` provider graph and each consumer placement into one JSON +manifest. Python deliberately receives paths rather than Bazel labels, so it +does not reconstruct Bazel repository names at Sphinx runtime. + +Each manifest entry contains: + +* ``src_root`` — the source directory relative to the main workspace or the + sandbox execroot; +* ``runtime_path`` — the Bazel short path used when a staged directory must be + walked; +* ``mount_at``, ``attach_to``, and ``entry_doc`` — the already-composed Sphinx + placement; and +* ``external`` — whether the directory belongs to another Bazel module. + +The rule rejects conflicting final placements before Sphinx starts. The Python +extension may therefore preserve declaration order and only translates each +manifest entry into the ``sphinx_mounts`` configuration format. + +Directory resolution +-------------------- + +``score_mounts._resolver.resolve_walk_dir`` selects the directory that +``sphinx_mounts`` walks. The source directory is never copied. + ++------------------------------+-----------------------------------------------+ +| Build context | Directory used | ++==============================+===============================================+ +| ``bazel run``, in-tree | live ``BUILD_WORKSPACE_DIRECTORY / src_root`` | ++------------------------------+-----------------------------------------------+ +| sandboxed build | staged ``execroot / src_root`` | ++------------------------------+-----------------------------------------------+ +| ``bazel run``, external | sibling repository in the runfiles tree | ++------------------------------+-----------------------------------------------+ + +External short paths begin with ``../+`` relative to the runfiles +``_main`` directory. The resolver therefore prefixes ``_main`` before turning +that path into an absolute path. This distinction is covered by unit tests and +by ``//src/tests/mounts_external:docs``; do not resolve an external runtime path +relative to the manifest file, because the manifest can live below a Bazel +package directory. + +Incremental builds use the same resolver in ``src/incremental.py`` to add every +mounted directory to ``sphinx-autobuild``'s watch list. Keep these two call +sites aligned when the manifest contract changes. + +TOML synchronization +-------------------- + +``score_mounts`` writes structured mount entries to ``config.mounts`` during +``config-inited``. ``score_sync_toml`` then serializes them through +``needs-config-writer`` into the repository-root ``ubproject.toml``. The +generated file is intentionally shared by the host and all in-tree bundles so +IDE tooling can discover one configuration while walking up from either source +tree. + +The ordering is significant: ``score_mounts`` runs before ``sphinx_mounts`` so +the manifest is authoritative at build time, and ``score_sync_toml`` runs after +it so the TOML writer sees the resolved structured entries. + +Why bundles are not materialized +-------------------------------- + +A ``source_dir`` bundle already has the mount-relative layout on disk. +Materializing a second directory would duplicate its files, point navigation at +a generated copy, and add a build action without changing the Sphinx input. + +Reconsider materialization only if a future bundle cannot be represented by one +existing directory, for example for a filtered file set, a custom +``strip_prefix``, or generated provider content. Prefer native filtering support +in ``sphinx_mounts`` first. If a copy action becomes necessary, extend the +manifest with a directory artifact and keep its identity stable for placement +deduplication. + +Validation +---------- + +Relevant checks are: + +* ``bazel test //src/extensions/score_mounts:score_mounts_tests`` for manifest + parsing and path resolution; +* ``bazel test //src/tests/mounts_contract:mount_docs_e2e_test`` for mounted + rendering and toctree attachment; +* ``bazel run //src/tests/mounts_external:docs`` for an external bundle under + ``bazel run``; and +* the negative fixtures in ``src/tests/mounts_conflict`` for placement conflicts. diff --git a/src/extensions/score_mounts/tests/test_resolver.py b/src/extensions/score_mounts/tests/test_resolver.py index c09d75bbd..09325d2e2 100644 --- a/src/extensions/score_mounts/tests/test_resolver.py +++ b/src/extensions/score_mounts/tests/test_resolver.py @@ -118,44 +118,6 @@ def test_load_non_object_raises(tmp_path: Path): load_mounts_manifest(str(manifest)) -@pytest.mark.parametrize( - ("entry", "message"), - [ - ( - { - "src_root": "../outside", - "runtime_path": "docs", - "mount_at": "x", - }, - "src_root must not contain", - ), - ( - { - "src_root": "src/docs", - "runtime_path": "../../outside", - "mount_at": "x", - }, - "runtime_path must not contain", - ), - ( - { - "src_root": "src/docs", - "runtime_path": "docs", - "mount_at": "x", - "external": True, - }, - "external runtime_path must start", - ), - ], -) -def test_load_rejects_paths_that_escape_bazel_roots( - tmp_path: Path, entry: dict[str, object], message: str -): - manifest = _write_manifest(tmp_path, {"mounts": [entry]}) - with pytest.raises(ValueError, match=message): - load_mounts_manifest(manifest) - - def test_external_mount_uses_execroot_path_in_sandbox(tmp_path: Path, monkeypatch): monkeypatch.chdir(tmp_path) manifest = _write_manifest( diff --git a/src/extensions/score_sync_toml/__init__.py b/src/extensions/score_sync_toml/__init__.py index 610f0e8d2..c1d197bc2 100644 --- a/src/extensions/score_sync_toml/__init__.py +++ b/src/extensions/score_sync_toml/__init__.py @@ -42,7 +42,7 @@ def setup(app: Sphinx) -> dict[str, str | bool]: config_setdefault(app.config, "needscfg_write_all", True) """Write full config, so the final configuration is visible in one file.""" - config_setdefault(app.config, "needscfg_exclude_defaults", False) + config_setdefault(app.config, "needscfg_exclude_defaults", True) """Exclude default values from the generated configuration.""" # This is disabled for right now as it causes a lot of issues diff --git a/src/tests/docs_e2e/support.py b/src/tests/docs_e2e/support.py index a84338d17..ccead1eb3 100644 --- a/src/tests/docs_e2e/support.py +++ b/src/tests/docs_e2e/support.py @@ -19,20 +19,11 @@ def runfile(path_env: str) -> Path: - """Resolve a test runfile without allowing its env value to escape runfiles.""" - runfiles_root = _runfiles_root() - candidate = os.path.normpath(os.path.join(runfiles_root, os.environ[path_env])) - if not candidate.startswith(str(runfiles_root) + os.sep): - raise ValueError(f"runfile path escapes TEST_SRCDIR: {candidate!r}") - return Path(candidate) - - -def _runfiles_root() -> Path: - """Return this test's Bazel-managed runfiles root.""" - return Path( - os.path.normpath( - os.path.join(os.environ["TEST_SRCDIR"], os.environ["TEST_WORKSPACE"]) - ) + """Resolve a runfiles-relative path passed through a test environment variable.""" + return ( + Path(os.environ["TEST_SRCDIR"]) + / os.environ["TEST_WORKSPACE"] + / os.environ[path_env] ) @@ -50,7 +41,7 @@ def run_docs_build( the small writable equivalent needed by a Bazel test while preserving the runfiles layout used to resolve in-tree mount sources. """ - runfiles_workspace = _runfiles_root() + runfiles_workspace = Path(os.environ["TEST_SRCDIR"]) / os.environ["TEST_WORKSPACE"] (tmp_path / "src").symlink_to(runfiles_workspace / "src", target_is_directory=True) copied_source_dir = tmp_path / "docs" @@ -60,12 +51,7 @@ def run_docs_build( env = os.environ.copy() env["SOURCE_DIRECTORY"] = str(copied_source_dir) - if mounts_manifest: - copied_manifest = tmp_path / "_mounts_manifest.json" - shutil.copyfile(mounts_manifest, copied_manifest) - env["MOUNTS_MANIFEST"] = copied_manifest.name - else: - env["MOUNTS_MANIFEST"] = "" + env["MOUNTS_MANIFEST"] = str(mounts_manifest) if mounts_manifest else "" env["DATA"] = "[]" env["ACTION"] = "incremental" env["SCORE_SOURCELINKS"] = str(sourcelinks) From 045754d98a4f590036a55ed741365994510b30eb Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 16:45:56 +0200 Subject: [PATCH 12/19] move bundle entry doc to bundle API --- .github/workflows/test.yml | 1 - bzl/bundle_rules.bzl | 19 ++++++------- docs.bzl | 7 +++-- docs/reference/bazel_macros.rst | 21 +++++++++------ .../score_mounts/docs/concept/index.rst | 8 +++--- .../score_mounts/docs/howto/index.rst | 10 ++++--- .../score_mounts/docs/internals/index.rst | 8 +++--- src/tests/mounts_conflict/BUILD | 27 +------------------ src/tests/mounts_contract/BUILD | 2 ++ src/tests/mounts_contract/README.md | 7 +++-- src/tests/mounts_contract/child/landing.rst | 20 ++++++++++++++ .../mounts_contract/test_mount_docs_e2e.py | 7 +++++ 12 files changed, 76 insertions(+), 61 deletions(-) create mode 100644 src/tests/mounts_contract/child/landing.rst diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6ca6b1299..3dfdb892a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,7 +59,6 @@ jobs: run: | ! bazel build --lockfile_mode=error //src/tests/mounts_conflict:bad ! bazel build --lockfile_mode=error //src/tests/mounts_conflict:bad_attach_to - ! bazel build --lockfile_mode=error //src/tests/mounts_conflict:bad_entry_doc - name: Build an external documentation bundle from runfiles run: bazel run --lockfile_mode=error //src/tests/mounts_external:docs diff --git a/bzl/bundle_rules.bzl b/bzl/bundle_rules.bzl index b6d5f7e54..cffd68803 100644 --- a/bzl/bundle_rules.bzl +++ b/bzl/bundle_rules.bzl @@ -108,7 +108,7 @@ def _bundle_execroot_path(runtime_path): return "external/" + runtime_path[3:] return runtime_path -def _rebase_bundle_entry(entry, mount_at, attach_to, entry_doc): +def _rebase_bundle_entry(entry, mount_at, attach_to): """Place a bundle entry below a requested documentation-tree location.""" is_bundle_root = not entry.mount_at if is_bundle_root: @@ -121,7 +121,7 @@ def _rebase_bundle_entry(entry, mount_at, attach_to, entry_doc): src_root = entry.src_root, mount_at = join_path(mount_at, entry.mount_at), attach_to = rebased_attach_to, - entry_doc = entry_doc if is_bundle_root else entry.entry_doc, + entry_doc = entry.entry_doc, external = entry.external, repository = entry.repository, ) @@ -160,7 +160,7 @@ def _parse_bundle_declaration(bundle): if type(bundle) != "dict": fail("each bundle declaration must be a dict, got %r" % bundle) - allowed_keys = ["bundle", "mount_at", "attach_to", "entry_doc"] + allowed_keys = ["bundle", "mount_at", "attach_to"] unknown = [key for key in bundle if key not in allowed_keys] if unknown: fail("unknown key(s) %r in %r; allowed keys: %r" % @@ -170,16 +170,13 @@ def _parse_bundle_declaration(bundle): mount_at = bundle["mount_at"] attach_to = bundle.get("attach_to", "") - entry_doc = bundle.get("entry_doc", "index") _validate_docname(mount_at, "mount_at", allow_empty = True) _validate_docname(attach_to, "attach_to", allow_empty = True) - _validate_docname(entry_doc, "entry_doc") return struct( bundle = bundle["bundle"], mount_at = mount_at, attach_to = attach_to, - entry_doc = entry_doc, ) def _docs_bundle_impl(ctx): @@ -199,7 +196,7 @@ def _docs_bundle_impl(ctx): src_root = _bundle_execroot_path(runtime_path), mount_at = "", attach_to = "", - entry_doc = "index", + entry_doc = ctx.attr.entry_doc, external = external, repository = ctx.label.workspace_name, )) @@ -221,7 +218,6 @@ def _docs_bundle_impl(ctx): entry, ctx.attr.bundle_mount_ats[index], ctx.attr.bundle_attach_tos[index], - ctx.attr.bundle_entry_docs[index], ) for entry in _entries_visible_through(ctx, child) ]) @@ -253,26 +249,27 @@ _docs_bundle = rule( "srcs": attr.label_list(allow_files = True), "sourcelinks": attr.label_list(allow_files = True), "strip_prefix": attr.string(default = ""), + "entry_doc": attr.string(default = "index"), "bundles": attr.label_list(providers = [DocsBundleInfo]), "bundle_mount_ats": attr.string_list(), "bundle_attach_tos": attr.string_list(), - "bundle_entry_docs": attr.string_list(), }, doc = "Internal rule that carries bundle files and their documentation-tree locations.", ) -def create_bundle(name, bundles, srcs = [], sourcelinks = [], strip_prefix = "", visibility = None, **kwargs): +def create_bundle(name, bundles, srcs = [], sourcelinks = [], strip_prefix = "", entry_doc = "index", visibility = None, **kwargs): """Create a reusable documentation bundle from files and child declarations.""" + _validate_docname(entry_doc, "entry_doc") parsed_bundles = [_parse_bundle_declaration(declaration) for declaration in bundles] _docs_bundle( name = name, srcs = srcs, sourcelinks = sourcelinks, strip_prefix = strip_prefix, + entry_doc = entry_doc, bundles = [bundle.bundle for bundle in parsed_bundles], bundle_mount_ats = [bundle.mount_at for bundle in parsed_bundles], bundle_attach_tos = [bundle.attach_to for bundle in parsed_bundles], - bundle_entry_docs = [bundle.entry_doc for bundle in parsed_bundles], visibility = visibility, **kwargs ) diff --git a/docs.bzl b/docs.bzl index 01f369848..ebaf07940 100644 --- a/docs.bzl +++ b/docs.bzl @@ -60,7 +60,7 @@ load( "create_mounts_manifest", ) -def docs_bundle(name, source_dir = None, bundles = [], scan_code = [], visibility = None, **kwargs): +def docs_bundle(name, source_dir = None, entry_doc = "index", bundles = [], scan_code = [], visibility = None, **kwargs): """A docs bundle, optionally composed of others. Args: @@ -68,6 +68,8 @@ def docs_bundle(name, source_dir = None, bundles = [], scan_code = [], visibilit source_dir: optional directory holding this bundle's own doc sources. It is globbed like `docs()` (same file kinds) and the contents are stored after stripping the `source_dir` prefix. Leave it unset for a pure aggregator. + entry_doc: bundle-relative docname attached when this bundle is mounted. + Defaults to `index`. bundles: nested bundles to compose, each a dict { "bundle": , @@ -98,6 +100,7 @@ def docs_bundle(name, source_dir = None, bundles = [], scan_code = [], visibilit srcs = srcs, sourcelinks = sourcelinks, strip_prefix = strip_prefix, + entry_doc = entry_doc, bundles = bundles, visibility = visibility, **kwargs @@ -164,7 +167,6 @@ def docs( "bundle": , "mount_at": , "attach_to": , - "entry_doc": , }. Note: a bundle label may also point at another module's auto-exposed bundle, e.g. "@score_process//:docs_bundle". @@ -213,6 +215,7 @@ def docs( docs_bundle( name = "docs_bundle", source_dir = source_dir, + entry_doc = "index", bundles = bundles, scan_code = scan_code, visibility = ["//visibility:public"], diff --git a/docs/reference/bazel_macros.rst b/docs/reference/bazel_macros.rst index 5e15504c8..d53336a5c 100644 --- a/docs/reference/bazel_macros.rst +++ b/docs/reference/bazel_macros.rst @@ -116,11 +116,12 @@ site). docs_bundle( name = "docs_dir", source_dir = "docs", + entry_doc = "index", bundles = [], visibility = ["//visibility:public"], ) -Signature: ``docs_bundle(name, source_dir = None, bundles = [], scan_code = [], visibility = None)``. +Signature: ``docs_bundle(name, source_dir = None, entry_doc = "index", bundles = [], scan_code = [], visibility = None)``. - ``source_dir`` (string, optional) Directory holding the bundle's own doc sources. It is globbed the same way as @@ -132,6 +133,11 @@ Signature: ``docs_bundle(name, source_dir = None, bundles = [], scan_code = [], directory directly — no copy is made. Leave it unset for a pure aggregator that only composes ``bundles``. +- ``entry_doc`` (string, optional) + Bundle-relative docname used as the canonical navigation entry. It defaults to + ``index``. Every mount attaches this entry to the parent ``index`` toctree by + default; a placement's ``attach_to`` may override that host document. + - ``bundles`` (list of composition dicts, optional) Nested bundles to compose into this one, so a bundle can aggregate other bundles transitively. Each entry is a dict: @@ -140,9 +146,8 @@ Signature: ``docs_bundle(name, source_dir = None, bundles = [], scan_code = [], - ``mount_at`` — the docname prefix at which the child bundle appears *inside* this bundle. - ``attach_to`` (optional) — a docname (relative to this bundle) whose toctree - receives the child's entry document. - - ``entry_doc`` (optional, default ``"index"``) — the child-relative docname of - the entry document, used together with ``attach_to``. + receives the child's bundle-defined entry document. When omitted, the parent + ``index`` document receives it. A child's ``mount_at``/``attach_to`` **prefix-stack** with the placement this bundle later receives, so composition is fully transitive. The same underlying @@ -152,10 +157,10 @@ Signature: ``docs_bundle(name, source_dir = None, bundles = [], scan_code = [], .. note:: - A bundle is **placement-free**: its ``mount_at`` / ``attach_to`` / - ``entry_doc`` are assigned by the mounter, never by the bundle itself. This is - what lets the same bundle be mounted at different locations by different - consumers. + A bundle is **placement-free**: its ``mount_at`` and ``attach_to`` are assigned + by the mounter, while its ``entry_doc`` belongs to the bundle. This lets the + same bundle be mounted at different locations by different consumers without + changing its canonical entry page. Edge cases ---------- diff --git a/src/extensions/score_mounts/docs/concept/index.rst b/src/extensions/score_mounts/docs/concept/index.rst index 5efa8d76b..2e8761a8b 100644 --- a/src/extensions/score_mounts/docs/concept/index.rst +++ b/src/extensions/score_mounts/docs/concept/index.rst @@ -33,9 +33,11 @@ A **bundle** is a named, mountable documentation directory. It owns its content, but not its position in a consuming documentation site. A **mount** makes a bundle visible in a host site. The host chooses the -**placement** with ``mount_at`` and can optionally attach the bundle's entry -document to a host toctree with ``attach_to``. This separation lets different -projects reuse the same bundle at different locations. +**placement** with ``mount_at``. The bundle defines its own ``entry_doc`` +(default ``index``); the mount adds that page to the parent ``index`` toctree +by default. ``attach_to`` overrides that host toctree document. This separation +lets different projects reuse the same bundle at different locations while +preserving its canonical entry page. Bundles are read from their original source directories. Consequently, an in-repository bundle remains editable and IDE navigation reaches its real diff --git a/src/extensions/score_mounts/docs/howto/index.rst b/src/extensions/score_mounts/docs/howto/index.rst index 3d51595ac..40dc20602 100644 --- a/src/extensions/score_mounts/docs/howto/index.rst +++ b/src/extensions/score_mounts/docs/howto/index.rst @@ -38,12 +38,15 @@ Create a ``docs_bundle`` next to the source directory that it exports: docs_bundle( name = "docs_dir", source_dir = "docs", + entry_doc = "overview", # default: "index" visibility = ["//visibility:public"], ) ``source_dir`` is the root of the mounted tree. Its supported documentation files are collected like those of ``docs()``; for example, ``src/docs/index.rst`` becomes ``index`` within the bundle. +``entry_doc`` names the bundle-relative page used for navigation when the +bundle is mounted. Mount a bundle in a project --------------------------- @@ -69,9 +72,10 @@ Pass a placement dictionary to ``docs(bundles = [...])``: docname prefix in this project. In this example, ``overview.rst`` is available as ``internals/code_docs/overview``. -``attach_to`` is optional. When it is set, the bundle entry document (``index`` -by default) is added to the first toctree of the named host document. Set -``entry_doc`` only when the entry document is not named ``index``. +Every mount adds the bundle's configured entry document to a host toctree. By +default that is the ``index`` beside ``mount_at``; ``attach_to`` overrides the +host document whose first toctree receives the entry. The entry itself belongs +to the ``docs_bundle`` and defaults to ``index``. The configuration has two separate responsibilities: diff --git a/src/extensions/score_mounts/docs/internals/index.rst b/src/extensions/score_mounts/docs/internals/index.rst index c9f2a3b41..773bffc1f 100644 --- a/src/extensions/score_mounts/docs/internals/index.rst +++ b/src/extensions/score_mounts/docs/internals/index.rst @@ -35,11 +35,13 @@ Each manifest entry contains: sandbox execroot; * ``runtime_path`` — the Bazel short path used when a staged directory must be walked; -* ``mount_at``, ``attach_to``, and ``entry_doc`` — the already-composed Sphinx - placement; and +* ``mount_at`` and ``attach_to`` — the already-composed Sphinx placement; and +* ``entry_doc`` — the canonical entry document declared by the source bundle. * ``external`` — whether the directory belongs to another Bazel module. -The rule rejects conflicting final placements before Sphinx starts. The Python +The rule rejects conflicting final placements before Sphinx starts. A mount +without ``attach_to`` is attached to the ``index`` document beside its +``mount_at``; ``attach_to`` overrides that target. The Python extension may therefore preserve declaration order and only translates each manifest entry into the ``sphinx_mounts`` configuration format. diff --git a/src/tests/mounts_conflict/BUILD b/src/tests/mounts_conflict/BUILD index 9cc1e58df..82dc36edf 100644 --- a/src/tests/mounts_conflict/BUILD +++ b/src/tests/mounts_conflict/BUILD @@ -15,6 +15,7 @@ load("//:docs.bzl", "docs_bundle") docs_bundle( name = "child", source_dir = "child", + entry_doc = "a", ) docs_bundle( @@ -51,22 +52,6 @@ docs_bundle( ], ) -docs_bundle( - name = "entry_doc_index", - source_dir = "_empty", - bundles = [ - {"bundle": ":child", "mount_at": "same", "entry_doc": "index"}, - ], -) - -docs_bundle( - name = "entry_doc_overview", - source_dir = "_empty", - bundles = [ - {"bundle": ":child", "mount_at": "same", "entry_doc": "overview"}, - ], -) - # The child reaches the final bundle at two different locations, so analysis # must stop before Sphinx would receive two copies of the same pages. docs_bundle( @@ -90,13 +75,3 @@ docs_bundle( ], tags = ["manual"], ) - -docs_bundle( - name = "bad_entry_doc", - source_dir = "_empty", - bundles = [ - {"bundle": ":entry_doc_index", "mount_at": "root"}, - {"bundle": ":entry_doc_overview", "mount_at": "root"}, - ], - tags = ["manual"], -) diff --git a/src/tests/mounts_contract/BUILD b/src/tests/mounts_contract/BUILD index 0ff7323cb..54049b8ab 100644 --- a/src/tests/mounts_contract/BUILD +++ b/src/tests/mounts_contract/BUILD @@ -18,6 +18,7 @@ load("//:score_pytest.bzl", "score_pytest") docs_bundle( name = "child", source_dir = "child", + entry_doc = "landing", scan_code = [":scan_code"], ) @@ -80,6 +81,7 @@ score_pytest( "host_docs/conf.py", "host_docs/concepts/index.rst", "host_docs/index.rst", + "child/landing.rst", ], deps = ["//src/tests/docs_e2e:e2e_support"], env = { diff --git a/src/tests/mounts_contract/README.md b/src/tests/mounts_contract/README.md index 9ac58f981..08b043749 100644 --- a/src/tests/mounts_contract/README.md +++ b/src/tests/mounts_contract/README.md @@ -76,8 +76,7 @@ it: Sphinx cannot extend the expected Toctree or the mounted page is absent. - `bazel test //src/extensions/score_mounts:score_mounts_tests` contains Python unit tests for the `score_mounts` extension. -- `bazel build //src/tests/mounts_conflict:bad`, `:bad_attach_to`, and - `:bad_entry_doc` are manually invoked negative fixtures. They must fail - because one source directory cannot produce conflicting mount locations, - Toctree targets, or entry documents. +- `bazel build //src/tests/mounts_conflict:bad` and `:bad_attach_to` are + manually invoked negative fixtures. They must fail because one source + directory cannot produce conflicting mount locations or Toctree targets. - `bazel run //:docs` remains the project-wide documentation smoke check. diff --git a/src/tests/mounts_contract/child/landing.rst b/src/tests/mounts_contract/child/landing.rst new file mode 100644 index 000000000..48974f8fb --- /dev/null +++ b/src/tests/mounts_contract/child/landing.rst @@ -0,0 +1,20 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Child landing page +================== + +.. toctree:: + + index diff --git a/src/tests/mounts_contract/test_mount_docs_e2e.py b/src/tests/mounts_contract/test_mount_docs_e2e.py index 14082546c..800f7da73 100644 --- a/src/tests/mounts_contract/test_mount_docs_e2e.py +++ b/src/tests/mounts_contract/test_mount_docs_e2e.py @@ -26,6 +26,10 @@ def test_docs_build_mounts_bundle_and_extends_toctree(tmp_path: Path): "concepts/example_bundle", "concepts/example_bundle/child", ] + assert [mount["entry_doc"] for mount in manifest["mounts"]] == [ + "index", + "landing", + ] sourcelinks = json.loads(runfile("FIXTURE_SOURCELINKS").read_text(encoding="utf-8")) assert sourcelinks == [ { @@ -53,6 +57,9 @@ def test_docs_build_mounts_bundle_and_extends_toctree(tmp_path: Path): assert ( tmp_path / "_build" / "concepts" / "example_bundle" / "index.html" ).is_file() + assert ( + tmp_path / "_build" / "concepts" / "example_bundle" / "child" / "landing.html" + ).is_file() toml = (tmp_path / "docs" / "ubproject.toml").read_text(encoding="utf-8") assert 'mount_at = "concepts/example_bundle"' in toml assert 'mount_at = "concepts/example_bundle/child"' in toml From b2cf1e7d99b30a632eb51d864e69a6806677629c Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 20:43:09 +0200 Subject: [PATCH 13/19] restructure tests + fix pyright --- .github/workflows/test.yml | 21 ++--- .../tests/test_rules_file_based.py | 13 +-- src/extensions/score_metamodel/yaml_parser.py | 2 +- src/extensions/score_mounts/_resolver.py | 31 ++++--- .../score_mounts/docs/internals/index.rst | 13 +-- .../score_mounts/tests/test_resolver.py | 18 ++-- .../score_source_code_linker/xml_parser.py | 13 +-- src/extensions/score_sync_toml/test_mounts.py | 8 +- src/incremental_dirty_build_test.py | 6 +- src/tests/README.md | 14 +++- src/tests/docs_bzl/README.md | 36 ++++++-- src/tests/docs_bzl/helpers.py | 76 +++++++++++------ .../basic => scenarios/basic_docs}/BUILD | 2 +- .../basic_docs}/docs/conf.py | 0 .../basic_docs}/docs/index.rst | 0 .../scenarios/external_bundle}/BUILD | 1 + .../external_bundle}/host_docs/conf.py | 0 .../external_bundle}/host_docs/index.rst | 0 .../external_needs/consumer/BUILD | 4 +- .../external_needs/consumer/docs/conf.py | 0 .../external_needs/consumer/docs/index.rst | 0 .../consumer/docs/metamodel.yaml | 0 .../external_needs/producer/BUILD | 0 .../external_needs/producer/docs/conf.py | 0 .../external_needs/producer/docs/index.rst | 0 .../producer/docs/metamodel.yaml | 0 .../invalid_bundle_placements}/BUILD | 0 .../invalid_bundle_placements}/child/a.rst | 0 .../metamodel_violation/BUILD | 0 .../metamodel_violation/docs/conf.py | 0 .../metamodel_violation/docs/index.rst | 0 .../metamodel_violation/docs/metamodel.yaml | 0 .../scenarios/nested_bundles}/BUILD | 32 +------- .../nested_bundles}/child/example.py | 0 .../scenarios/nested_bundles}/child/index.rst | 0 .../nested_bundles}/child/landing.rst | 0 .../host_docs/concepts/index.rst | 0 .../nested_bundles}/host_docs/conf.py | 0 .../nested_bundles}/host_docs/index.rst | 0 .../scenarios/nested_bundles}/other/index.rst | 0 .../nested_bundles}/parent/index.rst | 0 src/tests/docs_bzl/test_basic_docs.py | 34 ++++++++ src/tests/docs_bzl/test_docs_bzl.py | 74 ----------------- src/tests/docs_bzl/test_external_bundle.py | 33 ++++++++ src/tests/docs_bzl/test_external_needs.py | 42 ++++++++++ .../test_invalid_bundle_placements.py | 33 ++++++++ .../docs_bzl/test_metamodel_violation.py | 33 ++++++++ src/tests/docs_bzl/test_nested_bundles.py | 71 ++++++++++++++++ src/tests/docs_e2e/BUILD | 46 ----------- src/tests/docs_e2e/host_docs/conf.py | 16 ---- src/tests/docs_e2e/host_docs/index.rst | 16 ---- src/tests/docs_e2e/support.py | 66 --------------- src/tests/docs_e2e/test_docs_e2e.py | 30 ------- src/tests/mounts_contract/README.md | 82 ------------------- .../mounts_contract/test_mount_docs_e2e.py | 76 ----------------- 55 files changed, 417 insertions(+), 525 deletions(-) rename src/tests/docs_bzl/{fixtures/basic => scenarios/basic_docs}/BUILD (90%) rename src/tests/docs_bzl/{fixtures/basic => scenarios/basic_docs}/docs/conf.py (100%) rename src/tests/docs_bzl/{fixtures/basic => scenarios/basic_docs}/docs/index.rst (100%) rename src/tests/{mounts_external => docs_bzl/scenarios/external_bundle}/BUILD (93%) rename src/tests/{mounts_external => docs_bzl/scenarios/external_bundle}/host_docs/conf.py (100%) rename src/tests/{mounts_external => docs_bzl/scenarios/external_bundle}/host_docs/index.rst (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/external_needs/consumer/BUILD (84%) rename src/tests/docs_bzl/{fixtures => scenarios}/external_needs/consumer/docs/conf.py (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/external_needs/consumer/docs/index.rst (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/external_needs/consumer/docs/metamodel.yaml (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/external_needs/producer/BUILD (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/external_needs/producer/docs/conf.py (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/external_needs/producer/docs/index.rst (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/external_needs/producer/docs/metamodel.yaml (100%) rename src/tests/{mounts_conflict => docs_bzl/scenarios/invalid_bundle_placements}/BUILD (100%) rename src/tests/{mounts_conflict => docs_bzl/scenarios/invalid_bundle_placements}/child/a.rst (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/metamodel_violation/BUILD (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/metamodel_violation/docs/conf.py (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/metamodel_violation/docs/index.rst (100%) rename src/tests/docs_bzl/{fixtures => scenarios}/metamodel_violation/docs/metamodel.yaml (100%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/BUILD (64%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/child/example.py (100%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/child/index.rst (100%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/child/landing.rst (100%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/host_docs/concepts/index.rst (100%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/host_docs/conf.py (100%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/host_docs/index.rst (100%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/other/index.rst (100%) rename src/tests/{mounts_contract => docs_bzl/scenarios/nested_bundles}/parent/index.rst (100%) create mode 100644 src/tests/docs_bzl/test_basic_docs.py delete mode 100644 src/tests/docs_bzl/test_docs_bzl.py create mode 100644 src/tests/docs_bzl/test_external_bundle.py create mode 100644 src/tests/docs_bzl/test_external_needs.py create mode 100644 src/tests/docs_bzl/test_invalid_bundle_placements.py create mode 100644 src/tests/docs_bzl/test_metamodel_violation.py create mode 100644 src/tests/docs_bzl/test_nested_bundles.py delete mode 100644 src/tests/docs_e2e/BUILD delete mode 100644 src/tests/docs_e2e/host_docs/conf.py delete mode 100644 src/tests/docs_e2e/host_docs/index.rst delete mode 100644 src/tests/docs_e2e/support.py delete mode 100644 src/tests/docs_e2e/test_docs_e2e.py delete mode 100644 src/tests/mounts_contract/README.md delete mode 100644 src/tests/mounts_contract/test_mount_docs_e2e.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3dfdb892a..c4eeec519 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,32 +49,23 @@ jobs: - name: Setup venv run: bazel run --lockfile_mode=error //:ide_support - - name: Run docs_bzl tests - run: .venv_docs/bin/python -m pytest -vv src/tests/docs_bzl + - name: Prepare test report directory + run: mkdir -p tests-report + + - name: Run public docs.bzl integration tests + run: .venv_docs/bin/python -m pytest -vv src/tests/docs_bzl --junitxml=tests-report/docs_bzl.xml - name: Run bazel test targets run: bazel test --lockfile_mode=error //... --build_tests_only - - name: Verify mount conflicts are rejected - run: | - ! bazel build --lockfile_mode=error //src/tests/mounts_conflict:bad - ! bazel build --lockfile_mode=error //src/tests/mounts_conflict:bad_attach_to - - - name: Build an external documentation bundle from runfiles - run: bazel run --lockfile_mode=error //src/tests/mounts_external:docs - - name: Build all targets # Skip tests, since some are non buildable negative tests run: bazel build --lockfile_mode=error //... -- -//src/tests/... - name: Prepare bundled test report if: ${{ !cancelled() }} - # Creating tests-report directory # Follow Symlinks via '-L' to copy correctly - # Copy everything inside the 'test-reports' folder - run: | - mkdir -p tests-report - rsync -amL --include='*/' --include='test.xml' --include='test.log' --exclude='*' bazel-testlogs/ tests-report/ + run: rsync -amL --include='*/' --include='test.xml' --include='test.log' --exclude='*' bazel-testlogs/ tests-report/ - name: Upload bundled test report # No need to upload something that doesn't exist. diff --git a/src/extensions/score_metamodel/tests/test_rules_file_based.py b/src/extensions/score_metamodel/tests/test_rules_file_based.py index 72dad21ae..a9ef1e778 100644 --- a/src/extensions/score_metamodel/tests/test_rules_file_based.py +++ b/src/extensions/score_metamodel/tests/test_rules_file_based.py @@ -16,13 +16,14 @@ from collections.abc import Callable from dataclasses import dataclass, field from pathlib import Path -from typing import Any +from typing import Any, cast import pytest from score_metamodel.tests import need as test_need from sphinx.testing.util import SphinxTestApp from sphinx_needs.data import NeedsExtendType, SphinxNeedsData from sphinx_needs.need_item import NeedItem +from sphinx_needs.views import NeedsView from score_pytest.attribute_plugin import apply_test_metadata @@ -194,7 +195,7 @@ def _get_default_metadata_need() -> NeedItem: ) -def _get_test_metadata_need(needs_view, rst_data: RstData) -> NeedItem: +def _get_test_metadata_need(needs_view: NeedsView, rst_data: RstData) -> NeedItem: ### Return the single 'test_metadata' need, failing if there isn't exactly one. test_metadata_needs = needs_view.filter_types(["test_metadata"]).values() if not test_metadata_needs: @@ -233,7 +234,7 @@ def _check_need_warnings( line_nr = need.get("lineno") - for raw in need.get("expect") or []: + for raw in cast("list[str]", need.get("expect") or []): expected = raw.strip() if warning_matches(rst_data, line_nr, expected, warnings): continue @@ -247,7 +248,7 @@ def _check_need_warnings( pytrace=False, ) - for raw in need.get("expect_not") or []: + for raw in cast("list[str]", need.get("expect_not") or []): not_expected = raw.strip() unexpected = warning_matches(rst_data, line_nr, not_expected, warnings) if not unexpected: @@ -263,8 +264,8 @@ def _check_need_warnings( @pytest.mark.parametrize("rst_file", RST_FILES) def test_rst_files( - record_property, - record_xml_attribute, + record_property: Callable[[str, object], None], + record_xml_attribute: Callable[[str, str], None], rst_file: str, sphinx_app_setup: Callable[[Path], SphinxTestApp], monkeypatch: pytest.MonkeyPatch, diff --git a/src/extensions/score_metamodel/yaml_parser.py b/src/extensions/score_metamodel/yaml_parser.py index ae387cd15..4cc19d2c9 100644 --- a/src/extensions/score_metamodel/yaml_parser.py +++ b/src/extensions/score_metamodel/yaml_parser.py @@ -197,7 +197,7 @@ def _collect_all_custom_options( # # Note: "" is not encoded in the metamodel.yaml as there is no generic # demand exists at the moment. - params_str = {"schema": {"type": "string"}, "default": ""} + params_str: dict[str, object] = {"schema": {"type": "string"}, "default": ""} params_int: dict[str, Any] = {"schema": {"type": "integer"}, "default": 0} return { diff --git a/src/extensions/score_mounts/_resolver.py b/src/extensions/score_mounts/_resolver.py index 786af1b1b..a26713992 100644 --- a/src/extensions/score_mounts/_resolver.py +++ b/src/extensions/score_mounts/_resolver.py @@ -24,6 +24,7 @@ import os from dataclasses import dataclass from pathlib import Path +from typing import cast @dataclass(frozen=True) @@ -48,25 +49,35 @@ def load_mounts_manifest(manifest_path: str | Path) -> MountsManifest: exec root in a sandbox) is the caller's responsibility. """ manifest_path = Path(manifest_path) - data = json.loads(manifest_path.read_text(encoding="utf-8")) - if not isinstance(data, dict): + raw_data: object = json.loads(manifest_path.read_text(encoding="utf-8")) + if not isinstance(raw_data, dict): raise ValueError( - f"mounts manifest must be a JSON object, got {type(data).__name__}: {data!r}" + f"mounts manifest must be a JSON object, got {type(raw_data).__name__}: {raw_data!r}" ) + data = cast("dict[str, object]", raw_data) mounts: list[MountSpec] = [] - for entry in data.get("mounts", []): + mounts_data = data.get("mounts", []) + if not isinstance(mounts_data, list): + raise ValueError("mounts manifest field 'mounts' must be a list") + typed_mounts_data = cast("list[object]", mounts_data) + for raw_entry in typed_mounts_data: + if not isinstance(raw_entry, dict): + raise ValueError(f"mounts manifest entry must be an object: {raw_entry!r}") + entry = cast("dict[str, object]", raw_entry) if "src_root" not in entry or "mount_at" not in entry: raise ValueError( f"mounts manifest entry missing 'src_root'/'mount_at': {entry!r}" ) mounts.append( MountSpec( - src_root=entry["src_root"], - runtime_path=entry.get("runtime_path", ""), - mount_at=entry["mount_at"], - attach_to=entry.get("attach_to") or None, - entry_doc=entry.get("entry_doc") or "index", - external=entry.get("external", False), + src_root=str(entry["src_root"]), + runtime_path=str(entry.get("runtime_path", "")), + mount_at=str(entry["mount_at"]), + attach_to=str(entry["attach_to"]) if entry.get("attach_to") else None, + entry_doc=str(entry["entry_doc"]) + if entry.get("entry_doc") + else "index", + external=bool(entry.get("external", False)), ) ) return MountsManifest( diff --git a/src/extensions/score_mounts/docs/internals/index.rst b/src/extensions/score_mounts/docs/internals/index.rst index 773bffc1f..2fbf7c3d5 100644 --- a/src/extensions/score_mounts/docs/internals/index.rst +++ b/src/extensions/score_mounts/docs/internals/index.rst @@ -64,7 +64,7 @@ Directory resolution External short paths begin with ``../+`` relative to the runfiles ``_main`` directory. The resolver therefore prefixes ``_main`` before turning that path into an absolute path. This distinction is covered by unit tests and -by ``//src/tests/mounts_external:docs``; do not resolve an external runtime path +the public ``docs_bzl`` integration suite; do not resolve an external runtime path relative to the manifest file, because the manifest can live below a Bazel package directory. @@ -107,8 +107,9 @@ Relevant checks are: * ``bazel test //src/extensions/score_mounts:score_mounts_tests`` for manifest parsing and path resolution; -* ``bazel test //src/tests/mounts_contract:mount_docs_e2e_test`` for mounted - rendering and toctree attachment; -* ``bazel run //src/tests/mounts_external:docs`` for an external bundle under - ``bazel run``; and -* the negative fixtures in ``src/tests/mounts_conflict`` for placement conflicts. +* ``.venv_docs/bin/python -m pytest -vv src/tests/docs_bzl/test_nested_bundles.py + src/tests/docs_bzl/test_external_bundle.py + src/tests/docs_bzl/test_invalid_bundle_placements.py`` for + mounted rendering, toctree attachment, external bundles under ``bazel run``, + sandboxed external builds, and placement conflicts; and +* ``bazel test //...`` for the internal extension unit tests. diff --git a/src/extensions/score_mounts/tests/test_resolver.py b/src/extensions/score_mounts/tests/test_resolver.py index 09325d2e2..d5fdff2b2 100644 --- a/src/extensions/score_mounts/tests/test_resolver.py +++ b/src/extensions/score_mounts/tests/test_resolver.py @@ -29,14 +29,14 @@ ) -def _write_manifest(tmp_path: Path, payload: dict) -> Path: +def _write_manifest(tmp_path: Path, payload: dict[str, object]) -> Path: tmp_path.mkdir(parents=True, exist_ok=True) manifest = tmp_path / "_mounts_manifest.json" manifest.write_text(json.dumps(payload), encoding="utf-8") return manifest -def test_load_single_entry(tmp_path: Path): +def test_load_single_entry(tmp_path: Path) -> None: manifest = _write_manifest( tmp_path, { @@ -60,7 +60,7 @@ def test_load_single_entry(tmp_path: Path): ] -def test_load_entry_with_attach_to_and_entry_doc(tmp_path: Path): +def test_load_entry_with_attach_to_and_entry_doc(tmp_path: Path) -> None: manifest = _write_manifest( tmp_path, { @@ -80,7 +80,7 @@ def test_load_entry_with_attach_to_and_entry_doc(tmp_path: Path): assert spec.entry_doc == "start" -def test_external_mount_keeps_execroot_and_runfiles_locations(tmp_path: Path): +def test_external_mount_keeps_execroot_and_runfiles_locations(tmp_path: Path) -> None: manifest = _write_manifest( tmp_path, { @@ -105,20 +105,22 @@ def test_external_mount_keeps_execroot_and_runfiles_locations(tmp_path: Path): assert specs[1].external is True -def test_load_missing_required_key_raises(tmp_path: Path): +def test_load_missing_required_key_raises(tmp_path: Path) -> None: manifest = _write_manifest(tmp_path, {"mounts": [{"runtime_path": "src/docs_dir"}]}) with pytest.raises(ValueError, match="missing 'src_root'/'mount_at'"): load_mounts_manifest(str(manifest)) -def test_load_non_object_raises(tmp_path: Path): +def test_load_non_object_raises(tmp_path: Path) -> None: manifest = tmp_path / "_mounts_manifest.json" manifest.write_text('["not", "an", "object"]', encoding="utf-8") with pytest.raises(ValueError, match="must be a JSON object"): load_mounts_manifest(str(manifest)) -def test_external_mount_uses_execroot_path_in_sandbox(tmp_path: Path, monkeypatch): +def test_external_mount_uses_execroot_path_in_sandbox( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.chdir(tmp_path) manifest = _write_manifest( tmp_path, @@ -139,7 +141,7 @@ def test_external_mount_uses_execroot_path_in_sandbox(tmp_path: Path, monkeypatc ) -def test_external_mount_uses_runfiles_root_under_bazel_run(tmp_path: Path): +def test_external_mount_uses_runfiles_root_under_bazel_run(tmp_path: Path) -> None: manifest = _write_manifest( tmp_path / "_main" / "package", { diff --git a/src/extensions/score_source_code_linker/xml_parser.py b/src/extensions/score_source_code_linker/xml_parser.py index 194c2651f..131014889 100644 --- a/src/extensions/score_source_code_linker/xml_parser.py +++ b/src/extensions/score_source_code_linker/xml_parser.py @@ -26,7 +26,7 @@ import os import xml.etree.ElementTree as ET from pathlib import Path -from typing import Any +from typing import Any, cast from xml.etree.ElementTree import Element from sphinx.application import Sphinx @@ -64,12 +64,15 @@ def parse_testcase_source_dirs(v: str) -> list[str]: """ if v in ("[]", ""): return [] - parsed = json.loads(v) - if not isinstance(parsed, list): + raw_parsed: object = json.loads(v) + if not isinstance(raw_parsed, list): raise ValueError( - f"testcase_source_dirs must be a list, got {type(parsed).__name__}: {v!r}" + f"testcase_source_dirs must be a list, got {type(raw_parsed).__name__}: {v!r}" ) - return parsed + parsed = cast("list[object]", raw_parsed) + if not all(isinstance(path, str) for path in parsed): + raise ValueError("testcase_source_dirs must contain only strings") + return [path for path in parsed if isinstance(path, str)] def is_testcase_in_scope(test_file: str | None, allowed_dirs: list[str]) -> bool: diff --git a/src/extensions/score_sync_toml/test_mounts.py b/src/extensions/score_sync_toml/test_mounts.py index f057e2aa8..1f130b642 100644 --- a/src/extensions/score_sync_toml/test_mounts.py +++ b/src/extensions/score_sync_toml/test_mounts.py @@ -3,6 +3,10 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +from pathlib import Path + +import pytest + from src.extensions.score_sync_toml import _mounts from src.extensions.score_sync_toml._mounts import materialize_mounts @@ -42,8 +46,8 @@ def test_materialize_mounts_omits_default_fields(): def test_materialize_mounts_maps_external_runfiles_path_to_bazel_bin( - tmp_path, monkeypatch -): + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: runfiles_dir = tmp_path / "runfiles" walk_dir = runfiles_dir / "score_process+" / "process" walk_dir.mkdir(parents=True) diff --git a/src/incremental_dirty_build_test.py b/src/incremental_dirty_build_test.py index 9e9ecd01a..7c13e6c8c 100644 --- a/src/incremental_dirty_build_test.py +++ b/src/incremental_dirty_build_test.py @@ -18,7 +18,11 @@ from pyfakefs.fake_filesystem import FakeFilesystem as FFS -from incremental import _mounted_watch_dirs, clean_builddir_if_stale, update_module_hash +from incremental import ( + _mounted_watch_dirs, # pyright: ignore[reportPrivateUsage] - white-box unit test + clean_builddir_if_stale, + update_module_hash, +) _BUILD = Path("/build") _MODULE = Path("/MODULE.bazel") diff --git a/src/tests/README.md b/src/tests/README.md index 060487730..6c761f1b7 100644 --- a/src/tests/README.md +++ b/src/tests/README.md @@ -21,13 +21,21 @@ *Also known as system, product, or black-box tests.* -This directory contains end-to-end tests, namely: +This directory contains end-to-end tests at two scopes: -- `docs_bzl`: tests based on the public `docs()` macro. Must be executed via plain pytest (run via `.venv_docs`) as they execute real `bazel run`/`bazel build` internally. See [docs_bzl/README.md](docs_bzl/README.md) for details. +- `docs_bzl`: controlled integration scenarios for the public `docs.bzl` API: + `docs()`, `docs_bundle()`, composition, invalid configurations, and external + Bzlmod bundles. They run via plain pytest and issue the same real + `bazel build` / `bazel run` commands a consumer uses. See + [docs_bzl/README.md](docs_bzl/README.md) for layout and commands. - `downstream_compatibility`: *(formerly consumer tests)* Tests local changes and Git-based overrides against real consumer repositories. This provides broad, intentionally less-controlled coverage and helps detect breaking changes in the docs-as-code system before they affect downstream consumers. -### Targets +`bazel test //...` covers internal unit tests. It intentionally does not run +the public `docs_bzl` integration suite; CI and local contributors run that +suite explicitly through pytest. + +### Bazel test targets You can query targets in this directory with: diff --git a/src/tests/docs_bzl/README.md b/src/tests/docs_bzl/README.md index b483b4764..7c7a6ea02 100644 --- a/src/tests/docs_bzl/README.md +++ b/src/tests/docs_bzl/README.md @@ -6,17 +6,39 @@ ******************************************************************************* --> -# `docs()` end-to-end tests +# Public `docs.bzl` integration tests -These tests run **outside** Bazel with pytest and drive the public `docs()` -macro through real `bazel run` / `bazel build` commands against small fixture -packages in this directory. They test exactly what a user runs. +These tests run **outside** Bazel with pytest and drive the public `docs.bzl` +macros through real `bazel run` / `bazel build` commands. They cover exactly +what a consumer invokes: `docs()`, `docs_bundle()`, mounts, and failure cases. + +```text +docs_bzl/ +├── scenarios/ # one fixture per consumer scenario +│ ├── basic_docs/ +│ ├── external_needs/ +│ ├── metamodel_violation/ +│ ├── nested_bundles/ +│ ├── external_bundle/ +│ └── invalid_bundle_placements/ +└── test_.py +``` + +Each scenario has a fixture folder and a matching pytest file. The names describe +consumer behavior, not the Bazel mechanism used to execute it. Positive rendering +uses `bazel run`; sandbox-only behavior uses `bazel build`; invalid package +definitions are expected build failures. Assertions retain rendered HTML, +manifest order and metadata, source links, toctree attachment, and diagnostics. Note that these tests run `bazel` commands, so they are slow. They need to be executed -sequentially. Use sparingly. +sequentially. Use sparingly. They do not call `bazel clean`, so the persistent +Bazel server and its action, repository, and disk caches are reused between +cases. There is still a small analysis/startup cost per command; keep scenarios +coarse-grained and use `bazel run` only where runtime behavior matters. Run via: - pytest src/tests/docs_bzl -vv + .venv_docs/bin/python -m pytest -vv src/tests/docs_bzl -*(no venv is required)* +The suite is deliberately separate from `bazel test //...`, since pytest is its +driver. CI stores its JUnit XML together with the Bazel test reports. diff --git a/src/tests/docs_bzl/helpers.py b/src/tests/docs_bzl/helpers.py index 13611ed6f..e4024fe59 100644 --- a/src/tests/docs_bzl/helpers.py +++ b/src/tests/docs_bzl/helpers.py @@ -10,7 +10,7 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -"""Helpers for Category-2 e2e tests: run real bazel against in-repo fixtures.""" +"""Helpers for public docs.bzl integration tests driven through Bazel.""" import json import shutil @@ -18,13 +18,14 @@ import time from dataclasses import dataclass from pathlib import Path +from typing import cast from src.helper_lib import find_git_root -FIXTURES = Path(__file__).parent / "fixtures" +TEST_ROOT = Path(__file__).parent GIT_ROOT = find_git_root() assert GIT_ROOT -FIXTURES_BAZEL = "//" + str(FIXTURES.relative_to(GIT_ROOT)) +TEST_ROOT_BAZEL = "//" + str(TEST_ROOT.relative_to(GIT_ROOT)) def repo_root() -> Path: @@ -72,29 +73,36 @@ class RunResult: artifacts: dict[str, Path] | None -def run_fixture( +def run_package( bazel_cmd: str, - fixture: str, + package: str, target: str, expect_error: bool = False, ) -> RunResult: - """Run bazel in a fixture subdirectory.""" + """Run Bazel against one small public-API test package.""" assert bazel_cmd in ("build", "run"), "only build and run are supported" - assert target.startswith(":"), "target must be relative to fixture" + assert target.startswith(":"), "target must be relative to package" - if not (FIXTURES / fixture).is_dir(): - raise ValueError(f"fixture {fixture} does not exist in {FIXTURES}") + package_dir = TEST_ROOT / package + if not package_dir.is_dir(): + raise ValueError(f"test package {package} does not exist in {TEST_ROOT}") # Split logs by bazel commands visually without adding any fancy formatting dependency print("") - full_target = FIXTURES_BAZEL + "/" + fixture + target + full_target = TEST_ROOT_BAZEL + "/" + package + target - clean_build(FIXTURES / fixture) + # aspect_rules_py creates the runtime venv inside the target's runfiles + # tree. It cannot reliably replace an already-populated tree on a second + # ``bazel run``. Remove only that disposable venv; Bazel's server and all + # action/repository caches remain intact. + if bazel_cmd == "run": + runfiles_venv = built_output(package, f"{target[1:]}.runfiles/.docs.venv") + shutil.rmtree(runfiles_venv, ignore_errors=True) p1 = run_bazel([bazel_cmd, full_target], expect_error=expect_error) - if bazel_cmd == "build": + if bazel_cmd == "build" and target == ":needs_json": # Generic solution needs to use cquery. # That extra cquery is about 0,5 seconds, which sounds horrible for testing, # but considering how slow bazel is anyway... @@ -104,12 +112,12 @@ def run_fixture( artifacts_dir = Path(p2.stdout.strip().splitlines()[0]) else: assert GIT_ROOT - # e.g. bazel-out/k8-fastbuild/bin/src/tests/docs_bzl/fixtures/external_needs/producer/needs_json/_build/needs + # e.g. bazel-out/k8-fastbuild/bin/src/tests/docs_bzl/scenarios/external_needs/producer/needs_json/_build/needs artifacts_dir = ( GIT_ROOT / "bazel-out/k8-fastbuild/bin/" - / FIXTURES.relative_to(GIT_ROOT) - / fixture + / TEST_ROOT.relative_to(GIT_ROOT) + / package / target[1:] / "_build/needs" ) @@ -124,19 +132,41 @@ def run_fixture( return RunResult( stdout=p1.stdout, stderr=p1.stderr, - build_dir=FIXTURES / fixture / "_build", + build_dir=package_dir / "_build", artifacts=artifacts, ) -def clean_build(path: Path) -> None: - """Delete the _build directory in a path, if it exists.""" - shutil.rmtree(path / "_build", ignore_errors=True) +def run_scenario( + bazel_cmd: str, + scenario: str, + target: str, + expect_error: bool = False, +) -> RunResult: + """Run one general docs() scenario.""" + return run_package(bazel_cmd, f"scenarios/{scenario}", target, expect_error) + + +def built_output(package: str, filename: str) -> Path: + """Return a declared output from a package built in the current configuration.""" + root = repo_root() + return root / "bazel-bin" / TEST_ROOT.relative_to(root) / package / filename -def load_needs(needs_json: Path): - data = json.loads(needs_json.read_text(encoding="utf-8")) +def load_needs(needs_json: Path) -> dict[str, object]: + raw_data: object = json.loads(needs_json.read_text(encoding="utf-8")) + if not isinstance(raw_data, dict): + raise ValueError("needs.json must be an object") + data = cast("dict[str, object]", raw_data) needs: dict[str, object] = {} - for version in data.get("versions", {}).values(): - needs.update(version.get("needs", {})) + versions = data.get("versions", {}) + if not isinstance(versions, dict): + raise ValueError("needs.json field 'versions' must be an object") + typed_versions = cast("dict[str, object]", versions) + for version in typed_versions.values(): + if isinstance(version, dict): + typed_version = cast("dict[str, object]", version) + version_needs = typed_version.get("needs", {}) + if isinstance(version_needs, dict): + needs.update(cast("dict[str, object]", version_needs)) return needs diff --git a/src/tests/docs_bzl/fixtures/basic/BUILD b/src/tests/docs_bzl/scenarios/basic_docs/BUILD similarity index 90% rename from src/tests/docs_bzl/fixtures/basic/BUILD rename to src/tests/docs_bzl/scenarios/basic_docs/BUILD index a67698dba..5353407ad 100644 --- a/src/tests/docs_bzl/fixtures/basic/BUILD +++ b/src/tests/docs_bzl/scenarios/basic_docs/BUILD @@ -15,5 +15,5 @@ load("//:docs.bzl", "docs") docs( source_dir = "docs", - test_sources = ["src/tests/docs_bzl/fixtures/basic"], + test_sources = ["src/tests/docs_bzl/scenarios/basic_docs"], ) diff --git a/src/tests/docs_bzl/fixtures/basic/docs/conf.py b/src/tests/docs_bzl/scenarios/basic_docs/docs/conf.py similarity index 100% rename from src/tests/docs_bzl/fixtures/basic/docs/conf.py rename to src/tests/docs_bzl/scenarios/basic_docs/docs/conf.py diff --git a/src/tests/docs_bzl/fixtures/basic/docs/index.rst b/src/tests/docs_bzl/scenarios/basic_docs/docs/index.rst similarity index 100% rename from src/tests/docs_bzl/fixtures/basic/docs/index.rst rename to src/tests/docs_bzl/scenarios/basic_docs/docs/index.rst diff --git a/src/tests/mounts_external/BUILD b/src/tests/docs_bzl/scenarios/external_bundle/BUILD similarity index 93% rename from src/tests/mounts_external/BUILD rename to src/tests/docs_bzl/scenarios/external_bundle/BUILD index b9f98c06d..05e6f9847 100644 --- a/src/tests/mounts_external/BUILD +++ b/src/tests/docs_bzl/scenarios/external_bundle/BUILD @@ -18,6 +18,7 @@ load("//:docs.bzl", "docs") # than silently omitting it. docs( source_dir = "host_docs", + test_sources = ["src/tests/docs_bzl/scenarios/external_bundle"], bundles = [{ "bundle": "@score_process//:docs_bundle", "mount_at": "process", diff --git a/src/tests/mounts_external/host_docs/conf.py b/src/tests/docs_bzl/scenarios/external_bundle/host_docs/conf.py similarity index 100% rename from src/tests/mounts_external/host_docs/conf.py rename to src/tests/docs_bzl/scenarios/external_bundle/host_docs/conf.py diff --git a/src/tests/mounts_external/host_docs/index.rst b/src/tests/docs_bzl/scenarios/external_bundle/host_docs/index.rst similarity index 100% rename from src/tests/mounts_external/host_docs/index.rst rename to src/tests/docs_bzl/scenarios/external_bundle/host_docs/index.rst diff --git a/src/tests/docs_bzl/fixtures/external_needs/consumer/BUILD b/src/tests/docs_bzl/scenarios/external_needs/consumer/BUILD similarity index 84% rename from src/tests/docs_bzl/fixtures/external_needs/consumer/BUILD rename to src/tests/docs_bzl/scenarios/external_needs/consumer/BUILD index 977e96715..a9bcf2ee6 100644 --- a/src/tests/docs_bzl/fixtures/external_needs/consumer/BUILD +++ b/src/tests/docs_bzl/scenarios/external_needs/consumer/BUILD @@ -18,6 +18,6 @@ load("//:docs.bzl", "docs") # avoiding the workspace-wide bazel-testlogs scan during the html build. docs( source_dir = "docs", - data = ["//src/tests/docs_bzl/fixtures/external_needs/producer:needs_json"], - test_sources = ["src/tests/docs_bzl/fixtures/external_needs/consumer"], + data = ["//src/tests/docs_bzl/scenarios/external_needs/producer:needs_json"], + test_sources = ["src/tests/docs_bzl/scenarios/external_needs/consumer"], ) diff --git a/src/tests/docs_bzl/fixtures/external_needs/consumer/docs/conf.py b/src/tests/docs_bzl/scenarios/external_needs/consumer/docs/conf.py similarity index 100% rename from src/tests/docs_bzl/fixtures/external_needs/consumer/docs/conf.py rename to src/tests/docs_bzl/scenarios/external_needs/consumer/docs/conf.py diff --git a/src/tests/docs_bzl/fixtures/external_needs/consumer/docs/index.rst b/src/tests/docs_bzl/scenarios/external_needs/consumer/docs/index.rst similarity index 100% rename from src/tests/docs_bzl/fixtures/external_needs/consumer/docs/index.rst rename to src/tests/docs_bzl/scenarios/external_needs/consumer/docs/index.rst diff --git a/src/tests/docs_bzl/fixtures/external_needs/consumer/docs/metamodel.yaml b/src/tests/docs_bzl/scenarios/external_needs/consumer/docs/metamodel.yaml similarity index 100% rename from src/tests/docs_bzl/fixtures/external_needs/consumer/docs/metamodel.yaml rename to src/tests/docs_bzl/scenarios/external_needs/consumer/docs/metamodel.yaml diff --git a/src/tests/docs_bzl/fixtures/external_needs/producer/BUILD b/src/tests/docs_bzl/scenarios/external_needs/producer/BUILD similarity index 100% rename from src/tests/docs_bzl/fixtures/external_needs/producer/BUILD rename to src/tests/docs_bzl/scenarios/external_needs/producer/BUILD diff --git a/src/tests/docs_bzl/fixtures/external_needs/producer/docs/conf.py b/src/tests/docs_bzl/scenarios/external_needs/producer/docs/conf.py similarity index 100% rename from src/tests/docs_bzl/fixtures/external_needs/producer/docs/conf.py rename to src/tests/docs_bzl/scenarios/external_needs/producer/docs/conf.py diff --git a/src/tests/docs_bzl/fixtures/external_needs/producer/docs/index.rst b/src/tests/docs_bzl/scenarios/external_needs/producer/docs/index.rst similarity index 100% rename from src/tests/docs_bzl/fixtures/external_needs/producer/docs/index.rst rename to src/tests/docs_bzl/scenarios/external_needs/producer/docs/index.rst diff --git a/src/tests/docs_bzl/fixtures/external_needs/producer/docs/metamodel.yaml b/src/tests/docs_bzl/scenarios/external_needs/producer/docs/metamodel.yaml similarity index 100% rename from src/tests/docs_bzl/fixtures/external_needs/producer/docs/metamodel.yaml rename to src/tests/docs_bzl/scenarios/external_needs/producer/docs/metamodel.yaml diff --git a/src/tests/mounts_conflict/BUILD b/src/tests/docs_bzl/scenarios/invalid_bundle_placements/BUILD similarity index 100% rename from src/tests/mounts_conflict/BUILD rename to src/tests/docs_bzl/scenarios/invalid_bundle_placements/BUILD diff --git a/src/tests/mounts_conflict/child/a.rst b/src/tests/docs_bzl/scenarios/invalid_bundle_placements/child/a.rst similarity index 100% rename from src/tests/mounts_conflict/child/a.rst rename to src/tests/docs_bzl/scenarios/invalid_bundle_placements/child/a.rst diff --git a/src/tests/docs_bzl/fixtures/metamodel_violation/BUILD b/src/tests/docs_bzl/scenarios/metamodel_violation/BUILD similarity index 100% rename from src/tests/docs_bzl/fixtures/metamodel_violation/BUILD rename to src/tests/docs_bzl/scenarios/metamodel_violation/BUILD diff --git a/src/tests/docs_bzl/fixtures/metamodel_violation/docs/conf.py b/src/tests/docs_bzl/scenarios/metamodel_violation/docs/conf.py similarity index 100% rename from src/tests/docs_bzl/fixtures/metamodel_violation/docs/conf.py rename to src/tests/docs_bzl/scenarios/metamodel_violation/docs/conf.py diff --git a/src/tests/docs_bzl/fixtures/metamodel_violation/docs/index.rst b/src/tests/docs_bzl/scenarios/metamodel_violation/docs/index.rst similarity index 100% rename from src/tests/docs_bzl/fixtures/metamodel_violation/docs/index.rst rename to src/tests/docs_bzl/scenarios/metamodel_violation/docs/index.rst diff --git a/src/tests/docs_bzl/fixtures/metamodel_violation/docs/metamodel.yaml b/src/tests/docs_bzl/scenarios/metamodel_violation/docs/metamodel.yaml similarity index 100% rename from src/tests/docs_bzl/fixtures/metamodel_violation/docs/metamodel.yaml rename to src/tests/docs_bzl/scenarios/metamodel_violation/docs/metamodel.yaml diff --git a/src/tests/mounts_contract/BUILD b/src/tests/docs_bzl/scenarios/nested_bundles/BUILD similarity index 64% rename from src/tests/mounts_contract/BUILD rename to src/tests/docs_bzl/scenarios/nested_bundles/BUILD index 54049b8ab..4e31b5d08 100644 --- a/src/tests/mounts_contract/BUILD +++ b/src/tests/docs_bzl/scenarios/nested_bundles/BUILD @@ -13,7 +13,6 @@ load("//:bzl/mount_rules.bzl", "create_mounts_manifest") load("//:docs.bzl", "docs", "docs_bundle") -load("//:score_pytest.bzl", "score_pytest") docs_bundle( name = "child", @@ -58,38 +57,13 @@ create_mounts_manifest( bundle = ":ordered_aggregate", ) -# A complete docs() invocation is the synthetic host project used by the -# end-to-end test below. +# A complete docs() invocation is the synthetic host project driven by the +# pytest integration test in //src/tests/docs_bzl. docs( source_dir = "host_docs", + test_sources = ["src/tests/docs_bzl/scenarios/nested_bundles"], bundles = [{ "bundle": ":parent", "mount_at": "concepts/example_bundle", }], ) - -score_pytest( - name = "mount_docs_e2e_test", - size = "medium", - srcs = ["test_mount_docs_e2e.py"], - data = [ - ":docs", - ":_mounts_manifest", - ":ordered_aggregate_manifest", - ":sourcelinks_json", - ":parent", - "host_docs/conf.py", - "host_docs/concepts/index.rst", - "host_docs/index.rst", - "child/landing.rst", - ], - deps = ["//src/tests/docs_e2e:e2e_support"], - env = { - "FIXTURE_DOCS_BINARY": "$(rootpath :docs)", - "FIXTURE_MOUNTS_MANIFEST": "$(rootpath :_mounts_manifest)", - "ORDERED_AGGREGATE_MANIFEST": "$(rootpath :ordered_aggregate_manifest)", - "FIXTURE_SOURCELINKS": "$(rootpath :sourcelinks_json)", - "HOST_DOCS_CONF": "$(rootpath host_docs/conf.py)", - }, - pytest_config = "//:pyproject.toml", -) diff --git a/src/tests/mounts_contract/child/example.py b/src/tests/docs_bzl/scenarios/nested_bundles/child/example.py similarity index 100% rename from src/tests/mounts_contract/child/example.py rename to src/tests/docs_bzl/scenarios/nested_bundles/child/example.py diff --git a/src/tests/mounts_contract/child/index.rst b/src/tests/docs_bzl/scenarios/nested_bundles/child/index.rst similarity index 100% rename from src/tests/mounts_contract/child/index.rst rename to src/tests/docs_bzl/scenarios/nested_bundles/child/index.rst diff --git a/src/tests/mounts_contract/child/landing.rst b/src/tests/docs_bzl/scenarios/nested_bundles/child/landing.rst similarity index 100% rename from src/tests/mounts_contract/child/landing.rst rename to src/tests/docs_bzl/scenarios/nested_bundles/child/landing.rst diff --git a/src/tests/mounts_contract/host_docs/concepts/index.rst b/src/tests/docs_bzl/scenarios/nested_bundles/host_docs/concepts/index.rst similarity index 100% rename from src/tests/mounts_contract/host_docs/concepts/index.rst rename to src/tests/docs_bzl/scenarios/nested_bundles/host_docs/concepts/index.rst diff --git a/src/tests/mounts_contract/host_docs/conf.py b/src/tests/docs_bzl/scenarios/nested_bundles/host_docs/conf.py similarity index 100% rename from src/tests/mounts_contract/host_docs/conf.py rename to src/tests/docs_bzl/scenarios/nested_bundles/host_docs/conf.py diff --git a/src/tests/mounts_contract/host_docs/index.rst b/src/tests/docs_bzl/scenarios/nested_bundles/host_docs/index.rst similarity index 100% rename from src/tests/mounts_contract/host_docs/index.rst rename to src/tests/docs_bzl/scenarios/nested_bundles/host_docs/index.rst diff --git a/src/tests/mounts_contract/other/index.rst b/src/tests/docs_bzl/scenarios/nested_bundles/other/index.rst similarity index 100% rename from src/tests/mounts_contract/other/index.rst rename to src/tests/docs_bzl/scenarios/nested_bundles/other/index.rst diff --git a/src/tests/mounts_contract/parent/index.rst b/src/tests/docs_bzl/scenarios/nested_bundles/parent/index.rst similarity index 100% rename from src/tests/mounts_contract/parent/index.rst rename to src/tests/docs_bzl/scenarios/nested_bundles/parent/index.rst diff --git a/src/tests/docs_bzl/test_basic_docs.py b/src/tests/docs_bzl/test_basic_docs.py new file mode 100644 index 000000000..c6f8f3e06 --- /dev/null +++ b/src/tests/docs_bzl/test_basic_docs.py @@ -0,0 +1,34 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# ******************************************************************************* +"""Public docs() smoke scenario.""" + +from src.tests.docs_bzl.helpers import run_scenario + + +def test_basic_docs_builds_html(): + result = run_scenario("run", "basic_docs", ":docs") + + index_html = result.build_dir / "index.html" + + assert "Basic Test" in index_html.read_text(encoding="utf-8") diff --git a/src/tests/docs_bzl/test_docs_bzl.py b/src/tests/docs_bzl/test_docs_bzl.py deleted file mode 100644 index 701536777..000000000 --- a/src/tests/docs_bzl/test_docs_bzl.py +++ /dev/null @@ -1,74 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* - -""" -end-to-end tests for the public docs() macro. -See README.md in this directory for usage instructions. -""" - -from pathlib import Path - -from src.tests.docs_bzl.helpers import ( - load_needs, - run_fixture, -) - -FIXTURES = Path(__file__).parent / "fixtures" - - -def test_basic_docs_builds_html(): - result = run_fixture("run", "basic", ":docs") - - index_html = result.build_dir / "index.html" - - assert "Basic Test" in index_html.read_text(encoding="utf-8") - - -def test_metamodel_violation_fails_build(): - result = run_fixture( - "build", "metamodel_violation", ":needs_json", expect_error=True - ) - assert "is missing required attribute" in result.stderr - - -def test_producer_needs_json_contains_local_need(): - """ - The producer must export its own need - """ - result = run_fixture("build", "external_needs/producer", ":needs_json") - assert result.artifacts - - needs_json = result.artifacts["needs.json"] - needs = load_needs(needs_json) - - assert "test_req__producer__demo" in needs, sorted(needs) - - -def test_consumer_link_resolves(): - """ - The consumer must resolve the producer's need as an external link in its HTML output. - - Exit 0 under -W already proves no "not found" warning fired; - the html-content assert is the stronger guard: the producer id must - appear as a resolved external link (href into the producer's base_url), not - just as literal text. - """ - result = run_fixture("run", "external_needs/consumer", ":docs") - - index = result.build_dir / "index.html" - html = index.read_text(encoding="utf-8") - - # A resolved external link points at the producer's base_url + fragment. - assert "external-needs-producer/main/index.html#test_req__producer__demo" in html, ( - html - ) diff --git a/src/tests/docs_bzl/test_external_bundle.py b/src/tests/docs_bzl/test_external_bundle.py new file mode 100644 index 000000000..54db947bd --- /dev/null +++ b/src/tests/docs_bzl/test_external_bundle.py @@ -0,0 +1,33 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# ******************************************************************************* +"""External docs_bundle() scenario.""" + +from src.tests.docs_bzl.helpers import run_scenario + + +def test_external_bundle_builds_in_sandbox_and_at_runtime(): + run_scenario("build", "external_bundle", ":needs_json") + result = run_scenario("run", "external_bundle", ":docs") + + assert (result.build_dir / "index.html").is_file() diff --git a/src/tests/docs_bzl/test_external_needs.py b/src/tests/docs_bzl/test_external_needs.py new file mode 100644 index 000000000..41a3b7b29 --- /dev/null +++ b/src/tests/docs_bzl/test_external_needs.py @@ -0,0 +1,42 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# ******************************************************************************* +"""External needs scenario for the public docs() API.""" + +from src.tests.docs_bzl.helpers import load_needs, run_scenario + + +def test_producer_needs_json_contains_local_need(): + result = run_scenario("build", "external_needs/producer", ":needs_json") + assert result.artifacts + + needs = load_needs(result.artifacts["needs.json"]) + assert "test_req__producer__demo" in needs, sorted(needs) + + +def test_consumer_link_resolves(): + """A consumer renders the producer's need as an external HTML link.""" + result = run_scenario("run", "external_needs/consumer", ":docs") + + html = (result.build_dir / "index.html").read_text(encoding="utf-8") + assert "external-needs-producer/main/index.html#test_req__producer__demo" in html diff --git a/src/tests/docs_bzl/test_invalid_bundle_placements.py b/src/tests/docs_bzl/test_invalid_bundle_placements.py new file mode 100644 index 000000000..2c59249b9 --- /dev/null +++ b/src/tests/docs_bzl/test_invalid_bundle_placements.py @@ -0,0 +1,33 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# ******************************************************************************* +"""Invalid docs_bundle() placement scenario.""" + +from src.tests.docs_bzl.helpers import run_scenario + + +def test_invalid_bundle_placements_are_rejected_during_analysis(): + run_scenario("build", "invalid_bundle_placements", ":bad", expect_error=True) + run_scenario( + "build", "invalid_bundle_placements", ":bad_attach_to", expect_error=True + ) diff --git a/src/tests/docs_bzl/test_metamodel_violation.py b/src/tests/docs_bzl/test_metamodel_violation.py new file mode 100644 index 000000000..0dfe8b1ff --- /dev/null +++ b/src/tests/docs_bzl/test_metamodel_violation.py @@ -0,0 +1,33 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# ******************************************************************************* +"""Invalid metamodel scenario for the public docs() API.""" + +from src.tests.docs_bzl.helpers import run_scenario + + +def test_metamodel_violation_fails_build(): + result = run_scenario( + "build", "metamodel_violation", ":needs_json", expect_error=True + ) + assert "is missing required attribute" in result.stderr diff --git a/src/tests/docs_bzl/test_nested_bundles.py b/src/tests/docs_bzl/test_nested_bundles.py new file mode 100644 index 000000000..8936e42ea --- /dev/null +++ b/src/tests/docs_bzl/test_nested_bundles.py @@ -0,0 +1,71 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# ******************************************************************************* +"""Nested docs_bundle() scenario.""" + +import json + +from src.tests.docs_bzl.helpers import built_output, run_scenario + + +def test_nested_bundles_render_and_preserve_metadata(): + result = run_scenario("run", "nested_bundles", ":docs") + + manifest = json.loads( + built_output("scenarios/nested_bundles", "_mounts_manifest.json").read_text( + encoding="utf-8" + ) + ) + assert [mount["mount_at"] for mount in manifest["mounts"]] == [ + "concepts/example_bundle", + "concepts/example_bundle/child", + ] + assert [mount["entry_doc"] for mount in manifest["mounts"]] == [ + "index", + "landing", + ] + + sourcelinks = json.loads( + built_output("scenarios/nested_bundles", "sourcelinks_json.json").read_text( + encoding="utf-8" + ) + ) + assert ( + sourcelinks[0]["file"] + == "src/tests/docs_bzl/scenarios/nested_bundles/child/example.py" + ) + assert (result.build_dir / "concepts" / "example_bundle" / "index.html").is_file() + assert ( + result.build_dir / "concepts" / "example_bundle" / "child" / "landing.html" + ).is_file() + + +def test_nested_bundle_aggregator_preserves_declared_order(): + run_scenario("build", "nested_bundles", ":ordered_aggregate_manifest") + + manifest = json.loads( + built_output( + "scenarios/nested_bundles", "ordered_aggregate_manifest.json" + ).read_text(encoding="utf-8") + ) + assert [mount["mount_at"] for mount in manifest["mounts"]] == ["first", "second"] diff --git a/src/tests/docs_e2e/BUILD b/src/tests/docs_e2e/BUILD deleted file mode 100644 index 37b21f466..000000000 --- a/src/tests/docs_e2e/BUILD +++ /dev/null @@ -1,46 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* - -load("@aspect_rules_py//py:defs.bzl", "py_library") -load("//:docs.bzl", "docs") -load("//:score_pytest.bzl", "score_pytest") - -py_library( - name = "e2e_support", - srcs = ["support.py"], - imports = ["."], - visibility = ["//visibility:public"], -) - -docs( - source_dir = "host_docs", -) - -score_pytest( - name = "docs_e2e_test", - size = "medium", - srcs = ["test_docs_e2e.py"], - data = [ - ":docs", - ":sourcelinks_json", - "host_docs/conf.py", - "host_docs/index.rst", - ], - deps = [":e2e_support"], - env = { - "DOCS_BINARY": "$(rootpath :docs)", - "DOCS_CONF": "$(rootpath host_docs/conf.py)", - "SOURCELINKS": "$(rootpath :sourcelinks_json)", - }, - pytest_config = "//:pyproject.toml", -) diff --git a/src/tests/docs_e2e/host_docs/conf.py b/src/tests/docs_e2e/host_docs/conf.py deleted file mode 100644 index 717d335bf..000000000 --- a/src/tests/docs_e2e/host_docs/conf.py +++ /dev/null @@ -1,16 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* - -project = "Docs E2E fixture" -project_url = "https://example.invalid/docs-e2e" -extensions = ["score_sphinx_bundle"] diff --git a/src/tests/docs_e2e/host_docs/index.rst b/src/tests/docs_e2e/host_docs/index.rst deleted file mode 100644 index cfabcc31f..000000000 --- a/src/tests/docs_e2e/host_docs/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. - # ******************************************************************************* - # Copyright (c) 2026 Contributors to the Eclipse Foundation - # - # See the NOTICE file(s) distributed with this work for additional - # information regarding copyright ownership. - # - # This program and the accompanying materials are made available under the - # terms of the Apache License Version 2.0 which is available at - # https://www.apache.org/licenses/LICENSE-2.0 - # - # SPDX-License-Identifier: Apache-2.0 - # ******************************************************************************* - -Docs E2E fixture -================ diff --git a/src/tests/docs_e2e/support.py b/src/tests/docs_e2e/support.py deleted file mode 100644 index ccead1eb3..000000000 --- a/src/tests/docs_e2e/support.py +++ /dev/null @@ -1,66 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* -"""Shared runner for end-to-end tests of docs() binaries.""" - -import os -import shutil -import subprocess -from pathlib import Path - - -def runfile(path_env: str) -> Path: - """Resolve a runfiles-relative path passed through a test environment variable.""" - return ( - Path(os.environ["TEST_SRCDIR"]) - / os.environ["TEST_WORKSPACE"] - / os.environ[path_env] - ) - - -def run_docs_build( - tmp_path: Path, - *, - docs_binary: Path, - source_dir: Path, - sourcelinks: Path, - mounts_manifest: Path | None = None, -) -> subprocess.CompletedProcess[str]: - """Run a docs() binary in an isolated writable workspace. - - The binary normally runs from a developer's workspace. This helper provides - the small writable equivalent needed by a Bazel test while preserving the - runfiles layout used to resolve in-tree mount sources. - """ - runfiles_workspace = Path(os.environ["TEST_SRCDIR"]) / os.environ["TEST_WORKSPACE"] - (tmp_path / "src").symlink_to(runfiles_workspace / "src", target_is_directory=True) - - copied_source_dir = tmp_path / "docs" - shutil.copytree(source_dir, copied_source_dir) - for name in ["MODULE.bazel", "MODULE.bazel.lock", "BUILD"]: - (tmp_path / name).touch() - - env = os.environ.copy() - env["SOURCE_DIRECTORY"] = str(copied_source_dir) - env["MOUNTS_MANIFEST"] = str(mounts_manifest) if mounts_manifest else "" - env["DATA"] = "[]" - env["ACTION"] = "incremental" - env["SCORE_SOURCELINKS"] = str(sourcelinks) - - return subprocess.run( - [str(docs_binary)], - cwd=tmp_path, - env=env, - capture_output=True, - text=True, - check=False, - ) diff --git a/src/tests/docs_e2e/test_docs_e2e.py b/src/tests/docs_e2e/test_docs_e2e.py deleted file mode 100644 index d570262c3..000000000 --- a/src/tests/docs_e2e/test_docs_e2e.py +++ /dev/null @@ -1,30 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* -"""End-to-end test for a minimal docs() project without mounts.""" - -from pathlib import Path - -from support import run_docs_build, runfile - - -def test_docs_builds_html(tmp_path: Path): - """docs() builds the host documentation without a mount configuration.""" - result = run_docs_build( - tmp_path, - docs_binary=runfile("DOCS_BINARY"), - source_dir=runfile("DOCS_CONF").parent, - sourcelinks=runfile("SOURCELINKS"), - ) - - assert result.returncode == 0, result.stdout + result.stderr - assert (tmp_path / "_build" / "index.html").is_file() diff --git a/src/tests/mounts_contract/README.md b/src/tests/mounts_contract/README.md deleted file mode 100644 index 08b043749..000000000 --- a/src/tests/mounts_contract/README.md +++ /dev/null @@ -1,82 +0,0 @@ - - -# End-to-end test for Bazel documentation mounts - -This directory contains one black-box test for the public `docs_bundle()` and -`docs()` macros. It does not inspect a Starlark provider or the generated -mount manifest. Instead, it runs the same `:docs` executable that a user would -start with `bazel run` and asserts the visible Sphinx result. - -Run the test with: - -```console -bazel test //src/tests/mounts_contract:mount_docs_e2e_test -``` - -## Fixture - -The fixture builds this small documentation site: - -```text -host_docs/ -└── concepts/index - └── parent bundle at concepts/example_bundle - └── child bundle at child -``` - -`docs_bundle()` defines the parent and child bundles. `docs()` defines the -synthetic host project and creates its ordinary `:docs` binary, just as it -does in a real project. - -## Why the test starts a subprocess - -`docs()` is a Starlark macro, not an executable function. During `bazel test` -Bazel evaluates the macro and creates targets, including this fixture's -`:docs` `py_binary`. It does not run that binary merely because the macro was -evaluated. - -The Python test starts that already-built binary in a subprocess: - -```text -docs() macro - -> defines :docs -bazel test - -> builds :docs and starts the pytest test -pytest subprocess - -> executes :docs - -> incremental.py runs Sphinx - -> score_mounts configures sphinx-mounts -``` - -This is deliberately not a nested `bazel run`: a Bazel test must not start -another Bazel process in its sandbox. The subprocess directly executes the -`:docs` file that Bazel placed in the test runfiles. - -## What the test verifies - -`test_docs_build_mounts_bundle_and_extends_toctree` creates a writable -temporary workspace, runs the fixture's `:docs` binary, and checks: - -- Sphinx reports that it extended the `concepts/index` toctree. -- The mounted document is rendered as - `_build/concepts/example_bundle/index.html`. - -Together these assertions cover bundle composition, the mount manifest, -runfiles path resolution, the Sphinx extension, the target Toctree, and HTML -output. A wrong `mount_at` or `attach_to` fails this test as a user would see -it: Sphinx cannot extend the expected Toctree or the mounted page is absent. - -## Related tests - -- `bazel test //src/extensions/score_mounts:score_mounts_tests` contains - Python unit tests for the `score_mounts` extension. -- `bazel build //src/tests/mounts_conflict:bad` and `:bad_attach_to` are - manually invoked negative fixtures. They must fail because one source - directory cannot produce conflicting mount locations or Toctree targets. -- `bazel run //:docs` remains the project-wide documentation smoke check. diff --git a/src/tests/mounts_contract/test_mount_docs_e2e.py b/src/tests/mounts_contract/test_mount_docs_e2e.py deleted file mode 100644 index 800f7da73..000000000 --- a/src/tests/mounts_contract/test_mount_docs_e2e.py +++ /dev/null @@ -1,76 +0,0 @@ -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* -"""End-to-end test for a documentation build with a mounted bundle.""" - -import json -from pathlib import Path - -from src.tests.docs_e2e.support import run_docs_build, runfile - - -def test_docs_build_mounts_bundle_and_extends_toctree(tmp_path: Path): - """A nested bundle keeps its placement and becomes part of the host site.""" - mounts_manifest = runfile("FIXTURE_MOUNTS_MANIFEST") - manifest = json.loads(mounts_manifest.read_text(encoding="utf-8")) - assert [mount["mount_at"] for mount in manifest["mounts"]] == [ - "concepts/example_bundle", - "concepts/example_bundle/child", - ] - assert [mount["entry_doc"] for mount in manifest["mounts"]] == [ - "index", - "landing", - ] - sourcelinks = json.loads(runfile("FIXTURE_SOURCELINKS").read_text(encoding="utf-8")) - assert sourcelinks == [ - { - "file": "src/tests/mounts_contract/child/example.py", - "line": 14, - "tag": "# req-traceability:", - "need": "REQ_CHILD", - "full_line": "# req-traceability: REQ_CHILD", - "repo_name": "local_repo", - "hash": "", - "url": "", - } - ] - - result = run_docs_build( - tmp_path, - docs_binary=runfile("FIXTURE_DOCS_BINARY"), - source_dir=runfile("HOST_DOCS_CONF").parent, - mounts_manifest=mounts_manifest, - sourcelinks=runfile("FIXTURE_SOURCELINKS"), - ) - - assert result.returncode == 0, result.stdout + result.stderr - assert "extended toctree #0 in 'concepts/index'" in result.stdout - assert ( - tmp_path / "_build" / "concepts" / "example_bundle" / "index.html" - ).is_file() - assert ( - tmp_path / "_build" / "concepts" / "example_bundle" / "child" / "landing.html" - ).is_file() - toml = (tmp_path / "docs" / "ubproject.toml").read_text(encoding="utf-8") - assert 'mount_at = "concepts/example_bundle"' in toml - assert 'mount_at = "concepts/example_bundle/child"' in toml - - -def test_pure_aggregator_keeps_declared_mount_order(): - """An aggregator contributes no implicit package sources or reordered mounts.""" - manifest = json.loads( - runfile("ORDERED_AGGREGATE_MANIFEST").read_text(encoding="utf-8") - ) - assert [mount["mount_at"] for mount in manifest["mounts"]] == [ - "first", - "second", - ] From 90076e46cf93cdf987aad82ebd456b770a9c8c63 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 21:22:27 +0200 Subject: [PATCH 14/19] downstream tests needs graphviz now --- .github/workflows/downstream_compatibility.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/downstream_compatibility.yml b/.github/workflows/downstream_compatibility.yml index 27cb5ac91..c8a69dc58 100644 --- a/.github/workflows/downstream_compatibility.yml +++ b/.github/workflows/downstream_compatibility.yml @@ -41,6 +41,12 @@ jobs: with: egress-policy: audit + - name: Install Graphviz + uses: eclipse-score/apt-install@main + with: + packages: graphviz + cache: false + - name: Checkout PR uses: actions/checkout@v6.0.2 From 26f1ebfa0793e637df2ba73574dcb425239ced4e Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 21:41:43 +0200 Subject: [PATCH 15/19] downgrade rules_python --- MODULE.bazel | 4 +- MODULE.bazel.lock | 2249 +++++---------------------------------------- 2 files changed, 242 insertions(+), 2011 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 48591e823..c93b79be9 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -19,8 +19,8 @@ module(name = "score_docs_as_code") # Python version # ############################################################################### -bazel_dep(name = "rules_python", version = "2.2.0") -bazel_dep(name = "sphinxdocs", version = "2.2.0") # separate module since rules_python 2.0.0 +bazel_dep(name = "rules_python", version = "1.8.5") +bazel_dep(name = "sphinxdocs", version = "2.2.0") PYTHON_VERSION = "3.12" diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 9de850995..ac9ce3273 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -56,8 +56,6 @@ "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", - "https://bcr.bazel.build/modules/package_metadata/0.0.7/MODULE.bazel": "7adb03933fc8401f495800cf4eafcff0edc6da0ff55c7db223ef69d19f689486", - "https://bcr.bazel.build/modules/package_metadata/0.0.7/source.json": "50639625e937b56115012674c797cca7a05a96b4878c87d803c13dc2b31de8a0", "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", @@ -92,8 +90,7 @@ "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8", - "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84", - "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07", + "https://bcr.bazel.build/modules/rules_cc/0.1.5/source.json": "4bb4fed7f5499775d495739f785a5494a1f854645fa1bac5de131264f5acdf01", "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", @@ -149,8 +146,7 @@ "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43", "https://bcr.bazel.build/modules/rules_python/1.8.3/MODULE.bazel": "f343e159b59701334be3914416b9f1b72845801ba47920fcb288af4ce8c5cce3", "https://bcr.bazel.build/modules/rules_python/1.8.5/MODULE.bazel": "28b2d79ed8368d7d45b34bacc220e3c0b99cbcd9392641961b849e4c3f55dd30", - "https://bcr.bazel.build/modules/rules_python/2.2.0/MODULE.bazel": "9ce85518b14625a3abec3c95e3fa739ab0578e58240ef829af20efebcdebc41a", - "https://bcr.bazel.build/modules/rules_python/2.2.0/source.json": "274b1ca2363520292527f21b8237aa0f562003ea5912ad07d96d98e87738f618", + "https://bcr.bazel.build/modules/rules_python/1.8.5/source.json": "e261b03c8804f2582c9536013f987e1ea105a2b38c238aa2ac8f98fc34c8b18a", "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", "https://bcr.bazel.build/modules/rules_shell/0.4.1/MODULE.bazel": "00e501db01bbf4e3e1dd1595959092c2fadf2087b2852d3f553b5370f5633592", "https://bcr.bazel.build/modules/rules_shell/0.4.1/source.json": "4757bd277fe1567763991c4425b483477bb82e35e777a56fd846eb5cceda324a", @@ -164,8 +160,6 @@ "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", - "https://bcr.bazel.build/modules/toml.bzl/0.4.1/MODULE.bazel": "6bc0b938f03ade8d58c2fca0ad5c3fa12b4764e1e1927ad50b0c860286db2167", - "https://bcr.bazel.build/modules/toml.bzl/0.4.1/source.json": "86a90afd8b43c9b69ad31f5c03998c3adcf4b08175e621addb93e3a38eec538b", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", @@ -216,7 +210,6 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.14.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/jsoncpp/1.9.5/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/libpfm/4.11.0/MODULE.bazel": "not found", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/package_metadata/0.0.7/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.10/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.11/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.4/MODULE.bazel": "not found", @@ -246,7 +239,6 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.9/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.1.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.1.5/MODULE.bazel": "not found", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.2.17/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_fuzzing/0.5.2/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/4.0.0/MODULE.bazel": "not found", @@ -294,7 +286,6 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.0.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.8.3/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.8.5/MODULE.bazel": "not found", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/2.2.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.2.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.4.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_devcontainer/1.9.0/MODULE.bazel": "2a04a354eb7a77d478bb43ba20b1dac0758af858172a760e4290621bef1a2f28", @@ -307,7 +298,6 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.2/MODULE.bazel": "not found", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/toml.bzl/0.4.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.2.11/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "not found", @@ -606,10 +596,245 @@ ] } }, + "@@rules_python+//python/extensions:config.bzl%config": { + "general": { + "bzlTransitiveDigest": "TRGIl0CDmorwyNiblOYyhWuyKzi/kWFHT2uIofq7o9Y=", + "usagesDigest": "lDbpRfhoWmZCHSaNxwZv/8fF2y0wu2th0G0f/uqX7VM=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_python_internal": { + "repoRuleId": "@@rules_python+//python/private:internal_config_repo.bzl%internal_config_repo", + "attributes": { + "transition_setting_generators": {}, + "transition_settings": [] + } + }, + "pypi__build": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl", + "sha256": "75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__click": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", + "sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__colorama": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", + "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__importlib_metadata": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", + "sha256": "30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__installer": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl", + "sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__more_itertools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/50/e2/8e10e465ee3987bb7c9ab69efb91d867d93959095f4807db102d07995d94/more_itertools-10.2.0-py3-none-any.whl", + "sha256": "686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__packaging": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl", + "sha256": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pep517": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/25/6e/ca4a5434eb0e502210f591b97537d322546e4833dcb4d470a48c375c5540/pep517-0.13.1-py3-none-any.whl", + "sha256": "31b206f67165b3536dd577c5c3f1518e8fbaf38cbc57efff8369a392feff1721", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl", + "sha256": "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pip_tools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/0d/dc/38f4ce065e92c66f058ea7a368a9c5de4e702272b479c0992059f7693941/pip_tools-7.4.1-py3-none-any.whl", + "sha256": "4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__pyproject_hooks": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl", + "sha256": "7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__setuptools": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl", + "sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__tomli": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", + "sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__wheel": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/7d/cd/d7460c9a869b16c3dd4e1e403cce337df165368c71d6af229a74699622ce/wheel-0.43.0-py3-none-any.whl", + "sha256": "55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + }, + "pypi__zipp": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "url": "https://files.pythonhosted.org/packages/da/55/a03fd7240714916507e1fcf7ae355bd9d9ed2e6db492595f1a67f61681be/zipp-3.18.2-py3-none-any.whl", + "sha256": "dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e", + "type": "zip", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_python+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_python+", + "pypi__build", + "rules_python++config+pypi__build" + ], + [ + "rules_python+", + "pypi__click", + "rules_python++config+pypi__click" + ], + [ + "rules_python+", + "pypi__colorama", + "rules_python++config+pypi__colorama" + ], + [ + "rules_python+", + "pypi__importlib_metadata", + "rules_python++config+pypi__importlib_metadata" + ], + [ + "rules_python+", + "pypi__installer", + "rules_python++config+pypi__installer" + ], + [ + "rules_python+", + "pypi__more_itertools", + "rules_python++config+pypi__more_itertools" + ], + [ + "rules_python+", + "pypi__packaging", + "rules_python++config+pypi__packaging" + ], + [ + "rules_python+", + "pypi__pep517", + "rules_python++config+pypi__pep517" + ], + [ + "rules_python+", + "pypi__pip", + "rules_python++config+pypi__pip" + ], + [ + "rules_python+", + "pypi__pip_tools", + "rules_python++config+pypi__pip_tools" + ], + [ + "rules_python+", + "pypi__pyproject_hooks", + "rules_python++config+pypi__pyproject_hooks" + ], + [ + "rules_python+", + "pypi__setuptools", + "rules_python++config+pypi__setuptools" + ], + [ + "rules_python+", + "pypi__tomli", + "rules_python++config+pypi__tomli" + ], + [ + "rules_python+", + "pypi__wheel", + "rules_python++config+pypi__wheel" + ], + [ + "rules_python+", + "pypi__zipp", + "rules_python++config+pypi__zipp" + ] + ] + } + }, "@@rules_python+//python/uv:uv.bzl%uv": { "general": { - "bzlTransitiveDigest": "ELjwPp2kLku5M3S/gpjjVjy3TwT760/zVEQ70nJreHU=", - "usagesDigest": "LCPgc6OYAryd0HQJS9CtnCxvca1YfrjIZ67iPax2aRs=", + "bzlTransitiveDigest": "ijW9KS7qsIY+yBVvJ+Nr1mzwQox09j13DnE3iIwaeTM=", + "usagesDigest": "c4BCoL7WnccEomzDYulDuOys9pd6N93KaNI4mTVbqi0=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -648,1999 +873,5 @@ } } }, - "facts": { - "@@rules_python+//python/extensions:pip.bzl%pip": { - "dist_hashes": { - "${PIP_INDEX_URL:-https://pypi.org/simple/}": { - "accessible-pygments": { - "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl": "88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", - "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz": "40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872" - }, - "alabaster": { - "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl": "fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", - "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz": "c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e" - }, - "anyio": { - "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz": "334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", - "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl": "08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708" - }, - "attrs": { - "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl": "c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", - "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz": "d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32" - }, - "babel": { - "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl": "e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", - "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz": "b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d" - }, - "basedpyright": { - "https://files.pythonhosted.org/packages/97/47/08145d1bcc3083ed20059bdecbde404bd767f91b91e2764ec01cffec9f4b/basedpyright-1.39.0-py3-none-any.whl": "91b8ad50bc85ee4a985b928f9368c35c99eee5a56c44e99b2442fa12ecc3d670", - "https://files.pythonhosted.org/packages/ac/f4/4a77cc1ffb3dab7391642cde30163961d8ee973e9e6b6740c7d15aa3d3ba/basedpyright-1.39.0.tar.gz": "6666f51c378c7ac45877c4c1c7041ee0b5b83d755ebc82f898f47b6fafe0cc4f" - }, - "bazel-runfiles": { - "https://files.pythonhosted.org/packages/cf/68/8b13a99086308b8eccc76969eab7e774129b40fec31c9f79303cf5b20d48/bazel_runfiles-1.9.0-py3-none-any.whl": "66fb0f221e72ad904086eda6b208e873c76a2f5511ea308e7eae449534abc202" - }, - "beautifulsoup4": { - "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl": "0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", - "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz": "6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86" - }, - "cattrs": { - "https://files.pythonhosted.org/packages/80/56/60547f7801b97c67e97491dc3d9ade9fbccbd0325058fd3dfcb2f5d98d90/cattrs-26.1.0-py3-none-any.whl": "d1e0804c42639494d469d08d4f26d6b9de9b8ab26b446db7b5f8c2e97f7c3096", - "https://files.pythonhosted.org/packages/a0/ec/ba18945e7d6e55a58364d9fb2e46049c1c2998b3d805f19b703f14e81057/cattrs-26.1.0.tar.gz": "fa239e0f0ec0715ba34852ce813986dfed1e12117e209b816ab87401271cdd40" - }, - "certifi": { - "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl": "027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", - "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz": "e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7" - }, - "cffi": { - "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", - "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl": "3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", - "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl": "5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", - "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl": "b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", - "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", - "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", - "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl": "cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", - "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", - "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl": "737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", - "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl": "c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", - "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl": "89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", - "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl": "7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", - "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl": "cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", - "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl": "b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", - "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl": "6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", - "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl": "19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", - "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl": "81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", - "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl": "de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", - "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl": "9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", - "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl": "087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", - "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl": "a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", - "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", - "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl": "8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", - "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl": "45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", - "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl": "00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", - "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", - "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl": "2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", - "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", - "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl": "d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", - "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl": "b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", - "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl": "c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", - "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl": "e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", - "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", - "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl": "da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", - "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl": "9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", - "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl": "fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", - "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl": "0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", - "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl": "4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", - "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl": "c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", - "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", - "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", - "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", - "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl": "f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", - "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl": "dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", - "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl": "9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", - "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl": "1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", - "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", - "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", - "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl": "2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", - "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl": "94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", - "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl": "0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", - "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl": "66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", - "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", - "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", - "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", - "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl": "2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", - "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", - "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl": "203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", - "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl": "d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", - "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", - "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl": "d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", - "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl": "fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", - "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", - "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl": "38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", - "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl": "256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", - "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl": "dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", - "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", - "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", - "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", - "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", - "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl": "b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", - "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", - "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl": "8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", - "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", - "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", - "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl": "1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", - "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl": "0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", - "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl": "6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", - "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz": "44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", - "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl": "74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", - "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", - "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl": "1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", - "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl": "da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", - "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe" - }, - "charset-normalizer": { - "https://files.pythonhosted.org/packages/01/1b/ef725f8eb19b5a261b30f78efa9252ef9d017985cb499102f6f49834cd12/charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl": "177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217", - "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl": "cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", - "https://files.pythonhosted.org/packages/04/2a/c1f1f791467d865b48b749842c895668229e553dd79b71ad80498a0b646f/charset_normalizer-3.4.7-cp38-cp38-win32.whl": "320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207", - "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl": "adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", - "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", - "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl": "eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", - "https://files.pythonhosted.org/packages/0e/f1/40a59aae52edc5275e85813cbc49621c10758f481deeb27f71c97406cda0/charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_armv7l.whl": "bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60", - "https://files.pythonhosted.org/packages/12/46/fce169ad09419b8e8a5a81db61e08cd7b9fd31332221b84bd176fe0a3136/charset_normalizer-3.4.7-cp38-cp38-macosx_10_9_universal2.whl": "e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259", - "https://files.pythonhosted.org/packages/17/85/cacf6d45cff52be431468ee4cfa6f625eb622ab8f23a892218af8c77094d/charset_normalizer-3.4.7-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734", - "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl": "ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", - "https://files.pythonhosted.org/packages/1a/62/d9340c7a79c393e57807d7fb6c57e82060687891f81b74d3201958b919c1/charset_normalizer-3.4.7-cp39-cp39-win32.whl": "8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366", - "https://files.pythonhosted.org/packages/1a/64/3f9142293c88b1b10e199649ed1330f070c2a68e305335a5819fa7f25fa7/charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl": "d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00", - "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl": "94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", - "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl": "8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", - "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", - "https://files.pythonhosted.org/packages/20/ec/90339ff5cdc598b265748c1f231c7d7fbd9123a92cee10f757e0b1448de4/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl": "07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67", - "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", - "https://files.pythonhosted.org/packages/21/7b/c414866a138400b2e81973d006da7f694cfeaf895ef07d2cba9a8743841a/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a", - "https://files.pythonhosted.org/packages/21/e7/92901117e2ddc8facfe8235a3ecd4eb482185b2ad5d5b6606b37c1afea06/charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl": "12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444", - "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", - "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl": "cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", - "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl": "a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", - "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl": "8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", - "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl": "c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", - "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", - "https://files.pythonhosted.org/packages/2e/92/bdcf94997e06b223d826df3abed45a5ad6e17f609b7df9d25cd23b5bde30/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc", - "https://files.pythonhosted.org/packages/2e/e7/a7a6147f8e3375676309cf584b25c72a3bab784ea4085b0011fa07b23aeb/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl": "c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274", - "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl": "7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", - "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl": "6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", - "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl": "5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", - "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl": "708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", - "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", - "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl": "e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", - "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl": "6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", - "https://files.pythonhosted.org/packages/47/3f/9bb0864a92b4abf0ec0d1f40546297f45afd73851795e3216c899b360aa0/charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_riscv64.whl": "af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686", - "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", - "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl": "92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", - "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", - "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", - "https://files.pythonhosted.org/packages/52/f9/47a52cbcce0140f612ef7a37797b2929244bcaaf2f83ade3775429457252/charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_aarch64.whl": "82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545", - "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl": "1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", - "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl": "4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", - "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl": "aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", - "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", - "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl": "5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", - "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl": "3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", - "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl": "e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", - "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl": "bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", - "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl": "80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", - "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", - "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl": "2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", - "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl": "64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", - "https://files.pythonhosted.org/packages/6c/0e/0f722c41d983dd204b3142606fbfcdbb0a33c34b9b031ef3c1fe9e8187ad/charset_normalizer-3.4.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3", - "https://files.pythonhosted.org/packages/6f/5e/9e74560659e3f8a7650e09dac978749d408917c8e9764af13f5f81ceec53/charset_normalizer-3.4.7-cp38-cp38-win_amd64.whl": "1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83", - "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl": "7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", - "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", - "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl": "b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", - "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl": "4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", - "https://files.pythonhosted.org/packages/75/20/8b3cefb78df39d40272d7831dda07b51875d89af1f390f97a801eaedec78/charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl": "c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f", - "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl": "ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", - "https://files.pythonhosted.org/packages/76/79/0e09d2169b7ba38a04e9660669d124ea688605f66189030e4c2be44d8e27/charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_armv7l.whl": "1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5", - "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl": "03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", - "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl": "1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", - "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl": "8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", - "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl": "3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", - "https://files.pythonhosted.org/packages/81/76/14ab25789e14f83124c4318f0edbbf15a6ed535bd3d88720c42001a954df/charset_normalizer-3.4.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01", - "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", - "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", - "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl": "2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", - "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", - "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl": "bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", - "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl": "481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", - "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl": "d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", - "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", - "https://files.pythonhosted.org/packages/8f/9b/7c7f4b7f11525fcbdfba752455314ac60646bae91cdd671d531c1f7a97c6/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl": "2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42", - "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl": "a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", - "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl": "750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", - "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl": "effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", - "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl": "752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", - "https://files.pythonhosted.org/packages/96/53/6ea2906da0fd3773d57398e7cee5628d004d844b0c4903ea3038ae8488cd/charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0", - "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl": "c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", - "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", - "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl": "d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", - "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", - "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl": "56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", - "https://files.pythonhosted.org/packages/9f/57/301682e7469bdbfa2ce219a804f0668b2266ab8520570d85d3b3ef483ea3/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl": "813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4", - "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", - "https://files.pythonhosted.org/packages/a3/22/2f12878fbc680fbbb52386cd39a379801f62eaca74fc8b323381325f0f04/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5", - "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", - "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl": "6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", - "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl": "203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", - "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", - "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl": "e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", - "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl": "f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", - "https://files.pythonhosted.org/packages/bc/b6/10c84e789126ca97d4a7228863a30481e786980a8b8cfcbf4f30658ca63c/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9", - "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", - "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl": "66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", - "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl": "f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", - "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl": "4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", - "https://files.pythonhosted.org/packages/c1/d1/d8a6b7dd5c5636b76ce0d080bc57d8e56c7bbd6bc2ac941529a35e41d84a/charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776", - "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl": "0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", - "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", - "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl": "7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", - "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", - "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl": "c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", - "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl": "67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", - "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl": "d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", - "https://files.pythonhosted.org/packages/cc/4f/e1fb138201ad9a32499dd9a98aa4a5a5441fbf7f56b52b619a54b7ee8777/charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl": "bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c", - "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl": "38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", - "https://files.pythonhosted.org/packages/d3/5e/8161a7bbf4a7f88d0409091ab5a5762c014913c9ef80a48b50f806140918/charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_x86_64.whl": "dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7", - "https://files.pythonhosted.org/packages/d5/2a/41816ceda78a551cbfdfbeab6f3891152b0e3f758ce6580c2c18c829f774/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl": "ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24", - "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl": "0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", - "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", - "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl": "3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", - "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", - "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl": "65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", - "https://files.pythonhosted.org/packages/dd/8c/60ebe912379627d023eb96995b40bc50308729f210f43d66109ca0a7bbd2/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl": "a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319", - "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl": "c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", - "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl": "12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", - "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl": "f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", - "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", - "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl": "f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", - "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz": "ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", - "https://files.pythonhosted.org/packages/ec/5e/0739d2975ae6fd42505fdb80881ab5e99b4edfbff1d581f4cd5aa94f2d94/charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_s390x.whl": "84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706", - "https://files.pythonhosted.org/packages/ef/ec/e7961eea9977a4d5ac920627e78938784272cb9b752cf1209da91e93d006/charset_normalizer-3.4.7-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30", - "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", - "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", - "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", - "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl": "fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464" - }, - "click": { - "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz": "14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", - "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl": "1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d" - }, - "colorama": { - "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", - "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz": "08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44" - }, - "contourpy": { - "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl": "0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", - "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", - "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl": "451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", - "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", - "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl": "3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", - "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl": "6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", - "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl": "23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", - "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl": "ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", - "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl": "ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", - "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl": "1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", - "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl": "95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", - "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl": "8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", - "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl": "33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", - "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl": "2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", - "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", - "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl": "4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", - "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl": "459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", - "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl": "a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", - "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", - "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl": "fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", - "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl": "1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", - "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl": "d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", - "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", - "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl": "626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", - "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl": "6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", - "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl": "644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", - "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", - "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl": "556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", - "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz": "083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", - "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", - "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl": "b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", - "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", - "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl": "177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", - "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl": "fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", - "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", - "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl": "fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", - "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl": "cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", - "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl": "ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", - "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl": "3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", - "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", - "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl": "fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", - "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl": "709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", - "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl": "a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", - "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl": "e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", - "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl": "d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", - "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl": "3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", - "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl": "598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", - "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", - "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl": "cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", - "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl": "b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", - "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", - "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl": "b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", - "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", - "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl": "87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", - "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl": "ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", - "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl": "afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", - "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl": "283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", - "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl": "2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", - "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl": "b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", - "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl": "88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", - "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl": "ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", - "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", - "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl": "023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", - "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl": "07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", - "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", - "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl": "15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", - "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl": "66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", - "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl": "655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", - "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl": "9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", - "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl": "13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", - "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl": "322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", - "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl": "cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77" - }, - "cryptography": { - "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", - "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl": "ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", - "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl": "cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", - "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl": "5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", - "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl": "cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", - "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl": "d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", - "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl": "258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", - "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl": "fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", - "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl": "397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", - "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl": "420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", - "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl": "e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", - "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl": "8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", - "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl": "d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", - "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl": "60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", - "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl": "b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", - "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl": "c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", - "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl": "a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", - "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz": "e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", - "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", - "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl": "f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", - "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl": "fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", - "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl": "65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", - "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", - "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", - "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl": "9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", - "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl": "d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", - "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", - "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl": "d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", - "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl": "935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", - "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", - "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl": "73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", - "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl": "abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", - "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl": "fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", - "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl": "5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", - "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl": "d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", - "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", - "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl": "7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", - "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl": "462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", - "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl": "80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", - "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl": "cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", - "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl": "35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", - "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl": "ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", - "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl": "1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", - "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl": "42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", - "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl": "04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", - "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl": "91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", - "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl": "506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", - "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl": "3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", - "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl": "24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de" - }, - "cycler": { - "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz": "88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", - "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl": "85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30" - }, - "debugpy": { - "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl": "eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", - "https://files.pythonhosted.org/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl": "4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d", - "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl": "5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", - "https://files.pythonhosted.org/packages/17/b6/33ad879a4613b96dbae37a75101b32e927f830ab4104d759e6b8e19251c3/debugpy-1.8.20-cp38-cp38-macosx_15_0_x86_64.whl": "b773eb026a043e4d9c76265742bc846f2f347da7e27edf7fe97716ea19d6bfc5", - "https://files.pythonhosted.org/packages/22/bf/856f2eee220effd2e49733bdd74db1ee162753a02a0a0323a3155776188b/debugpy-1.8.20-cp38-cp38-win32.whl": "0dfd9adb4b3c7005e9c33df430bcdd4e4ebba70be533e0066e3a34d210041b66", - "https://files.pythonhosted.org/packages/2b/6b/668f21567e3250463beb6a401e7d598baa2a0907224000d7f68b9442c243/debugpy-1.8.20-cp39-cp39-macosx_15_0_x86_64.whl": "bff8990f040dacb4c314864da95f7168c5a58a30a66e0eea0fb85e2586a92cd6", - "https://files.pythonhosted.org/packages/2c/37/88710a0b7ccc2649815723749b451d1f528c708bc3766a53417065460809/debugpy-1.8.20-cp38-cp38-manylinux_2_34_x86_64.whl": "20d6e64ea177ab6732bffd3ce8fc6fb8879c60484ce14c3b3fe183b1761459ca", - "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl": "9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", - "https://files.pythonhosted.org/packages/51/56/c3baf5cbe4dd77427fd9aef99fcdade259ad128feeb8a786c246adb838e5/debugpy-1.8.20-cp311-cp311-macosx_15_0_universal2.whl": "eada6042ad88fa1571b74bd5402ee8b86eded7a8f7b827849761700aff171f1b", - "https://files.pythonhosted.org/packages/71/be/8bd693a0b9d53d48c8978fa5d889e06f3b5b03e45fd1ea1e78267b4887cb/debugpy-1.8.20-cp310-cp310-macosx_15_0_x86_64.whl": "157e96ffb7f80b3ad36d808646198c90acb46fdcfd8bb1999838f0b6f2b59c64", - "https://files.pythonhosted.org/packages/72/43/09d49106e770fe558ced5e80df2e3c2ebee10e576eda155dcc5670473663/debugpy-1.8.20-cp310-cp310-win_amd64.whl": "3ca85463f63b5dd0aa7aaa933d97cbc47c174896dcae8431695872969f981893", - "https://files.pythonhosted.org/packages/77/1b/85326d07432086a06361d493d2743edd0c4fc2ef62162be7f8618441ac37/debugpy-1.8.20-cp310-cp310-manylinux_2_34_x86_64.whl": "c1178ae571aff42e61801a38b007af504ec8e05fde1c5c12e5a7efef21009642", - "https://files.pythonhosted.org/packages/7d/f2/1e8f8affe51e12a26f3a8a8a4277d6e60aa89d0a66512f63b1e799d424a4/debugpy-1.8.20-cp311-cp311-win32.whl": "773e839380cf459caf73cc533ea45ec2737a5cc184cf1b3b796cd4fd98504fec", - "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl": "84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", - "https://files.pythonhosted.org/packages/9a/7d/4fa79a57a8e69fe0d9763e98d1110320f9ecd7f1f362572e3aafd7417c9d/debugpy-1.8.20-cp311-cp311-manylinux_2_34_x86_64.whl": "7de0b7dfeedc504421032afba845ae2a7bcc32ddfb07dae2c3ca5442f821c344", - "https://files.pythonhosted.org/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl": "a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3", - "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl": "da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", - "https://files.pythonhosted.org/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl": "88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b", - "https://files.pythonhosted.org/packages/b1/24/9f219c9290fe8bee4f63f9af8ebac440c802e6181d7f39a79abcb5fdff2f/debugpy-1.8.20-cp39-cp39-win32.whl": "9eeed9f953f9a23850c85d440bf51e3c56ed5d25f8560eeb29add815bd32f7ee", - "https://files.pythonhosted.org/packages/ba/f3/4a12d7b1b09e3b79ba6e3edfa0c677b8b8bdf110bc4b3607e0f29fb4e8b3/debugpy-1.8.20-cp39-cp39-win_amd64.whl": "760813b4fff517c75bfe7923033c107104e76acfef7bda011ffea8736e9a66f8", - "https://files.pythonhosted.org/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl": "4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390", - "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl": "077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", - "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl": "352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", - "https://files.pythonhosted.org/packages/cf/49/223143d1da586b891f35a45515f152742ad85bfc10d2e02e697f65c83b32/debugpy-1.8.20-cp39-cp39-manylinux_2_34_x86_64.whl": "70ad9ae09b98ac307b82c16c151d27ee9d68ae007a2e7843ba621b5ce65333b5", - "https://files.pythonhosted.org/packages/d5/92/1cb532e88560cbee973396254b21bece8c5d7c2ece958a67afa08c9f10dc/debugpy-1.8.20-cp311-cp311-win_amd64.whl": "1f7650546e0eded1902d0f6af28f787fa1f1dbdbc97ddabaf1cd963a405930cb", - "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz": "55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", - "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl": "5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", - "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl": "a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", - "https://files.pythonhosted.org/packages/e8/60/3e08462ee3eccd10998853eb35947c416e446bfe2bc37dbb886b9044586c/debugpy-1.8.20-cp310-cp310-win32.whl": "c29dd9d656c0fbd77906a6e6a82ae4881514aa3294b94c903ff99303e789b4a2", - "https://files.pythonhosted.org/packages/eb/dd/daf84fabab699d2dad45c71fa0cfb0612caa9446f7d307a77d6469792aa1/debugpy-1.8.20-cp38-cp38-win_amd64.whl": "60f89411a6c6afb89f18e72e9091c3dfbcfe3edc1066b2043a1f80a3bbb3e11f" - }, - "docutils": { - "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl": "d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", - "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz": "4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968" - }, - "esbonio": { - "https://files.pythonhosted.org/packages/67/c5/0c89af3da1f3133b53f3ba8ae677ed4d4ddff33eec50dbf32c95e01ed2d2/esbonio-0.16.5.tar.gz": "acab2e16c6cf8f7232fb04e0d48514ce50566516b1f6fcf669ccf2f247e8b10f", - "https://files.pythonhosted.org/packages/d8/ca/a0296fca375d4324f471bb34d2ce8a585b48fb9eae21cf9abe00913eb899/esbonio-0.16.5-py3-none-any.whl": "04ba926e3603f7b1fde1abc690b47afd60749b64b1029b6bce8e1de0bb284921" - }, - "fonttools": { - "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", - "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl": "68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", - "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl": "59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", - "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl": "403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", - "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl": "8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", - "https://files.pythonhosted.org/packages/24/7f/66d3f8a9338a9b67fe6e1739f47e1cd5cee78bd3bc1206ef9b0b982289a5/fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl": "9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14", - "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", - "https://files.pythonhosted.org/packages/28/b1/0c2ab56a16f409c6c8a68816e6af707827ad5d629634691ff60a52879792/fonttools-4.62.1-cp312-cp312-win_amd64.whl": "9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42", - "https://files.pythonhosted.org/packages/29/0b/5cbef6588dc9bd6b5c9ad6a4d5a8ca384d0cea089da31711bbeb4f9654a6/fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl": "19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d", - "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl": "fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", - "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", - "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl": "7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", - "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl": "c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", - "https://files.pythonhosted.org/packages/3d/03/3c8f09aad64230cd6d921ae7a19f9603c36f70930b00459f112706f6769a/fonttools-4.62.1-cp310-cp310-win32.whl": "486f32c8047ccd05652aba17e4a8819a3a9d78570eb8a0e3b4503142947880ed", - "https://files.pythonhosted.org/packages/42/09/7dbe3d7023f57d9b580cfa832109d521988112fd59dddfda3fddda8218f9/fonttools-4.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "7bca7a1c1faf235ffe25d4f2e555246b4750220b38de8261d94ebc5ce8a23c23", - "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl": "1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", - "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl": "2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", - "https://files.pythonhosted.org/packages/47/d4/dbacced3953544b9a93088cc10ef2b596d348c983d5c67a404fa41ec51ba/fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl": "90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974", - "https://files.pythonhosted.org/packages/4a/47/b3a5342d381595ef439adec67848bed561ab7fdb1019fa522e82101b7d9c/fonttools-4.62.1-cp312-cp312-win32.whl": "a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c", - "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl": "aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", - "https://files.pythonhosted.org/packages/4c/28/40f15523b5188598018e7956899fed94eb7debec89e2dd70cb4a8df90492/fonttools-4.62.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "c9b9e288b4da2f64fd6180644221749de651703e8d0c16bd4b719533a3a7d6e3", - "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl": "49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", - "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", - "https://files.pythonhosted.org/packages/54/6c/af95d9c4efb15cabff22642b608342f2bd67137eea6107202d91b5b03184/fonttools-4.62.1-cp311-cp311-win32.whl": "942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53", - "https://files.pythonhosted.org/packages/5a/ff/532ed43808b469c807e8cb6b21358da3fe6fd51486b3a8c93db0bb5d957f/fonttools-4.62.1-cp310-cp310-macosx_10_9_universal2.whl": "ad5cca75776cd453b1b035b530e943334957ae152a36a88a320e779d61fc980c", - "https://files.pythonhosted.org/packages/66/9e/a769c8e99b81e5a87ab7e5e7236684de4e96246aae17274e5347d11ebd78/fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl": "12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9", - "https://files.pythonhosted.org/packages/69/64/f19a9e3911968c37e1e620e14dfc5778299e1474f72f4e57c5ec771d9489/fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936", - "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl": "1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", - "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl": "d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", - "https://files.pythonhosted.org/packages/85/e4/2318d2b430562da7227010fb2bb029d2fa54d7b46443ae8942bab224e2a0/fonttools-4.62.1-cp310-cp310-macosx_10_9_x86_64.whl": "0b3ae47e8636156a9accff64c02c0924cbebad62854c4a6dbdc110cd5b4b341a", - "https://files.pythonhosted.org/packages/88/39/23ff32561ec8d45a4d48578b4d241369d9270dc50926c017570e60893701/fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl": "40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7", - "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl": "b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", - "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl": "92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", - "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz": "e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", - "https://files.pythonhosted.org/packages/9b/8a/99c8b3c3888c5c474c08dbfd7c8899786de9604b727fcefb055b42c84bba/fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392", - "https://files.pythonhosted.org/packages/a5/80/df28131379eed93d9e6e6fccd3bf6e3d077bebbfe98cc83f21bbcd83ed02/fonttools-4.62.1-cp310-cp310-musllinux_1_2_x86_64.whl": "2d850f66830a27b0d498ee05adb13a3781637b1826982cd7e2b3789ef0cc71ae", - "https://files.pythonhosted.org/packages/aa/53/5276ceba7bff95da7793a07c5284e1da901cf00341ce5e2f3273056c0cca/fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7", - "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", - "https://files.pythonhosted.org/packages/cc/a1/40a5c4d8e28b0851d53a8eeeb46fbd73c325a2a9a165f290a5ed90e6c597/fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b", - "https://files.pythonhosted.org/packages/d1/2d/84509a2e32cb925371560ef5431365d8da2183c11d98e5b4b8b4e42426a5/fonttools-4.62.1-cp310-cp310-musllinux_1_2_aarch64.whl": "b4e0fcf265ad26e487c56cb12a42dffe7162de708762db951e1b3f755319507d", - "https://files.pythonhosted.org/packages/d1/c6/0f904540d3e6ab463c1243a0d803504826a11604c72dd58c2949796a1762/fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl": "0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04", - "https://files.pythonhosted.org/packages/d3/97/bf54c5b3f2be34e1f143e6db838dfdc54f2ffa3e68c738934c82f3b2a08d/fonttools-4.62.1-cp311-cp311-win_amd64.whl": "e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2", - "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl": "bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", - "https://files.pythonhosted.org/packages/dd/ec/f53f626f8f3e89f4cadd8fc08f3452c8fd182c951ad5caa35efac22b29ab/fonttools-4.62.1-cp310-cp310-win_amd64.whl": "5a648bde915fba9da05ae98856987ca91ba832949a9e2888b48c47ef8b96c5a9", - "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", - "https://files.pythonhosted.org/packages/e3/be/d378fca4c65ea1956fee6d90ace6e861776809cbbc5af22388a090c3c092/fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl": "a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1", - "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl": "c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", - "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl": "93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", - "https://files.pythonhosted.org/packages/f8/d9/ae6a1d0693a4185a84605679c8a1f719a55df87b9c6e8e817bfdd9ef5936/fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl": "268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416", - "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl": "7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd" - }, - "gitdb": { - "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz": "5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", - "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl": "67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf" - }, - "gitpython": { - "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl": "d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", - "https://files.pythonhosted.org/packages/33/f6/354ae6491228b5eb40e10d89c4d13c651fe1cf7556e35ebdded50cff57ce/gitpython-3.1.50.tar.gz": "80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc" - }, - "h11": { - "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz": "4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", - "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl": "63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" - }, - "idna": { - "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz": "ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", - "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl": "048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8" - }, - "ignore-python": { - "https://files.pythonhosted.org/packages/00/19/64cc90ed114d1ec38cf20e7e1825c4885bf4d66b2ce9ca1208ca13540c64/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "9bccb48b57b7a85677c1022afbaaf86e5cda8c1ecff00ad96877e10166d1eddc", - "https://files.pythonhosted.org/packages/03/76/2b0cf84c80d973b285831ae59f795635eb518342e00aab1d96a0c918bbe6/ignore_python-0.3.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "a999ef004caa048e5ecccb5f3383d857105baa37b8895a0a2b7cd66f9cb0b0b4", - "https://files.pythonhosted.org/packages/06/a2/6db321813eba49f04f680af7b83dddda117f09e5a33e8fa2de7cbbe26f36/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "4f8c85a6738c632abd477c217297f8929ec1cafb261b94fa05a7fcf28095d70f", - "https://files.pythonhosted.org/packages/0c/f8/8ae1f5dc1e9b5fd3b4460ac685bb84c22a327f14f76daeebcd7eeeaba9ff/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl": "22c216e3130077060eb4cce99a8bf79074826655bbea6c594d36d8a0735fac6c", - "https://files.pythonhosted.org/packages/0d/93/2a28bd91ab75f22e21448e47b2972670ea5320fb37bf5d3f3f0167be8743/ignore_python-0.3.3-cp311-cp311-win_amd64.whl": "0a6b2d7900ce82acd61ab6714b10103c37d0a1c16ee7af46fb6e94d14a2e4dca", - "https://files.pythonhosted.org/packages/0e/00/aee2481903dc3578998df4edf94dc0460644f7f8a9fbee6e84189a24d278/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "68f393318292a6346c6d72c2b8ee301a081bff778dfb0e6ef6f0c36da4053374", - "https://files.pythonhosted.org/packages/0f/e4/e260e5cb1b289230a6be99de15db5eb0697e6334830cb1ff3907568414ab/ignore_python-0.3.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl": "df78545bc5d54abf875a70a70db7e1e12e606377d1c9f4e68b6763e8c701a748", - "https://files.pythonhosted.org/packages/12/30/976ce0f8e1002fc19781ea48b6d5519200f734b19be03d769eefeb0ca749/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "71dc7505c0520e066c5d567f49d7173703c34192af1b8f89ce401a34098391f5", - "https://files.pythonhosted.org/packages/13/96/1b66b3be8862b771dc6eae3c522609ee22309c91276a48cfee1f17b469b5/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "f93a3425169961aa7f0c7193dc12f01414700f86f4fb99ec078c18780432b77a", - "https://files.pythonhosted.org/packages/1c/0d/f0b3e6a35b66570d6212850711067a719604034b72ee0c8cec312e5be13f/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "2b608a30d3505720f9aceaba7d6d26769867d71c212fc5c8a80fae1a8afec663", - "https://files.pythonhosted.org/packages/1c/76/ea5c9a892f9f10eefda168b0ad8e894b838dc0128317e5f965d7b546205d/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "875fdfa0e3e9102164a540509ca2d5ad959f1f53858cf11a4174a1845e3c575c", - "https://files.pythonhosted.org/packages/1c/b7/41b9bea87bdab57c2b705767bdd9c930417e5f1f14502aac9ac6044d86ee/ignore_python-0.3.3-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl": "a30070520aa114133feffc2413ba62bfcd8ef2f9826ebe7616de123f73b57977", - "https://files.pythonhosted.org/packages/1e/ce/f82e5156b64c3f8e8835c36ef1cb046d280af8294192672ec99a5f0efb6d/ignore_python-0.3.3-cp314-cp314-macosx_11_0_arm64.whl": "c3430b73a99af300b0b1203da2cd30f1831f504466d94343b065b1ef0435802c", - "https://files.pythonhosted.org/packages/23/2f/509eeeb1d61e6e66518601274c4fe1c1ebb45493ae5704033c4a01f2a721/ignore_python-0.3.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl": "c68dc5db03a22aada43f23f55e359109e00afe3886824d6fecfbd6821dcdbd6f", - "https://files.pythonhosted.org/packages/28/d3/91b086d42cd2e9654e8d532975d068fdfe64dda176a56642608f3627002b/ignore_python-0.3.3-cp310-cp310-win_amd64.whl": "be6a4e3244c33f133d3c0bc43f9725c61dc9a11f7100c615639ce1daee064766", - "https://files.pythonhosted.org/packages/29/94/17a644d95f13c08845bc6be9750f16e19e7d0650a6f43e6ce63de57556b0/ignore_python-0.3.3-cp314-cp314t-musllinux_1_2_i686.whl": "8dd30b865dfd3206756212796cb13686b6c45befa5cc495ccc9866108215f7c1", - "https://files.pythonhosted.org/packages/2a/fd/dbb1ed9af9bc50d53a2edcb71e0e236d32d28fdb60a62ccc965f3cad8811/ignore_python-0.3.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "0f0edb622f5a8b7f735e14a7ca13d4cb7ca04b6fd7e844f5fa0fd9f86622b986", - "https://files.pythonhosted.org/packages/2d/bd/8db6909e5ab266b8827fc5659fe98ea905d15bad818fa088d923da46643e/ignore_python-0.3.3-cp39-cp39-musllinux_1_2_x86_64.whl": "5055772fa6f09148a09e6958770c4e4f4435f6e3de33476f815af9c5db13e29c", - "https://files.pythonhosted.org/packages/30/60/d0c7be4619a9d8537e0e930b26d358b870f14820ec89eaba80e97d00481d/ignore_python-0.3.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "1206189e1c988a3d0fb7de3298e5f7dd5284b4a23781878fb8f9f6224659b270", - "https://files.pythonhosted.org/packages/34/36/eb0337f76ea09ffe68246228f9cb1d14d8c84deb6bda8dcbbedf16bdb373/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "8d26f91ac1fea52abd16dab224b4ac016d8914f28db02e582f4e589ee2b5faa5", - "https://files.pythonhosted.org/packages/37/46/3c93a7bcbd21d23faa9eb0c90613b711e6afb218d09b6122325cac4f3ae0/ignore_python-0.3.3-cp311-cp311-musllinux_1_2_x86_64.whl": "3b6536698628af08b6db260d338b41e7c48b2a6c5c93b12de4d1ecafc1bb86ae", - "https://files.pythonhosted.org/packages/3a/17/f0d111f0d90ae0d2322ba0c4a2002b2b6634dd64555ad9f42eaae20b633b/ignore_python-0.3.3-cp314-cp314-musllinux_1_2_x86_64.whl": "dda677506171a0c4f27925e13f9f8b4b652941969f0e0e6b555ce795e18b7467", - "https://files.pythonhosted.org/packages/3d/00/176044e51c351465221f6b98ced3639d7660330bb12d89fb99e13423803c/ignore_python-0.3.3-cp313-cp313-win_amd64.whl": "2af502d988282cc360094dc7b2733a7b68e54a8e3cf0128178ab1ba84f9ec290", - "https://files.pythonhosted.org/packages/40/dc/76b1fc8e4d51680656c963338d5ca2f17b6bc04ad1c4425c2cc498630908/ignore_python-0.3.3-cp311-cp311-macosx_11_0_arm64.whl": "c478ee58fd2d6f5f7b75b32288d8a099904f710e83dd878f40058a309a0f6060", - "https://files.pythonhosted.org/packages/40/f0/3ad575b0a10d4d69efe87e745c7e94a4bbc824963707b24272d49a150bda/ignore_python-0.3.3-cp312-cp312-win_arm64.whl": "3623ce12eb96976c0db36a0ad99c65c669fdafece71e7221a538f12fa6fa41ef", - "https://files.pythonhosted.org/packages/43/d7/7ad0916a3aa863f26f8654dfe095adf65e720a201043093e1f8614dc0e16/ignore_python-0.3.3-cp310-cp310-musllinux_1_2_i686.whl": "275f5b3e4c5b25fcb58ad1eedd983a346866939a6140e564b9c56ee9f8fc7760", - "https://files.pythonhosted.org/packages/43/ee/611f7c7d8973d288e2908a14aaf48b8e65ee0d3e11ba59c7b62e8b07ef69/ignore_python-0.3.3-cp312-cp312-musllinux_1_2_x86_64.whl": "e9c95f8c3a68449f7d3bd5860ea832b5827a04803337796da72d8340786e9268", - "https://files.pythonhosted.org/packages/49/14/32e3a2c4313367a38b9c8b564785444bb3ed31317386fb81efd80496a5af/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "0f50dd3c3f0ef982e256d9e702b44c1d81cbe0da2d98746d5b189bc1fd5191cd", - "https://files.pythonhosted.org/packages/4d/45/fcb27a731e98ff8d71ff7559f5558091efdb107eafd4ee7aef6ac91f5dad/ignore_python-0.3.3-cp311-cp311-musllinux_1_2_armv7l.whl": "901a862196bf610745e164d29c518e0cbf727eaaedc83d5058a7895721be2173", - "https://files.pythonhosted.org/packages/4e/4b/c5abae65901dce53a197c6e37178821b259b0cc40b8ff99183e409946a71/ignore_python-0.3.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl": "931744cdcbb84d77159daf6b54e3b459e9f0a0ba52ea6b99d1d228e32556c2b9", - "https://files.pythonhosted.org/packages/58/fc/f7cfe4a5ea6be67bbaa42afb6fe3d8fbaca6d3a71aa63bf86c22a7fac53b/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "44bf617535f5ead500a6f83178426f7c607e015bcf9e614e18ae88aa3de1a340", - "https://files.pythonhosted.org/packages/5c/83/ba162a3b82eca12ddbb7f229a3d05b79ec5be66bde44007eab3de7abed7b/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl": "0c9408949156bf4ae07e60d5c8f356114be1482dc8f708ecd5785151e9ae21fa", - "https://files.pythonhosted.org/packages/5e/e9/b8e55bd15b231ff44028b7ad5ffd231cf038b8181bf288b58f8ce2324072/ignore_python-0.3.3-cp313-cp313-macosx_10_12_x86_64.whl": "593679d7714b4228d7e8c3bda0005badb9f0d4cb37cd8dda8385ef734e675e23", - "https://files.pythonhosted.org/packages/5f/f3/b6aa2183016cefb19175308a5b65ba72dddc2f9ff2dce6ef9bb9f2e61b1e/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl": "d94d9b91eece76104077a7e0b3276bb14728a4bff00ba23b0f0bea2a10c0e6e8", - "https://files.pythonhosted.org/packages/61/2c/fe119eea2eb12c3fa3cf2793b0a8664e396c9b158967b37f2ed3e959f6d2/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "82fecbeb7fa309245aaaa7e3aaa09c744f1059fc238e2f7acd889d803f1a0be7", - "https://files.pythonhosted.org/packages/62/d0/ac58193a1ec6c8ec06da209fcbead510938a8dde89fc6478cace59cb77b7/ignore_python-0.3.3-cp314-cp314-musllinux_1_2_i686.whl": "4252f803f3cdae6c8775f1e905f5babd6e77860781f4c29cf798452ee5fd6936", - "https://files.pythonhosted.org/packages/63/03/c1890e428c05445eb14523e76fe87ba71151bae370874e05bb0f32276700/ignore_python-0.3.3-cp313-cp313t-musllinux_1_2_i686.whl": "7e9bea86436a59eb3f24e8e15bb3b3a36cdb98aec950c70aa619e33f35eb6beb", - "https://files.pythonhosted.org/packages/63/63/31483b9985ca2c4b1d828457ecccc1e60b02f3887d548ecbaa565176618a/ignore_python-0.3.3-cp314-cp314t-musllinux_1_2_armv7l.whl": "356b783877c7e88eba69c99bcc5e2d9fb07c2fe54f2c64e03897b7ae2adce2a7", - "https://files.pythonhosted.org/packages/6a/b8/7ef6326e648aacbc34ee31f2c4c9db073473f85d1f649cd5bda799f47d60/ignore_python-0.3.3-cp314-cp314-musllinux_1_2_armv7l.whl": "b0f0bc9eab99a36f54e0a4e3043768e529107565bc67a7f420b4febff8006f32", - "https://files.pythonhosted.org/packages/6b/37/57e38f4bc0f7584b578751dbfbb9081cf5af38848893aec69a814db24d03/ignore_python-0.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl": "b123951f9befe6052a7397778fa64ade18983345d79fd9477160b11dfd736df0", - "https://files.pythonhosted.org/packages/71/81/f2dd6e0ab7aa9b860d82c1b3ce195ed5d7284e92cde2cbe5a6915241a8c4/ignore_python-0.3.3-cp314-cp314-win_amd64.whl": "fabb25c4352d3d4f1a2a197d6fb403848026344aab73e1674bcb31e4a7f54914", - "https://files.pythonhosted.org/packages/74/ef/767d321b08e9b21d8d7a5b34d3f9d4b4eaf4d3c33b4761dcd0b6c0d87560/ignore_python-0.3.3-cp311-cp311-musllinux_1_2_i686.whl": "477bb090189b79b8a81753c74a59ccb6c0ebf91985f30e926177f062c8616d05", - "https://files.pythonhosted.org/packages/75/be/c220b77b9997f2ae81d30af3562bf7dee8e4c68a4de0a0404098b98fb49f/ignore_python-0.3.3-cp313-cp313-musllinux_1_2_armv7l.whl": "0dfa531bbf65f45f9a233e2809f8b0a49aa9078686dbb0e4f3e30848e09cc208", - "https://files.pythonhosted.org/packages/77/d2/be1a05941346a51384a5a71e1aa9933cfdc8a4508ed6f03ae925bb37dffc/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl": "8e9085cec8d730b43ac8c86ef5da0c902f0f57da8da2ee009045e7006fe4860f", - "https://files.pythonhosted.org/packages/7a/dc/a90ba9f5eab933e8bfd65396ee88c14221641e8fb48d93ce45454083e50d/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl": "7d63688dc696b72d54623dd55f269d5203fcd5de5eeeba7bc276864300a8d790", - "https://files.pythonhosted.org/packages/7b/8f/083356b171a87168b281a0af3da5d558e55fde13a792ee7b079a99ad01ee/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "5756229d699ec5ab8caf4cec3ce9827d02145059c79a2416f363e2df4406d378", - "https://files.pythonhosted.org/packages/7c/b4/aadf114682a3495361f12b827a38d1f4ee8f452166747513cfbc18818121/ignore_python-0.3.3-cp312-cp312-musllinux_1_2_i686.whl": "de10b31770a8978e381c8f0c05479e18d6ea1defa18a0aa825b0487acd1f082c", - "https://files.pythonhosted.org/packages/7e/ea/4bd00b6848a5c93a7c9f179709ab1e09edea7728287177145f878c68630d/ignore_python-0.3.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl": "8f0e8379d4eff6842b61c01c7f03dc7415afac994b96e4a7d24f80b1078d5c1d", - "https://files.pythonhosted.org/packages/80/b8/167a424011bfe0af21304de7c8df8114e7e680e290f5e1a7aa1318179420/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl": "1b23770925db422fe9c94920da82e0606516aca09fd851880bc8c8ed68fe6455", - "https://files.pythonhosted.org/packages/83/2b/f52307ec26252690f6a4f31df7d727a53807a39c2e70727342b3ae4343e7/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl": "4ba31985a790af71bc45c16643d4773b829b8233acf4175bba2af466a2bbc959", - "https://files.pythonhosted.org/packages/86/62/7989a0a1e3d133b4d70727732020cd690969e693227615e45e275b6af46c/ignore_python-0.3.3-cp310-cp310-musllinux_1_2_aarch64.whl": "7e3b89f96cda6df85687b9532eac4faffdf15d2d918b239399cab6c78dd5282b", - "https://files.pythonhosted.org/packages/89/7e/908bf9ee614a5e3c4cc695e620c07c3a1449508a1ebdd54ab688a6c71e8b/ignore_python-0.3.3-cp310-cp310-musllinux_1_2_armv7l.whl": "1225e6210e302e0725265504a11a367f0b8cb882e3e2e231748559d5b05179c8", - "https://files.pythonhosted.org/packages/8b/b2/24102dc6c85b3fcfb29c9928d69e8d8656557e14ec50d1dd5caf437a6e6f/ignore_python-0.3.3-cp312-cp312-win_amd64.whl": "152c5aecf42e709138c8e3da2ac733d00f5efdcd004c240a95193e62b6bd024e", - "https://files.pythonhosted.org/packages/8c/00/f3f82228067b68f0f66594261a1f51e090fbc32616e1494fbe32339a13fd/ignore_python-0.3.3-cp313-cp313-musllinux_1_2_aarch64.whl": "1b2ff29dbe59bbbd370feacb99e5bec7791abe730dd535b5db848de5b5210d7e", - "https://files.pythonhosted.org/packages/8c/90/7d1d5ddca496189b61d8ccccbc0fbb579a7c2c5b2dbd5e41eec944068dc8/ignore_python-0.3.3-cp313-cp313t-musllinux_1_2_armv7l.whl": "cc49302f8fdbd3426eba4b99671ba10f0d150d90ea4f344a0204e4ac8e4fcb66", - "https://files.pythonhosted.org/packages/90/db/d28201d52730132f63ecc3bebcc4ba8694a2764157c65425ffd86ba05903/ignore_python-0.3.3-cp314-cp314-musllinux_1_2_aarch64.whl": "1ac4491082df61d370f7fc087d5c0b16bc84b647e126e3f45a97d31cf3b2f514", - "https://files.pythonhosted.org/packages/94/09/a89778efcd14cc4d7c332133a58ce2cb3933ee0dc066aeeb7f749637c3b9/ignore_python-0.3.3-cp312-cp312-macosx_10_12_x86_64.whl": "429a9b792afdca7dd9cff59f5ace44c2f55d01fc30b0ce3d28d63bee223116ed", - "https://files.pythonhosted.org/packages/94/8e/360c8ec57ec2b7be346609571b3fa070882c0b20cc60b0c0cb1dd8c11f5f/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "d9778479aaeac4f000d2b9c0f6da76149926011588b08a6b22892ae921c4f563", - "https://files.pythonhosted.org/packages/94/c1/28ad4b5e4e4108407542f7adebccd98d554bfa5f6fcd99f959d6aea9057b/ignore_python-0.3.3-cp38-cp38-musllinux_1_2_x86_64.whl": "f65475871a57413dee3094cc5d479ed202af85c38bc90bda5d7ee4435ff96819", - "https://files.pythonhosted.org/packages/95/cf/1417966361c38b3acb80e2a427cf34883b9c1dc4274dc1097cd9c3c6af2d/ignore_python-0.3.3-cp313-cp313-musllinux_1_2_i686.whl": "e4786eac47d2e9dab245fc376157bdc458f4b3269b81006b80a74d514b37efb7", - "https://files.pythonhosted.org/packages/97/ff/c3707e735606b0197484c4be4ac8e6a32a8899c95d0db25a9bedc301776c/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "c8b112dd1906487fe56525361cbeca57b07adcc9496b641560327654aced7e8f", - "https://files.pythonhosted.org/packages/a2/67/13e76d9a9bf4484b212645631884f48503cdf37ca1f8a0cd761d63faf8ac/ignore_python-0.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "b4f2df2bbca999f9749430e54dcd13bcc35289520b63e09ed4d2e1877a524260", - "https://files.pythonhosted.org/packages/a4/12/60370837ef24be9ae16ee5189b8c2070d429e45a616106303acece8d9d0e/ignore_python-0.3.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl": "56e8f00aef89a19b0305cb233fb59736ad14aa8c6db05720b13ac0bca811b5e7", - "https://files.pythonhosted.org/packages/a4/bc/d510141c02207147c6ca5eb16412cfbbab556abd8ec2fc4cba061cd67981/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "e397f034d675ef14980f2df0a074dec3218963a912b7011d0e8e94f9a3d87d41", - "https://files.pythonhosted.org/packages/a6/1b/10bc2fce9d9f7d669755ea6b22f463f1950eb5538806cbf873f25092b9d3/ignore_python-0.3.3-cp311-cp311-macosx_10_12_x86_64.whl": "5134be429fb954ec589857dbc6152dc09014902313e3d2293af9338a4b7799ae", - "https://files.pythonhosted.org/packages/a8/c6/53f2cff5be0b05f153aaa1e4f72e14f43c5b3980d4453672d9d718e62ff9/ignore_python-0.3.3-cp312-cp312-musllinux_1_2_armv7l.whl": "b730d11384a02bc4fb195b8a73555cb450e325d2676b39c8b5d20a0786102f37", - "https://files.pythonhosted.org/packages/aa/8e/97ed261d173b12101c590682b812759d6e787cd1161fb49921707399149b/ignore_python-0.3.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "c73743c4c8622ebed7998990c5f18a2c2a4fae915288798aeb8fef6d5411743b", - "https://files.pythonhosted.org/packages/aa/b1/c3d851fbf68d1a70cbe49e66b17d10f43af831ef06c34d7d6d385b59499e/ignore_python-0.3.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl": "d8be9b991c63fd8c76a6a9deac24ed164533824da1bdb2356a33511bfa446d54", - "https://files.pythonhosted.org/packages/ab/c6/d9daf9b3919a8d60d1128507d1ef96992563c6d4158f3a0409255542f19f/ignore_python-0.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl": "7ad2cc34fb600ab4aa22014bc8cc9b8bae2d467f772074d3a4929deb4adf64d6", - "https://files.pythonhosted.org/packages/ac/83/1d9de21d59d37ebf984b4caa40c745cfebb10f0d10400b1783e4231b5283/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "8fc3b2fcb6ee1c2b1512a0b29d26bb9b9e945d0d50c0a85673a675622fbfb0f4", - "https://files.pythonhosted.org/packages/af/93/c2aef57d2a83cc33fae854d5a8a33f13bd1d4e8a5accbee52bb2c8ef41d6/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "59e26bf7bbbd5937a196f01480050d3d80418ade8933164b28e3e36734baecd7", - "https://files.pythonhosted.org/packages/b2/30/2f337d260e0169a3a8ee1425c59093b08e055735110c9b62dab5a785f4bf/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "05ccc5bdf2ad1f840a0a2296efff5f4761a26f015185ed0bb835ad1901f08ee8", - "https://files.pythonhosted.org/packages/be/44/0e091481938ffff7bbef9f1964a49249d0a098f11a3b459f05c279d15429/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "85f3ec2d15ba134e6159ddebdb1a0af89f99431d93336f8b3cb71aa2de9f3324", - "https://files.pythonhosted.org/packages/bf/89/abbd4c122ccc82cff70ccec42c9f2f4a096e5e3c9946cd76436b28b1d6c3/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "2d637f1a6b2ec7bcb74b2ac11f75e15eaf6092535b8dc31415305a73a3762e3e", - "https://files.pythonhosted.org/packages/c0/1b/90b799876f7129bc045811ee80025f5f1a2cfed300fed972bb5790466b35/ignore_python-0.3.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "17749af58a6fe6aaa3198b285c874fbf0f036ef2cb684225ef62288b62948d26", - "https://files.pythonhosted.org/packages/c2/a9/a326f1a295e3ecbcadf90699c01b7389276f6405fa4da67472bcc9423ada/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "e61a305384a59997fa1971a6c886db9b8e2ff59fca3ee9bb4e1fe191b6d02593", - "https://files.pythonhosted.org/packages/c6/bd/a37cd31e6d4c6eaabe6ceb22defdb278246973d4f50f9deffea12b1ea037/ignore_python-0.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl": "3c40627f3bde32a37e75950b97733b586e9167fd545c958195dbf1bb73d96a81", - "https://files.pythonhosted.org/packages/c7/64/dbbbe3d4bc43207772aba5ea9aee8c24444e578790088bca4bb7acd8572e/ignore_python-0.3.3-cp312-cp312-musllinux_1_2_aarch64.whl": "af35c6b8c3a9a27721e5c2a849e2ee21973e14b8b7d2e76e15940475a8ace443", - "https://files.pythonhosted.org/packages/c7/89/c1a25074ee04c2db36ec1a1638a72dc91f0056674924783ed3f227fb2346/ignore_python-0.3.3-cp313-cp313-win32.whl": "97db61620be5c56a78115967d05e0d7a130a27a68f401eb98bf0753d3f770cb5", - "https://files.pythonhosted.org/packages/c8/c9/8929fdfbc3c0464ed4f5c5d6ad4b31a8c003dec1bca4446fcf9b8ba44866/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "dab6441f4403483a420de9481987e68ab75a28ade5bac8f1d82a3533ed83a6eb", - "https://files.pythonhosted.org/packages/c8/d7/545c0aabd0c51a08f2245062dfa4be2ea50285148ae78a5fcf013841a37a/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "1ed9c8c858dfe2ba91bc4ab60ebd9d12dd4602a4d0d555e0fee9c8621f9ca292", - "https://files.pythonhosted.org/packages/cb/2a/86804702f3d0820b75c67fa19180812a6cb7c945a720d7054862823c3246/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "39e7b9d976c12c68b09b9944328a000b012725f1f7c4655510eb890761016fb3", - "https://files.pythonhosted.org/packages/cc/32/3e44ceceb0111c9d6cdcb24aaf7d531c4dc0037c51cdd22a75fd2d71de9d/ignore_python-0.3.3-cp313-cp313-musllinux_1_2_x86_64.whl": "4f88aa2ee7335399c823aa050edec118e28ddafe7859e0db4093de0ca815467e", - "https://files.pythonhosted.org/packages/cc/cb/30c8862795c88c8c249a34e584ee95e27748ca890466ab9044e94f32b717/ignore_python-0.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl": "4aac18bf9346f06fd17412d5bcfca53090a72456f53f3ffdf3b1e896f15cc356", - "https://files.pythonhosted.org/packages/cc/dd/9b22e9d958261346fffc5f5062b03af44c7cb77f9ec9265c25ec607e4c39/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "48cf09c7668b5b8bb7dcaf185618130b8ea7839df0eb0f6ae4fae7b7162a0c37", - "https://files.pythonhosted.org/packages/cd/92/02801ce20abad2103c8e634582d1b45cca21985a226d29870ee9ebde0338/ignore_python-0.3.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "0c9ea5d81e6b1a1284f5463203f57cffb3c7e391c0d8ce3fe0e4d838621367fb", - "https://files.pythonhosted.org/packages/cd/b5/aaba0c76d2694873a1dbf31886575c15b11bf7e250a1958deac7221c502a/ignore_python-0.3.3-cp39-cp39-musllinux_1_2_i686.whl": "b2c072a4615c9610bd43cde816882ede0c910599702e54cbc07371874d1ca95b", - "https://files.pythonhosted.org/packages/d2/a8/84baf48e05b603480b963f655e73b97230f7296433d3cc8206b742c167fd/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "ebb00b54e5a01d8b10a51a7de7d2f884359375f4abb5352378f6f7f2c8878a58", - "https://files.pythonhosted.org/packages/d2/b6/2f093454e0c4390128eeeec265181b2b770214ecf1dba920664cdef56756/ignore_python-0.3.3-cp38-cp38-musllinux_1_2_armv7l.whl": "3001adecf33250dfa90c6e727affd559b31ad3b32b8519920c14bef53410444f", - "https://files.pythonhosted.org/packages/d5/d8/b868b289dc2466f3339e72602861379905d4ba1ef32d5c89c0874bf5a1e0/ignore_python-0.3.3-cp313-cp313-win_arm64.whl": "842572b228382c9bb6283428f14ff4481b3822cb7488ce4388281a8c6c465a81", - "https://files.pythonhosted.org/packages/d6/31/71cc339d9a76585f0d3dedf9ea47e070278e792ba87ab88779d02e3765a2/ignore_python-0.3.3-cp313-cp313-macosx_11_0_arm64.whl": "000ae12f6187791bc4748e40e7073b9769ca6ddb0cabfc11d1c682ca0e6a3953", - "https://files.pythonhosted.org/packages/d7/2c/ca2816e41d182f1b60d859906414e831b0cb6182006eb76f7cb4ade0630b/ignore_python-0.3.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl": "f24c6af8fd78b5c58e0f2683004e0b6fac146ee047b2d8fc8d7b16c69e0a3aaa", - "https://files.pythonhosted.org/packages/d7/cf/ae857c74b643ad0cda78501c3fd9ef4107cc4551d0dce04b0a3907f616f5/ignore_python-0.3.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "89b2efb712c5bc0c57cc5a9deabfbcb2196504139d2d108a4afedd140c02063f", - "https://files.pythonhosted.org/packages/d9/3a/33afae6b7102e957506dd702ae74346fe368a541d4675e9aba5725381997/ignore_python-0.3.3-cp312-cp312-macosx_11_0_arm64.whl": "365ab0bf94b64f3def2264fdca58f6e9811763830ad1a48a41d70574a496e5b9", - "https://files.pythonhosted.org/packages/d9/4b/2a07a65878f256effc14bce8afd8d1245549da231c364e4489a6b0cf773d/ignore_python-0.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl": "62afd51edaf5634e21206a65e9e244e038b747e39ba969ebcc9361b63825a4f9", - "https://files.pythonhosted.org/packages/db/20/54bfd6688b19fcd7978163e39078c43f397c9e1730a3ae6149c9573b6401/ignore_python-0.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl": "7e49780796e39812ade8001b0c7d2a2f1a9aeac90964e8e757c2013d30dfbe4e", - "https://files.pythonhosted.org/packages/db/de/aa50c31cdabf694b4e76e42f0a771ba6527e5d9e2b0d1c2bdc9e2a3a6b2b/ignore_python-0.3.3-cp314-cp314-win_arm64.whl": "c545cfd062a463c6d8e90b2738ec93fb9228f12c0aa80866fdc80f64fbc8f1a3", - "https://files.pythonhosted.org/packages/de/d4/2e9a778269b535c17ef016f6e4300e4811688119beaf0367df9fee909690/ignore_python-0.3.3-cp311-cp311-musllinux_1_2_aarch64.whl": "e571e0e3de9bca0b70afc4261aa2df6a305bfff94b092942ccd081fa07b8a148", - "https://files.pythonhosted.org/packages/e3/da/299d98fdb31e2a51d9f814fb0a341efbf3febed212fe69c8796aad8d3811/ignore_python-0.3.3-cp39-cp39-musllinux_1_2_armv7l.whl": "a894e6bf85988edee94474ff1399b61685d5fa7399d1c25a40efa28f7a2799ea", - "https://files.pythonhosted.org/packages/e5/e7/8df35dc4e6e162306c07c9912e8bf4e4aca2bc3e41dbbaf4feb953089d2f/ignore_python-0.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "da0ebc45da1d4e918fba53a2bc059339d00a1f666b3b72b9acd4fe4eeaec8343", - "https://files.pythonhosted.org/packages/e6/ae/9cbd37a23b4f7367369fb289d7729b0995c68e651f1b06b720e24a5169f3/ignore_python-0.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "5f3d88554e779f03567c05286f31d2ce21f6103892c7412bdf350ef2fb50184a", - "https://files.pythonhosted.org/packages/e8/cb/f246ba7e4b6ea4c819eb6f6469bcb68d5abc372068117f305849d04f8be6/ignore_python-0.3.3-cp38-cp38-musllinux_1_2_i686.whl": "fb712f94825c04fa605b989d6f5889195b51c927f5045c34b9ff7a448cd0a6f0", - "https://files.pythonhosted.org/packages/eb/ea/f30078aa0359d777056bf83efc38463258056ae6e904246588200f55157d/ignore_python-0.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl": "a2cdfbd3c9df9e98dd067858fce7d6ab919f2fb038f6b3124fc5f05b8825b546", - "https://files.pythonhosted.org/packages/ec/66/70338e7d59df3050658b3e34512f572f1865849520352b5002b7723025aa/ignore_python-0.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl": "3da9e102800f162468ddb5d2d392b79e2481d3709e886f875037ef7066df5481", - "https://files.pythonhosted.org/packages/f0/01/6c3396e839ddc339bff4245015a259496986afc0ffa1f0aec28957829502/ignore_python-0.3.3-cp39-cp39-musllinux_1_2_aarch64.whl": "e3754a2529b0c163bece327be6c5e92fe885cd01d066bc4a3fc13224eae5d221", - "https://files.pythonhosted.org/packages/f0/63/9c4665a4ea2894eb269bd69043f2004c571024a5699cc231a11219aa00a9/ignore_python-0.3.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl": "130d9ace07988b69669e871ed84dc77088d7b5ce35265efbb0b0d425085e2a99", - "https://files.pythonhosted.org/packages/f0/65/4730b11443d4da0fef9ed7525879529d452d862001c5287f1383d9c2a9ac/ignore_python-0.3.3-cp314-cp314-macosx_10_12_x86_64.whl": "d01b0578252d0df17b64400103ffbfea5b8332483a3da01d116f4919467128de", - "https://files.pythonhosted.org/packages/f3/7f/ab5f380507de1e1d1e1e20f8b64a241581a538d234ce3b66d3f74d4ed66a/ignore_python-0.3.3-cp310-cp310-musllinux_1_2_x86_64.whl": "12827fc970d57865b6f31a82f79838bea24b63d85d9f61f1821b7cf8bd01128c", - "https://files.pythonhosted.org/packages/f3/a1/e907e02bc252f8c7efdab0d317019741cb243dc5eb00bf07e6680f66096f/ignore_python-0.3.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "8528c819c151ccabbd1bb9e591dd99495c2d0423b10ccdef47d48fe25da2b2d6", - "https://files.pythonhosted.org/packages/f4/4a/37928a560a345c6efb207452cf81d3c14f25a6d83df0fa5a00752c0c912b/ignore_python-0.3.3.tar.gz": "dc80ac80ace112da6d02f44681b6beb2ccecb68d6ac2b5e1b82d7f84347e1cf6", - "https://files.pythonhosted.org/packages/f9/a5/2aee7c1997f904aa0fdf5e34471c6e3c5cd6254a55b9aa8c3f2e0a158353/ignore_python-0.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl": "49ccb834be168a7e72b104ffc02024d4eb4d91840bc30e2948787551f5242ff0", - "https://files.pythonhosted.org/packages/fc/15/728225102093748271ef402e8d9c1ad1cce823289de79e236ab436d64979/ignore_python-0.3.3-cp38-cp38-musllinux_1_2_aarch64.whl": "558f79f48d2cd7bd42ce3e6747752be9483504d3f2f84c1d39c39bcf1961bac3", - "https://files.pythonhosted.org/packages/fd/34/af959f5525f98f93167a2a06e70373a1532734f5f1fc716d9f885c9a6164/ignore_python-0.3.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "d4f8b4137b0153c95ea7e4e3acff4c5e6f6c25206d3e3b86dbe879658b00c927", - "https://files.pythonhosted.org/packages/ff/67/bd846385bc059e58cc2be198de8953fdfcf7417cced6bc9686b62a523338/ignore_python-0.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl": "cb82264552ae789f2d8c2543b3b1eb2b3e091977c22f180ab268f5b677825279" - }, - "imagesize": { - "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl": "5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", - "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz": "8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3" - }, - "iniconfig": { - "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz": "c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", - "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl": "f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12" - }, - "jinja2": { - "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl": "85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", - "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz": "0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d" - }, - "jsonschema-rs": { - "https://files.pythonhosted.org/packages/04/cd/e881e8a5aaeecb56e93d20de1b0dfbb085eba972ffba3b5793a5ebf635f0/jsonschema_rs-0.37.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl": "75f3b4e0707dcb3dccf911ff49e387b4db54957fe1a19d3423015a65e3762057", - "https://files.pythonhosted.org/packages/17/e9/644c293e0169425e0a321be197266162d5df2718af18014444edfb185eb9/jsonschema_rs-0.37.4-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "e159075b1846718466998d5a9294c661113b347b8b4749767680a97c8ed2bf4d", - "https://files.pythonhosted.org/packages/34/0f/9693e268eb55d13bf9624f2914a5f09b6a75ee086db554d1495074f116de/jsonschema_rs-0.37.4-pp311-pypy311_pp73-win_amd64.whl": "10fd978a145a6f8d11373879e7d0ff232b409f77c7faf608e6b4549a7f90aaed", - "https://files.pythonhosted.org/packages/48/17/073c55453ec65644957acfe9b435ab45e4a8208faba43fec7ceb0f92607c/jsonschema_rs-0.37.4-cp310-abi3-macosx_10_12_x86_64.whl": "1d3f8c8b376966c19fd4183fa979dbadc9fdd6070f2bfa4d127bdf70946963cc", - "https://files.pythonhosted.org/packages/5c/01/fc8a5338167c45cdaf8b2ebc79e1c404c881b9cb1a0f69312acc6d38ec59/jsonschema_rs-0.37.4-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl": "5975e448092e99d6cc60793a71f0fee516dbf0fd1e6d2f6f1e4689627268f344", - "https://files.pythonhosted.org/packages/7b/d8/d2f94cca643bd4bd70f43762c981e566b9afe16c367a2f05fe97d7c8200c/jsonschema_rs-0.37.4-cp310-abi3-win_amd64.whl": "03b34f911e99343fc388651688683010daee538a3cf8cf86a7997bca28fdf16b", - "https://files.pythonhosted.org/packages/9f/b0/e5fbee8d0e32bacbf8efb372be3a1973e02856c7415c8f2af78b4332f241/jsonschema_rs-0.37.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "e93a8720f6e858d872dc2882e7d3b3243ee76f7aa4d60048272773d44df466e7", - "https://files.pythonhosted.org/packages/aa/4b/e65a64839d8c8f55352ff12ccf37035cfbc8f38a383ef57ba0621007fc1b/jsonschema_rs-0.37.4-cp310-abi3-win32.whl": "0f17a61deb557faa57dffb9596e4f022873404f935114367788b1eebdec2bb00", - "https://files.pythonhosted.org/packages/e2/52/06a843d1c21d11ae22fc15abc5cb7e3b83c8d5aa7e6056c009e7189f4a58/jsonschema_rs-0.37.4.tar.gz": "67f36f1c445c70f9975d17a84ce37f79593f6234d7eb292830d7749e5fa58ff4", - "https://files.pythonhosted.org/packages/eb/3e/584247d45ad6fe020d040f892316d7100affb51d7bb2b8b31dde9633b353/jsonschema_rs-0.37.4-cp310-abi3-manylinux_2_12_i686.manylinux2010_i686.whl": "393ece7037a0d19fd528f7a67a32749453876468871a0bd2267909a57d8d4e32", - "https://files.pythonhosted.org/packages/eb/60/2a376a86787004245b04867babb1719949a01f45dca47404c935e9e288d9/jsonschema_rs-0.37.4-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl": "a56d154b638deb947dbd0dfc285c349eb23a877221f2b0496a2dfa25948cc239", - "https://files.pythonhosted.org/packages/ff/85/b3f795037abf89087f62a7e49baca82e34b4f0d576ac30a740ca61cd33ea/jsonschema_rs-0.37.4-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "dedf72e5e673e3af5b9925979fd71484debada61fb7a3dfabf9bbc74b8012664" - }, - "kiwisolver": { - "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl": "56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", - "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", - "https://files.pythonhosted.org/packages/0a/66/fd0e4a612e3a286c24e6d6f3a5428d11258ed1909bc530ba3b59807fd980/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl": "cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537", - "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl": "0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", - "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl": "59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", - "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl": "f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", - "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl": "3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", - "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl": "9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", - "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl": "d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", - "https://files.pythonhosted.org/packages/17/6f/6fd4f690a40c2582fa34b97d2678f718acf3706b91d270c65ecb455d0a06/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl": "295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4", - "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", - "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl": "fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", - "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl": "fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", - "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl": "5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", - "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl": "6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", - "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl": "f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", - "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", - "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", - "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl": "940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", - "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl": "5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", - "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl": "1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", - "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl": "1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", - "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl": "5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", - "https://files.pythonhosted.org/packages/45/2a/6e19368803a038b2a90857bf4ee9e3c7b667216d045866bf22d3439fd75e/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl": "f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede", - "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl": "bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", - "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl": "893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", - "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", - "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl": "0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", - "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl": "0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", - "https://files.pythonhosted.org/packages/4b/34/3a901559a1e0c218404f9a61a93be82d45cb8f44453ba43088644980f033/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl": "c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e", - "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl": "01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", - "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl": "4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", - "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl": "fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", - "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", - "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl": "3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", - "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl": "f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", - "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl": "7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", - "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl": "50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", - "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl": "6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", - "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl": "ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", - "https://files.pythonhosted.org/packages/6b/dd/644d0dde6010a8583b4cd66dd41c5f83f5325464d15c4f490b3340ab73b4/kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl": "41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc", - "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", - "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl": "d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", - "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl": "fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", - "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl": "be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", - "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl": "f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", - "https://files.pythonhosted.org/packages/75/2b/3f641dfcbe72e222175d626bacf2f72c3b34312afec949dd1c50afa400f5/kiwisolver-1.5.0-cp310-cp310-win_amd64.whl": "94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2", - "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl": "413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", - "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl": "3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", - "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", - "https://files.pythonhosted.org/packages/82/a0/2355d5e3b338f13ce63f361abb181e3b6ea5fffdb73f739b3e80efa76159/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl": "51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca", - "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl": "c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", - "https://files.pythonhosted.org/packages/84/eb/8476a0818850c563ff343ea7c9c05dcdcbd689a38e01aa31657df01f91fa/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl": "cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd", - "https://files.pythonhosted.org/packages/87/9e/f78c466ea20527822b95ad38f141f2de1dcd7f23fb8716b002b0d91bbe59/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl": "8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb", - "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl": "800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", - "https://files.pythonhosted.org/packages/8a/e4/3f43a011bc8a0860d1c96f84d32fa87439d3feedf66e672fef03bf5e8bac/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b", - "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl": "e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", - "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl": "c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", - "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl": "ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", - "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl": "58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", - "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl": "fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", - "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", - "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl": "5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", - "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl": "d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", - "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl": "d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", - "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl": "dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", - "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl": "530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", - "https://files.pythonhosted.org/packages/ac/f8/06549565caa026e540b7e7bab5c5a90eb7ca986015f4c48dace243cd24d9/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl": "32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374", - "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl": "f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", - "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl": "db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", - "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl": "0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", - "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl": "8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", - "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl": "6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", - "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl": "1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", - "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl": "4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", - "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl": "d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", - "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl": "bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", - "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl": "beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", - "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl": "0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", - "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl": "72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", - "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl": "e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", - "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl": "1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", - "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", - "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl": "0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", - "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", - "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", - "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", - "https://files.pythonhosted.org/packages/c8/b9/1d50e610ecadebe205b71d6728fd224ce0e0ca6aba7b9cbe1da049203ac5/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f", - "https://files.pythonhosted.org/packages/ca/d3/9d0c18f1b52ea8074b792452cf17f1f5a56bd0302a85191f405cfbf9da16/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl": "d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c", - "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl": "7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", - "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl": "9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", - "https://files.pythonhosted.org/packages/cd/ee/b85ffcd75afed0357d74f0e6fc02a4507da441165de1ca4760b9f496390d/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed", - "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl": "c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", - "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz": "d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", - "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl": "70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", - "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl": "89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", - "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl": "ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", - "https://files.pythonhosted.org/packages/da/88/299b137b9e0025d8982e03d2d52c123b0a2b159e84b0ef1501ef446339cf/kiwisolver-1.5.0-cp310-cp310-win_arm64.whl": "dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875", - "https://files.pythonhosted.org/packages/dc/8e/6cac929e0049539e5ee25c1ee937556f379ba5204840d03008363ced662d/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl": "ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4", - "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl": "38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", - "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl": "5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", - "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl": "1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", - "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl": "8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", - "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl": "cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", - "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl": "b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", - "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl": "012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", - "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", - "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl": "ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", - "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl": "7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", - "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl": "16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", - "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", - "https://files.pythonhosted.org/packages/f1/0e/ba4ae25d03722f64de8b2c13e80d82ab537a06b30fc7065183c6439357e3/kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl": "62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22", - "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl": "b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", - "https://files.pythonhosted.org/packages/f3/c4/f9c8a6b4c21aed4198566e45923512986d6cef530e7263b3a5f823546561/kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl": "86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476", - "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", - "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl": "c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", - "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl": "ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398" - }, - "lsprotocol": { - "https://files.pythonhosted.org/packages/8d/37/2351e48cb3309673492d3a8c59d407b75fb6630e560eb27ecd4da03adc9a/lsprotocol-2023.0.1-py3-none-any.whl": "c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2", - "https://files.pythonhosted.org/packages/9d/f6/6e80484ec078d0b50699ceb1833597b792a6c695f90c645fbaf54b947e6f/lsprotocol-2023.0.1.tar.gz": "cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d" - }, - "markdown-it-py": { - "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz": "cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", - "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl": "87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147" - }, - "markupsafe": { - "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", - "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl": "9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", - "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl": "1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", - "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl": "ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", - "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl": "0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", - "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl": "5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", - "https://files.pythonhosted.org/packages/18/1f/8d9c20e1c9440e215a44be5ab64359e207fcb4f675543f1cf9a2a7f648d0/markupsafe-3.0.3-cp39-cp39-win_amd64.whl": "7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", - "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", - "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl": "bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", - "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl": "4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", - "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", - "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", - "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", - "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl": "f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", - "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl": "bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", - "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl": "1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", - "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", - "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl": "d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", - "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", - "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl": "83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", - "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl": "eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", - "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl": "3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", - "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl": "e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", - "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", - "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl": "1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", - "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", - "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", - "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl": "729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", - "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", - "https://files.pythonhosted.org/packages/4e/d3/fe08482b5cd995033556d45041a4f4e76e7f0521112a9c9991d40d39825f/markupsafe-3.0.3-cp39-cp39-win_arm64.whl": "38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", - "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", - "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl": "177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", - "https://files.pythonhosted.org/packages/56/23/0d8c13a44bde9154821586520840643467aee574d8ce79a17da539ee7fed/markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl": "15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", - "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl": "9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", - "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl": "d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", - "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl": "e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", - "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl": "f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", - "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl": "12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", - "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl": "5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", - "https://files.pythonhosted.org/packages/6f/bc/4dc914ead3fe6ddaef035341fee0fc956949bbd27335b611829292b89ee2/markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", - "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl": "32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", - "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl": "0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", - "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz": "722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", - "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", - "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl": "69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", - "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl": "de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", - "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl": "2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", - "https://files.pythonhosted.org/packages/89/6e/5fe81fbcfba4aef4093d5f856e5c774ec2057946052d18d168219b7bd9f9/markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", - "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl": "1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", - "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl": "77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", - "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", - "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl": "e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", - "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl": "8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", - "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl": "3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", - "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl": "1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", - "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl": "ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", - "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl": "116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", - "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl": "7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", - "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", - "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl": "26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", - "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", - "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl": "068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", - "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl": "c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", - "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl": "8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", - "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", - "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl": "d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", - "https://files.pythonhosted.org/packages/bc/e4/6be85eb81503f8e11b61c0b6369b6e077dcf0a74adbd9ebf6b349937b4e9/markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", - "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", - "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl": "a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", - "https://files.pythonhosted.org/packages/c8/25/651753ef4dea08ea790f4fbb65146a9a44a014986996ca40102e237aa49a/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl": "591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", - "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl": "c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", - "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl": "be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", - "https://files.pythonhosted.org/packages/cd/1b/a7782984844bd519ad4ffdbebbba2671ec5d0ebbeac34736c15fb86399e8/markupsafe-3.0.3-cp39-cp39-win32.whl": "df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", - "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl": "0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", - "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl": "e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", - "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl": "c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", - "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", - "https://files.pythonhosted.org/packages/dc/0a/c3cf2b4fef5f0426e8a6d7fce3cb966a17817c568ce59d76b92a233fdbec/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl": "a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", - "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl": "4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", - "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl": "218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", - "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl": "35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", - "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl": "2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", - "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl": "795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", - "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", - "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl": "7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", - "https://files.pythonhosted.org/packages/f6/f6/e0e5a3d3ae9c4020f696cd055f940ef86b64fe88de26f3a0308b9d3d048c/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl": "3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", - "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl": "915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", - "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl": "f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", - "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl": "2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe" - }, - "matplotlib": { - "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", - "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", - "https://files.pythonhosted.org/packages/04/30/3afaa31c757f34b7725ab9d2ba8b48b5e89c2019c003e7d0ead143aabc5a/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl": "6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1", - "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl": "fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", - "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl": "3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", - "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl": "41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", - "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl": "eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", - "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl": "dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", - "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl": "b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", - "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl": "3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", - "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", - "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", - "https://files.pythonhosted.org/packages/48/2f/6334aec331f57485a642a7c8be03cb286f29111ae71c46c38b363230063c/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a", - "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl": "595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", - "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl": "99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", - "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl": "37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", - "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl": "00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", - "https://files.pythonhosted.org/packages/5a/f4/b8347351da9a5b3f41e26cf547252d861f685c6867d179a7c9d60ad50189/matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2", - "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl": "113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", - "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl": "f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", - "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl": "e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", - "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl": "c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", - "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl": "b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", - "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl": "2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", - "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl": "32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", - "https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl": "18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9", - "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", - "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", - "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", - "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl": "25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", - "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl": "b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", - "https://files.pythonhosted.org/packages/89/dd/a0b6588f102beab33ca6f5218b31725216577b2a24172f327eaf6417d5c9/matplotlib-3.10.8-cp311-cp311-win_arm64.whl": "bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2", - "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz": "2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", - "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl": "56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", - "https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4", - "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", - "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl": "83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", - "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl": "64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", - "https://files.pythonhosted.org/packages/9e/c0/c7b914e297efe0bc36917bf216b2acb91044b91e930e878ae12981e461e5/matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl": "238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6", - "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl": "ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", - "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl": "3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", - "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl": "0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", - "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", - "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl": "f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", - "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", - "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", - "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl": "f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", - "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl": "15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", - "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", - "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl": "3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", - "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", - "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", - "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl": "f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", - "https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl": "6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160", - "https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl": "a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78" - }, - "mdit-py-plugins": { - "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz": "f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", - "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl": "07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f" - }, - "mdurl": { - "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl": "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", - "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz": "bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" - }, - "minijinja": { - "https://files.pythonhosted.org/packages/0b/34/d0a4f228510b3784717063f63d6784ebd4021afe77df8c868ef06e2140c2/minijinja-2.19.0-cp38-abi3-musllinux_1_2_armv7l.whl": "c2bacdeca8ca5d30035e8e78d119faca248b5ba0597200937a56980880d7d11d", - "https://files.pythonhosted.org/packages/0e/28/5b7eabd21a7e3b0c7f2b33d11305e6ae6fadbc022d09ed2730adae4cb479/minijinja-2.19.0-cp38-abi3-win_amd64.whl": "0cf0ee1abf477c91caf8730cd19ab01960c6be124af9cd1fcbd546a4bfde36bb", - "https://files.pythonhosted.org/packages/26/ac/014e16acde50f6542877b02ff2f80e610193edc8899ee11363271e4373fb/minijinja-2.19.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl": "cc7ef1f9dbdaa9d47c75d7f903d55cbd766c55b38ec195c0ba11e880566bbcf3", - "https://files.pythonhosted.org/packages/49/bd/4b0b92360bf44a9c0ec4db57537cecb3774dead6e5bed8e88c5079541f4e/minijinja-2.19.0-cp38-abi3-musllinux_1_2_i686.whl": "38ed4fef67f550ae8552b261f3a40d389bab782f1dc19e8a015b3ad25da03bc1", - "https://files.pythonhosted.org/packages/67/89/8fd3e725297e9e1c8751f29535654b06af5770c36e7cea75ed62d4b21024/minijinja-2.19.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "2725643b1d19f4f60614153bee6ea1a291c6aa410071b61299113cfca1c23e20", - "https://files.pythonhosted.org/packages/8a/92/86d8c40ff6035ef19bf8f35b25b13fcd763c7624e3763f6b2e2ce5ccd959/minijinja-2.19.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "86693fff42fdf9e6c6684b5b44e79ee708b9419e6331ffe217bbcbe7c2de6016", - "https://files.pythonhosted.org/packages/98/29/ac1ac223755881885ccbcbdaad164a4a323606d9d0abdeedcc262d51a1d9/minijinja-2.19.0-cp38-abi3-musllinux_1_2_x86_64.whl": "695ce4264dbe6422ef45a344dc2c1044eb3a7543606e1b4a255df5b443074462", - "https://files.pythonhosted.org/packages/b7/12/b48b6a2d60fe7c211773ede48bcefb1c6fe1c973509381f8a21b9841a71a/minijinja-2.19.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "a59e1d23b5e62aca1c527869a0ba09126fdc9c69d43beeccb3cf07c36fc8fbe5", - "https://files.pythonhosted.org/packages/c2/4f/c06a20fa16a63dacc131aa82cf0cbf95239cfb851fb60330c8016e9862b7/minijinja-2.19.0.tar.gz": "c2e95fd56a8ab9419403ea7f25273a6b570495116bc1af21fb4c178f4d9c5ff7", - "https://files.pythonhosted.org/packages/c6/48/b78f84441a81be2cd528dcc54f8726f6e10ecb613357cfadb7479e832857/minijinja-2.19.0-cp38-abi3-win32.whl": "fa11ceea225e0458ff8126de18c3baad936106cefe95ac9c9b65795bd71b018f", - "https://files.pythonhosted.org/packages/d3/d9/3d4ef6c327acd40f6efafc8d834c6bd288a7271b4b342cbac07726eba72e/minijinja-2.19.0-cp38-abi3-musllinux_1_2_aarch64.whl": "3defd79efd5af849dd48e3ab03b67772dc30fe53935316c953f77868938da77d", - "https://files.pythonhosted.org/packages/f0/e8/45957b91756603f6e60241a1d3919fbdb94a5e11bd1b9570a13df1af207f/minijinja-2.19.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl": "487d2def6d16d30ad0cb7b0e949afb06815e144e502d0cf4465cd34fa429361e" - }, - "myst-parser": { - "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz": "f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", - "https://files.pythonhosted.org/packages/d3/ac/686789b9145413f1a61878c407210e41bfdb097976864e0913078b24098c/myst_parser-5.0.0-py3-none-any.whl": "ab31e516024918296e169139072b81592336f2fef55b8986aa31c9f04b5f7211" - }, - "needs-config-writer": { - "https://files.pythonhosted.org/packages/24/a1/9f1fc6c25eaed7379834417a977606750eea71ab4c53ac1361e37f7099ce/needs_config_writer-0.2.4-py3-none-any.whl": "0f0702574081bb8ed7d896aadfb73c0e48af099dc0d4227cc2bac957ed8ea4f6", - "https://files.pythonhosted.org/packages/c5/0e/edfe8fa008ce27a12b0848a2982f70d5213420c73298e48e4a0e1e4613e5/needs_config_writer-0.2.4.tar.gz": "7c89375848c822e891b3cca48783f3cc3f7cbd3c02cba19418de146ca077f212" - }, - "nodejs-wheel-binaries": { - "https://files.pythonhosted.org/packages/05/16/119e4168bf7ed17ad7961d122701c75ac86135fa243a958b960a3f1b7055/nodejs_wheel_binaries-24.14.1-py2.py3-none-musllinux_1_2_aarch64.whl": "a3f64daa1235fa6a83c778ded98d5fe4e74979ca54aa2ffb807f0805c57c3abe", - "https://files.pythonhosted.org/packages/13/b7/adb21cf549934579e98934531e7f9b038d583fc9c2dd4b82ea01cc31bdd2/nodejs_wheel_binaries-24.14.1-py2.py3-none-win_arm64.whl": "978fdfe76624c48111ab99ed0f99f9d4c1c682e420b0212ac9e1daee52f20283", - "https://files.pythonhosted.org/packages/27/da/396d1a48cbf3d5899461bda12fa97ae4010d7e4013e7f28230cb04af5818/nodejs_wheel_binaries-24.14.1-py2.py3-none-win_amd64.whl": "7a087b6a727fb9242d1cc83c8b121711bd0e9686408d27de48b34b23dfb26ac5", - "https://files.pythonhosted.org/packages/45/36/bbbee3adf6afd00944e5a86ebd64987dea90bd347090155a4989dc3e8594/nodejs_wheel_binaries-24.14.1-py2.py3-none-manylinux_2_28_x86_64.whl": "7863c62f8a3946b727831f71375a9ae00205b3258478476034b49c3a1d57ac12", - "https://files.pythonhosted.org/packages/6f/15/bc2fa51ee31ce597b2af1905081e5a5add07fe0cf619bfa531d7df2f1f1b/nodejs_wheel_binaries-24.14.1-py2.py3-none-macosx_13_0_x86_64.whl": "634f57829ebfdfe95d096f32a50c5cdd3a6c72a94dcf2b92a8bef9868cccb13e", - "https://files.pythonhosted.org/packages/8b/b7/9765d9a5d3b95475829ef5965d4a4f6f4badb034ee4e18c2d5f8b9b65d6f/nodejs_wheel_binaries-24.14.1-py2.py3-none-macosx_13_0_arm64.whl": "d9e856ba0f2d3d2659869e6e0f4cae6874faeeeca7f879131a88451356373ac4", - "https://files.pythonhosted.org/packages/8c/a6/d581996827b9d1133094dc347f1c4e3d2a70557973ce7a427a03337b1427/nodejs_wheel_binaries-24.14.1-py2.py3-none-musllinux_1_2_x86_64.whl": "810a48ce096925ead0690f7d143e48fb902ebfc9212097e8f6cb3ac6cbe8f314", - "https://files.pythonhosted.org/packages/a6/dd/92ff0831262af4bbb5473d4e7964fd27afb0901a2690a6ff7bc3d220d97f/nodejs_wheel_binaries-24.14.1-py2.py3-none-manylinux_2_28_aarch64.whl": "404b563467129e6a0ea7006a38b3d8af0ebfbc340b31a6a0af2c59ea3af90b7c", - "https://files.pythonhosted.org/packages/f1/87/e5755ad739daafce2e152ab609293d65e6c663b399e28a4bbcd0f4af1f45/nodejs_wheel_binaries-24.14.1.tar.gz": "d00ae0c86d7e1bfa798e8f8ad282db751af157cdcaa1208a1b9a2cf2a85ac821" - }, - "numpy": { - "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl": "f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", - "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl": "42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", - "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", - "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl": "08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", - "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl": "15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", - "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl": "ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", - "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl": "4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", - "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl": "b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", - "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl": "62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", - "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl": "99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", - "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", - "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl": "2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", - "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl": "5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", - "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl": "ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", - "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl": "846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", - "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", - "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl": "6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", - "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl": "0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", - "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl": "1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", - "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", - "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl": "58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", - "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl": "f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", - "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", - "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl": "2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", - "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl": "fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", - "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", - "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl": "0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", - "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", - "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl": "fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", - "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl": "0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", - "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl": "86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", - "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl": "2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", - "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl": "0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", - "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", - "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl": "4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", - "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl": "874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", - "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl": "8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", - "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl": "07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", - "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", - "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl": "51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", - "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl": "e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", - "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl": "2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", - "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl": "4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", - "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl": "ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", - "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl": "ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", - "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", - "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl": "6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", - "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl": "e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", - "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl": "9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", - "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl": "23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", - "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl": "a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", - "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", - "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl": "d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", - "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", - "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", - "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl": "93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", - "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl": "28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", - "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz": "2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", - "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl": "72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", - "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl": "e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", - "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl": "f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", - "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl": "e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", - "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl": "4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", - "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl": "30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", - "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", - "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl": "4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", - "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl": "dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", - "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl": "f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", - "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl": "8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", - "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", - "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl": "715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", - "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl": "6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5" - }, - "packaging": { - "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz": "00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", - "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl": "b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529" - }, - "pillow": { - "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl": "2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", - "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", - "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl": "6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", - "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl": "50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", - "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl": "af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", - "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", - "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", - "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl": "d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", - "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", - "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl": "144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", - "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl": "2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", - "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", - "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl": "3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", - "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl": "b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", - "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl": "5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", - "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", - "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl": "a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", - "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", - "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl": "390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", - "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl": "51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", - "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", - "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", - "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl": "71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", - "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", - "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl": "8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", - "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", - "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl": "042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", - "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", - "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl": "0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", - "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", - "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl": "2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", - "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl": "7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", - "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", - "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl": "9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", - "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", - "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl": "34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", - "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl": "deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", - "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", - "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl": "fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", - "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl": "8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", - "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl": "6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", - "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl": "766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", - "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl": "632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", - "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl": "56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", - "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", - "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", - "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl": "56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", - "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl": "2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", - "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", - "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl": "00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", - "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl": "90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", - "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl": "01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", - "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz": "a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", - "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl": "dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", - "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", - "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", - "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl": "ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", - "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", - "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl": "6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", - "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", - "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl": "57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", - "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl": "6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", - "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl": "80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", - "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl": "bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", - "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl": "8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", - "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl": "58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", - "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl": "3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", - "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl": "dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", - "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl": "9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", - "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl": "9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", - "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl": "325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", - "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl": "8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", - "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", - "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl": "6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", - "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl": "4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", - "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", - "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", - "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl": "b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", - "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl": "f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", - "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", - "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", - "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl": "673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", - "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", - "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl": "e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", - "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", - "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl": "1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", - "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", - "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", - "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", - "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl": "aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", - "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl": "0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd" - }, - "platformdirs": { - "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl": "e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", - "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz": "3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a" - }, - "pluggy": { - "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl": "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", - "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz": "7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3" - }, - "pycparser": { - "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl": "b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", - "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz": "600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29" - }, - "pydata-sphinx-theme": { - "https://files.pythonhosted.org/packages/9d/b7/a2bae25aae3568fe9f17040b31f9c190b4c5d86856d8869d0a30364a2567/pydata_sphinx_theme-0.17.0-py3-none-any.whl": "cec5c92f41f4a11541b6df8210c446b4aa9c3badb7fcf2db7893405b786d5c99", - "https://files.pythonhosted.org/packages/de/bb/4a97aaa840b26601d6d04deca1389c35025336428706a4a732051187fbd3/pydata_sphinx_theme-0.17.0.tar.gz": "529c5631582cb3328cf4814fb9eb80611d1704c854406d282a75c9c86e3a1955" - }, - "pyfakefs": { - "https://files.pythonhosted.org/packages/98/0d/c80012ee6e885c293ad63c5f5b049d3ef3fd2b32bbe6fa8739145f392ec6/pyfakefs-6.2.0.tar.gz": "e59a36db447bf509ce9c97ab3d1510c08cc51895c5311325a560a5e5b5dc1940", - "https://files.pythonhosted.org/packages/b2/80/97571ac8295289c267367b7b60aadeae1a9a841e83f0a96ad9b65d1dd3c0/pyfakefs-6.2.0-py3-none-any.whl": "0968a49db692694ffed420e54a9f1cbae4636637b880e8ab09c8ccc0f11bd7ae" - }, - "pygithub": { - "https://files.pythonhosted.org/packages/2f/de/72e02bc7674e161b155a4b5a03b2347129d0626115bc97ba5bad5070cac9/pygithub-2.9.0-py3-none-any.whl": "5e2b260ce327bffce9b00f447b65953ef7078ffe93e5a5425624a3075483927c", - "https://files.pythonhosted.org/packages/a6/9a/44f918e9be12e49cb8b053f09d5d0733b74df52bf4dabc570da1c3ecd9f6/pygithub-2.9.0.tar.gz": "a26abda1222febba31238682634cad11d8b966137ed6cc3c5e445b29a11cb0a4" - }, - "pygls": { - "https://files.pythonhosted.org/packages/11/19/b74a10dd24548e96e8c80226cbacb28b021bc3a168a7d2709fb0d0185348/pygls-1.3.1-py3-none-any.whl": "6e00f11efc56321bdeb6eac04f6d86131f654c7d49124344a9ebb968da3dd91e", - "https://files.pythonhosted.org/packages/86/b9/41d173dad9eaa9db9c785a85671fc3d68961f08d67706dc2e79011e10b5c/pygls-1.3.1.tar.gz": "140edceefa0da0e9b3c533547c892a42a7d2fd9217ae848c330c53d266a55018" - }, - "pygments": { - "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz": "6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", - "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl": "81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176" - }, - "pyjwt": { - "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz": "41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", - "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl": "66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728" - }, - "pynacl": { - "https://files.pythonhosted.org/packages/06/ea/43fe2f7eab5f200e40fb10d305bf6f87ea31b3bbc83443eac37cd34a9e1e/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_x86_64.whl": "2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b", - "https://files.pythonhosted.org/packages/1e/b4/e927e0653ba63b02a4ca5b4d852a8d1d678afbf69b3dbf9c4d0785ac905c/pynacl-1.6.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0", - "https://files.pythonhosted.org/packages/29/7d/5945b5af29534641820d3bd7b00962abbbdfee84ec7e19f0d5b3175f9a31/pynacl-1.6.2-cp38-abi3-win_arm64.whl": "834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c", - "https://files.pythonhosted.org/packages/33/33/7873dc161c6a06f43cda13dec67b6fe152cb2f982581151956fa5e5cdb47/pynacl-1.6.2-cp314-cp314t-win_arm64.whl": "d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2", - "https://files.pythonhosted.org/packages/3e/d0/f301f83ac8dbe53442c5a43f6a39016f94f754d7a9815a875b65e218a307/pynacl-1.6.2-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl": "8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c", - "https://files.pythonhosted.org/packages/41/ad/334600e8cacc7d86587fe5f565480fde569dfb487389c8e1be56ac21d8ac/pynacl-1.6.2-cp38-abi3-win_amd64.whl": "62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0", - "https://files.pythonhosted.org/packages/48/47/e761c254f410c023a469284a9bc210933e18588ca87706ae93002c05114c/pynacl-1.6.2-cp38-abi3-win32.whl": "5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa", - "https://files.pythonhosted.org/packages/4b/79/0e3c34dc3c4671f67d251c07aa8eb100916f250ee470df230b0ab89551b4/pynacl-1.6.2-cp314-cp314t-macosx_10_10_universal2.whl": "622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594", - "https://files.pythonhosted.org/packages/4d/54/c9ea116412788629b1347e415f72195c25eb2f3809b2d3e7b25f5c79f13a/pynacl-1.6.2-cp314-cp314t-win32.whl": "a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145", - "https://files.pythonhosted.org/packages/55/ad/6efc57ab75ee4422e96b5f2697d51bbcf6cdcc091e66310df91fbdc144a8/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl": "44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634", - "https://files.pythonhosted.org/packages/68/f7/322f2f9915c4ef27d140101dd0ed26b479f7e6f5f183590fd32dfc48c4d3/pynacl-1.6.2-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87", - "https://files.pythonhosted.org/packages/78/b7/928ee9c4779caa0a915844311ab9fb5f99585621c5d6e4574538a17dca07/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_aarch64.whl": "a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88", - "https://files.pythonhosted.org/packages/7f/81/d60984052df5c97b1d24365bc1e30024379b42c4edcd79d2436b1b9806f2/pynacl-1.6.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4", - "https://files.pythonhosted.org/packages/85/42/fe60b5f4473e12c72f977548e4028156f4d340b884c635ec6b063fe7e9a5/pynacl-1.6.2-cp38-abi3-musllinux_1_2_aarch64.whl": "68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e", - "https://files.pythonhosted.org/packages/87/74/8d4b718f8a22aea9e8dcc8b95deb76d4aae380e2f5b570cc70b5fd0a852d/pynacl-1.6.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9", - "https://files.pythonhosted.org/packages/be/7b/4845bbf88e94586ec47a432da4e9107e3fc3ce37eb412b1398630a37f7dd/pynacl-1.6.2-cp38-abi3-macosx_10_10_universal2.whl": "c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465", - "https://files.pythonhosted.org/packages/c4/58/fc6e649762b029315325ace1a8c6be66125e42f67416d3dbd47b69563d61/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl": "26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130", - "https://files.pythonhosted.org/packages/c9/a8/b917096b1accc9acd878819a49d3d84875731a41eb665f6ebc826b1af99e/pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl": "c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6", - "https://files.pythonhosted.org/packages/ce/04/64e9d76646abac2dccf904fccba352a86e7d172647557f35b9fe2a5ee4a1/pynacl-1.6.2-cp314-cp314t-win_amd64.whl": "320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590", - "https://files.pythonhosted.org/packages/d9/9a/4019b524b03a13438637b11538c82781a5eda427394380381af8f04f467a/pynacl-1.6.2.tar.gz": "018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c", - "https://files.pythonhosted.org/packages/eb/1c/23a26e931736e13b16483795c8a6b2f641bf6a3d5238c22b070a5112722c/pynacl-1.6.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0", - "https://files.pythonhosted.org/packages/f3/2f/5e7ea8d85f9f3ea5b6b87db1d8388daa3587eed181bdeb0306816fdbbe79/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_aarch64.whl": "3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444", - "https://files.pythonhosted.org/packages/f7/a9/1bdba746a2be20f8809fee75c10e3159d75864ef69c6b0dd168fc60e485d/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_x86_64.whl": "bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14", - "https://files.pythonhosted.org/packages/fa/f9/e40e318c604259301cc091a2a63f237d9e7b424c4851cafaea4ea7c4834e/pynacl-1.6.2-cp38-abi3-musllinux_1_2_x86_64.whl": "8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577", - "https://files.pythonhosted.org/packages/fd/73/be4fdd3a6a87fe8a4553380c2b47fbd1f7f58292eb820902f5c8ac7de7b0/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl": "04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574" - }, - "pyparsing": { - "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl": "850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", - "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz": "c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc" - }, - "pyspellchecker": { - "https://files.pythonhosted.org/packages/11/70/df4b8816f5e1aa8b46fbc2b1d104d8f5bc13ec92fb69310b35bc56ea7260/pyspellchecker-0.9.0-py3-none-any.whl": "97eaed776e0854f69aafb723f6962b403099e50c9a1d9c05525c819f4b0f5d54", - "https://files.pythonhosted.org/packages/b9/70/58443d052adb868fee9fafb26553d482f2cc4d7c5ed5bb46846a7ce62a01/pyspellchecker-0.9.0.tar.gz": "eb1a594161c84a4e67df7db1c269a3392a645bd50e4c82e1298892db0cdb0965" - }, - "pytest": { - "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz": "b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", - "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl": "2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9" - }, - "python-dateutil": { - "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz": "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", - "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl": "a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" - }, - "pyyaml": { - "https://files.pythonhosted.org/packages/02/72/d972384252432d57f248767556ac083793292a4adf4e2d85dfe785ec2659/PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4", - "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", - "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl": "02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", - "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz": "d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", - "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl": "8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", - "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", - "https://files.pythonhosted.org/packages/0d/a2/09f67a3589cb4320fb5ce90d3fd4c9752636b8b6ad8f34b54d76c5a54693/PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl": "c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f", - "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", - "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl": "652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", - "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl": "64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", - "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl": "5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", - "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl": "4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", - "https://files.pythonhosted.org/packages/25/a2/b725b61ac76a75583ae7104b3209f75ea44b13cfd026aa535ece22b7f22e/PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6", - "https://files.pythonhosted.org/packages/29/3d/6f5e0d58bd924fb0d06c3a6bad00effbdae2de5adb5cda5648006ffbd8d3/pyyaml-6.0.3-cp39-cp39-win32.whl": "1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0", - "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl": "bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", - "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", - "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl": "eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", - "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", - "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl": "8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", - "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", - "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl": "c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", - "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", - "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", - "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl": "44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", - "https://files.pythonhosted.org/packages/6f/b0/b2227677b2d1036d84f5ee95eb948e7af53d59fe3e4328784e4d290607e0/PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl": "6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369", - "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", - "https://files.pythonhosted.org/packages/73/b9/793686b2d54b531203c160ef12bec60228a0109c79bae6c1277961026770/pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a", - "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl": "5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", - "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", - "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl": "96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", - "https://files.pythonhosted.org/packages/76/b2/2b69cee94c9eb215216fc05778675c393e3aa541131dc910df8e52c83776/PyYAML-6.0.3-cp38-cp38-win_amd64.whl": "5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b", - "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", - "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", - "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl": "02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", - "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl": "5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", - "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", - "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl": "fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", - "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", - "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", - "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl": "418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", - "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", - "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl": "79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", - "https://files.pythonhosted.org/packages/99/a5/718a8ea22521e06ef19f91945766a892c5ceb1855df6adbde67d997ea7ed/PyYAML-6.0.3-cp38-cp38-win32.whl": "3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295", - "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl": "8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", - "https://files.pythonhosted.org/packages/9f/62/67fc8e68a75f738c9200422bf65693fb79a4cd0dc5b23310e5202e978090/pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl": "b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da", - "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl": "28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", - "https://files.pythonhosted.org/packages/a7/3b/6c58ac0fa7c4e1b35e48024eb03d00817438310447f93ef4431673c24138/PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3", - "https://files.pythonhosted.org/packages/a9/86/a137b39a611def2ed78b0e66ce2fe13ee701a07c07aebe55c340ed2a050e/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl": "fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926", - "https://files.pythonhosted.org/packages/ae/92/861f152ce87c452b11b9d0977952259aa7df792d71c1053365cc7b09cc08/pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl": "c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917", - "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl": "2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", - "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", - "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl": "34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", - "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl": "41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", - "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl": "66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", - "https://files.pythonhosted.org/packages/d0/cd/f0cfc8c74f8a030017a2b9c771b7f47e5dd702c3e28e5b2071374bda2948/pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9", - "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl": "8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", - "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl": "7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", - "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl": "5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", - "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl": "16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", - "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl": "9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", - "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl": "7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", - "https://files.pythonhosted.org/packages/dd/62/71c27c94f457cf4418ef8ccc71735324c549f7e3ea9d34aba50874563561/pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl": "27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7", - "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl": "1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", - "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl": "d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", - "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", - "https://files.pythonhosted.org/packages/ef/b2/18f2bd28cd2055a79a46c9b0895c0b3d987ce40ee471cecf58a1a0199805/pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5", - "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl": "2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", - "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl": "4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", - "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl": "ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", - "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl": "37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", - "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl": "214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", - "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl": "93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", - "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl": "f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6" - }, - "requests": { - "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz": "18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", - "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl": "4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a" - }, - "requests-file": { - "https://files.pythonhosted.org/packages/72/97/bf44e6c6bd8ddbb99943baf7ba8b1a8485bcd2fe0e55e5708d7fee4ff1ae/requests_file-2.1.0.tar.gz": "0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658", - "https://files.pythonhosted.org/packages/d7/25/dd878a121fcfdf38f52850f11c512e13ec87c2ea72385933818e5b6c15ce/requests_file-2.1.0-py2.py3-none-any.whl": "cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c" - }, - "rich": { - "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl": "793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", - "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz": "b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b" - }, - "roman-numerals": { - "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl": "647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", - "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz": "1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2" - }, - "ruamel-yaml": { - "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl": "27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", - "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz": "53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993" - }, - "six": { - "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz": "ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", - "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl": "4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274" - }, - "smmap": { - "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz": "4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", - "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl": "c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f" - }, - "snowballstemmer": { - "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz": "6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", - "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl": "6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064" - }, - "soupsieve": { - "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl": "ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", - "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz": "3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349" - }, - "sphinx": { - "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl": "c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", - "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz": "7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb" - }, - "sphinx-autobuild": { - "https://files.pythonhosted.org/packages/d7/20/56411b52f917696995f5ad27d2ea7e9492c84a043c5b49a3a3173573cd93/sphinx_autobuild-2025.8.25-py3-none-any.whl": "b750ac7d5a18603e4665294323fd20f6dcc0a984117026d1986704fa68f0379a", - "https://files.pythonhosted.org/packages/e0/3c/a59a3a453d4133777f7ed2e83c80b7dc817d43c74b74298ca0af869662ad/sphinx_autobuild-2025.8.25.tar.gz": "9cf5aab32853c8c31af572e4fecdc09c997e2b8be5a07daf2a389e270e85b213" - }, - "sphinx-collections": { - "https://files.pythonhosted.org/packages/1d/18/2f258a70fb9059014d7dc589b4aab09ffd9a47ba6e4235b77b888d0220f8/sphinx_collections-0.3.1.tar.gz": "4dda762479d2ad2163ccb074b15f36f72810d9cd08be4daa69854a6e34c99f92", - "https://files.pythonhosted.org/packages/73/85/8f0de226be865c7bda58c46db8d27cee0be7f9125bbc21568e5d40699286/sphinx_collections-0.3.1-py3-none-any.whl": "fb93b979cc9275bd2ad980a71fd57be5521c0f879f90f8189917a8f7ca0436ab" - }, - "sphinx-data-viewer": { - "https://files.pythonhosted.org/packages/57/60/5e902d53a5eb3ec7166a8eda602bd78d4b8671a73917567edc8f13a3b366/sphinx_data_viewer-0.1.5-py3-none-any.whl": "b74b1d304c505c464d07c7b225ed0d84ea02dcc88bc1c49cdad7c2275fbbdad4", - "https://files.pythonhosted.org/packages/71/3a/7aeeb805327af5b0e6f072312f9bc660615045e791a5a8f7258276fdddd1/sphinx_data_viewer-0.1.5.tar.gz": "a7d5e58613562bb745380bfe61ca8b69997998167fd6fa9aea55606c9a4b17e4" - }, - "sphinx-design": { - "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz": "d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", - "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl": "f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282" - }, - "sphinx-mounts": { - "https://files.pythonhosted.org/packages/50/43/3ed3fafb632fd168a62f8b9a1b5edc00e000802961f81dd4e922df946e60/sphinx_mounts-0.1.0.tar.gz": "d1818e3a0b7e0b327c6fa265afcf4d43f5bb7147276e69a9b35cd046deeb9439", - "https://files.pythonhosted.org/packages/5e/a2/1318ed0af240e580d1b3c3f1e33708b8bb4fba91dfc0b3c9754b08ab337a/sphinx_mounts-0.1.0-py3-none-any.whl": "aff3450756727d0ca43538ea72b67fddb8b8799c52110f41de8871c59d3d1540" - }, - "sphinx-needs": { - "https://files.pythonhosted.org/packages/b9/dd/69c5d151e1b4d98e85371fdf8ce2c1fe497f4345f32e0429cec20442d4de/sphinx_needs-8.0.0-py3-none-any.whl": "540c380c074d4088a557ea353e91513bfc1cb7712b10925c13ac9e5ebb7be091", - "https://files.pythonhosted.org/packages/e4/77/909f87ae766363e8a0bb3907f54af4a7a963538a7cdc7fe6d331e7098ed1/sphinx_needs-8.0.0.tar.gz": "c4336ee0e3c949eff9eb11a14910f7b6b68cb8284d731cfddf97694037337674" - }, - "sphinxcontrib-applehelp": { - "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl": "4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", - "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz": "2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1" - }, - "sphinxcontrib-devhelp": { - "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl": "aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", - "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz": "411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad" - }, - "sphinxcontrib-htmlhelp": { - "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl": "166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", - "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz": "c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9" - }, - "sphinxcontrib-jquery": { - "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl": "f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", - "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz": "1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a" - }, - "sphinxcontrib-jsmath": { - "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz": "a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", - "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl": "2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178" - }, - "sphinxcontrib-mermaid": { - "https://files.pythonhosted.org/packages/03/46/25d64bcd7821c8d6f1080e1c43d5fcdfc442a18f759a230b5ccdc891093e/sphinxcontrib_mermaid-2.0.1-py3-none-any.whl": "9dca7fbe827bad5e7e2b97c4047682cfd26e3e07398cfdc96c7a8842ae7f06e7", - "https://files.pythonhosted.org/packages/2b/ae/999891de292919b66ea34f2c22fc22c9be90ab3536fbc0fca95716277351/sphinxcontrib_mermaid-2.0.1.tar.gz": "a21a385a059a6cafd192aa3a586b14bf5c42721e229db67b459dc825d7f0a497" - }, - "sphinxcontrib-plantuml": { - "https://files.pythonhosted.org/packages/08/8f/f26dd61b92176c4cba6b7898ca2875e7dfc29263d8e1b3b63d6acf81434f/sphinxcontrib_plantuml-0.31.tar.gz": "fd74752f8ea070e641c3f8a402fccfa1d4a4056e0967b56033d2a76282d9f956" - }, - "sphinxcontrib-qthelp": { - "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl": "b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", - "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz": "4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab" - }, - "sphinxcontrib-serializinghtml": { - "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz": "e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", - "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl": "6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331" - }, - "starlette": { - "https://files.pythonhosted.org/packages/08/a3/84e821cc54b4ab50ae6dbc6ac3800a651b65ec35f045cc73785380654057/starlette-1.0.1.tar.gz": "512399c5f1de7fac99c88572212ded9ddeddef2fb32afa82d724000e88b38f4f", - "https://files.pythonhosted.org/packages/ec/e1/b2df4bc09a1e51ff664c1e17018a4274b42e5e9352e4a478ea540512dc88/starlette-1.0.1-py3-none-any.whl": "7c0e69b2ee1c848bd54669d908500117a3ee13de603a21427e5c6fc1adf98dcd" - }, - "tomli": { - "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl": "7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", - "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", - "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl": "8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", - "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl": "36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", - "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", - "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", - "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl": "2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", - "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl": "88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", - "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl": "eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", - "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl": "2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", - "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl": "5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", - "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz": "7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", - "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl": "47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", - "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl": "52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", - "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl": "7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", - "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl": "fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", - "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl": "5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", - "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl": "d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", - "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", - "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl": "eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", - "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", - "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", - "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", - "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl": "a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", - "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl": "ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", - "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl": "f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", - "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl": "8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", - "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl": "ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", - "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl": "4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", - "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl": "504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", - "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", - "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl": "0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", - "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl": "4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", - "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl": "ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", - "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl": "3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", - "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl": "5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", - "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl": "c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", - "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl": "c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", - "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", - "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl": "51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", - "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl": "b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", - "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl": "da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", - "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl": "7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", - "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", - "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl": "d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", - "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl": "b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", - "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl": "f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30" - }, - "tomli-w": { - "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz": "2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", - "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl": "188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90" - }, - "typing-extensions": { - "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl": "f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", - "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz": "0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" - }, - "urllib3": { - "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz": "231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", - "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897" - }, - "uvicorn": { - "https://files.pythonhosted.org/packages/5e/da/6eee1ff8b6cbeed47eeb5229749168e81eb4b7b999a1a15a7176e51410c9/uvicorn-0.44.0.tar.gz": "6c942071b68f07e178264b9152f1f16dfac5da85880c4ce06366a96d70d4f31e", - "https://files.pythonhosted.org/packages/b7/23/a5bbd9600dd607411fa644c06ff4951bec3a4d82c4b852374024359c19c0/uvicorn-0.44.0-py3-none-any.whl": "ce937c99a2cc70279556967274414c087888e8cec9f9c94644dfca11bd3ced89" - }, - "watchfiles": { - "https://files.pythonhosted.org/packages/00/db/38a2c52fdbbfe2fc7ffaaaaaebc927d52b9f4d5139bba3186c19a7463001/watchfiles-1.1.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl": "cdab464fee731e0884c35ae3588514a9bcf718d0e2c82169c1c4a85cc19c3c7f", - "https://files.pythonhosted.org/packages/04/9d/b07d4491dde6db6ea6c680fdec452f4be363d65c82004faf2d853f59b76f/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl": "b9c4702f29ca48e023ffd9b7ff6b822acdf47cb1ff44cb490a3f1d5ec8987e9c", - "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl": "859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", - "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", - "https://files.pythonhosted.org/packages/0e/24/0759ae15d9a0c9c5fe946bd4cf45ab9e7bad7cfede2c06dc10f59171b29f/watchfiles-1.1.1-cp39-cp39-win32.whl": "6c9c9262f454d1c4d8aaa7050121eb4f3aea197360553699520767daebf2180b", - "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl": "a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", - "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl": "3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", - "https://files.pythonhosted.org/packages/17/71/7ffcaa9b5e8961a25026058058c62ec8f604d2a6e8e1e94bee8a09e1593f/watchfiles-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "4b943d3668d61cfa528eb949577479d3b077fd25fb83c641235437bc0b5bc60e", - "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl": "30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", - "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl": "3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", - "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl": "672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", - "https://files.pythonhosted.org/packages/1f/5d/884074a5269317e75bd0b915644b702b89de73e61a8a7446e2b225f45b1f/watchfiles-1.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "5524298e3827105b61951a29c3512deb9578586abf3a7c5da4a8069df247cccc", - "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl": "52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", - "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl": "f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", - "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", - "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl": "f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", - "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl": "8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", - "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl": "5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", - "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl": "a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", - "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", - "https://files.pythonhosted.org/packages/41/7a/da7ada566f48beaa6a30b13335b49d1f6febaf3a5ddbd1d92163a1002cf4/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "79ff6c6eadf2e3fc0d7786331362e6ef1e51125892c75f1004bd6b52155fb956", - "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl": "de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", - "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl": "88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", - "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl": "f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", - "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", - "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", - "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", - "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", - "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", - "https://files.pythonhosted.org/packages/56/03/e64dcab0a1806157db272a61b7891b062f441a30580a581ae72114259472/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "acb08650863767cbc58bca4813b92df4d6c648459dcaa3d4155681962b2aa2d3", - "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl": "620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", - "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl": "c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", - "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl": "57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", - "https://files.pythonhosted.org/packages/5c/8e/a827cf4a8d5f2903a19a934dcf512082eb07675253e154d4cd9367978a58/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl": "08af70fd77eee58549cd69c25055dc344f918d992ff626068242259f98d598a2", - "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl": "bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", - "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", - "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl": "311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", - "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl": "28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", - "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl": "04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", - "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", - "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl": "8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", - "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl": "36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", - "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl": "563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", - "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl": "39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", - "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", - "https://files.pythonhosted.org/packages/7e/3b/eb26cddd4dfa081e2bf6918be3b2fc05ee3b55c1d21331d5562ee0c6aaad/watchfiles-1.1.1-cp39-cp39-win_amd64.whl": "74472234c8370669850e1c312490f6026d132ca2d396abfad8830b4f1c096957", - "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl": "9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", - "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", - "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl": "3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", - "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", - "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl": "91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", - "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl": "b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", - "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl": "831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", - "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl": "afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", - "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", - "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl": "35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", - "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl": "a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", - "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl": "bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", - "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl": "74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", - "https://files.pythonhosted.org/packages/99/b8/d1857ce9ac76034c053fa7ef0e0ef92d8bd031e842ea6f5171725d31e88f/watchfiles-1.1.1-cp39-cp39-macosx_11_0_arm64.whl": "d6ff426a7cb54f310d51bfe83fe9f2bbe40d540c741dc974ebc30e6aa238f52e", - "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl": "1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", - "https://files.pythonhosted.org/packages/a4/68/a7303a15cc797ab04d58f1fea7f67c50bd7f80090dfd7e750e7576e07582/watchfiles-1.1.1-cp39-cp39-macosx_10_12_x86_64.whl": "c882d69f6903ef6092bedfb7be973d9319940d56b8427ab9187d1ecd73438a70", - "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl": "bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", - "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl": "eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", - "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl": "3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", - "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl": "319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", - "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", - "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", - "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl": "5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", - "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", - "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", - "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl": "03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", - "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", - "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", - "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl": "17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", - "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl": "130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", - "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", - "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", - "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl": "059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", - "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl": "55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", - "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz": "a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", - "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", - "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl": "d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", - "https://files.pythonhosted.org/packages/c4/64/bc3331150e8f3c778d48a4615d4b72b3d2d87868635e6c54bbd924946189/watchfiles-1.1.1-cp39-cp39-musllinux_1_1_aarch64.whl": "cf57a27fb986c6243d2ee78392c503826056ffe0287e8794503b10fb51b881be", - "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl": "c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", - "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", - "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", - "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl": "836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", - "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl": "dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", - "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", - "https://files.pythonhosted.org/packages/d1/43/d7e8b71f6c21ff813ee8da1006f89b6c7fff047fb4c8b16ceb5e840599c5/watchfiles-1.1.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl": "3dbd8cbadd46984f802f6d479b7e3afa86c42d13e8f0f322d669d79722c8ec34", - "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", - "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl": "db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", - "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", - "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", - "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl": "ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", - "https://files.pythonhosted.org/packages/dc/a6/94fed0b346b85b22303a12eee5f431006fae6af70d841cac2f4403245533/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "6c3631058c37e4a0ec440bf583bc53cdbd13e5661bb6f465bc1d88ee9a0a4d02", - "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", - "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl": "842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", - "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", - "https://files.pythonhosted.org/packages/e2/b2/7cb9e0d5445a8d45c4cccd68a590d9e3a453289366b96ff37d1075aaebef/watchfiles-1.1.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "c1f5210f1b8fc91ead1283c6fd89f70e76fb07283ec738056cf34d51e9c1d62c", - "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", - "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl": "a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", - "https://files.pythonhosted.org/packages/e4/84/f39e19549c2f3ec97225dcb2ceb9a7bb3c5004ed227aad1f321bf0ff2051/watchfiles-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl": "d7e7067c98040d646982daa1f37a33d3544138ea155536c2e0e63e07ff8a7e0f", - "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", - "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl": "3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", - "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl": "b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", - "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl": "399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", - "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl": "421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741" - }, - "websockets": { - "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl": "37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", - "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz": "5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", - "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl": "335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", - "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl": "a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", - "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl": "583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", - "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl": "9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", - "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl": "8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", - "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", - "https://files.pythonhosted.org/packages/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl": "04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", - "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl": "52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", - "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl": "abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", - "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl": "86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", - "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", - "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl": "485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", - "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl": "a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", - "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl": "8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", - "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl": "f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", - "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl": "af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", - "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl": "781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", - "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl": "3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", - "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl": "1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", - "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl": "1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", - "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl": "349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", - "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl": "569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", - "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl": "5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", - "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl": "52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", - "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", - "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl": "71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", - "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", - "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl": "d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", - "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", - "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl": "c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", - "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl": "eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", - "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", - "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl": "a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", - "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl": "6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", - "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl": "7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", - "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl": "b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", - "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", - "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl": "e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", - "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", - "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl": "2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", - "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl": "8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", - "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl": "8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", - "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl": "19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", - "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl": "fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", - "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl": "95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", - "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl": "b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", - "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl": "5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", - "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl": "5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", - "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl": "a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", - "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", - "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl": "878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", - "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl": "08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", - "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl": "a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", - "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl": "50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", - "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl": "daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", - "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl": "31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", - "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl": "8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", - "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl": "417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", - "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82" - } - }, - "https://pypi.org/simple": { - "absl-py": { - "https://files.pythonhosted.org/packages/01/e4/dc0a1dcc4e74e08d7abedab278c795eef54a224363bb18f5692f416d834f/absl_py-2.0.0-py3-none-any.whl": "9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3", - "https://files.pythonhosted.org/packages/c6/09/fe8d9ab3e640b12322f8f73448db3428bf417b7dcfe14702fc7413e6c5c9/absl-py-2.0.0.tar.gz": "d9690211c5fcfefcdd1a45470ac2b5c5acd45241c3af71eed96bc5441746c0d5" - }, - "backports-tarfile": { - "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz": "d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", - "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl": "77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34" - }, - "certifi": { - "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz": "47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", - "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl": "0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de" - }, - "cffi": { - "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", - "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl": "3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", - "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl": "5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", - "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl": "b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", - "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", - "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", - "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl": "cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", - "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl": "7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", - "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl": "737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", - "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl": "c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", - "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl": "89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", - "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl": "7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", - "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl": "cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", - "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl": "b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", - "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl": "6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", - "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl": "19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", - "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl": "81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", - "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl": "de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", - "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl": "9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", - "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl": "087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", - "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl": "a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", - "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", - "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl": "8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", - "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl": "45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", - "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl": "00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", - "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", - "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl": "2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", - "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", - "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl": "d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", - "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl": "b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", - "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl": "c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", - "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl": "e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", - "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", - "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl": "da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", - "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl": "9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", - "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl": "fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", - "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl": "0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", - "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl": "4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", - "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl": "c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", - "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", - "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", - "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", - "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl": "f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", - "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl": "dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", - "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl": "9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", - "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl": "1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", - "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", - "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", - "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl": "2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", - "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl": "94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", - "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl": "0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", - "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl": "66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", - "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", - "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", - "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", - "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl": "2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", - "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", - "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl": "203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", - "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl": "d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", - "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", - "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl": "d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", - "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl": "fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", - "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", - "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl": "38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", - "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl": "256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", - "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl": "dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", - "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", - "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", - "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", - "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", - "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl": "b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", - "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", - "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl": "8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", - "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", - "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl": "6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", - "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl": "1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", - "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl": "0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", - "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl": "6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", - "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz": "44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", - "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl": "74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", - "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl": "f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", - "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl": "1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", - "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl": "da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", - "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl": "21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe" - }, - "charset-normalizer": { - "https://files.pythonhosted.org/packages/00/bd/ef9c88464b126fa176f4ef4a317ad9b6f4d30b2cffbc43386062367c3e2c/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9", - "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl": "ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", - "https://files.pythonhosted.org/packages/04/9a/914d294daa4809c57667b77470533e65def9c0be1ef8b4c1183a99170e9d/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7", - "https://files.pythonhosted.org/packages/05/35/bb59b1cd012d7196fc81c2f5879113971efc226a63812c9cf7f89fe97c40/charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl": "5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40", - "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl": "42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", - "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl": "30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", - "https://files.pythonhosted.org/packages/0c/52/8b0c6c3e53f7e546a5e49b9edb876f379725914e1130297f3b423c7b71c5/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b", - "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", - "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", - "https://files.pythonhosted.org/packages/1a/79/ae516e678d6e32df2e7e740a7be51dc80b700e2697cb70054a0f1ac2c955/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b", - "https://files.pythonhosted.org/packages/20/30/5f64fe3981677fe63fa987b80e6c01042eb5ff653ff7cec1b7bd9268e54e/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl": "2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312", - "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", - "https://files.pythonhosted.org/packages/22/82/63a45bfc36f73efe46731a3a71cb84e2112f7e0b049507025ce477f0f052/charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl": "0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c", - "https://files.pythonhosted.org/packages/2a/91/26c3036e62dfe8de8061182d33be5025e2424002125c9500faff74a6735e/charset_normalizer-3.4.3-cp310-cp310-win32.whl": "d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f", - "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl": "1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", - "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl": "cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", - "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl": "78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", - "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", - "https://files.pythonhosted.org/packages/39/c6/99271dc37243a4f925b09090493fb96c9333d7992c6187f5cfe5312008d2/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e", - "https://files.pythonhosted.org/packages/39/f5/3b3836ca6064d0992c58c7561c6b6eee1b3892e9665d650c803bd5614522/charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl": "86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc", - "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", - "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl": "4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", - "https://files.pythonhosted.org/packages/45/8c/dcef87cfc2b3f002a6478f38906f9040302c68aebe21468090e39cde1445/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl": "88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34", - "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl": "939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", - "https://files.pythonhosted.org/packages/50/10/c117806094d2c956ba88958dab680574019abc0c02bcf57b32287afca544/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl": "a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b", - "https://files.pythonhosted.org/packages/50/ee/f4704bad8201de513fdc8aac1cabc87e38c5818c93857140e06e772b5892/charset_normalizer-3.4.3-cp312-cp312-win32.whl": "fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37", - "https://files.pythonhosted.org/packages/59/c0/a74f3bd167d311365e7973990243f32c35e7a94e45103125275b9e6c479f/charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4", - "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", - "https://files.pythonhosted.org/packages/61/c5/dc3ba772489c453621ffc27e8978a98fe7e41a93e787e5e5bde797f1dddb/charset_normalizer-3.4.3-cp38-cp38-win32.whl": "ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557", - "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl": "96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", - "https://files.pythonhosted.org/packages/63/86/9cbd533bd37883d467fcd1bd491b3547a3532d0fbb46de2b99feeebf185e/charset_normalizer-3.4.3-cp39-cp39-win32.whl": "16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432", - "https://files.pythonhosted.org/packages/64/d1/f9d141c893ef5d4243bc75c130e95af8fd4bc355beff06e9b1e941daad6e/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl": "5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a", - "https://files.pythonhosted.org/packages/64/d4/9eb4ff2c167edbbf08cdd28e19078bf195762e9bd63371689cab5ecd3d0d/charset_normalizer-3.4.3-cp311-cp311-win32.whl": "6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849", - "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", - "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl": "14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", - "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl": "18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", - "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", - "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl": "d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", - "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl": "fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", - "https://files.pythonhosted.org/packages/7a/03/cbb6fac9d3e57f7e07ce062712ee80d80a5ab46614684078461917426279/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl": "d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb", - "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", - "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl": "53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", - "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", - "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl": "b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", - "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", - "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz": "6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", - "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", - "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", - "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", - "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl": "ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", - "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl": "3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", - "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl": "fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", - "https://files.pythonhosted.org/packages/9a/8f/ae790790c7b64f925e5c953b924aaa42a243fb778fed9e41f147b2a5715a/charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl": "cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef", - "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", - "https://files.pythonhosted.org/packages/a3/ad/b0081f2f99a4b194bcbb1934ef3b12aa4d9702ced80a37026b7607c72e58/charset_normalizer-3.4.3-cp313-cp313-win32.whl": "6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce", - "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl": "02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", - "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl": "027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", - "https://files.pythonhosted.org/packages/b0/a8/6f5bcf1bcf63cb45625f7c5cadca026121ff8a6c8a3256d8d8cd59302663/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl": "257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7", - "https://files.pythonhosted.org/packages/b7/8c/9839225320046ed279c6e839d51f028342eb77c91c89b8ef2549f951f3ec/charset_normalizer-3.4.3-cp314-cp314-win32.whl": "c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce", - "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl": "320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", - "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl": "6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", - "https://files.pythonhosted.org/packages/c2/ca/9a0983dd5c8e9733565cf3db4df2b0a2e9a82659fd8aa2a868ac6e4a991f/charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl": "70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05", - "https://files.pythonhosted.org/packages/c4/72/d3d0e9592f4e504f9dea08b8db270821c909558c353dc3b457ed2509f2fb/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl": "1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19", - "https://files.pythonhosted.org/packages/c5/35/9c99739250742375167bc1b1319cd1cec2bf67438a70d84b2e1ec4c9daa3/charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl": "b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942", - "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", - "https://files.pythonhosted.org/packages/ce/d6/7e805c8e5c46ff9729c49950acc4ee0aeb55efb8b3a56687658ad10c3216/charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl": "d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca", - "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl": "b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", - "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl": "fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", - "https://files.pythonhosted.org/packages/e1/ef/dd08b2cac9284fd59e70f7d97382c33a3d0a926e45b15fc21b3308324ffd/charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl": "511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc", - "https://files.pythonhosted.org/packages/e2/c6/f05db471f81af1fa01839d44ae2a8bfeec8d2a8b4590f16c4e7393afd323/charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl": "c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669", - "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", - "https://files.pythonhosted.org/packages/e4/69/132eab043356bba06eb333cc2cc60c6340857d0a2e4ca6dc2b51312886b3/charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99", - "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl": "e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", - "https://files.pythonhosted.org/packages/ee/7a/36fbcf646e41f710ce0a563c1c9a343c6edf9be80786edeb15b6f62e17db/charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl": "73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c", - "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl": "c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", - "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl": "8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", - "https://files.pythonhosted.org/packages/f4/9c/996a4a028222e7761a96634d1820de8a744ff4327a00ada9c8942033089b/charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl": "31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c", - "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl": "bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", - "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl": "c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018" - }, - "cryptography": { - "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", - "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl": "ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", - "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl": "cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", - "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl": "5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", - "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl": "cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", - "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl": "d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", - "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl": "258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", - "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl": "fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", - "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl": "397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", - "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl": "420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", - "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl": "e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", - "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl": "8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", - "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl": "d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", - "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl": "60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", - "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl": "b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", - "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl": "c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", - "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl": "a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", - "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz": "e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", - "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", - "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl": "f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", - "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl": "fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", - "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl": "65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", - "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", - "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl": "fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", - "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl": "9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", - "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl": "d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", - "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", - "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl": "d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", - "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl": "935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", - "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl": "128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", - "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl": "73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", - "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl": "abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", - "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl": "fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", - "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl": "5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", - "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl": "d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", - "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl": "84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", - "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl": "7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", - "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl": "462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", - "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl": "80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", - "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl": "cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", - "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl": "35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", - "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl": "ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", - "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl": "1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", - "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl": "42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", - "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl": "04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", - "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl": "91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", - "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl": "506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", - "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl": "3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", - "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl": "24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de" - }, - "docutils": { - "https://files.pythonhosted.org/packages/4a/c0/89fe6215b443b919cb98a5002e107cb5026854ed1ccb6b5833e0768419d1/docutils-0.22.2.tar.gz": "9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d", - "https://files.pythonhosted.org/packages/66/dd/f95350e853a4468ec37478414fc04ae2d61dad7a947b3015c3dcc51a09b9/docutils-0.22.2-py3-none-any.whl": "b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8" - }, - "idna": { - "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", - "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz": "12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9" - }, - "importlib-metadata": { - "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl": "e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", - "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz": "d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000" - }, - "jaraco-classes": { - "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz": "47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", - "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl": "f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" - }, - "jaraco-context": { - "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz": "9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", - "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl": "f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4" - }, - "jaraco-functools": { - "https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl": "227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8", - "https://files.pythonhosted.org/packages/f7/ed/1aa2d585304ec07262e1a83a9889880701079dde796ac7b1d1826f40c63d/jaraco_functools-4.3.0.tar.gz": "cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294" - }, - "jeepney": { - "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz": "cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", - "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl": "97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683" - }, - "keyring": { - "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz": "0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", - "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl": "552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd" - }, - "markdown-it-py": { - "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz": "cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", - "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl": "87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147" - }, - "mdurl": { - "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl": "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", - "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz": "bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" - }, - "more-itertools": { - "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl": "52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", - "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz": "f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd" - }, - "nh3": { - "https://files.pythonhosted.org/packages/0c/e0/cf1543e798ba86d838952e8be4cb8d18e22999be2a24b112a671f1c04fd6/nh3-0.3.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl": "ec6cfdd2e0399cb79ba4dcffb2332b94d9696c52272ff9d48a630c5dca5e325a", - "https://files.pythonhosted.org/packages/10/71/2fb1834c10fab6d9291d62c95192ea2f4c7518bd32ad6c46aab5d095cb87/nh3-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl": "0649464ac8eee018644aacbc103874ccbfac80e3035643c3acaab4287e36e7f5", - "https://files.pythonhosted.org/packages/23/1e/80a8c517655dd40bb13363fc4d9e66b2f13245763faab1a20f1df67165a7/nh3-0.3.0-cp313-cp313t-win_amd64.whl": "423201bbdf3164a9e09aa01e540adbb94c9962cc177d5b1cbb385f5e1e79216e", - "https://files.pythonhosted.org/packages/2f/d6/f1c6e091cbe8700401c736c2bc3980c46dca770a2cf6a3b48a175114058e/nh3-0.3.0-cp313-cp313t-win32.whl": "7275fdffaab10cc5801bf026e3c089d8de40a997afc9e41b981f7ac48c5aa7d5", - "https://files.pythonhosted.org/packages/33/c1/8f8ccc2492a000b6156dce68a43253fcff8b4ce70ab4216d08f90a2ac998/nh3-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl": "1adeb1062a1c2974bc75b8d1ecb014c5fd4daf2df646bbe2831f7c23659793f9", - "https://files.pythonhosted.org/packages/39/2c/6394301428b2017a9d5644af25f487fa557d06bc8a491769accec7524d9a/nh3-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "f416c35efee3e6a6c9ab7716d9e57aa0a49981be915963a82697952cba1353e1", - "https://files.pythonhosted.org/packages/4c/3c/cba7b26ccc0ef150c81646478aa32f9c9535234f54845603c838a1dc955c/nh3-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl": "80fe20171c6da69c7978ecba33b638e951b85fb92059259edd285ff108b82a6d", - "https://files.pythonhosted.org/packages/4e/9a/344b9f9c4bd1c2413a397f38ee6a3d5db30f1a507d4976e046226f12b297/nh3-0.3.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl": "37d3003d98dedca6cd762bf88f2e70b67f05100f6b949ffe540e189cc06887f9", - "https://files.pythonhosted.org/packages/5b/76/3165e84e5266d146d967a6cc784ff2fbf6ddd00985a55ec006b72bc39d5d/nh3-0.3.0-cp38-abi3-win_arm64.whl": "d97d3efd61404af7e5721a0e74d81cdbfc6e5f97e11e731bb6d090e30a7b62b2", - "https://files.pythonhosted.org/packages/5c/86/a96b1453c107b815f9ab8fac5412407c33cc5c7580a4daf57aabeb41b774/nh3-0.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl": "ce5e7185599f89b0e391e2f29cc12dc2e206167380cea49b33beda4891be2fe1", - "https://files.pythonhosted.org/packages/63/da/c5fd472b700ba37d2df630a9e0d8cc156033551ceb8b4c49cc8a5f606b68/nh3-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl": "ba0caa8aa184196daa6e574d997a33867d6d10234018012d35f86d46024a2a95", - "https://files.pythonhosted.org/packages/66/3f/cd37f76c8ca277b02a84aa20d7bd60fbac85b4e2cbdae77cb759b22de58b/nh3-0.3.0-cp38-abi3-musllinux_1_2_aarch64.whl": "634e34e6162e0408e14fb61d5e69dbaea32f59e847cfcfa41b66100a6b796f62", - "https://files.pythonhosted.org/packages/6a/1b/b15bd1ce201a1a610aeb44afd478d55ac018b4475920a3118ffd806e2483/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl": "e9e6a7e4d38f7e8dda9edd1433af5170c597336c1a74b4693c5cb75ab2b30f2a", - "https://files.pythonhosted.org/packages/8c/ae/324b165d904dc1672eee5f5661c0a68d4bab5b59fbb07afb6d8d19a30b45/nh3-0.3.0-cp38-abi3-win_amd64.whl": "bae63772408fd63ad836ec569a7c8f444dd32863d0c67f6e0b25ebbd606afa95", - "https://files.pythonhosted.org/packages/8f/14/079670fb2e848c4ba2476c5a7a2d1319826053f4f0368f61fca9bb4227ae/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl": "7852f038a054e0096dac12b8141191e02e93e0b4608c4b993ec7d4ffafea4e49", - "https://files.pythonhosted.org/packages/97/03/03f79f7e5178eb1ad5083af84faff471e866801beb980cc72943a4397368/nh3-0.3.0-cp38-abi3-musllinux_1_2_i686.whl": "c7a32a7f0d89f7d30cb8f4a84bdbd56d1eb88b78a2434534f62c71dac538c450", - "https://files.pythonhosted.org/packages/97/33/11e7273b663839626f714cb68f6eb49899da5a0d9b6bc47b41fe870259c2/nh3-0.3.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl": "389d93d59b8214d51c400fb5b07866c2a4f79e4e14b071ad66c92184fec3a392", - "https://files.pythonhosted.org/packages/9a/e0/af86d2a974c87a4ba7f19bc3b44a8eaa3da480de264138fec82fe17b340b/nh3-0.3.0-cp313-cp313t-win_arm64.whl": "16f8670201f7e8e0e05ed1a590eb84bfa51b01a69dd5caf1d3ea57733de6a52f", - "https://files.pythonhosted.org/packages/a3/e5/ac7fc565f5d8bce7f979d1afd68e8cb415020d62fa6507133281c7d49f91/nh3-0.3.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl": "af5aa8127f62bbf03d68f67a956627b1bd0469703a35b3dad28d0c1195e6c7fb", - "https://files.pythonhosted.org/packages/ad/7f/7c6b8358cf1222921747844ab0eef81129e9970b952fcb814df417159fb9/nh3-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl": "7c915060a2c8131bef6a29f78debc29ba40859b6dbe2362ef9e5fd44f11487c2", - "https://files.pythonhosted.org/packages/b4/11/340b7a551916a4b2b68c54799d710f86cf3838a4abaad8e74d35360343bb/nh3-0.3.0-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl": "a537ece1bf513e5a88d8cff8a872e12fe8d0f42ef71dd15a5e7520fecd191bbb", - "https://files.pythonhosted.org/packages/c3/a4/96cff0977357f60f06ec4368c4c7a7a26cccfe7c9fcd54f5378bf0428fd3/nh3-0.3.0.tar.gz": "d8ba24cb31525492ea71b6aac11a4adac91d828aadeff7c4586541bf5dc34d2f", - "https://files.pythonhosted.org/packages/c9/50/76936ec021fe1f3270c03278b8af5f2079038116b5d0bfe8538ffe699d69/nh3-0.3.0-cp38-abi3-win32.whl": "6d68fa277b4a3cf04e5c4b84dd0c6149ff7d56c12b3e3fab304c525b850f613d", - "https://files.pythonhosted.org/packages/ce/55/1974bcc16884a397ee699cebd3914e1f59be64ab305533347ca2d983756f/nh3-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl": "3f1b4f8a264a0c86ea01da0d0c390fe295ea0bcacc52c2103aca286f6884f518", - "https://files.pythonhosted.org/packages/ee/db/7aa11b44bae4e7474feb1201d8dee04fabe5651c7cb51409ebda94a4ed67/nh3-0.3.0-cp38-abi3-musllinux_1_2_armv7l.whl": "b0612ccf5de8a480cf08f047b08f9d3fecc12e63d2ee91769cb19d7290614c23", - "https://files.pythonhosted.org/packages/f3/ba/59e204d90727c25b253856e456ea61265ca810cda8ee802c35f3fadaab00/nh3-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl": "e90883f9f85288f423c77b3f5a6f4486375636f25f793165112679a7b6363b35" - }, - "pkginfo": { - "https://files.pythonhosted.org/packages/24/03/e26bf3d6453b7fda5bd2b84029a426553bb373d6277ef6b5ac8863421f87/pkginfo-1.12.1.2.tar.gz": "5cd957824ac36f140260964eba3c6be6442a8359b8c48f4adf90210f33a04b7b", - "https://files.pythonhosted.org/packages/fa/3d/f4f2ba829efb54b6cd2d91349c7463316a9cc55a43fc980447416c88540f/pkginfo-1.12.1.2-py3-none-any.whl": "c783ac885519cab2c34927ccfa6bf64b5a704d7c69afaea583dd9b7afe969343" - }, - "pycparser": { - "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl": "e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", - "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz": "78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2" - }, - "pygments": { - "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz": "636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", - "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl": "86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" - }, - "pywin32-ctypes": { - "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz": "d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", - "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl": "8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8" - }, - "readme-renderer": { - "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz": "8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", - "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl": "2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151" - }, - "requests": { - "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz": "c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", - "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl": "3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b" - }, - "requests-toolbelt": { - "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl": "cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", - "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz": "7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6" - }, - "rfc3986": { - "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz": "97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", - "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl": "50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd" - }, - "rich": { - "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl": "536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", - "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz": "e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8" - }, - "secretstorage": { - "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz": "2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", - "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl": "f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99" - }, - "six": { - "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz": "1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl": "8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - }, - "twine": { - "https://files.pythonhosted.org/packages/5d/ec/00f9d5fd040ae29867355e559a94e9a8429225a0284a3f5f091a3878bfc0/twine-5.1.1-py3-none-any.whl": "215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997", - "https://files.pythonhosted.org/packages/77/68/bd982e5e949ef8334e6f7dcf76ae40922a8750aa2e347291ae1477a4782b/twine-5.1.1.tar.gz": "9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db" - }, - "urllib3": { - "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl": "bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", - "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz": "1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed" - }, - "zipp": { - "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl": "071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", - "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz": "a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166" - } - } - }, - "fact_version": "v1" - } - } + "facts": {} } From fe8aac18fb7b8808b9d19ebb775e4335643cf657 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 21:58:36 +0200 Subject: [PATCH 16/19] fix: treat known-good as optional --- scripts_bazel/merge_sourcelinks.py | 9 +++++++- scripts_bazel/tests/merge_sourcelinks_test.py | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/scripts_bazel/merge_sourcelinks.py b/scripts_bazel/merge_sourcelinks.py index 08b275e3d..83b18d1a3 100644 --- a/scripts_bazel/merge_sourcelinks.py +++ b/scripts_bazel/merge_sourcelinks.py @@ -67,7 +67,14 @@ def main(): ) # As we can't deal with bad JSON structure we just skip it continue - if metadata["repo_name"] and metadata["repo_name"] != "local_repo": + # A known-good file is optional for standalone builds that include + # documentation from external modules. In that case, keep the + # metadata produced by the individual sourcelinks file. + if ( + args.known_good + and metadata["repo_name"] + and metadata["repo_name"] != "local_repo" + ): hash, repo = parse_info_from_known_good( known_good_json=args.known_good, repo_name=metadata["repo_name"] ) diff --git a/scripts_bazel/tests/merge_sourcelinks_test.py b/scripts_bazel/tests/merge_sourcelinks_test.py index f20501a3f..e13d07963 100644 --- a/scripts_bazel/tests/merge_sourcelinks_test.py +++ b/scripts_bazel/tests/merge_sourcelinks_test.py @@ -321,3 +321,26 @@ def test_merge_sourcelinks_with_known_good( } assert expected_dict1 in data assert expected_dict2 in data + + +def test_merge_sourcelinks_without_known_good_preserves_external_metadata( + create_external_repo_json_files: tuple[Path, Path, Path], + monkeypatch: pytest.MonkeyPatch, +): + file1, file2, output_file = create_external_repo_json_files + test_args: list[Path | str] = [ + _MY_PATH.parent / "merge_sourcelinks.py", + "--output", + str(output_file), + str(file1), + str(file2), + ] + monkeypatch.setattr(sys, "argv", test_args) + + assert scripts_bazel.merge_sourcelinks.main() == 0 + + data: list[dict[str, str | int]] = json.loads(output_file.read_text()) + external_link = next(entry for entry in data if entry["file"] == "test1.py") + assert external_link["repo_name"] == "score_baselibs" + assert external_link["url"] == "https://github.com/eclipse-score/baselibs.git" + assert external_link["hash"] == "158fe6a7b791c58f6eac5f7e4662b8db0cf9ac6e" From 8c518f18b1bfc7bb9b39140bbc6421d1c7dab9dc Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Wed, 29 Jul 2026 22:08:58 +0200 Subject: [PATCH 17/19] fix: improve support for repos that dont use our bundle extension --- docs.bzl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs.bzl b/docs.bzl index 5ce691f90..307037ca2 100644 --- a/docs.bzl +++ b/docs.bzl @@ -318,12 +318,13 @@ def docs( "--jobs", "auto", "--define=external_needs_source=" + str(data), - # ``sphinx_docs`` is a sandboxed build action, so it needs the - # action-input path rather than the runfiles-relative spelling. - "--define=mounts_manifest=" + ("$(location :_mounts_manifest)" if bundles else ""), "--define=score_sourcelinks_json=$(location :sourcelinks_json)", "--define=score_source_code_linker_plain_links=1", - ] + (["--define=score_metamodel_yaml=$(location " + str(metamodel) + ")"] if metamodel else []), + ] + ( + # ``sphinx_docs`` is a sandboxed build action, so it needs the + # action-input path rather than the runfiles-relative spelling. + ["--define=mounts_manifest=$(location :_mounts_manifest)"] if bundles else [] + ) + (["--define=score_metamodel_yaml=$(location " + str(metamodel) + ")"] if metamodel else []), formats = ["needs"], sphinx = ":sphinx_build", tools = data + metamodel_label + [":sourcelinks_json", ":docs_bundle"] + mounts_manifest_label, From 396022d93c55ca9ed11c69d1dd52f001da1e653a Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Thu, 30 Jul 2026 00:41:09 +0200 Subject: [PATCH 18/19] fix: simplify bundle validation --- bzl/bundle_rules.bzl | 37 +++++++++--------- docs.bzl | 2 +- src/extensions/score_sync_toml/__init__.py | 22 ++++++++--- src/extensions/score_sync_toml/test_mounts.py | 38 +++++++++++++++++++ 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/bzl/bundle_rules.bzl b/bzl/bundle_rules.bzl index cffd68803..2cb4c23e3 100644 --- a/bzl/bundle_rules.bzl +++ b/bzl/bundle_rules.bzl @@ -72,29 +72,23 @@ def _parent_index_docname(mount_at): parent = mount_at.rsplit("/", 1)[0] if "/" in mount_at else "" return join_path(parent, "index") -def _validate_and_deduplicate_entries(entries): - """Keep one entry per source directory and reject conflicting metadata.""" +def _ensure_unique_entries(entries): + """Reject a source directory reached through more than one bundle path.""" seen = {} - out = [] for entry in entries: key = entry.runtime_path if key in seen: - differing_fields = [ - field - for field in ["mount_at", "attach_to", "entry_doc", "src_root", "external", "repository"] - if getattr(seen[key], field) != getattr(entry, field) - ] - if differing_fields: - fail(("bundle conflict: source directory %r has conflicting %s; " + - "a bundle must resolve to one complete placement declaration") % - (key, differing_fields)) - continue + fail(("bundle conflict: source directory %r is included through more " + + "than one bundle path; include every documentation source directory once") % key) seen[key] = entry - out.append(entry) - return out def _bundle_runtime_path(ctx): - """Return this bundle source directory's Bazel runtime path.""" + """Return this bundle source directory's Bazel runtime path. + + Bazel spells a source in an external repository as ``..//...`` in + runfiles. Keep that spelling here; ``_bundle_execroot_path`` converts it to + the corresponding ``external//...`` form for build actions. + """ source_file = ctx.files.srcs[0].short_path external_prefix = "" if source_file.startswith("../"): @@ -109,7 +103,12 @@ def _bundle_execroot_path(runtime_path): return runtime_path def _rebase_bundle_entry(entry, mount_at, attach_to): - """Place a bundle entry below a requested documentation-tree location.""" + """Place a bundle entry below a requested documentation-tree location. + + A bundle's own root has no ``mount_at`` yet. For that root, an omitted + ``attach_to`` means the parent directory's ``index`` page. Nested entries + retain their existing attachment and are rebased below ``mount_at``. + """ is_bundle_root = not entry.mount_at if is_bundle_root: rebased_attach_to = attach_to or _parent_index_docname(mount_at) @@ -225,7 +224,7 @@ def _docs_bundle_impl(ctx): child_external_runfiles.append(child[DocsBundleInfo].external_runfiles) sourcelinks.extend(_sourcelinks_visible_through(ctx, child)) - deduplicated_entries = _validate_and_deduplicate_entries(entries) + _ensure_unique_entries(entries) all_source_files = depset( direct = own_source_files, transitive = child_source_files, @@ -237,7 +236,7 @@ def _docs_bundle_impl(ctx): return [ DefaultInfo(files = all_source_files), DocsBundleInfo( - entries = deduplicated_entries, + entries = entries, sourcelinks = sourcelinks, external_runfiles = external_runfiles, ), diff --git a/docs.bzl b/docs.bzl index 307037ca2..3b92eed59 100644 --- a/docs.bzl +++ b/docs.bzl @@ -74,7 +74,7 @@ def docs_bundle(name, source_dir = None, entry_doc = "index", bundles = [], scan { "bundle": , "mount_at": , - "attach_to": + "attach_to": }. scan_code: Source-code targets to scan for source-code links owned by this bundle. diff --git a/src/extensions/score_sync_toml/__init__.py b/src/extensions/score_sync_toml/__init__.py index c1d197bc2..cf868ba1a 100644 --- a/src/extensions/score_sync_toml/__init__.py +++ b/src/extensions/score_sync_toml/__init__.py @@ -25,15 +25,27 @@ def setup(app: Sphinx) -> dict[str, str | bool]: See https://needs-config-writer.useblocks.com """ + # A Bazel build action has no Git worktree. In that context this extension + # must be inactive: writing a fallback file into the sandbox is useless and + # can make generated configuration appear to work when it is discarded. + git_root = find_git_root() + if git_root is None: + app.config.suppress_warnings += [ + "needs_config_writer.unsupported_type", + "needs_config_writer.path_conversion", + ] + return { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } + # Emit a single ubproject.toml at the git repo root, where UI extensions # (ubCode / esbonio) look for it. needs-config-writer relativizes every path # field against the output file's directory, so anchoring the file at the # root yields root-relative paths automatically. find_git_root() resolves the - # root under `bazel run` and esbonio alike; in a sandbox build it returns None - # and we fall back to the confdir default (that copy is ephemeral / discarded). - git_root = find_git_root() - outpath = str(git_root / "ubproject.toml") if git_root else "ubproject.toml" - config_setdefault(app.config, "needscfg_outpath", outpath) + # root under `bazel run` and esbonio alike. + config_setdefault(app.config, "needscfg_outpath", str(git_root / "ubproject.toml")) """Write a single ubproject.toml at the git repo root.""" config_setdefault(app.config, "needscfg_overwrite", True) diff --git a/src/extensions/score_sync_toml/test_mounts.py b/src/extensions/score_sync_toml/test_mounts.py index 1f130b642..5938e9301 100644 --- a/src/extensions/score_sync_toml/test_mounts.py +++ b/src/extensions/score_sync_toml/test_mounts.py @@ -4,9 +4,12 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* from pathlib import Path +from typing import Any, cast import pytest +from sphinx.application import Sphinx +from src.extensions import score_sync_toml from src.extensions.score_sync_toml import _mounts from src.extensions.score_sync_toml._mounts import materialize_mounts @@ -67,3 +70,38 @@ def test_materialize_mounts_maps_external_runfiles_path_to_bazel_bin( assert fragment.read_text(encoding="utf-8") == ( '[[mounts]]\ndir = "bazel-bin/external/score_process+/process"\nmount_at = "process"\n' ) + + +def test_setup_skips_toml_sync_without_git_worktree( + monkeypatch: pytest.MonkeyPatch, +) -> None: + class ConfigWithoutTomlSync: + suppress_warnings: list[str] = [] + + @property + def _raw_config(self) -> Any: + raise AssertionError( + "setup must not configure TOML sync without a Git worktree" + ) + + class AppWithoutGitWorktree: + config = ConfigWithoutTomlSync() + + def connect(self, *args: Any, **kwargs: Any) -> None: + raise AssertionError( + "setup must not register TOML sync without a Git worktree" + ) + + monkeypatch.setattr(score_sync_toml, "find_git_root", lambda: None) + + metadata = score_sync_toml.setup(cast(Sphinx, AppWithoutGitWorktree())) + + assert metadata == { + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, + } + assert AppWithoutGitWorktree.config.suppress_warnings == [ + "needs_config_writer.unsupported_type", + "needs_config_writer.path_conversion", + ] From fb5ae249a4e23225c5e999f6cb087e6d69a66af0 Mon Sep 17 00:00:00 2001 From: Alexander Lanin Date: Thu, 30 Jul 2026 00:49:17 +0200 Subject: [PATCH 19/19] refactor: simplify bundle declarations --- bzl/bundle_rules.bzl | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/bzl/bundle_rules.bzl b/bzl/bundle_rules.bzl index 2cb4c23e3..f4884675b 100644 --- a/bzl/bundle_rules.bzl +++ b/bzl/bundle_rules.bzl @@ -134,26 +134,13 @@ def _entries_visible_through(ctx, child): return [entry for entry in entries if entry.repository == child_repository] def _sourcelinks_visible_through(ctx, child): - """Apply the same external-module boundary to traceability metadata.""" + """Return source-code links that may cross a module boundary.""" sourcelinks = child[DocsBundleInfo].sourcelinks child_repository = child.label.workspace_name if child_repository == ctx.label.workspace_name: return sourcelinks return [link for link in sourcelinks if link.repository == child_repository] -def _validate_docname(value, field_name, allow_empty = False): - """Validate a relative documentation docname used in a bundle declaration.""" - if type(value) != "string": - fail("%s must be a string, got %r" % (field_name, value)) - if not value: - if allow_empty: - return - fail("%s must not be empty" % field_name) - invalid_segments = [segment for segment in value.split("/") if not segment or segment in [".", ".."]] - if invalid_segments: - fail("%s must be a relative docname without empty, '.' or '..' segments; got %r" % - (field_name, value)) - def _parse_bundle_declaration(bundle): """Read one nested-bundle declaration and fill in optional values.""" if type(bundle) != "dict": @@ -169,8 +156,6 @@ def _parse_bundle_declaration(bundle): mount_at = bundle["mount_at"] attach_to = bundle.get("attach_to", "") - _validate_docname(mount_at, "mount_at", allow_empty = True) - _validate_docname(attach_to, "attach_to", allow_empty = True) return struct( bundle = bundle["bundle"], @@ -258,7 +243,6 @@ _docs_bundle = rule( def create_bundle(name, bundles, srcs = [], sourcelinks = [], strip_prefix = "", entry_doc = "index", visibility = None, **kwargs): """Create a reusable documentation bundle from files and child declarations.""" - _validate_docname(entry_doc, "entry_doc") parsed_bundles = [_parse_bundle_declaration(declaration) for declaration in bundles] _docs_bundle( name = name,