From 59e5e7d309c9a946e786f56feaf39a30085f3943 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:51:34 +0200 Subject: [PATCH 01/24] chore: drop uv.lock Nothing reads it. poetry.lock locks the project, and upstream deleted uv.lock in June 2025. --- uv.lock | 2528 ------------------------------------------------------- 1 file changed, 2528 deletions(-) delete mode 100644 uv.lock diff --git a/uv.lock b/uv.lock deleted file mode 100644 index a5c2bec..0000000 --- a/uv.lock +++ /dev/null @@ -1,2528 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12, <3.14" -resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version < '3.13'", -] - -[[package]] -name = "accessible-pygments" -version = "0.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, -] - -[[package]] -name = "alabaster" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, -] - -[[package]] -name = "annotated-doc" -version = "0.0.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, -] - -[[package]] -name = "annotated-types" -version = "0.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, -] - -[[package]] -name = "arrow" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, -] - -[[package]] -name = "attrs" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, -] - -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, -] - -[[package]] -name = "backports-zstd" -version = "1.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/f0/9ba1b05811aa5f5434f69768253129460a5744e1814f359efba39a01ce20/backports_zstd-1.7.0.tar.gz", hash = "sha256:1a967189c1822b6e85a2e550fdfc88a3272c17633ea0a4732dac5911a8034f2b", size = 1003722, upload-time = "2026-08-15T17:26:43.96Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/23/240495dec973dcfb34816248956ca8d05b32fb75936c226c1cf497b83b83/backports_zstd-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5548a857bb0fcc5449cc3687353547396c6b1ecd4bd882f1cd34fa8d29e70ca", size = 439047, upload-time = "2026-08-15T17:25:38.084Z" }, - { url = "https://files.pythonhosted.org/packages/6d/24/5556959c7d03bfee5ff14d7f07dd9bf8de737c69f81d823a32784ab39c34/backports_zstd-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bab192b934fdf5a03df4752556d9c8af2d058163fdfbafd4a253cdfe25449a6f", size = 367666, upload-time = "2026-08-15T17:25:39.233Z" }, - { url = "https://files.pythonhosted.org/packages/b2/cb/557db98001c4a7202beed19e8bd42603a2315b80fd5def7e21a0b048ec3b/backports_zstd-1.7.0-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:8344260bed9842c415a93d9bfe23ea834e5f27758827d56933d8c0d06db507a2", size = 508475, upload-time = "2026-08-15T17:25:40.367Z" }, - { url = "https://files.pythonhosted.org/packages/40/4b/820acbc2c1d1d945aedca0c0d22546a948630ffb186df523098fbd669a95/backports_zstd-1.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c55e55e1e9dee312bc5e186386e6aa5207482a6d2242bd7c14709ded254f87f", size = 478240, upload-time = "2026-08-15T17:25:41.806Z" }, - { url = "https://files.pythonhosted.org/packages/f9/76/77fa9b385e79d4c106ce15d66681978f39a844b0eb5db02682687246b716/backports_zstd-1.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cf609af3735c7e697ccd13f6b0c88da57c201b6ea63c6afbfe81d6f9b50e298c", size = 583611, upload-time = "2026-08-15T17:25:43.104Z" }, - { url = "https://files.pythonhosted.org/packages/95/a4/fbb7c73336f3279dad36da94382a59755100b656301ea836ebaa42736581/backports_zstd-1.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:676a37971f676830d4f90cee8fdf4e438781596fb2f2d1984ac76c9b3eb39a69", size = 642496, upload-time = "2026-08-15T17:25:44.322Z" }, - { url = "https://files.pythonhosted.org/packages/1e/40/121917bd2671bc3f1507c25503c0554f0b52483edcca4e6210e6d22228df/backports_zstd-1.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:470895d0bcddc850766e593d1b26764fb138c2feed149f515a2627ef9587d54c", size = 496195, upload-time = "2026-08-15T17:25:45.503Z" }, - { url = "https://files.pythonhosted.org/packages/de/c7/c6379a0d734bea1c7f14d07c23258108cc92b994654e25cfe3a3e88cd785/backports_zstd-1.7.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:02f2f6649a342d0901ddb35596ddadb7c3bb1cf6bb54d691e5e0285f1fa0674f", size = 570964, upload-time = "2026-08-15T17:25:46.648Z" }, - { url = "https://files.pythonhosted.org/packages/7a/a4/372c3dd3017c3f93cda0acbc282f8073b70efdc1b56d1fdeebe023660725/backports_zstd-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:132ba81fad59d44958b7d10da31545e7128c469cfbc2e268d0eaab96daa64175", size = 484276, upload-time = "2026-08-15T17:25:48.129Z" }, - { url = "https://files.pythonhosted.org/packages/f5/50/83fa7bdd5e1d808203b9143848fdf7e15de399b8119a0d4378b2aea9be78/backports_zstd-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a3e1c6ce0b232ee6703ed24ee126e8186107f5a4e56edbd21cd1aa5a8c6bfd12", size = 511963, upload-time = "2026-08-15T17:25:49.666Z" }, - { url = "https://files.pythonhosted.org/packages/56/d2/d4ed32c353148acc18f3b665ab24a677b9c49d3640244424c5d6046400c5/backports_zstd-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d7a7cb964eb8d1bb5d039970b16fe54802ea47dc935ae96d9874844a126bf8ff", size = 588025, upload-time = "2026-08-15T17:25:51.288Z" }, - { url = "https://files.pythonhosted.org/packages/ae/5a/df8b5b848e8dfdec6edca55f22067ffbafa081d81aec1313e28155c3fea3/backports_zstd-1.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:12a9842a2ec2854cbec7f252ab29d44c2b772788a9bbafded743ca4bf73b115f", size = 568223, upload-time = "2026-08-15T17:25:52.633Z" }, - { url = "https://files.pythonhosted.org/packages/43/8c/f970f15e7fdbf8a251f121c91364fa68bbc2dfab4d5eca058427dec63397/backports_zstd-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:138154eea8ced84394991bf0e819dba6b690306a178dd528c28eee724b7d4aec", size = 632937, upload-time = "2026-08-15T17:25:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6a/d0/e36c18c87a74421954502d123ff7027e0a63a7624dffa99ec0f7474deff9/backports_zstd-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:468b636ed365627b364c94be1c35a52858e13b5bc1fa3f068bbc71b1af65f3d7", size = 500735, upload-time = "2026-08-15T17:25:56.064Z" }, - { url = "https://files.pythonhosted.org/packages/1c/57/fc72280334d2aa94238c5882052263bd7796c1fa924044353c30d058e0c3/backports_zstd-1.7.0-cp312-cp312-win32.whl", hash = "sha256:f026fe2e89b7ff01ba6ebec6abaff34c6063919151a32afb68714cf139e17c50", size = 292394, upload-time = "2026-08-15T17:25:57.469Z" }, - { url = "https://files.pythonhosted.org/packages/71/89/6cea747bdeef34cd12482b17e604b832fdb0962987132b99496f1a6c3f82/backports_zstd-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:2ea62ba2f1a6e6c9e6dc108921f9ae881969ca72e073162fa488d0de3eb2713f", size = 329876, upload-time = "2026-08-15T17:25:58.798Z" }, - { url = "https://files.pythonhosted.org/packages/64/28/b4a17c07d5a50a45cb04592960d1593cdf3b3728968371f332aa3643b804/backports_zstd-1.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:cefb983345c55ccaa20423a4eb96434730e6d640ffa2db9b60e5bedb0fbef94e", size = 292461, upload-time = "2026-08-15T17:25:59.928Z" }, - { url = "https://files.pythonhosted.org/packages/6f/24/32b3358ae3a4df0ebad85ebbce721818c6d76a836119bee76089d103e951/backports_zstd-1.7.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:a3fbcbf819bee2b06b8666b13742098d0f40663ee34e64a12bc360ec0f5e3d89", size = 400913, upload-time = "2026-08-15T17:26:01.089Z" }, - { url = "https://files.pythonhosted.org/packages/af/f3/39ef7dd75eb1e699e25a19212737a73d3c030a0c9fd1d0ed1572b5f8e493/backports_zstd-1.7.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:efee02f18e04c2e9e6d694c5cf9b7457c4bda3ea96f48b1ee69769e06bb9d89f", size = 454915, upload-time = "2026-08-15T17:26:02.294Z" }, - { url = "https://files.pythonhosted.org/packages/76/e8/8209081e094aa98b2f28bac388619c85b1a44aed813d6b3c54d1da79d19a/backports_zstd-1.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ecc95fa0e91d92951d74468e7789afdf91d9e702f40af2d0fcbf0ded4d0f650a", size = 357992, upload-time = "2026-08-15T17:26:03.552Z" }, - { url = "https://files.pythonhosted.org/packages/b3/65/64025302bae4ba924d613e404c6120bf194b5636786960ece274622a4a3e/backports_zstd-1.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:34154d82fc0246738159084d146401073f9ac9cfd755b66bb8853ca06037810c", size = 366686, upload-time = "2026-08-15T17:26:04.812Z" }, - { url = "https://files.pythonhosted.org/packages/4a/b9/c4d24d113d28b774662152c462d38d28109741d6d45c1aea7834371741dc/backports_zstd-1.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:44b687b1c0be5cb279693d2682f91ff84c559d679b2ef2fbe501fe4b2db2c4bb", size = 447221, upload-time = "2026-08-15T17:26:05.979Z" }, - { url = "https://files.pythonhosted.org/packages/cb/9f/8db55c7f77aec60879844a879ac026065d8f03aab74080701acc060c4168/backports_zstd-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dcdbd368659f46b570114eeea36b75347716523870d71f6bc5d7801862aefd6e", size = 438571, upload-time = "2026-08-15T17:26:07.421Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f8/72930ae4bb7bf6b9d6c7c31bce7b3e5751c062269a4ee718066e25f1973b/backports_zstd-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eda97fa535d4651a4ccdeed4ee7dde3978369046abc8a7456a7117d4271f9333", size = 367041, upload-time = "2026-08-15T17:26:08.537Z" }, - { url = "https://files.pythonhosted.org/packages/17/9b/7289dc191b34279d8f176bf5b181c3b26f8e049d14a2c0a2637650f034e5/backports_zstd-1.7.0-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:7e3999b5141d7f85171822d06112f70f7f317d162f0120530dd2c7a28dbf8add", size = 507676, upload-time = "2026-08-15T17:26:09.909Z" }, - { url = "https://files.pythonhosted.org/packages/7c/4d/6dd730b79ab96532e23fe851003545b4cc79e50c5b4c79ffcbe1b724eec4/backports_zstd-1.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69367726f4075c2574746f5883b0dc045805c5b02a81fdf8c829c26d33969de3", size = 477744, upload-time = "2026-08-15T17:26:11.038Z" }, - { url = "https://files.pythonhosted.org/packages/e1/53/11687e5019d56ea47893cf2ba59a6b4884a4e2d1496d0e653aed373b973f/backports_zstd-1.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:15e97edfd173ade365c01bac7d9d297fa906686015cdbcb5f32a0d410887826b", size = 583215, upload-time = "2026-08-15T17:26:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c7/5a8c58542469ab31680c403b844770c119a976fd4cf1000fd7d53e7d0f77/backports_zstd-1.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:32a94cdcf16b44395cd55086ea38877395ca6bf3362cb507b0eb86db2a45a6a4", size = 644125, upload-time = "2026-08-15T17:26:13.651Z" }, - { url = "https://files.pythonhosted.org/packages/11/35/be5485e65df95b86c4981ad4a577b505cfeec6b700a46a86e2e3175ac718/backports_zstd-1.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3f4887a8a1fd1290017fe5a1d29a7d1dc5c57f9477fbd64f119316a7e3ae769", size = 492714, upload-time = "2026-08-15T17:26:14.838Z" }, - { url = "https://files.pythonhosted.org/packages/96/8b/a0603458ca08e4a56f09ae58588ce3c0453425e753df704d9aeaabb66ae5/backports_zstd-1.7.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e590313ce156f1d8986dff3107e8ed1651d6d106a56b3a95f965ff8d845ba979", size = 567953, upload-time = "2026-08-15T17:26:16.276Z" }, - { url = "https://files.pythonhosted.org/packages/02/87/2296db4c3c578947c35ccd8dcdf7992316d7e1f5f43cc829c062b3ed9319/backports_zstd-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:565270b0d6497970fa97a0df59593ae0d225e4176678bbce851d39e5f8aa422b", size = 483564, upload-time = "2026-08-15T17:26:17.493Z" }, - { url = "https://files.pythonhosted.org/packages/c0/d8/f53a79e6bf3cdb7ae08f95220c80bd0d606f3d6c3482995deaf21d024fb9/backports_zstd-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:37ef23c6c522fe935726c8fba6344350973c4a23b06d10194d90d0868b09ff7a", size = 511193, upload-time = "2026-08-15T17:26:18.7Z" }, - { url = "https://files.pythonhosted.org/packages/31/ea/d4e2eb159cd5813debd5a34d0644caff5fe7cf2e569bf5b02a82934aeee7/backports_zstd-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b3975330159f1efdd1fba76afe1c7b84f66f26e2bf209b32630fb148d647e0d5", size = 587748, upload-time = "2026-08-15T17:26:20.148Z" }, - { url = "https://files.pythonhosted.org/packages/81/d2/b5ec9709660fb1c193508215d9c30e781fac406183faac7c3c36b1c583a9/backports_zstd-1.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b40bc8cd0a86cbbe8263a9c3a2bf2e34897483516c6d799725412a19524c32e3", size = 565808, upload-time = "2026-08-15T17:26:21.349Z" }, - { url = "https://files.pythonhosted.org/packages/bd/13/004735cc4536483cbd973a60346a9dbc7bb977b13c28b55a11da14bb0a1e/backports_zstd-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f37e12ef10747f76901b1f20ef70d33221e861de177dba5ba08552242c6fd4bd", size = 634520, upload-time = "2026-08-15T17:26:22.944Z" }, - { url = "https://files.pythonhosted.org/packages/a3/28/05b11f7084d1100491cf7c60962aafd900c3dd01b1fc1ce85914476cdae0/backports_zstd-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5992143b2a8b71b4d17afed20cce2df50f8718228e31d6e716493b1fe9201712", size = 497098, upload-time = "2026-08-15T17:26:24.181Z" }, - { url = "https://files.pythonhosted.org/packages/f5/a5/bdc98d039ddbd5815fc1dd71912bbfb9f820a46ec12004ead51c8d60ea50/backports_zstd-1.7.0-cp313-cp313-win32.whl", hash = "sha256:31ae30d216ffae9243dfa607bcb995f94a70de5765bb8fae1e35ea1ad6497959", size = 291974, upload-time = "2026-08-15T17:26:25.512Z" }, - { url = "https://files.pythonhosted.org/packages/13/7d/fb0da7351e8b152d5149127594972922829281c316618df37a7e724f2eb9/backports_zstd-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:8086b4a7443bb2863f7ef8edb317b715d5f3ccec6c5512619bd23d57661ba1b7", size = 329550, upload-time = "2026-08-15T17:26:26.683Z" }, - { url = "https://files.pythonhosted.org/packages/37/f9/109ac272d650483fbdfa611c0040253a405f640604fbc90acc8076c6d37f/backports_zstd-1.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:7eaceeec75e1dbdce40b81fb0ed1ffdb7ce492d970db7f8aabd6a95ccd6c3dd3", size = 292174, upload-time = "2026-08-15T17:26:27.819Z" }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571, upload-time = "2026-06-07T16:44:20.453Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924, upload-time = "2026-06-07T16:44:21.566Z" }, -] - -[[package]] -name = "binaryornot" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/72/4755b85101f37707c71526a301c1203e413c715a0016ecb592de3d2dcfff/binaryornot-0.6.0.tar.gz", hash = "sha256:cc8d57cfa71d74ff8c28a7726734d53a851d02fad9e3a5581fb807f989f702f0", size = 478718, upload-time = "2026-03-08T16:26:28.804Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/0c/31cfaa6b56fe23488ecb993bc9fc526c0d84d89607decdf2a10776426c2e/binaryornot-0.6.0-py3-none-any.whl", hash = "sha256:900adfd5e1b821255ba7e63139b0396b14c88b9286e74e03b6f51e0200331337", size = 14185, upload-time = "2026-03-08T16:26:27.466Z" }, -] - -[[package]] -name = "brotli" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/ee/b0a11ab2315c69bb9b45a2aaed022499c9c24a205c3a49c3513b541a7967/brotli-1.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84", size = 861543, upload-time = "2025-11-05T18:38:24.183Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2f/29c1459513cd35828e25531ebfcbf3e92a5e49f560b1777a9af7203eb46e/brotli-1.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b", size = 444288, upload-time = "2025-11-05T18:38:25.139Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6f/feba03130d5fceadfa3a1bb102cb14650798c848b1df2a808356f939bb16/brotli-1.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d", size = 1528071, upload-time = "2025-11-05T18:38:26.081Z" }, - { url = "https://files.pythonhosted.org/packages/2b/38/f3abb554eee089bd15471057ba85f47e53a44a462cfce265d9bf7088eb09/brotli-1.2.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca", size = 1626913, upload-time = "2025-11-05T18:38:27.284Z" }, - { url = "https://files.pythonhosted.org/packages/03/a7/03aa61fbc3c5cbf99b44d158665f9b0dd3d8059be16c460208d9e385c837/brotli-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f", size = 1419762, upload-time = "2025-11-05T18:38:28.295Z" }, - { url = "https://files.pythonhosted.org/packages/21/1b/0374a89ee27d152a5069c356c96b93afd1b94eae83f1e004b57eb6ce2f10/brotli-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28", size = 1484494, upload-time = "2025-11-05T18:38:29.29Z" }, - { url = "https://files.pythonhosted.org/packages/cf/57/69d4fe84a67aef4f524dcd075c6eee868d7850e85bf01d778a857d8dbe0a/brotli-1.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7", size = 1593302, upload-time = "2025-11-05T18:38:30.639Z" }, - { url = "https://files.pythonhosted.org/packages/d5/3b/39e13ce78a8e9a621c5df3aeb5fd181fcc8caba8c48a194cd629771f6828/brotli-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036", size = 1487913, upload-time = "2025-11-05T18:38:31.618Z" }, - { url = "https://files.pythonhosted.org/packages/62/28/4d00cb9bd76a6357a66fcd54b4b6d70288385584063f4b07884c1e7286ac/brotli-1.2.0-cp312-cp312-win32.whl", hash = "sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161", size = 334362, upload-time = "2025-11-05T18:38:32.939Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4e/bc1dcac9498859d5e353c9b153627a3752868a9d5f05ce8dedd81a2354ab/brotli-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44", size = 369115, upload-time = "2025-11-05T18:38:33.765Z" }, - { url = "https://files.pythonhosted.org/packages/6c/d4/4ad5432ac98c73096159d9ce7ffeb82d151c2ac84adcc6168e476bb54674/brotli-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab", size = 861523, upload-time = "2025-11-05T18:38:34.67Z" }, - { url = "https://files.pythonhosted.org/packages/91/9f/9cc5bd03ee68a85dc4bc89114f7067c056a3c14b3d95f171918c088bf88d/brotli-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c", size = 444289, upload-time = "2025-11-05T18:38:35.6Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b6/fe84227c56a865d16a6614e2c4722864b380cb14b13f3e6bef441e73a85a/brotli-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f", size = 1528076, upload-time = "2025-11-05T18:38:36.639Z" }, - { url = "https://files.pythonhosted.org/packages/55/de/de4ae0aaca06c790371cf6e7ee93a024f6b4bb0568727da8c3de112e726c/brotli-1.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6", size = 1626880, upload-time = "2025-11-05T18:38:37.623Z" }, - { url = "https://files.pythonhosted.org/packages/5f/16/a1b22cbea436642e071adcaf8d4b350a2ad02f5e0ad0da879a1be16188a0/brotli-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c", size = 1419737, upload-time = "2025-11-05T18:38:38.729Z" }, - { url = "https://files.pythonhosted.org/packages/46/63/c968a97cbb3bdbf7f974ef5a6ab467a2879b82afbc5ffb65b8acbb744f95/brotli-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48", size = 1484440, upload-time = "2025-11-05T18:38:39.916Z" }, - { url = "https://files.pythonhosted.org/packages/06/9d/102c67ea5c9fc171f423e8399e585dabea29b5bc79b05572891e70013cdd/brotli-1.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18", size = 1593313, upload-time = "2025-11-05T18:38:41.24Z" }, - { url = "https://files.pythonhosted.org/packages/9e/4a/9526d14fa6b87bc827ba1755a8440e214ff90de03095cacd78a64abe2b7d/brotli-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5", size = 1487945, upload-time = "2025-11-05T18:38:42.277Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e8/3fe1ffed70cbef83c5236166acaed7bb9c766509b157854c80e2f766b38c/brotli-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a", size = 334368, upload-time = "2025-11-05T18:38:43.345Z" }, - { url = "https://files.pythonhosted.org/packages/ff/91/e739587be970a113b37b821eae8097aac5a48e5f0eca438c22e4c7dd8648/brotli-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8", size = 369116, upload-time = "2025-11-05T18:38:44.609Z" }, -] - -[[package]] -name = "brotlicffi" -version = "1.2.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/97/7845739a36828ffe751a1c6b240692f552fd7ecf65026c51326c0a4aa369/brotlicffi-1.2.0.2.tar.gz", hash = "sha256:5e0fbd13644cf1f6015e75fa5e0ad8fdce1048d9c9ff90b0ce826174b249ee35", size = 478755, upload-time = "2026-08-21T17:29:18.415Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/71/c27f24b8334f65f2492601c7764338f156cb904d2ffe0061e6004a76d9cc/brotlicffi-1.2.0.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:d5a8ffa154f16660ab818d78045b55fa6f9970f1ca4c38998766e99c672071cb", size = 438885, upload-time = "2026-08-21T17:29:04.113Z" }, - { url = "https://files.pythonhosted.org/packages/ef/22/d8fd1a4d09b7ab563b89380395e09151d2ef1344be31594df6a6987d4028/brotlicffi-1.2.0.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec6b1af7b7a8ce788354f2c603651ada0fba166ec31ab879e2eec462a3e6dbf4", size = 1534365, upload-time = "2026-08-21T17:29:05.878Z" }, - { url = "https://files.pythonhosted.org/packages/06/78/076419ed6c2c6aa3eaac6fd6b076502b4be89d50625fcdc513cd4aeca718/brotlicffi-1.2.0.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22916101de0e7ff535f2edf54b52a85591853b8ae9a98737643defdd3c063a3a", size = 1536851, upload-time = "2026-08-21T17:29:07.599Z" }, - { url = "https://files.pythonhosted.org/packages/35/dd/31ae9945cbd605339fb51c9a609f7dbb182cd361adeabc1d470142357206/brotlicffi-1.2.0.2-cp39-abi3-win32.whl", hash = "sha256:df1d34c4ad9adbf7f63a6b42f7d0e4dfd259c88141b85145b57abecc1abc3b24", size = 342379, upload-time = "2026-08-21T17:29:09.05Z" }, - { url = "https://files.pythonhosted.org/packages/95/ae/afd54e744df93b51cc29f6a19beccf9998b25743d7177697390de10479d1/brotlicffi-1.2.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:489ca4da3ee65926d72bf01584b61088a9da6bdd1bb01b2040901e1beaffa8f0", size = 379761, upload-time = "2026-08-21T17:29:10.687Z" }, -] - -[[package]] -name = "certifi" -version = "2026.7.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, -] - -[[package]] -name = "cffi" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" }, - { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" }, - { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" }, - { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" }, - { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" }, - { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" }, - { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" }, - { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" }, - { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" }, - { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" }, - { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, - { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, - { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, - { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, - { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, - { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, - { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, - { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, - { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, - { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, - { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, - { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/3f/143b048436775b0f76ac3eec145c019e8173ccc2885c8f20319b996d5e83/charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3", size = 171764, upload-time = "2026-08-15T08:20:44.807Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/27/78873dc8b6a56357517b74b6bb9568b80450e7bb4f6ef7e3fa9d22aa0bd7/charset_normalizer-3.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f", size = 344456, upload-time = "2026-08-15T08:17:10.072Z" }, - { url = "https://files.pythonhosted.org/packages/9a/4c/be49ada26b1f0232d57aa89bbebf997a5cc2332a5616b6eca26ff680044d/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa", size = 238530, upload-time = "2026-08-15T08:17:11.563Z" }, - { url = "https://files.pythonhosted.org/packages/76/84/6f1290fa07ae6978d3960caa3eb1b8019bf9284ab7c2297b00c099ef4250/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369", size = 230200, upload-time = "2026-08-15T08:17:12.919Z" }, - { url = "https://files.pythonhosted.org/packages/e7/a0/47b18adeed31c8f16ba9700f32c1b18594cfa09f47eb672a488c273c22bf/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893", size = 262222, upload-time = "2026-08-15T08:17:14.571Z" }, - { url = "https://files.pythonhosted.org/packages/38/fe/341861ac118dae06f3ec0eb487488af52128f2ef2faf0b11003944d22259/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0", size = 258951, upload-time = "2026-08-15T08:17:16.158Z" }, - { url = "https://files.pythonhosted.org/packages/6f/89/bb5108dc6c3651dca963f2b0a3ba19bbcb370c94e1b6d3e0e844a58e6dca/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08", size = 248801, upload-time = "2026-08-15T08:17:17.683Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ba/ef83ae3aca816393decfa3530976f38a79812d707b80b580ac33b83f9877/charset_normalizer-3.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada", size = 244070, upload-time = "2026-08-15T08:17:19.191Z" }, - { url = "https://files.pythonhosted.org/packages/f6/0b/c5292a2462d69b7378ea89793bbb5b2b6fcf6f7dd6d1667f9619094ad553/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9", size = 240110, upload-time = "2026-08-15T08:17:20.547Z" }, - { url = "https://files.pythonhosted.org/packages/46/22/111e5be3b740d5c2a5bfcedb3d237b6591e5c2e82ae9d6ffcb121fe0909c/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e", size = 232836, upload-time = "2026-08-15T08:17:21.895Z" }, - { url = "https://files.pythonhosted.org/packages/f9/d2/d2aad6fe0dbb44b194bf3becb60f5a0ac48446ade999a47fe7bb41eb09a7/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6", size = 262712, upload-time = "2026-08-15T08:17:23.727Z" }, - { url = "https://files.pythonhosted.org/packages/35/5a/337e4663a5eae6de99db940ee8066d4145caafb61327db62deda15313cce/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf", size = 242977, upload-time = "2026-08-15T08:17:25.157Z" }, - { url = "https://files.pythonhosted.org/packages/ca/85/f82f8a92e31c7519410e2e1afdc630f28ec47490ce2c09a11c1a43cbb459/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71", size = 260207, upload-time = "2026-08-15T08:17:26.602Z" }, - { url = "https://files.pythonhosted.org/packages/b7/52/643d11ffd60e9ac2fd1fb87e167a19285b9eefeff4a40e63c87cbfbeab36/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573", size = 250562, upload-time = "2026-08-15T08:17:27.971Z" }, - { url = "https://files.pythonhosted.org/packages/62/16/46556278c2168d12df9da7fede5dc6fc70e60301b26a82bbeec238c9cfe3/charset_normalizer-3.5.1-cp312-cp312-win32.whl", hash = "sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2", size = 178507, upload-time = "2026-08-15T08:17:29.277Z" }, - { url = "https://files.pythonhosted.org/packages/9d/7a/4c6c298171e6b3e745633180ff59350fc0ca0db1ffd28df1e369e0579f71/charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2", size = 200551, upload-time = "2026-08-15T08:17:30.668Z" }, - { url = "https://files.pythonhosted.org/packages/cd/d7/eb95a042f0dd22e304b0b6472b154f3546a1a039a9ee89ccb2a7f61591fc/charset_normalizer-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a", size = 180700, upload-time = "2026-08-15T08:17:32.028Z" }, - { url = "https://files.pythonhosted.org/packages/bc/61/2cb6ad133dbbb449fa2d37ccae973232f4827e799af258d15e589a3d1e9e/charset_normalizer-3.5.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9", size = 211584, upload-time = "2026-08-15T08:17:33.597Z" }, - { url = "https://files.pythonhosted.org/packages/18/57/a305c968be1ca13f3dd1b32f445877e97addf55d80b65c7cb35fac82b777/charset_normalizer-3.5.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491", size = 223359, upload-time = "2026-08-15T08:17:35.022Z" }, - { url = "https://files.pythonhosted.org/packages/09/0a/d3646670292ce8d8f8cc11ac067d44885e697a5591f57a9221128da5e7b3/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7", size = 194464, upload-time = "2026-08-15T08:17:36.452Z" }, - { url = "https://files.pythonhosted.org/packages/de/93/d51ec556e01042fed6f993ea859311bc7917b466684182fbbceb6ca24762/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e", size = 197676, upload-time = "2026-08-15T08:17:37.819Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a0/562247944386f7d4ef94467e84876600cc1e0f1b93239aaa9213d2bc3cbd/charset_normalizer-3.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d", size = 340473, upload-time = "2026-08-15T08:17:39.303Z" }, - { url = "https://files.pythonhosted.org/packages/31/e7/1d994be1b93d41e9502b8b0460eaa88a1dd8df335df415db87d6c3e91ab2/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a", size = 240156, upload-time = "2026-08-15T08:17:40.66Z" }, - { url = "https://files.pythonhosted.org/packages/09/53/27923ce5cc6cbccb832037b27dca98882d9c53e9b69e866bbbef4aae7fc8/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe", size = 228246, upload-time = "2026-08-15T08:17:42.003Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/5a97e84d63af1d55c07439cb80e56d99a8efb4295700eb4e18c0d1615d2c/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac", size = 263660, upload-time = "2026-08-15T08:17:43.627Z" }, - { url = "https://files.pythonhosted.org/packages/7a/c2/071575791dcc88316c0a9a65ce38897a82e4cfe4a325f0f7fe1b1ac47bcf/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e", size = 260354, upload-time = "2026-08-15T08:17:45.094Z" }, - { url = "https://files.pythonhosted.org/packages/fb/af/63240b0c0248c075c2535a1f1bd992821d8251b9f173abc13329661d09e4/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3", size = 250638, upload-time = "2026-08-15T08:17:46.496Z" }, - { url = "https://files.pythonhosted.org/packages/4d/66/70dfad64f15be09c15ccfee81330a7e515895dbe296dd23114e9a231268a/charset_normalizer-3.5.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876", size = 244583, upload-time = "2026-08-15T08:17:47.963Z" }, - { url = "https://files.pythonhosted.org/packages/c0/24/ef36367d38b9ddd4bccbf72888c342e8de1f5ae506fa0b2dcf970e2732a1/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6", size = 242038, upload-time = "2026-08-15T08:17:49.481Z" }, - { url = "https://files.pythonhosted.org/packages/db/ab/55e683ba0fff2e43adafc10daa3001eac90fdaa419a97227d5a7067eedde/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2", size = 233677, upload-time = "2026-08-15T08:17:50.845Z" }, - { url = "https://files.pythonhosted.org/packages/bd/67/0f40eaf8d1b6e7cf15e82382a2965efaca787fc1c2794b7021d37aaf5036/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591", size = 264491, upload-time = "2026-08-15T08:17:52.61Z" }, - { url = "https://files.pythonhosted.org/packages/5c/64/12b4c2a11ee8df4fcc518c78b0d93e3a92bd3d5253d1617ce74ff0e8c7ef/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c", size = 245196, upload-time = "2026-08-15T08:17:54.023Z" }, - { url = "https://files.pythonhosted.org/packages/37/2e/651d910af6d0fba325eee1cda37ec5443462ed25360e666c144166eb6091/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c", size = 261660, upload-time = "2026-08-15T08:17:55.491Z" }, - { url = "https://files.pythonhosted.org/packages/90/c6/b09e05e6db7f64338e0dc067c79577b1138da86c1e38369096851d96be88/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f", size = 252618, upload-time = "2026-08-15T08:17:57.025Z" }, - { url = "https://files.pythonhosted.org/packages/76/4e/362d4f9fdcdf5556fb2aa3ce7d4a58ebce03ed1ff03aa1d9aca8d02f13f3/charset_normalizer-3.5.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4", size = 140362, upload-time = "2026-08-15T08:17:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d4/703be739b26acce318bd29eb3b25b7209e1b1f527f9eae3d1f1f01fdde2b/charset_normalizer-3.5.1-cp313-cp313-win32.whl", hash = "sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3", size = 177755, upload-time = "2026-08-15T08:18:00.037Z" }, - { url = "https://files.pythonhosted.org/packages/8a/33/56d97ade41c8db611e727168c52ae46c9224c362ec28d4b65d7e9869e8da/charset_normalizer-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6", size = 199295, upload-time = "2026-08-15T08:18:01.506Z" }, - { url = "https://files.pythonhosted.org/packages/5b/75/5b20dd1e6573a01a08158fe104104fa2c8abf941745596954185726cd46c/charset_normalizer-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0", size = 179856, upload-time = "2026-08-15T08:18:02.929Z" }, - { url = "https://files.pythonhosted.org/packages/5b/97/fb4e82231aba271ffd775a1b4993b0defc4e3059f286ae41d9433409fe85/charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2", size = 331467, upload-time = "2026-08-15T08:19:50.959Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2f/fe3f187327aac18e2d54e9d2b08e15d27bf9b642d9e51c219f130fc34d1a/charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99", size = 253057, upload-time = "2026-08-15T08:19:52.654Z" }, - { url = "https://files.pythonhosted.org/packages/d7/c7/9e48cee5c161fe24da823b61bf381921d77cb994a0a4de148e95018c1984/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2", size = 240930, upload-time = "2026-08-15T08:19:54.163Z" }, - { url = "https://files.pythonhosted.org/packages/49/e0/716601f3cc69be7b198951150c75ead1ece33c3c8036ff6ffa46029659a0/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235", size = 230822, upload-time = "2026-08-15T08:19:55.807Z" }, - { url = "https://files.pythonhosted.org/packages/d3/05/71bfc5caa0abcc45aea1f6a4d50ac68e59605ddc7666fe8494f4cd229665/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598", size = 260037, upload-time = "2026-08-15T08:19:57.312Z" }, - { url = "https://files.pythonhosted.org/packages/c3/92/de7e32ed05341e7a9c4c877c318418197b7f2d66a3b68d561bf2ac57ca3e/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96", size = 255097, upload-time = "2026-08-15T08:19:59.056Z" }, - { url = "https://files.pythonhosted.org/packages/f5/7b/ade0a122600319dfa0b1000ab0f9731c94a817904cf3c5de408c73a4ede7/charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962", size = 250166, upload-time = "2026-08-15T08:20:00.612Z" }, - { url = "https://files.pythonhosted.org/packages/75/9c/019fbb9f4834491a160951349b1a3714439376f66e5f7cf18b4f18f0c7aa/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3", size = 241821, upload-time = "2026-08-15T08:20:02.321Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/11d4840bfc99330cc7fbcc2681ee5a044553a6e77655508d8f9b2bff7b34/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950", size = 232529, upload-time = "2026-08-15T08:20:04.008Z" }, - { url = "https://files.pythonhosted.org/packages/18/96/2b3a21492d9f65171ac75d872f5018260013d00bfa0ff70ec9f179148cbd/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8", size = 260348, upload-time = "2026-08-15T08:20:05.877Z" }, - { url = "https://files.pythonhosted.org/packages/d6/aa/a69a2028e8bd052476c245460ab19d7de595de084dd968f2d75cd50c3e25/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031", size = 247234, upload-time = "2026-08-15T08:20:07.487Z" }, - { url = "https://files.pythonhosted.org/packages/35/8a/3d130aeabcaf3d2466af76b7b141c08d9e89c9016ab4b7cdd0f7dc2d1c62/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072", size = 256917, upload-time = "2026-08-15T08:20:09.142Z" }, - { url = "https://files.pythonhosted.org/packages/80/c2/a7379b840292d0c1ab9fbd17d1f3967aa81794dc95bc74be8999d7fedcf7/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d", size = 254846, upload-time = "2026-08-15T08:20:10.727Z" }, - { url = "https://files.pythonhosted.org/packages/01/65/d43b714731bb2f40d4053dfa00ecfc1c5a301f8e3316c5db3a09af59fe94/charset_normalizer-3.5.1-cp37-abi3-win32.whl", hash = "sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc", size = 174216, upload-time = "2026-08-15T08:20:12.334Z" }, - { url = "https://files.pythonhosted.org/packages/35/4f/b911ed898b26a09789eba9c9200c999aff6c61b4bafaf4838e56d1a1e1a3/charset_normalizer-3.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959", size = 199764, upload-time = "2026-08-15T08:20:13.908Z" }, - { url = "https://files.pythonhosted.org/packages/f0/a7/920baf467bfd9bf689f3b318340f37aee4572a71f162bd8db51da55ba4fa/charset_normalizer-3.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e", size = 287318, upload-time = "2026-08-15T08:20:15.551Z" }, - { url = "https://files.pythonhosted.org/packages/cc/61/d01fc49b8dea277640b55a9e15960dbca9fdc8c9fde18e572d39c59f4019/charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6", size = 68658, upload-time = "2026-08-15T08:20:43.306Z" }, -] - -[[package]] -name = "clanguru" -version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "libclang" }, - { name = "mashumaro" }, - { name = "openpyxl" }, - { name = "py-app-dev" }, - { name = "pyyaml" }, - { name = "typer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8e/83/e4cf6f23ff4d2251d74a08480e20b164a7e074cecfab5fea5f3952c51427/clanguru-0.21.0.tar.gz", hash = "sha256:a13d9275aef37b6a87e4cd9c131c5fe7a31afeaeaff619b83cb8dbea667c448d", size = 42313, upload-time = "2026-09-09T18:48:35.508Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/a0/aaa4944711d5098a52cc60c9352f86a1c918456ca6452f4a8aa7c9521967/clanguru-0.21.0-py3-none-any.whl", hash = "sha256:6c9de23ad211be5275a2bd943216a8c0d2f1f9c63131de94eef6c9d23dc2e2a5", size = 43892, upload-time = "2026-09-09T18:48:34.045Z" }, -] - -[[package]] -name = "click" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, -] - -[[package]] -name = "click-option-group" -version = "0.5.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/ff/d291d66595b30b83d1cb9e314b2c9be7cfc7327d4a0d40a15da2416ea97b/click_option_group-0.5.9.tar.gz", hash = "sha256:f94ed2bc4cf69052e0f29592bd1e771a1789bd7bfc482dd0bc482134aff95823", size = 22222, upload-time = "2025-10-09T09:38:01.474Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/45/54bb2d8d4138964a94bef6e9afe48b0be4705ba66ac442ae7d8a8dc4ffef/click_option_group-0.5.9-py3-none-any.whl", hash = "sha256:ad2599248bd373e2e19bec5407967c3eec1d0d4fc4a5e77b08a0481e75991080", size = 11553, upload-time = "2025-10-09T09:38:00.066Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "colorlog" -version = "6.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8c/55/ba79756cb90c8d69d599d57785398ac87bba7b19c80e87f4e8a562197c93/colorlog-6.12.0.tar.gz", hash = "sha256:2a7924c1dadf18b22a0eb8b06d1c7b01d5341707ec1641eb6fcc4fde0c3e8e5f", size = 18151, upload-time = "2026-07-23T13:40:40.71Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/19/0b6647bf5e331521e55d2b63bfbdc210bd9cd605189273f03614a05f702d/colorlog-6.12.0-py3-none-any.whl", hash = "sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e", size = 12239, upload-time = "2026-07-23T13:40:39.562Z" }, -] - -[[package]] -name = "comment-parser" -version = "1.2.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.13'", -] -dependencies = [ - { name = "python-magic", version = "0.4.24", source = { registry = "https://pypi.org/simple" } }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/cd/6e4c79d4a25e3b6542d1a3728b3e2b2c4650e3f8c3cde950aaecc6f64764/comment_parser-1.2.4.tar.gz", hash = "sha256:4e03c421c07d146f5a4d248423820347f3589f60d17acea55f7fb5683faffa22", size = 8350, upload-time = "2022-01-26T04:50:40.828Z" } - -[[package]] -name = "comment-parser" -version = "1.2.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", -] -dependencies = [ - { name = "python-magic", version = "0.4.27", source = { registry = "https://pypi.org/simple" } }, -] -sdist = { url = "https://files.pythonhosted.org/packages/37/6a/354d8e640b5f90996ac07c002189a552226a0ddaad85efd14863166aaa14/comment_parser-1.2.5.tar.gz", hash = "sha256:5606b769228cafce03d538e361472896581b386f3bc44bd62f4b61ff45ff05ec", size = 8852, upload-time = "2024-12-25T05:08:14.618Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/53/12ceba92ce55851a3d34123b2774a32689f745ce4c0c9bebca1a2a07e266/comment_parser-1.2.5-py3-none-any.whl", hash = "sha256:fb3dee8f351ebd1cfc22d3f19dabf93a4f7d4c9ea13b2092242de086a4449d9e", size = 13222, upload-time = "2026-03-16T03:30:00.77Z" }, -] - -[[package]] -name = "contourpy" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, - { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, - { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, - { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, - { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, - { url = "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", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, - { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, - { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, - { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, - { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, - { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, - { url = "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", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, - { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, - { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, - { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, - { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, - { url = "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", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, - { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, -] - -[[package]] -name = "cookiecutter" -version = "2.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "arrow" }, - { name = "binaryornot" }, - { name = "click" }, - { name = "jinja2" }, - { name = "python-slugify" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "rich" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/92/03/f4c96d8fd4f5e8af0210bf896eb63927f35d3014a8e8f3bf9d2c43ad3332/cookiecutter-2.7.1.tar.gz", hash = "sha256:ca7bb7bc8c6ff441fbf53921b5537668000e38d56e28d763a1b73975c66c6138", size = 142854, upload-time = "2026-03-04T04:06:02.786Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/a9/8c855c14b401dc67d20739345295af5afce5e930a69600ab20f6cfa50b5c/cookiecutter-2.7.1-py3-none-any.whl", hash = "sha256:cee50defc1eaa7ad0071ee9b9893b746c1b3201b66bf4d3686d0f127c8ed6cf9", size = 41317, upload-time = "2026-03-04T04:06:01.221Z" }, -] - -[[package]] -name = "cycler" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, -] - -[[package]] -name = "defusedxml" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, -] - -[[package]] -name = "deprecated" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, -] - -[[package]] -name = "docopt" -version = "0.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } - -[[package]] -name = "docutils" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, -] - -[[package]] -name = "dotty-dict" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/ab/88d67f02024700b48cd8232579ad1316aa9df2272c63049c27cc094229d6/dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15", size = 7699, upload-time = "2022-07-09T18:50:57.727Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/91/e0d457ee03ec33d79ee2cd8d212debb1bc21dfb99728ae35efdb5832dc22/dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f", size = 7014, upload-time = "2022-07-09T18:50:55.058Z" }, -] - -[[package]] -name = "et-xmlfile" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, -] - -[[package]] -name = "fonttools" -version = "4.65.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/51/d63c7e52163ac14393a35bd14bd7c0da95f8f74be5d7cc988092f9965129/fonttools-4.65.0.tar.gz", hash = "sha256:762ba5431358d0dbd4a01982484a1d494fb267e91f974cdcf20b80eab8560f6f", size = 3674467, upload-time = "2026-09-10T15:35:54.955Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/db/242fa4fce7f632c5f7ab15585343393b25792510c0c32bd218ad24d59f1c/fonttools-4.65.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e844a45c9e5ced6536f184cf1a65b5d65e8f7e711993b413e10500a8223622e5", size = 3097120, upload-time = "2026-09-10T15:33:46Z" }, - { url = "https://files.pythonhosted.org/packages/a0/b6/42fa4d373416675f74446421cf0b2badb82a4245c60745f05f424f75c649/fonttools-4.65.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b30e953de049bf43fc0a63c7d0c44d205c923e4bbf24716aae1518c0e65f977c", size = 2584658, upload-time = "2026-09-10T15:33:48.473Z" }, - { url = "https://files.pythonhosted.org/packages/75/6f/d589b9d62280a846c77a2c383d852c6dcb79ae8aa02bf0fa46c8577af145/fonttools-4.65.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09c34bdeed8915bfb53bee0c8ed2254dbd8ec69c0014b7f3702f347c049bf358", size = 5425737, upload-time = "2026-09-10T15:33:51.473Z" }, - { url = "https://files.pythonhosted.org/packages/b5/09/de2c0c20a42c18e565a2617932beb08c06697bbdd0d3f62b108262e11583/fonttools-4.65.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:05595385ae99f4b9626cebb973bf171b8fe38a8f40708e6e42abba0ed7537778", size = 5402463, upload-time = "2026-09-10T15:33:54.907Z" }, - { url = "https://files.pythonhosted.org/packages/79/2e/bc0f5c9dce21821454bb5812d3b23410bca33c8bbd5468386d0327aa0cff/fonttools-4.65.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d95b34dd68fbfc0e4a1740c421597656117f979ed8dc85de66e08f9f9981806e", size = 5362168, upload-time = "2026-09-10T15:33:57.481Z" }, - { url = "https://files.pythonhosted.org/packages/1f/0d/2116763ade7e71e0e5d421babe1785d745be9b3d605bf914792ce1c97f79/fonttools-4.65.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:924d06e6130429168318db71c40174a765ad016fc4b56ca811287e3d7373b3a6", size = 5524117, upload-time = "2026-09-10T15:34:00.021Z" }, - { url = "https://files.pythonhosted.org/packages/e2/5e/f9600553b9f645e3068553831685ff1dab6259536a23b38d2e048de38f17/fonttools-4.65.0-cp312-cp312-win32.whl", hash = "sha256:04f73dd01005752a6e75cf4a8dc6b70dc724d1d4bc34cc89522153f4a2f07680", size = 2432089, upload-time = "2026-09-10T15:34:02.803Z" }, - { url = "https://files.pythonhosted.org/packages/3a/02/e436a6a1863b9862bab9f82d6da33055dd7aa3738017edd902a163525dc2/fonttools-4.65.0-cp312-cp312-win_amd64.whl", hash = "sha256:3b5d9ba89edf778b376e669b879ae33a198bf45cf5a23c3f6514f935cf9d0d9d", size = 2483475, upload-time = "2026-09-10T15:34:05.09Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5c/343a4225e83eb06f82c1d8bf41fc5e5f71eab2f62bc7ca215c764722c0a9/fonttools-4.65.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8b7bb52817a24731d2e4f4df0e71fdde05e6c806c8f8f1517b015d142fdacfa5", size = 3094663, upload-time = "2026-09-10T15:34:07.235Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c9/49b2401be932741d9218181c08948c96db32eab21ecaaf79283b752e13e7/fonttools-4.65.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e2c21772fcf70325189707b19f346812690bb1b0bd7e207e6ac205244806b303", size = 2584521, upload-time = "2026-09-10T15:34:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/51/c9/48b07e6c5cf44fa56f758a04c3772a66c0e9ba82bbe22077e4076746a62b/fonttools-4.65.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c9b26816415b5e3d899e9077109d327b22140fe3c4066644d8cdbad5bb1569", size = 5395399, upload-time = "2026-09-10T15:34:12.38Z" }, - { url = "https://files.pythonhosted.org/packages/fa/2d/5cc5a10c8ed56079d6c2e9e3e5920622529a2ae045c9f47a6055ccb1a319/fonttools-4.65.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6dd6243f60e2d6160c2966e1e14020dc261ffd741b69a2e4ca8bfd051592e4b7", size = 5376659, upload-time = "2026-09-10T15:34:15.239Z" }, - { url = "https://files.pythonhosted.org/packages/ab/6f/ccf33739d936bb3afa1655a225be7ee5d63d6d3c8b7d0e570bc5a2a7b899/fonttools-4.65.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:834962fd7cf21c58e81ac50a59e6ed2306f9df5e3dd481dad1cd7d2c4c60b773", size = 5336795, upload-time = "2026-09-10T15:34:17.817Z" }, - { url = "https://files.pythonhosted.org/packages/7e/c1/ffd17483f2094f0f4118974295b514b5b82afd4f6e3c80c23f01684e97a6/fonttools-4.65.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:580eb68ff7bd6954a7a76afddd864bfc66eaaf5f5c20dd6ead9186d0055a4ffe", size = 5496530, upload-time = "2026-09-10T15:34:20.241Z" }, - { url = "https://files.pythonhosted.org/packages/b1/19/9aca7712d0676ba5f8d1530ce20478a9bb09cccd0153d56693337379cdcf/fonttools-4.65.0-cp313-cp313-win32.whl", hash = "sha256:7a18b2ffd44249fe84289253197aa65ad4f2de554c0d381f18b1f5939bc6bc60", size = 2430392, upload-time = "2026-09-10T15:34:22.906Z" }, - { url = "https://files.pythonhosted.org/packages/1f/6f/f015dea0f4354e0798751b657cea2dee482914737f88bf1a078349ce92cf/fonttools-4.65.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ae1846b0f192fd485d26a455af19b8f5cf05aff08f9836f533913d8fcea133c", size = 2481620, upload-time = "2026-09-10T15:34:25.816Z" }, - { url = "https://files.pythonhosted.org/packages/e6/35/f894ceb867118c0261d0f69a9bd516b045a3754238f76c88a49513ac7a83/fonttools-4.65.0-py3-none-any.whl", hash = "sha256:3060b8c1fc2329fa20265b7c138614143ea7c1624e26c5c180c76aeb74deae6f", size = 1196441, upload-time = "2026-09-10T15:35:52.347Z" }, -] - -[[package]] -name = "gcovr" -version = "8.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorlog" }, - { name = "jinja2" }, - { name = "lxml" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/07/37/b4a87dff166dc0a5002e9d03fcb6ca8eeff048247b011b67f047e31122c9/gcovr-8.6.tar.gz", hash = "sha256:b2e7042abca9321cadbab8a06eb34d19f801b831557b28cdc30a029313de8b9e", size = 199997, upload-time = "2026-01-13T20:04:30.019Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/be/f722c843e7875c7cf92cf0e0c1604cddda55a70278c768c6327a78fdba79/gcovr-8.6-py3-none-any.whl", hash = "sha256:dbf9d87c38042752ad6f530aa8210427e22b526611bb7b7bfed0e81977d1f1ef", size = 254618, upload-time = "2026-01-13T20:04:28.15Z" }, -] - -[[package]] -name = "gitdb" -version = "4.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "smmap" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, -] - -[[package]] -name = "gitpython" -version = "3.1.62" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitdb" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e0/db/3ca813cbacb23ab6fe46ff38a9b5ef8e73e970c8051f2ce903aacafe0446/gitpython-3.1.62.tar.gz", hash = "sha256:1791de66309bc0c7cfca40bf8d2e3de7ca091cbf94e6051be1ad0722c61062af", size = 231728, upload-time = "2026-09-07T02:57:21.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/0b/29d7965215f8ef830a7ca1f42997fe13e5693d85e9edb18f938d063ef5f2/gitpython-3.1.62-py3-none-any.whl", hash = "sha256:7002251225e10e29d2e1f49e6532613fe5d5d9f0b6f1f02997a52b38fe56899e", size = 222753, upload-time = "2026-09-07T02:57:19.762Z" }, -] - -[[package]] -name = "giturlparse" -version = "0.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/8a/b70b84cc78f9059d627f09560c81a11e8f046570d220a24e169489cad6c9/giturlparse-0.15.0.tar.gz", hash = "sha256:9af3f1fd5c4a0cac94ddb283593635005646393ee0debbe330d1bdff8866bf2c", size = 16138, upload-time = "2026-06-16T07:28:56.021Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/96/147a2771ab655b9353781fb2f95c94eaf1d8576dccc991c1a61d0d355067/giturlparse-0.15.0-py2.py3-none-any.whl", hash = "sha256:76d2e6983b037356ab99b30683e533ac3db96409b68e2163a20fc3aff6446f10", size = 16683, upload-time = "2026-06-16T07:28:55.184Z" }, -] - -[[package]] -name = "hammocking" -version = "1.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "libclang" }, - { name = "py-app-dev" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fd/0e/b0c0ee876af8ad404c3c1e277d24da9bc9c82e747a93ea31283afe8a5825/hammocking-1.0.2.tar.gz", hash = "sha256:309a3fff0a1c4461390128c82abe7881599f018ba6be828b1cbc51860fe60f47", size = 406743, upload-time = "2026-04-28T20:55:58.345Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/86/70140c041f2e4210054e81bf26afca6dffe4527f8a7d915aa5da1769bf62/hammocking-1.0.2-py3-none-any.whl", hash = "sha256:7fa0124b268b8941794a83427a0aa9d3001c75a9f3b458ab03316d07febec7a9", size = 12563, upload-time = "2026-04-28T20:55:57.319Z" }, -] - -[[package]] -name = "idna" -version = "3.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f5/08/8eea9d4b8302028f3abb2c0813953f7aec26d33b7a8960ed760e65ff29fa/idna-3.20.tar.gz", hash = "sha256:a7db850025b95ded1eae8a46181a1a6c56c92c96f0e2b005d9ff8dc0210cab44", size = 216463, upload-time = "2026-09-17T14:11:04.752Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl", hash = "sha256:ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c", size = 69583, upload-time = "2026-09-17T14:11:03.168Z" }, -] - -[[package]] -name = "ignore-python" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/79/39bfb3743a176742c9075be3b7b12b4ec7100bb4cae13448b1839fb0d9df/ignore_python-0.4.1.tar.gz", hash = "sha256:dbd8a009d554b86c600f7b5cb180a126813ebf485c08d7ae05bcf9754fc25073", size = 12234, upload-time = "2026-07-01T11:59:59.307Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/0e/6f4e47c3c730066dc1ddd4d25eda3896d5e24ce51819693db3de87a30496/ignore_python-0.4.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7fa78c104e2cf751ca00491f6b8108153d40e07ea6731a5d0039ddbaf8a7ebb", size = 886147, upload-time = "2026-07-01T11:58:15.993Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2d/6f552951f000cd10773b754042b16c5d8550022d5648234edcf80d3a0326/ignore_python-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88381ad01c24506933cb8d5cdf94417435f20cdf078ffca5bd06b1325c7fe5cb", size = 840284, upload-time = "2026-07-01T11:58:17.219Z" }, - { url = "https://files.pythonhosted.org/packages/e3/0b/d9a27ace2a7cbcc652c8431488933a1e9e54a6704ca536f4cb73436af840/ignore_python-0.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e87d887140602de486130da3f6deee5b03b82302488a92e388dda1f29bfeb30", size = 924643, upload-time = "2026-07-01T11:58:18.298Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/6d70979897a10e8b453a559cd3a3a1d170e475f20231f183fc945f13da71/ignore_python-0.4.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fe1511740bed936c5a82ceb7a570d6ebb4291dd13b37f8607a0e4072f72bd3e4", size = 894406, upload-time = "2026-07-01T11:58:19.589Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1d/111e67c9be54c9fda510643dfe840cbd4e8e82c8c4677c8c37fb51c86e01/ignore_python-0.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96f3caf5db26f44fec5ce20d837f6e9bd391f5dd8559e8e08fe9d45d74927cde", size = 1177927, upload-time = "2026-07-01T11:58:20.873Z" }, - { url = "https://files.pythonhosted.org/packages/a1/1d/1ec7398522d437dcf5808f3d0c170100dbaff2cfb89886a2d837b87dd4f3/ignore_python-0.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7cf900d9e3ef608eee780a5146846bf9cac4ecfcdfb2a99ae59b2c08baf25c59", size = 953287, upload-time = "2026-07-01T11:58:22.158Z" }, - { url = "https://files.pythonhosted.org/packages/30/5f/ccf9faa05129b10571528867a565cf42cbc2dd58387bed5c297c8dff017f/ignore_python-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3718a8a2c17c7953812c473e035b97c697cd2fbd3c1d165450c37fe31f946d91", size = 966483, upload-time = "2026-07-01T11:58:23.508Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f0/6fa4ce058b27017455ac6d2efb8e29bc729041b900af0ad4684f80453a23/ignore_python-0.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:90f7e52145073cf6d8a658e4e259345743a1483d078fc4b09431b9df336635e8", size = 1004278, upload-time = "2026-07-01T11:58:24.633Z" }, - { url = "https://files.pythonhosted.org/packages/46/ce/5fea86be6ebf40c4783974ddf942ffa3bdec390d7ae666d93ddfe151dc23/ignore_python-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a62eb1130a62333e7d0e2d97fe7107004c6e1495cc934e1c57f979640204ce0", size = 1102070, upload-time = "2026-07-01T11:58:25.85Z" }, - { url = "https://files.pythonhosted.org/packages/e5/99/06c6c35902b15e1040bce8c0bc1804a08a1eef583b4acd84380e22ce011b/ignore_python-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4dd65cec2aed8883fd8e82d522f00bf177e30412770b3ddac7599182b02c3f4c", size = 1167818, upload-time = "2026-07-01T11:58:27.008Z" }, - { url = "https://files.pythonhosted.org/packages/df/05/f5ad9dc01f18a879a6f47bbae05a40558c2ebf009a65733edeafc55318fc/ignore_python-0.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9dbbb14d4ec825f4279dc499bc6793884e0bc2dc29086a1bda6335bdc438835f", size = 1179445, upload-time = "2026-07-01T11:58:28.29Z" }, - { url = "https://files.pythonhosted.org/packages/ee/61/67e45639e141443830d7dfe8bfac014ba0ad215e04ece6540aa8c1cbeaa9/ignore_python-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:68a0c6754f237e5618a76343d236deee3372f9bc593842d1228a13fb870b1408", size = 1176735, upload-time = "2026-07-01T11:58:29.839Z" }, - { url = "https://files.pythonhosted.org/packages/1d/48/2fa7ebd1e52818379d6686d6242263c8ee3c0210d357c61c4de4a8efeab0/ignore_python-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:c174dc85629eabeed5b2a2a05d6480f70cb6c366e520ba6d1bbe7f540473408f", size = 749842, upload-time = "2026-07-01T11:58:31.047Z" }, - { url = "https://files.pythonhosted.org/packages/eb/51/6b2241f3f07aed3497e8f733c40dd5435fa58c6b0a860fd71bfd37d5bd8f/ignore_python-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:1dafbf90809a5ecd18851a0bd115cb5823d14df9bd84839a8bb14aa83db59721", size = 689895, upload-time = "2026-07-01T11:58:32.25Z" }, - { url = "https://files.pythonhosted.org/packages/ce/02/f351359f18ee87f770cb7126056baa3fe825d58563e2f3c0c0f46f97a442/ignore_python-0.4.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:fe7a1d6774a48df4e63761217fb14f83f7ee664dfd5aa7c43c68e52478fa47e8", size = 885345, upload-time = "2026-07-01T11:58:33.295Z" }, - { url = "https://files.pythonhosted.org/packages/64/44/9433f5dfafa68f98e6f24a2f5b3765d5d3fa0270ab3b1ba21530d49be14f/ignore_python-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbb867ecec784c3cc3491c8c42e55f1a4dcb4016e3855179a5125e1ec206dba5", size = 839369, upload-time = "2026-07-01T11:58:34.42Z" }, - { url = "https://files.pythonhosted.org/packages/ba/2e/bfa075c5b065a44a39c1e890ecb4031e97e73711129e23087ee58f32418f/ignore_python-0.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dce0d63c67f33bbd0c2fd86db24607c1c92c24329b637b0ad0ecacd0e1c5f3a", size = 924307, upload-time = "2026-07-01T11:58:35.556Z" }, - { url = "https://files.pythonhosted.org/packages/13/f6/78505e82c84aee247f0fe53ceef3ccbe7de57b9826d307c80465dd357312/ignore_python-0.4.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:64093ddd989a262e0d8175ce6d62ac25bb578009d39db8f09b485ea4dbb298f3", size = 894260, upload-time = "2026-07-01T11:58:36.702Z" }, - { url = "https://files.pythonhosted.org/packages/bb/c3/89383366e9050b5030b30b860f2de190d092c390ba244d46a61252044963/ignore_python-0.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:88eb3af05b964ef8d5741a0c2fac3528734a9bcce6f63b8abb4a7121abfa1041", size = 1177366, upload-time = "2026-07-01T11:58:37.912Z" }, - { url = "https://files.pythonhosted.org/packages/25/33/df3b3146da1f9f4e36e4bea9e47cc51771b34b6676344c90876c2316a734/ignore_python-0.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:064092cf100d573543cd4f5c03abb69476a0bf2987a1f6ba756c8d524e86898a", size = 953720, upload-time = "2026-07-01T11:58:39.104Z" }, - { url = "https://files.pythonhosted.org/packages/9d/63/2b6cc85a92820926cdab55452f758bac2b60d6c2c15d1f122297950eae67/ignore_python-0.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19b6cbede28ba33116b3c88695375c40a5ebf658ad4afe28a1d54727edf44161", size = 965884, upload-time = "2026-07-01T11:58:40.347Z" }, - { url = "https://files.pythonhosted.org/packages/98/de/b0e11d32d74802f02377c6980b18b9d64498e36dc26d55304fd8e0387a7a/ignore_python-0.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76068ce9bc1b53e9cdaf18eaf73a5f658accf326217f748ffdcb4723284fa7e8", size = 1003545, upload-time = "2026-07-01T11:58:41.544Z" }, - { url = "https://files.pythonhosted.org/packages/20/32/c2826b3c34c15b1324a303e1ba0b6fe5bfcac1e3f0a9f471df7728c724e1/ignore_python-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0891e2ac657b51a19fcedebe04c5dc0a432abd4650184086c7a88ce1e824b2f1", size = 1101743, upload-time = "2026-07-01T11:58:42.704Z" }, - { url = "https://files.pythonhosted.org/packages/40/8d/c4aa2bc153497ce125f7966f79a8637f00809b44f9a185d9538f5b69c1bf/ignore_python-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a303ebb4a89d58239e232076eee586e0b9020139044ba776066433d5fbafb925", size = 1167333, upload-time = "2026-07-01T11:58:44.412Z" }, - { url = "https://files.pythonhosted.org/packages/fc/70/022b0163e0e3f2fa596efa3fb245a43b1973a8de7a4ed3f97cccbf0f35df/ignore_python-0.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4fe4684917607a29b15f476b8623f19b31c3a831c17e15d20c4d02800e6527b2", size = 1178935, upload-time = "2026-07-01T11:58:45.783Z" }, - { url = "https://files.pythonhosted.org/packages/34/07/25484608155c064046da3b1411aab56c0d8983036e41ab6e39dc97a4a589/ignore_python-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58987f4ae122212ac4e0971812c9293138d1326c609f1d94a691aff69f0b4137", size = 1175839, upload-time = "2026-07-01T11:58:47.028Z" }, - { url = "https://files.pythonhosted.org/packages/cf/64/2c075a6a0141bbce26dfc8b913e9bba5da7a428472cc395411391dbd16c3/ignore_python-0.4.1-cp313-cp313-win32.whl", hash = "sha256:0d42e50dd0ccefa515c42704d7fb5571b4ccebe91d33eba6261fad4b99b480d9", size = 657403, upload-time = "2026-07-01T11:58:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/15/d6/21edb0a8145de5d371b8fa80aaea32d6d3bfc4e96e9de261288f9ac2d5af/ignore_python-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:a5487ddfccf518973e18ee2f43cd2b52505c55eb3e9fefc056774a10e30376cb", size = 749016, upload-time = "2026-07-01T11:58:49.515Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a4/84e8e55c99fa0d9c924b09b0c0e1d20ef0d8edfc7562262195a1e78ac731/ignore_python-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:aee1c6043cd1b8d00983c7dec209d668eb1f0cc702d7eda886581b90cd5c274a", size = 689126, upload-time = "2026-07-01T11:58:50.606Z" }, -] - -[[package]] -name = "imagesize" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/5e/513ff06670c84e7b9887c1fdf61b2d42b4f574a831f2f1d2222023049d8a/imagesize-2.0.1.tar.gz", hash = "sha256:b2ba6a4dea487a7ebcd53248d3476aca449d30db12a2dde5e0c5ca9624fd77e5", size = 1883774, upload-time = "2026-08-24T12:35:19.13Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/f9/575c8d760eae1fc99651b7cc5efd96ad5379ca4d6b53750b0fb4fe983f34/imagesize-2.0.1-py3-none-any.whl", hash = "sha256:ea0c9a0384df69ed86a943a15cde37d0360b82491b3910dc2215e202e62b5b02", size = 14794, upload-time = "2026-08-24T12:35:12.548Z" }, -] - -[[package]] -name = "importlib-resources" -version = "6.5.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, -] - -[[package]] -name = "inflate64" -version = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3e/f3/41bb2901543abe7aad0b0b0284ae5854bb75f848cf406bf8a046bf525f67/inflate64-1.0.4.tar.gz", hash = "sha256:b398c686960c029777afc0ed281a86f66adb956cfc3fbf6667cc6453f7b407ce", size = 902542, upload-time = "2025-11-28T10:55:52.641Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/33/5cfa7468960de1be0833e7e41adf5b7804a0aef2fb46f3679df3876bf3ab/inflate64-1.0.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c8009e4a4918ee6c8cbc49e58fe159464895064cfdf0565fed3f49ca81e45272", size = 58619, upload-time = "2025-11-28T10:54:48.315Z" }, - { url = "https://files.pythonhosted.org/packages/cd/0a/583c7c2832da36e986c5758d0afb6f5944599e55c5b798b066a9ef63e581/inflate64-1.0.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0d173a7a0e865bb7d19685c5b1ad2994712b8361b24136d7e94abeff58505647", size = 35865, upload-time = "2025-11-28T10:54:49.389Z" }, - { url = "https://files.pythonhosted.org/packages/72/4b/9c6acfbe900e5c8698132244c68036b0455bd2169f46e356c83dc0366f11/inflate64-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8bad992f2d034f5f7e36208e54502d1b0829ce772c898e5dc59109833420148a", size = 36019, upload-time = "2025-11-28T10:54:50.813Z" }, - { url = "https://files.pythonhosted.org/packages/7f/66/c0c3d3b4b863aab2c2ce631d219a8eb3b95b78acd5f40d3212f071e693db/inflate64-1.0.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6bfcf806912ced77a21394f7363805ecacd626b79f93cba87d505a48e88ede78", size = 98765, upload-time = "2025-11-28T10:54:52.273Z" }, - { url = "https://files.pythonhosted.org/packages/37/00/1a2351a85d36b26c5b2b8cfbb37ad86084c98f592dd7590f8577d8b33993/inflate64-1.0.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62d1aac3aba094ae42e27ce7581b414c90f218248be0953b6aeb11a127225e5d", size = 100594, upload-time = "2025-11-28T10:54:53.684Z" }, - { url = "https://files.pythonhosted.org/packages/f7/3e/5d18ff5b86aaaf54117e1bb6ce15cb17163f56035f9c480e609d35f258ab/inflate64-1.0.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8065166f355122484f004225b379d403346bdae69ec624786a9334f025580675", size = 96745, upload-time = "2025-11-28T10:54:55.277Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/306d5d6aca1e04e596d2a504a59ff9a900623a6ec852f38aab99f384562d/inflate64-1.0.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94a95f32087d223d2e119ff5c7c264109e8d4cb7e421e7a688a899a6fe021b38", size = 99795, upload-time = "2025-11-28T10:54:56.485Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ec/d4caa4bf3c9e520c15e900fee5a00fc523953843e14aa378ca1abfb2b4ea/inflate64-1.0.4-cp312-cp312-win32.whl", hash = "sha256:ad4fa490bb7dc2a4640a3adaa2d5950f4a465ba034bbcf184c2103646e58ad97", size = 32956, upload-time = "2025-11-28T10:54:57.642Z" }, - { url = "https://files.pythonhosted.org/packages/33/c4/c0de4e9bdf12e449360b710e9ab5b5248804610f382b538773cbd07b72bb/inflate64-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:2c6befdf83d088a6e0d10d0873a9d4bfde2ce00ad7a52c8189cf303306f98030", size = 35577, upload-time = "2025-11-28T10:54:59.14Z" }, - { url = "https://files.pythonhosted.org/packages/80/07/03a5ef74ad3869b3c5af3b09216321f5a1a5a45265f7bd6d5abc669c7622/inflate64-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:2b263c619469f90a75f29c421c53d31b208ad494a078235a8f6db2bc96583fdc", size = 33465, upload-time = "2025-11-28T10:55:00.568Z" }, - { url = "https://files.pythonhosted.org/packages/c2/55/b7de7ae318a4f233f892c4f7c8b7e0e8643abe3fdcc53ed35020a9fe3f47/inflate64-1.0.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c3f37540d0e64884a935fd62a7d17e40ab69f05ec63e815483b6513675d01bef", size = 58633, upload-time = "2025-11-28T10:55:01.931Z" }, - { url = "https://files.pythonhosted.org/packages/b8/5f/6f89c8524503fd7a9ca2bd91fe60d7291b3f684e9d41edb38ef49e10fc3d/inflate64-1.0.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4d24112180c95d12f279cade9a1e21f8be7f4790c4109c293292edf87d061992", size = 35864, upload-time = "2025-11-28T10:55:03.075Z" }, - { url = "https://files.pythonhosted.org/packages/7d/14/9eadd59244b38cf85ccd0ca43d1296c50b3a33aa37be4fb68a1928efa58c/inflate64-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5c098dab17821f466fc6e6a3d78fc6e0295bb51458015f03416b1d58d6a8df4f", size = 36019, upload-time = "2025-11-28T10:55:04.126Z" }, - { url = "https://files.pythonhosted.org/packages/ca/04/399e82d8f5003dd92c8a0c5c1a9a8ce0919114710a496cbe88848bff3a72/inflate64-1.0.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a984b9287ff0fb596eb058d66a9e94530556afd2b7c054b44f2e0aeeff894e8f", size = 98973, upload-time = "2025-11-28T10:55:05.229Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c5/038dc2593bbc4272d87eac8c9f75692267d47f834ced888f6d81995df606/inflate64-1.0.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f62a13d0327631778fa2a47c308ae2b07b2659b7bb8564783259ac65949f8c0c", size = 100777, upload-time = "2025-11-28T10:55:06.41Z" }, - { url = "https://files.pythonhosted.org/packages/74/4a/f6d3031dd3578510894a41bfe1ac149228970ce1629a43f20e0c5abbe8d1/inflate64-1.0.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:513201336fb3b0b7e2aee5dbbbe30a9f1b23291738b5ceb80076fc285f2ec2f1", size = 96967, upload-time = "2025-11-28T10:55:07.594Z" }, - { url = "https://files.pythonhosted.org/packages/26/ef/7ab3bde4e176609ae0e607a8a9bb38d201885275664a6d574299f5bf7850/inflate64-1.0.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:84ce3a97272ba745fce52b38363855c7201968f6402a794bbade774e64c657b9", size = 99997, upload-time = "2025-11-28T10:55:08.85Z" }, - { url = "https://files.pythonhosted.org/packages/39/59/8256b3e802e203c8645e0b32b25e6bd94508ad572593f0cdf8234db3879a/inflate64-1.0.4-cp313-cp313-win32.whl", hash = "sha256:332051a9d7e50579b90a3f555d68f53414b06f636c9ffe82e97c0baae3c8fbcc", size = 32958, upload-time = "2025-11-28T10:55:10.343Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4f/5784ee1eb8260f2310e24ef2883f1f494f9332bcfde4ed14ee780372149e/inflate64-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:3983f53b590ff7d0ba243f664ce852aca882482f30f7a8eab33e10d769336d0c", size = 35578, upload-time = "2025-11-28T10:55:11.389Z" }, - { url = "https://files.pythonhosted.org/packages/39/e8/8d927770ce25dc9764c8104207a80653d65471d0a6a8f9ead350016e4586/inflate64-1.0.4-cp313-cp313-win_arm64.whl", hash = "sha256:118d8286f085e99a14341c76ef9fbffd56619ccc80318a9a204aea3dbfa71470", size = 33465, upload-time = "2025-11-28T10:55:12.824Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[[package]] -name = "jsonschema-rs" -version = "0.52.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e6/48/771a96c524eafadcd2366271c13bd6e5b79af9032821f11e22846d36da3f/jsonschema_rs-0.52.1.tar.gz", hash = "sha256:25ec1da52bc1c11c012e17a01b97e24d33a15d1d76c705fd5a7c276d1bbe7677", size = 2631333, upload-time = "2026-08-30T09:52:05.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/37/92f23a0e53b6448faaae98ea4896f549c104cea8c07b8d213a22b2fb84dd/jsonschema_rs-0.52.1-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:cf728999f2cf4fb4fba8abf8d5a110565f716f4a460f13157de59e836c03706d", size = 10119218, upload-time = "2026-08-30T09:51:25.959Z" }, - { url = "https://files.pythonhosted.org/packages/71/3e/52261a693a148d8746db1ff67591e9e9f4b56b155be3d48840f7e5cbab58/jsonschema_rs-0.52.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d32934b6a3032e968e59bb172addb8e752d7fdbff742834390f87549889c86db", size = 5277560, upload-time = "2026-08-30T09:51:28.055Z" }, - { url = "https://files.pythonhosted.org/packages/82/07/94db20d60141955d1987b73bcae8fdd3db6cfdf7ef4ad68fb9b399cd436d/jsonschema_rs-0.52.1-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5eb2562cca5effc43134c4f8d2ba0c2b04b97c7cfcb6ac27997f0a5b2aab662f", size = 5179470, upload-time = "2026-08-30T09:51:29.328Z" }, - { url = "https://files.pythonhosted.org/packages/5f/5d/cd7ab77544d6a8fad152c0ec1c332de1506da1c753475357fbfbf5ed91a2/jsonschema_rs-0.52.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:150845b65c701d5db2a397c97beb98356123548b29c70095ec9a851a611cc736", size = 5418427, upload-time = "2026-08-30T09:51:30.682Z" }, - { url = "https://files.pythonhosted.org/packages/ac/30/8b3d95e7be67daa93996a1444ca2238bdfcb58debf8bccf6469b5f183dc8/jsonschema_rs-0.52.1-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:895e7ec4c6e7ff564786f86e197c95d15017a42177523319bd45ede20e4c4417", size = 4999697, upload-time = "2026-08-30T09:51:31.998Z" }, - { url = "https://files.pythonhosted.org/packages/c7/24/001bb2ee8ca4ba19a7e31daad391de8e10f554a4f8c8396f83775eb585fa/jsonschema_rs-0.52.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4b05b745f05859ca8b2d056e22a3fae036e922a61805680cbe77cac31141be2e", size = 5185040, upload-time = "2026-08-30T09:51:33.943Z" }, - { url = "https://files.pythonhosted.org/packages/f8/84/50cd77eb2d1763625e8b394d5da6ffebcb148f71f4350b7bf3872ba25996/jsonschema_rs-0.52.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f709d4666f7fc728829503ff653c8a578634ab1ed54e9f11097da89dd18baf75", size = 5658962, upload-time = "2026-08-30T09:51:35.305Z" }, - { url = "https://files.pythonhosted.org/packages/02/5d/a588c8fc4d3f9fc06e222d35003c9c60a28bbcc492c49f273e19fa205ef8/jsonschema_rs-0.52.1-cp310-abi3-win32.whl", hash = "sha256:9131e083499ea49157e559dff813215f3ed116ed3a2996b108fed2faf1b80c0a", size = 4613745, upload-time = "2026-08-30T09:51:36.776Z" }, - { url = "https://files.pythonhosted.org/packages/1d/64/6b08a726984c8a3eb648cc516efa5bf20d218f6c26b912ffc126746c8147/jsonschema_rs-0.52.1-cp310-abi3-win_amd64.whl", hash = "sha256:3c9152a9aaec6a656b89fa203ccc26de54065e26cf6785378d54b6390ad403f2", size = 5340066, upload-time = "2026-08-30T09:51:38.183Z" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "junitparser" -version = "5.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/c9/edeb58b0565b74ba0ffedf822d63aa40368bb4ff2da49751768eea696f5a/junitparser-5.0.3.tar.gz", hash = "sha256:f824ec1b7afbf2fd3b6813b87967b02fcda750e135d27be17cae497147419a04", size = 12120, upload-time = "2026-09-01T06:13:00.32Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/36/dbb6f74621890e5daa831c5f474b81e8b773298adf5ea835c03215a9359c/junitparser-5.0.3-py3-none-any.whl", hash = "sha256:553535b8cd1672a9c8bd2c7326ae1ae69279267fbc562106b5aa44d3c5f769af", size = 15000, upload-time = "2026-09-01T06:12:58.01Z" }, -] - -[[package]] -name = "kconfiglib" -version = "14.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/59/29/d557718c84ef1a8f275faa4caf8e353778121beefbe9fadfa0055ca99aae/kconfiglib-14.1.0.tar.gz", hash = "sha256:bed2cc2216f538eca4255a83a4588d8823563cdd50114f86cf1a2674e602c93c", size = 162613, upload-time = "2020-01-31T06:40:50.563Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/f1/d98a89231e779b079b977590efcc31249d959c8f1d4b5858cad69695ff9c/kconfiglib-14.1.0-py2.py3-none-any.whl", hash = "sha256:edcd35a20e7e138a9a9e96149027f805f785e818d2eae400b6fa8b0c8845114a", size = 145875, upload-time = "2020-01-31T06:40:47.252Z" }, -] - -[[package]] -name = "kiwisolver" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/07/bd78e6a8fae171ea041ef5bba3ed21a003522fa088834b069b1909981f30/kiwisolver-1.5.1.tar.gz", hash = "sha256:f1303ef2eec81262a4b708c3e858afe58d7c75ad91c1c05266eda7673369859a", size = 104395, upload-time = "2026-08-28T10:28:27.153Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/9b/65b302742389c6f96f2956bef5decf26011309feb2fc5d79613af18adea4/kiwisolver-1.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:63fb7294b768f444eb4b068965f2662f28c2fd4161e23bd60fcf3ff27b74c046", size = 123876, upload-time = "2026-08-28T10:25:27.44Z" }, - { url = "https://files.pythonhosted.org/packages/71/74/c21f339956f6f691b2ed7e31d5f3ae767304df6c460192739fc830853051/kiwisolver-1.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ebdef3eae5336568147c39a55be6a2036ffde53faa9ca2d978989ae7c2da12c", size = 66487, upload-time = "2026-08-28T10:25:28.728Z" }, - { url = "https://files.pythonhosted.org/packages/84/e5/bdb34e21523e01dceda064d63713f3bdec91388af24fba1eca7ea5e85864/kiwisolver-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1798e83840c3f627246104c4d8a9639c60fa068adf9ce92b61791781fa8a68c1", size = 64660, upload-time = "2026-08-28T10:25:30.071Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f4/dadfec469313c7f428efa7e84b4aba9732f813c13ea7131a24b7b008ef57/kiwisolver-1.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34633ecf50d16187ab8e5528b7a2530f2feb4e23f300db4672538b51cfc5cd38", size = 1477929, upload-time = "2026-08-28T10:25:31.495Z" }, - { url = "https://files.pythonhosted.org/packages/6f/35/09c58daac34e6f6ea5c6dee0094b422118e5a7c265586008a95fd135ac5f/kiwisolver-1.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d27c2123977cb9269c30a49ba45f03a4323017ef693e19db4ec9dbe1299a3002", size = 1278499, upload-time = "2026-08-28T10:25:33.375Z" }, - { url = "https://files.pythonhosted.org/packages/19/32/739765e24fbad29d13f83e546ea4abc215a78cea9d677ca09025b027724d/kiwisolver-1.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6a797a1cefc8b9c93170db580337e1fe3d011ad18b1299943231279406342048", size = 1296677, upload-time = "2026-08-28T10:25:35.059Z" }, - { url = "https://files.pythonhosted.org/packages/df/32/03304d1010e2cc45e5b3b52cef7e43fed3a2a5cd6c87a89b4a88e1d85b5d/kiwisolver-1.5.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2551cf9917af48ee7c4b29cc82320489508cf96fd26a51f6fc124de661cd44c7", size = 1346037, upload-time = "2026-08-28T10:25:36.705Z" }, - { url = "https://files.pythonhosted.org/packages/3e/57/4c49377bfd274450dd72ecaa13eaac32ea804a03363e4d1db0c5aa999ceb/kiwisolver-1.5.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:38f6e0deb4d0a4615efe0c4efc5990b06ae450ab50a0b321c0b078b6d238c083", size = 988248, upload-time = "2026-08-28T10:25:38.299Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c3/38df144a08b6c5d75ca4504e5cc3141bb3bfef64c04f4ef48204f42711b6/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bfd1de989b3330420e29de39352f5c049905c9e3ee67233a50d550e3d652c148", size = 2228722, upload-time = "2026-08-28T10:25:40.038Z" }, - { url = "https://files.pythonhosted.org/packages/e7/11/3221838a89cd64d9b386353e000cd8a296069a20fbe3584507fdfd5bebae/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1209042a623ddfda5497e4066c7b77651dde8e1d3a9dd97599dc7e97f3b9b78c", size = 2325216, upload-time = "2026-08-28T10:25:41.699Z" }, - { url = "https://files.pythonhosted.org/packages/83/d4/075c219230697bb5db910d37262b9bacf880f92b4811a02ab81ed073a253/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:26e8268480be5061d509e29669d59103c067a26377a56491630ece11762e3858", size = 1977689, upload-time = "2026-08-28T10:25:43.559Z" }, - { url = "https://files.pythonhosted.org/packages/bb/08/1d219c3c2dd960983d0d4da623d916e9de6385df2b0bab3d1af0e9b8fccc/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d79308fa689fac89cbcfbd4dbfc80b5f95c54c5a7fd4d194be221f9d33d026e6", size = 2491443, upload-time = "2026-08-28T10:25:45.242Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d3/024208ec1079d273f1047468d1bdffbf38bb75b7b268090fd3a0301b9d9a/kiwisolver-1.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b03af77d77e50edba2030fd5f7c352ff209314b09030a3cba7c14edf9a09a444", size = 2295200, upload-time = "2026-08-28T10:25:46.984Z" }, - { url = "https://files.pythonhosted.org/packages/6e/7c/7b210498f9f92e1cd7855f260fa69ef056881087b199ee20c208f0e4189a/kiwisolver-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:06a6917674de9e0fe3f66f5430787f59a9f2ddb64af9b714eaec547e29ef5c19", size = 70748, upload-time = "2026-08-28T10:25:48.444Z" }, - { url = "https://files.pythonhosted.org/packages/94/61/ef0daa157c8bb23672f7423e0d14c39db1dc6ef8ed47e6bc54c9c1bef3bf/kiwisolver-1.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:ad8b9671348d7c8716715652ae11f85ed0eb99e265a2df2ca490577d69860b2c", size = 68324, upload-time = "2026-08-28T10:25:49.81Z" }, - { url = "https://files.pythonhosted.org/packages/08/c1/88018321d976f53c421e379c43bc6993e70ce0c8a3ec5edc4bfe102257f6/kiwisolver-1.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b6ae6a0328f0bc035741820fdeecdcd67bf4694eee03972e843663107122f450", size = 62272, upload-time = "2026-08-28T10:25:51.02Z" }, - { url = "https://files.pythonhosted.org/packages/85/d2/712bc17ea4f1d216034928069d612defbc6c95a471c55a7203a39faecb1a/kiwisolver-1.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:886fc26012f0e8b5f69d1cfe6d711f6b11f194621539bf8e6bb1c25c5dc82724", size = 64481, upload-time = "2026-08-28T10:25:52.22Z" }, - { url = "https://files.pythonhosted.org/packages/1b/e6/6c5380d676f43b6d918033962ea5e72360ca69e5a404154bc496b598ffdb/kiwisolver-1.5.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:aefe930d113798330e9462f7874542977869c0613cba3262e2de3a8d5dee8f3a", size = 66260, upload-time = "2026-08-28T10:25:53.387Z" }, - { url = "https://files.pythonhosted.org/packages/92/6a/7087f5822cc8bb272679641404b1966a42504dac9ee74e2b33840475a0aa/kiwisolver-1.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5ca5aebae78a0bc13c1943af4af615d4966c5b650b05d5aa83b50e427196fee", size = 123876, upload-time = "2026-08-28T10:25:54.644Z" }, - { url = "https://files.pythonhosted.org/packages/5c/4b/9f385087ca09ee5ab9c09c6832561a7d2f7c78d3e5661d511e669f70e439/kiwisolver-1.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ed0f5e49d0ceff8b72190824d9e59c062fbbc02c231b853112c78474b3f5ec2", size = 66487, upload-time = "2026-08-28T10:25:55.899Z" }, - { url = "https://files.pythonhosted.org/packages/47/8b/40d33ffd2f378094ed462e9a9a0907e59d4de9845e65a59561272da350d4/kiwisolver-1.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:77a4c8187a5948d7f8795adb765a3c7b553d07d86d88e43038fc32fc1fb9a3f3", size = 64673, upload-time = "2026-08-28T10:25:57.048Z" }, - { url = "https://files.pythonhosted.org/packages/ab/43/86aacc027959108b4c66eeae8b73cedb057dfa6eb3a335d05ad65197081c/kiwisolver-1.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:74ad5c3dad54a4641b4c28cd15ded70899d04459c6c7aeacafea716be97cce6d", size = 1477992, upload-time = "2026-08-28T10:25:58.479Z" }, - { url = "https://files.pythonhosted.org/packages/ed/40/b1d0369048c79733a32c8abb0f2718532e6630641368e33a81384246e844/kiwisolver-1.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e46b23a2da695c364124817bc01d970effd5483147f8d66a6a7167e3f6b851", size = 1278821, upload-time = "2026-08-28T10:26:00.121Z" }, - { url = "https://files.pythonhosted.org/packages/61/a1/fa71c1792272ff9461432715ae60ffb7e11a4d7ac3bf68961b9cab6c60cf/kiwisolver-1.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75d9b1cf8258462dbdc1eeda718c96ea7f079324c09067f6daabfcf37712b7fe", size = 1296805, upload-time = "2026-08-28T10:26:01.868Z" }, - { url = "https://files.pythonhosted.org/packages/a3/49/3f0bd94af8e06ecc47eb834b195a06f05d48711ceb2352c56d6835160f0e/kiwisolver-1.5.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fca690b00c4c48f6c2a547b0160ed511357093a4e4c9b47e0fadf3128066d89", size = 1346109, upload-time = "2026-08-28T10:26:03.59Z" }, - { url = "https://files.pythonhosted.org/packages/a4/44/3afe6ef9cf06d61220953a8963e94eca978491be1d9547cb01d82a1efa08/kiwisolver-1.5.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:876bbfd276473d3daffe30e8c975df4ed9429967b41a6cb362dbb5155b6f13ad", size = 988252, upload-time = "2026-08-28T10:26:05.306Z" }, - { url = "https://files.pythonhosted.org/packages/e4/30/a12bd7a7285a211e1747c3eec77b8c614dbfcc1dad942f7611a1a6921ae5/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f942903fde7363d1d879057ec5de01310efda2597161784d752fa9953a01a71a", size = 2228846, upload-time = "2026-08-28T10:26:07.312Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6b/233e2958abf0dab7b18d07e52f286e02e519d7651bfbbe97af9347564109/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c90d3022d8a94778939cda8638c6c8da8fa757b8958dad7ec868ce29c87681b8", size = 2325583, upload-time = "2026-08-28T10:26:09.093Z" }, - { url = "https://files.pythonhosted.org/packages/42/8e/7673060a27b01405b580058510adef34687069d229800239f5e44682d4d0/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8a34616dc2521cc8dc1d7d081734da63539f021ac0450ce950908340c6e7aa2f", size = 1978221, upload-time = "2026-08-28T10:26:11.127Z" }, - { url = "https://files.pythonhosted.org/packages/3f/7b/1b882fc1a8b4a0bb8084e7d1d85004116d08c92c0705d31f2928dec607f2/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:8bf4df63592c2a66b4f8edc5df2544998c288aa02f96ce0acd880cd1de8c8127", size = 2491819, upload-time = "2026-08-28T10:26:13.348Z" }, - { url = "https://files.pythonhosted.org/packages/39/9c/426deb49e62c5f69464b64bbeca064d3b758a7506b8913d986ef34f4619c/kiwisolver-1.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d09037ca068d784ebc4aec290ef952ca27ac15dd9c0b5801a88c6e1096b83e6b", size = 2295520, upload-time = "2026-08-28T10:26:15.042Z" }, - { url = "https://files.pythonhosted.org/packages/f5/22/deabbb3ad6d918d74b7831b2d8ae7151b09d21c87974e5ee8a456f58c94c/kiwisolver-1.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:dc23390afe9f4ef9ac3bcc72a03a56eebbde03f4c571a32cb38f859cff9a6524", size = 70758, upload-time = "2026-08-28T10:26:16.504Z" }, - { url = "https://files.pythonhosted.org/packages/b9/71/02fa5c2fd92068bb8952847e70ee6c5cb280e7febe11653d17812acc53dd/kiwisolver-1.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:186884a58486651e3c217b6acea0a53eaa9498fdd472057c46f2f0fb5c25aad5", size = 68329, upload-time = "2026-08-28T10:26:17.658Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c4/1407df7512a5b36cc79840e01710dc575733c461b13ab866cae77eaf87f3/kiwisolver-1.5.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:482676e5bd48d70ac99d9fc78863469845421e01184fa83f1f9366dc49f7e974", size = 134002, upload-time = "2026-08-28T10:28:08.881Z" }, - { url = "https://files.pythonhosted.org/packages/16/45/c37a21ad5c0ab581a93c55ad544721aaa1f0ae94edb29c6a678a23d013e6/kiwisolver-1.5.1-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:072bdb15a3c19a5b5dbc8f8fb1f4e1884bf4f3507eeb4cc6334401274d37a5c0", size = 194292, upload-time = "2026-08-28T10:28:11.06Z" }, - { url = "https://files.pythonhosted.org/packages/a6/c1/69f00d627949580e43d57af0aa465df46868d7c29801c137a55374101294/kiwisolver-1.5.1-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:a5a00665d1a0e26763a7338d7e911d4598fbc1d50dd0d6b7919b7dc6c5d6569f", size = 73362, upload-time = "2026-08-28T10:28:12.449Z" }, -] - -[[package]] -name = "libclang" -version = "18.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6e/5c/ca35e19a4f142adffa27e3d652196b7362fa612243e2b916845d801454fc/libclang-18.1.1.tar.gz", hash = "sha256:a1214966d08d73d971287fc3ead8dfaf82eb07fb197680d8b3859dbbbbf78250", size = 39612, upload-time = "2024-03-17T16:04:37.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/49/f5e3e7e1419872b69f6f5e82ba56e33955a74bd537d8a1f5f1eff2f3668a/libclang-18.1.1-1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b2e143f0fac830156feb56f9231ff8338c20aecfe72b4ffe96f19e5a1dbb69a", size = 25836045, upload-time = "2024-06-30T17:40:31.646Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e5/fc61bbded91a8830ccce94c5294ecd6e88e496cc85f6704bf350c0634b70/libclang-18.1.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6f14c3f194704e5d09769108f03185fce7acaf1d1ae4bbb2f30a72c2400cb7c5", size = 26502641, upload-time = "2024-03-18T15:52:26.722Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/1df62b44db2583375f6a8a5e2ca5432bbdc3edb477942b9b7c848c720055/libclang-18.1.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:83ce5045d101b669ac38e6da8e58765f12da2d3aafb3b9b98d88b286a60964d8", size = 26420207, upload-time = "2024-03-17T15:00:26.63Z" }, - { url = "https://files.pythonhosted.org/packages/1d/fc/716c1e62e512ef1c160e7984a73a5fc7df45166f2ff3f254e71c58076f7c/libclang-18.1.1-py2.py3-none-manylinux2010_x86_64.whl", hash = "sha256:c533091d8a3bbf7460a00cb6c1a71da93bffe148f172c7d03b1c31fbf8aa2a0b", size = 24515943, upload-time = "2024-03-17T16:03:45.942Z" }, - { url = "https://files.pythonhosted.org/packages/3c/3d/f0ac1150280d8d20d059608cf2d5ff61b7c3b7f7bcf9c0f425ab92df769a/libclang-18.1.1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:54dda940a4a0491a9d1532bf071ea3ef26e6dbaf03b5000ed94dd7174e8f9592", size = 23784972, upload-time = "2024-03-17T16:12:47.677Z" }, - { url = "https://files.pythonhosted.org/packages/fe/2f/d920822c2b1ce9326a4c78c0c2b4aa3fde610c7ee9f631b600acb5376c26/libclang-18.1.1-py2.py3-none-manylinux2014_armv7l.whl", hash = "sha256:cf4a99b05376513717ab5d82a0db832c56ccea4fd61a69dbb7bccf2dfb207dbe", size = 20259606, upload-time = "2024-03-17T16:17:42.437Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c2/de1db8c6d413597076a4259cea409b83459b2db997c003578affdd32bf66/libclang-18.1.1-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:69f8eb8f65c279e765ffd28aaa7e9e364c776c17618af8bff22a8df58677ff4f", size = 24921494, upload-time = "2024-03-17T16:14:20.132Z" }, - { url = "https://files.pythonhosted.org/packages/0b/2d/3f480b1e1d31eb3d6de5e3ef641954e5c67430d5ac93b7fa7e07589576c7/libclang-18.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:4dd2d3b82fab35e2bf9ca717d7b63ac990a3519c7e312f19fa8e86dcc712f7fb", size = 26415083, upload-time = "2024-03-17T16:42:21.703Z" }, - { url = "https://files.pythonhosted.org/packages/71/cf/e01dc4cc79779cd82d77888a88ae2fa424d93b445ad4f6c02bfc18335b70/libclang-18.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:3f0e1f49f04d3cd198985fea0511576b0aee16f9ff0e0f0cad7f9c57ec3c20e8", size = 22361112, upload-time = "2024-03-17T16:42:59.565Z" }, -] - -[[package]] -name = "loguru" -version = "0.7.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "win32-setctime", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, -] - -[[package]] -name = "lxml" -version = "6.1.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/ad/28ecd7cb894d172f3c9c80a075eeeb2017ac62e3632cee05a5f9493547eb/lxml-6.1.3.tar.gz", hash = "sha256:45222d94ddd511536f3b2f7d9deae3b2339b4ce0f075f1ca25703b07cad9dd21", size = 4211198, upload-time = "2026-09-02T14:48:02.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dd/1f/a180b57d9eeabaab77f9d5aa30356898ea749c4795596a8f66d1eb6bef2e/lxml-6.1.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c0710ac085a157b593c38fbcacd950f15c4afa8e2057527185875ab302752bc", size = 8602094, upload-time = "2026-09-02T14:47:26.054Z" }, - { url = "https://files.pythonhosted.org/packages/a8/25/070c92013a1c029a602b03560d68772313d918268667fa993da7961759c9/lxml-6.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:623c8799c17128753c65699f1c3aa32402657393a9ad6db09ed8b98ddf76611d", size = 4638308, upload-time = "2026-09-02T14:47:29.587Z" }, - { url = "https://files.pythonhosted.org/packages/1e/1c/722e88883173097a1a375153e3c2447eba3060d0231522cf6596e99f4195/lxml-6.1.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f683dc6300317700025e41d89a43e0276692ded16113a3c43eab704d605c58e5", size = 4939696, upload-time = "2026-09-02T14:47:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/db/36/aa413bc214dc4f785ad2b2ddd8cc99aae7062d49ab155e91e6011af00daf/lxml-6.1.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:379f8a75cf6eb7eef0af074b55f49ab73b868388a98de14646abcdfa4564bb11", size = 5105247, upload-time = "2026-09-02T14:47:36.734Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a0/a1f7f1313795bfec67b77f01ef3b1128d49f2d7f66a8413fa55d47f4e25f/lxml-6.1.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b37772102d44bb6628186accca3a121b1fa3a6b3d97518a8c29a5229ca4c0d0a", size = 5011915, upload-time = "2026-09-02T14:47:39.846Z" }, - { url = "https://files.pythonhosted.org/packages/b9/78/840e7e3f1d0cc7a5cfac5d8505b97e25b6427fd774ac4bae672aaebfb4b5/lxml-6.1.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ddcf547bea2aee967d6a77779376a45e77e610e8465147a1f3d7e20d539d6e32", size = 5638175, upload-time = "2026-09-02T14:47:43.644Z" }, - { url = "https://files.pythonhosted.org/packages/0a/20/e022dbc6b4753a9bc9fc5fb28a27163430c1731b9913997f6544c1b2518c/lxml-6.1.3-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:909f4e927bb051f7740d6367285fc60cdcfdaf0258c2dba4ff5ba7eadadc250c", size = 5244675, upload-time = "2026-09-02T14:47:47.635Z" }, - { url = "https://files.pythonhosted.org/packages/99/83/82cde81d2b5eb38d1539fdfdf318abdd014a7e604f4df01c9cd3deb18f2a/lxml-6.1.3-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:a5c18810318303ce9afb3f95e2ddb54834f96fa699a8600433fd5a93dcf44c56", size = 5358205, upload-time = "2026-09-02T14:47:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/d2/a1/f3b057371c8cb29f2a9c9c44ea320592446e40b74a4b0af68c3d8e65bc73/lxml-6.1.3-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:3e42265103fb385d8642a78672edf376c6f7e1d3598a7a4f9cb1278f2f6b5f6f", size = 4704495, upload-time = "2026-09-02T14:47:53.251Z" }, - { url = "https://files.pythonhosted.org/packages/1a/a4/230eb28be5d412152ffc3c679b51fe1aeede5a53f3a8eb6e9748f2f4754f/lxml-6.1.3-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:21402998e4b78e7cce237d2788841aaa21ac9a4d1574d04dc2d12ee41ae807b5", size = 5255117, upload-time = "2026-09-02T14:47:55.963Z" }, - { url = "https://files.pythonhosted.org/packages/a3/18/1969f56763af24ce42ea156007b0b2d73fddea552e283b2010416394f0f4/lxml-6.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:38fc4e4e4e084e0bd491949482527d406788045c546d4f8789e93fc527b91385", size = 5054424, upload-time = "2026-09-02T14:47:58.131Z" }, - { url = "https://files.pythonhosted.org/packages/f4/d4/2a90acc1f6fabaa3a8db9340437822bd8d041b205d626a4b3e8621aaa390/lxml-6.1.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5609efdb0d3c95499c00046bc53648b3482ec2175b5503d6e611b3f0555dc71d", size = 4785572, upload-time = "2026-09-02T14:48:01.029Z" }, - { url = "https://files.pythonhosted.org/packages/a5/1e/b90e845b1dcd0f2f3f26b98283d857f25909223aacd265eee032c34ab8b1/lxml-6.1.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:97ce49699d87ebf8aad631b55d65b33219a4f1bfefbbf5bff19dc9af160aeaf9", size = 5656516, upload-time = "2026-09-02T14:48:03.419Z" }, - { url = "https://files.pythonhosted.org/packages/eb/ab/0a1b802c57f3fba5c4efd77d5c6b78adaa8f7b681f0c90456b140fe8bf6c/lxml-6.1.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:48542c9acba9ff9450bd18d871d2c2c8787fdb283572b623d206f1b927cd7d9e", size = 5245982, upload-time = "2026-09-02T14:48:06.109Z" }, - { url = "https://files.pythonhosted.org/packages/da/ee/2c016fbceb3778137459292538d9dfa7e3ad9070fe409c15254ddd90d2cc/lxml-6.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c55e71a9b1db1f107efb60da49c093689b74c5c31a708e5379e2fd9439d4fbb5", size = 5267340, upload-time = "2026-09-02T14:48:08.374Z" }, - { url = "https://files.pythonhosted.org/packages/9c/b1/736d18fd6f0835761923b7bac1f0c27d60c1200384e9093f05d8c5100525/lxml-6.1.3-cp312-cp312-win32.whl", hash = "sha256:b3ff39654f0ce6ebd4db154211136dbe7e8157bcc3bed2344c87f32c7c6ecb6c", size = 3602606, upload-time = "2026-09-02T14:48:10.384Z" }, - { url = "https://files.pythonhosted.org/packages/3a/5b/6ed903e4e6278a020c8a6f0dbbe78030d041840a6b4a64ea441a1e414077/lxml-6.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:3e9a00d1c2c30936f7add097c41afc5da6556c580909104aafd382cac92a855c", size = 4005999, upload-time = "2026-09-02T14:48:12.51Z" }, - { url = "https://files.pythonhosted.org/packages/e4/1b/7bcebb7b6332cb3ae85e9c13b139adb6f23f75c71d84041c56a5005d9a29/lxml-6.1.3-cp312-cp312-win_arm64.whl", hash = "sha256:1aeca87830c4fe649dcf93fe2b059525b71c72587f21be4ae4af7103082a79fa", size = 3666631, upload-time = "2026-09-02T14:48:14.567Z" }, - { url = "https://files.pythonhosted.org/packages/52/05/3ef45db776baea068044c799bbba68f3ca00a440c0e930a17c572f3d9639/lxml-6.1.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3a48093cdb058a93af842ede9703520e810b05dcd0fc6d7190a06376c3bfb6bd", size = 8590357, upload-time = "2026-09-02T14:48:17.413Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a5/eee2fc77eee5ea68e4a4334b1def1781a3beaeefd3d98e81b4a38dc447b7/lxml-6.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:887c021d9a977cff89cb273047c1352997b772a8908a25c21836861f69b92be1", size = 4632616, upload-time = "2026-09-02T14:48:20.745Z" }, - { url = "https://files.pythonhosted.org/packages/35/42/df27b56848acd29d8a720acc28977911aab36f2a09df4208d5502e887415/lxml-6.1.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:611a51e61c92f62345a50b0035df6fc0d678f9299f33728826d831598862f59d", size = 4936186, upload-time = "2026-09-02T14:48:22.94Z" }, - { url = "https://files.pythonhosted.org/packages/ab/8d/8a7b91df0b54d09d25f5f44885d6b3e0a6d6643a8c070191580318d20c42/lxml-6.1.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b477912f42c5c33405a10c759d22f80cf5af043ae02d95b9d8e5e5bc555739ed", size = 5093324, upload-time = "2026-09-02T14:48:25.132Z" }, - { url = "https://files.pythonhosted.org/packages/c6/7e/8f340ddcd43790332fb0de8a26628d571a492da3300cd191821698407c96/lxml-6.1.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cffe18571ccc51d742cd08cbb3f8b756de9311d18c7ea98f5d92f37b8fb60c2", size = 4998850, upload-time = "2026-09-02T14:48:27.394Z" }, - { url = "https://files.pythonhosted.org/packages/c5/c1/9c5bb572f1f09ec9e4322bd4a4e9f4ad48347fc56ef94cf4df58a5279dc8/lxml-6.1.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75cc6569e86be5785b6188ef1642670c6adbc984e81ec35e224842ecd9eefcc8", size = 5626813, upload-time = "2026-09-02T14:48:29.61Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7d/8bf1fd8bae8247743968bb76d027a1ac5bd2c4b44495fba6a71b30d10706/lxml-6.1.3-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d85dfab42dd672f87a7f76e9de7172962aee69fa12044f0d6e1a23cbd53fb80e", size = 5232385, upload-time = "2026-09-02T14:48:31.969Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2e/6cef69ed81cb7df0d03b0dd09d08e6e2cf5061a743ff6f42f0b741548e9b/lxml-6.1.3-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:42632b4024ab24a6b488f559ac851312509888b6b80ae2aa11cf29a646a0d245", size = 5347088, upload-time = "2026-09-02T14:48:34.13Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e1/8e5fd8ddc8c7d685badb0f2db149e3c9da84eefc2827c01c658df2c4e3cb/lxml-6.1.3-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:febd35ef45f603c2d74b74655efdbf45e14f55fc0aef4ac82b663ca829b283e0", size = 4707227, upload-time = "2026-09-02T14:48:36.62Z" }, - { url = "https://files.pythonhosted.org/packages/7a/7e/00041382a11be40a88bf405ebff11c8efabd3de79f2691e1638b1c47a8a0/lxml-6.1.3-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a43b3bdf11e477dc7770609d3477316f974354dfc8425d596f64f471cc8daf6e", size = 5240208, upload-time = "2026-09-02T14:48:38.893Z" }, - { url = "https://files.pythonhosted.org/packages/fd/fe/316538b5cff0936fa63d45d421c655730fcbb5a28dcac728c175083002bc/lxml-6.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d582042c69857c364e8153de6e18e0da9b7b515a6a8113caf69a6ec8e0520f2", size = 5050271, upload-time = "2026-09-02T14:48:41.213Z" }, - { url = "https://files.pythonhosted.org/packages/c9/91/455bcccb3ac725373007344d351151810cd19762d1673b64b811f4359a42/lxml-6.1.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e49a646acfab83c68974f4aa1d0a2acca9e88d7d627ae0fc13201b14b76d310", size = 4780433, upload-time = "2026-09-02T14:48:43.779Z" }, - { url = "https://files.pythonhosted.org/packages/cb/f6/580440e2f52cf00bba5c5e1080bfa88cdfcde73be71a11d95170ddbb663f/lxml-6.1.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0dee106e9aa97fb00541b1ed7827070564d0549c3d3fba8920e6b20fd980f748", size = 5645928, upload-time = "2026-09-02T14:48:46.187Z" }, - { url = "https://files.pythonhosted.org/packages/f6/dc/d123c1f244306543d545f62443f794959e4f1ea709fe100f8740d514e74a/lxml-6.1.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:dd5e90f34cffcfed97f36cf066325773d2b6021c60c29942e53a18b028501b1d", size = 5231184, upload-time = "2026-09-02T14:48:48.691Z" }, - { url = "https://files.pythonhosted.org/packages/c3/3c/fe55b2bd5c6113c906511cd88f6a470195c5fbff1124f19970ab706c3477/lxml-6.1.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9b3e7d71bf6acff341233417abbdface29c647e3113892d9aaedc02eb4aa2bc", size = 5255814, upload-time = "2026-09-02T14:48:50.948Z" }, - { url = "https://files.pythonhosted.org/packages/e7/a7/485df55acf55dc35e4ca89d2f48f03889e5a3241826b18b85102b32ce9d8/lxml-6.1.3-cp313-cp313-win32.whl", hash = "sha256:160fcf381f76c3aeac28a756bec44f48942a8f7245a87aa28e3a523b4d90cd87", size = 3602214, upload-time = "2026-09-02T14:48:53.236Z" }, - { url = "https://files.pythonhosted.org/packages/c0/28/e46a7702bd95e9043291f7c3539b6184cba66f96cea9936f20939b284eeb/lxml-6.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:e477aca0bc0d19f3b4ae9e4f2a1cfd687c31bf772d78734910658186b40b2477", size = 4004091, upload-time = "2026-09-02T14:48:55.699Z" }, - { url = "https://files.pythonhosted.org/packages/8a/1d/154c78e20479a43916e63f19cb720d83f44f024b03228be44c92d9a97b24/lxml-6.1.3-cp313-cp313-win_arm64.whl", hash = "sha256:b1cc980905221a5d8b3c476330730b3adb40ff80add71ffbdb6215ba055656f1", size = 3665468, upload-time = "2026-09-02T14:48:57.703Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "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", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "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", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "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", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "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", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "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", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "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", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, -] - -[[package]] -name = "mashumaro" -version = "3.22" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/e3/a06dcd2e6df094c5e294721926f1f76da30f50823e2ac233a9198e804891/mashumaro-3.22.tar.gz", hash = "sha256:64538cc365204402a060ebde683a86505b5a4344acf6870d79021e9fbfe57360", size = 197845, upload-time = "2026-05-26T14:39:21.859Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/1c/92fd926c2e7763535454683250dbdd8d10aa2f2c62f58d6abbcce4d8b3fc/mashumaro-3.22-py3-none-any.whl", hash = "sha256:17dc4d7294c33ef380a8b929dda0608577aa2141988c00a0c4932310108fe71d", size = 95916, upload-time = "2026-05-26T14:39:20.233Z" }, -] - -[[package]] -name = "matplotlib" -version = "3.11.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e7/c8/9aa712a0afb882649424dd8de8ad9aa6235e796e84c6052e8f6dc1598d0d/matplotlib-3.11.2.tar.gz", hash = "sha256:cec596316640f2b394b8f0daa0ea61a8eae82d017b620b9f202befb972a59ea4", size = 32660610, upload-time = "2026-09-11T19:05:31.214Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/ce/1bfcc4873b121597791ad74032943b123218c0613af0b97e8dd05e916fb1/matplotlib-3.11.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef752769cd962f39ea0b6ffc82d1ea43a0012c5a6157c7a075212fa509cfcff2", size = 9476466, upload-time = "2026-09-11T19:03:19.45Z" }, - { url = "https://files.pythonhosted.org/packages/a6/c4/7f5f3601ee69baf072c0c7d3ce60c03e0618621c5a56c34a62a460e29d11/matplotlib-3.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ef31985c4dedb5f1424e1aec6849a47dd37689cb7fa3c20b1b82187f26806261", size = 9305583, upload-time = "2026-09-11T19:03:22.39Z" }, - { url = "https://files.pythonhosted.org/packages/b8/90/2b3fd67ee273163faeda6d514be70b5596eaa0fd77b60ffc294ad0b34f5f/matplotlib-3.11.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:df4f7784aca81a94f254c0a2767d592ee25f407e488f5fa7203e51093fb6ca27", size = 9860353, upload-time = "2026-09-11T19:03:25.049Z" }, - { url = "https://files.pythonhosted.org/packages/f4/84/32549e7a462dc311aed2ab62e5d2538028840b5c53a8be0195c789937c3d/matplotlib-3.11.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b9a7ad579856284135e401ecc918c5f8a017ee30539298862a109f51b971710", size = 10670348, upload-time = "2026-09-11T19:03:28.108Z" }, - { url = "https://files.pythonhosted.org/packages/84/39/02e21b74f7439bd643d717ea846006d46e309e6d29b632b57751265311d6/matplotlib-3.11.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3aa4b8516fd26659e4363abbf317c703d9116496c5db2e9d0609a2866dd39dd2", size = 10810580, upload-time = "2026-09-11T19:03:31.402Z" }, - { url = "https://files.pythonhosted.org/packages/04/93/0d239bde12b308262265c2d98909b2f0ecad2b5deeae96241642e822e9c7/matplotlib-3.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:c5c1c68ee401fc98271263410f0e5ce88285abacf7627132914e8adf3d70ff43", size = 9349409, upload-time = "2026-09-11T19:03:34.195Z" }, - { url = "https://files.pythonhosted.org/packages/49/a8/06baf901c02246c8a222b655cc4540ef9c15e1549a04e07927f9cde716c3/matplotlib-3.11.2-cp312-cp312-win_arm64.whl", hash = "sha256:643ff850d8e0f5b8319337f87ed3cb59506afb3df3cc48de777d85871233be7b", size = 9028084, upload-time = "2026-09-11T19:03:37.032Z" }, - { url = "https://files.pythonhosted.org/packages/bf/ce/fc58855a3d17f6cb8a87b6f9ccd100f65432a3a7ffed57309cbf14c78987/matplotlib-3.11.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d43ff8cebb50840648cb6429b2228621dbd709010f3117b3740abb20abf21c0", size = 9476987, upload-time = "2026-09-11T19:03:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/a9/29/2a47b700545aca050e0ed73c89ba21e08407e11f5f42a567421b220de6c1/matplotlib-3.11.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2a8285cea8ef4d92aa041d1c33788bcae82248503300f93f1ca2136b9049452f", size = 9306050, upload-time = "2026-09-11T19:03:43.103Z" }, - { url = "https://files.pythonhosted.org/packages/a9/df/e7c10292f348cf55178a5b3c0092a38ea68891a3945f199ae8b7b8ac9dc1/matplotlib-3.11.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1944895967f87c84c9b4bad29a707b31f5b36df4a3a2339ea1f8ea3ce5105539", size = 9861060, upload-time = "2026-09-11T19:03:46.044Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b9/b978de6d43d47f1d4d8e653fac32c16d8d2e5f46bbbb822e26d7cb1adece/matplotlib-3.11.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af2661f6ac6bbd1d081996f54fdd9385715c625ace8cec0869055c0cfbf38981", size = 10670885, upload-time = "2026-09-11T19:03:48.942Z" }, - { url = "https://files.pythonhosted.org/packages/a3/80/d72f61bb2631ac15d17fada1f17e0f99fecba69d8376d34f17cf93b7eddd/matplotlib-3.11.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e5a90f8a707ebb6004a713b1cff091a40cc5df4c7c1cb165e5a505ebc11c292", size = 10811222, upload-time = "2026-09-11T19:03:51.665Z" }, - { url = "https://files.pythonhosted.org/packages/94/2e/6d1d33f242c705b5c6c6fb3f0fc141add51cb1abb62912e59de0835d28c4/matplotlib-3.11.2-cp313-cp313-win_amd64.whl", hash = "sha256:bc067c462a86f0e57bf52fc6e058d90171a5420007dac00c46e90153050c69a7", size = 9349539, upload-time = "2026-09-11T19:03:54.534Z" }, - { url = "https://files.pythonhosted.org/packages/bb/bb/c0aa86a7f4c0e62f1c3c0711e361f6f23f87eb9018732a5e11994bd1b7ec/matplotlib-3.11.2-cp313-cp313-win_arm64.whl", hash = "sha256:3e8576f7c47e02fd4f21f44171302d2d1d58d4471d46da3d71fe8899d19539d9", size = 9028295, upload-time = "2026-09-11T19:03:57.261Z" }, -] - -[[package]] -name = "mdit-py-plugins" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/59/fc/f8d0863f8862f25602c0404d75568e89fb6b4109804645e5cdfb1be5cf56/mdit_py_plugins-0.6.1.tar.gz", hash = "sha256:a2bca0f039f39dbd35fb74ae1b5f998608c437463371f0ff7f49a19a17a114d0", size = 56114, upload-time = "2026-05-13T09:03:38.91Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/69/6da5581c6a7fede7dc261bf4e67d6adca4196f176b43288b55b3db395b6e/mdit_py_plugins-0.6.1-py3-none-any.whl", hash = "sha256:214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d", size = 66663, upload-time = "2026-05-13T09:03:37.76Z" }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, -] - -[[package]] -name = "minijinja" -version = "2.24.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/18/2dbb84297175b77b5eb497e07e66e71c71b8073bdbe3d3cb1d3309823d66/minijinja-2.24.0.tar.gz", hash = "sha256:8e9dc11bd5c7a20869c42d6c7c0caa5cd437c62a00a042f65df20036f0def93c", size = 299288, upload-time = "2026-08-31T19:28:46.351Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/16/9170db3a85bf7fed14c5281e13927d310385e5fd945518dda80e2ec0a631/minijinja-2.24.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f286106f9d2ffd73702a470cc4d9262b1588eb48916fc84b98d19df4e3064bd4", size = 2174596, upload-time = "2026-08-31T19:28:25.642Z" }, - { url = "https://files.pythonhosted.org/packages/82/ec/9a1e7513d7e18ff11907eb6ae92f060f2abd85e0df029625816b8f469eb4/minijinja-2.24.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e252fa4c59cff1794dfffb5349da6a4885de97aaaf2fbc6983becc1eb973e9", size = 1140515, upload-time = "2026-08-31T19:28:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/66/0c/835223a549b924b1bf9a7a2e6723783342a05896c90cf49a10bbef72a253/minijinja-2.24.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:94964dca64c80b2a85b598037ab07f01a794d238c66d5556d01c4f400b630fd0", size = 1115064, upload-time = "2026-08-31T19:28:29.634Z" }, - { url = "https://files.pythonhosted.org/packages/ad/ed/14056592c0008a0945e96966c5e2b215579d6f3487032d7ba2d8ad2e9c12/minijinja-2.24.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5a4dbd73f2c02ab6d0ab0b62b68a7d2557e5d45723d3b4e423b7c3877f31cad", size = 1178991, upload-time = "2026-08-31T19:28:31.538Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6b/71ca5f679c03a67af5d551b32e306c6bb87aa7157767c3a7a678323d748f/minijinja-2.24.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d633999cb06509ba3de9a0e5798f97d5ba5bc0d337f9d8f8ea02f349050cbd", size = 1312790, upload-time = "2026-08-31T19:28:33.473Z" }, - { url = "https://files.pythonhosted.org/packages/56/13/3c7c6f34c41eece458f4f416dbab229024502173de780ee978ef38a4c605/minijinja-2.24.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:90cc9e1196d98e56fdc87b69a5949bc76ecbbc512c7654081483c5acabf8ca8f", size = 1308284, upload-time = "2026-08-31T19:28:36.272Z" }, - { url = "https://files.pythonhosted.org/packages/2e/c9/bbcf76a1bd02a503c2d05f996d6271358c77bab68893689d3bd14514fdab/minijinja-2.24.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:194364c0a060606e96ae635cd997c7e014b4ef026d12c02fe9f8912749622481", size = 1381830, upload-time = "2026-08-31T19:28:37.994Z" }, - { url = "https://files.pythonhosted.org/packages/20/91/980d6b2572a0985ec033c1b93bb8eea6be87f920a7cab2097ef740894c61/minijinja-2.24.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:6c2106a399eaff009ebf308ccebed397787ff4442159de9598543298f3eee522", size = 1463332, upload-time = "2026-08-31T19:28:39.749Z" }, - { url = "https://files.pythonhosted.org/packages/92/98/75d6a76499fd40f839a0a1a8fc46aa43cf6c47ea9680d4174289fd6fec3a/minijinja-2.24.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5e0cdc27520d2163ec8a2078e4ff20c3afff4fb89eb05d21679c3facd30369ab", size = 1388664, upload-time = "2026-08-31T19:28:41.421Z" }, - { url = "https://files.pythonhosted.org/packages/7d/f1/e8d82609d7aad5791d52fdec4c642fab46974094cef9102110efa6fc2713/minijinja-2.24.0-cp38-abi3-win32.whl", hash = "sha256:b68aa1bd72da3a6175afa721090592f19e637b7a02408f796298d342b709d06b", size = 1058094, upload-time = "2026-08-31T19:28:43.157Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e5/d86c8c706b3820fc6f58baa0f7d322e78885843ddba7e9cba77985a3fbc7/minijinja-2.24.0-cp38-abi3-win_amd64.whl", hash = "sha256:541959f4529017e437b8822cdaabaaab9d4090c140138dec40dbd739b3b0f778", size = 1118117, upload-time = "2026-08-31T19:28:44.843Z" }, -] - -[[package]] -name = "mlx-traceability" -version = "11.8.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "matplotlib" }, - { name = "natsort" }, - { name = "python-decouple" }, - { name = "requests" }, - { name = "sphinx" }, - { name = "sphinxcontrib-jquery" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/ea/110e5ef3b5656b1ebf7e74cee598e3270713a7e72db734b5a4d1f6b89c51/mlx_traceability-11.8.3.tar.gz", hash = "sha256:2ef56e6f2d4d1d32592f746b9d5bd194abe7610fd55ae55258d223bac108b5ab", size = 70594, upload-time = "2026-02-24T14:07:44.631Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/70/2dd58eefbfbeb578a33045aed7541bd15308f215925e9aecbe9b84a447a7/mlx_traceability-11.8.3-py3-none-any.whl", hash = "sha256:49507b2301e18b037037b7e893dc7a457cccf81e8fca656e482b6320933568e2", size = 81101, upload-time = "2026-02-24T14:07:43.359Z" }, -] - -[[package]] -name = "multivolumefile" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/50/f0/a7786212b5a4cb9ba05ae84a2bbd11d1d0279523aea0424b6d981d652a14/multivolumefile-0.2.3.tar.gz", hash = "sha256:a0648d0aafbc96e59198d5c17e9acad7eb531abea51035d08ce8060dcad709d6", size = 77984, upload-time = "2021-04-29T12:18:39.882Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/31/ec5f46fd4c83185b806aa9c736e228cb780f13990a9cf4da0beb70025fcc/multivolumefile-0.2.3-py3-none-any.whl", hash = "sha256:237f4353b60af1703087cf7725755a1f6fcaeeea48421e1896940cd1c920d678", size = 17037, upload-time = "2021-04-29T12:18:38.886Z" }, -] - -[[package]] -name = "myst-parser" -version = "4.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "jinja2" }, - { name = "markdown-it-py" }, - { name = "mdit-py-plugins" }, - { name = "pyyaml" }, - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, -] - -[[package]] -name = "natsort" -version = "8.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload-time = "2023-06-20T04:17:19.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload-time = "2023-06-20T04:17:17.522Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, - { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, - { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, - { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, - { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, - { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, - { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, - { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, - { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, - { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, - { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, - { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, - { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, - { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, - { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, - { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, - { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, - { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, - { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, - { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, - { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, - { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, - { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, - { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, - { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, - { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, -] - -[[package]] -name = "object-deps-report" -version = "0.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "clanguru" }, - { name = "py-app-dev" }, - { name = "typer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1f/de/cf9b2fc0a535aa3cacc3bd21bd5d93da96edd4f0211f34d201ad6236097d/object_deps_report-0.0.1.tar.gz", hash = "sha256:e97d349a385081503fd6254ed55c833e7f5677ecf923b5a53e0a69710a4c083e", size = 7457, upload-time = "2026-08-03T12:17:30.967Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/c6/fe5277fd033af3bd5d37c40a179703f440c0d33132aaf464fc5370d68c03/object_deps_report-0.0.1-py3-none-any.whl", hash = "sha256:45c559bd01432e54e8121c6eb227ab755e4b046af71e3ee8077ae3412f00bbe2", size = 9480, upload-time = "2026-08-03T12:17:29.871Z" }, -] - -[[package]] -name = "openpyxl" -version = "3.1.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "et-xmlfile" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, -] - -[[package]] -name = "packaging" -version = "26.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, -] - -[[package]] -name = "pillow" -version = "12.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, - { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, - { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, - { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, - { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, - { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, - { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, - { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, - { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, - { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, - { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, - { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, - { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, - { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, - { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, - { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, - { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, - { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, -] - -[[package]] -name = "pip" -version = "26.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/15/4500e320e6b101ec3b719ae85b697d9940b6cda672bc555bd6016fc60c6f/pip-26.2.1.tar.gz", hash = "sha256:f6ad667e89a1fe78046c8f13232b247200f5258d7828f3f7883d660878e0813f", size = 1848877, upload-time = "2026-08-04T22:51:14.148Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6e/1736e5b4ae2b778ef2f81c47d797de9f891d4d8acb047a24ca37a60294dd/pip-26.2.1-py3-none-any.whl", hash = "sha256:71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e", size = 1816632, upload-time = "2026-08-04T22:51:12.472Z" }, -] - -[[package]] -name = "pip-system-certs" -version = "5.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pip" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/6a/563b05a4f6c9ddc205c98bb413e74221368efb98b8fb9cca96b578b8930c/pip_system_certs-5.3.tar.gz", hash = "sha256:19c8bf9957bcce7d69c4dbc2d0b2ef13de1984d53f50a59012e6dbbad0af67c6", size = 6395, upload-time = "2025-10-16T06:14:55.217Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/57/752b63c609affae8f26ae0f1d1103d6ea7e707ad45943f62f7422936071d/pip_system_certs-5.3-py3-none-any.whl", hash = "sha256:3fbb5de62e374a99b688b1ad06e64ee5c4aeb633ef23e3a677d32e3e84fd863c", size = 6896, upload-time = "2025-10-16T06:14:54.072Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "poks" -version = "0.11.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitpython" }, - { name = "mashumaro" }, - { name = "pip-system-certs" }, - { name = "py-app-dev" }, - { name = "py7zr" }, - { name = "requests" }, - { name = "typer" }, - { name = "zstandard" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a4/20/56ebe9b8b7607e775a5c8a1977ecb4555346a95f27212d329a642e1ce550/poks-0.11.1.tar.gz", hash = "sha256:1248c470d6bc7a97ea375e7afb774d3956c7c3ec51f6d11d6bfd7f3debd904af", size = 25655, upload-time = "2026-03-22T14:56:57.159Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/e8/9677a57ef26c79dad2056c79758170c085fc5387be5920387bc6420b1f4d/poks-0.11.1-py3-none-any.whl", hash = "sha256:d3ede0a3d449ba456510be03d91c554f96ff8cd55e08b5494e2d2942ab54070e", size = 27790, upload-time = "2026-03-22T14:56:56.022Z" }, -] - -[[package]] -name = "psutil" -version = "7.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, - { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, - { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, - { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, - { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, - { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, - { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, - { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, - { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, - { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, -] - -[[package]] -name = "py-app-dev" -version = "2.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "loguru" }, - { name = "mashumaro" }, - { name = "psutil" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/e6/8a14ebf396ebb644d75ad402a25692f0b36cbfdbae00842cca03936bb00e/py_app_dev-2.23.0.tar.gz", hash = "sha256:51864d8875f9b61944e567e301301645c4c77cdeacd182e1474618d69ac327b0", size = 24611, upload-time = "2026-09-15T08:36:59.355Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/22/17b7ca1df048647373e9381db30878631bfd4e64ace03f96d019da74368a/py_app_dev-2.23.0-py3-none-any.whl", hash = "sha256:e8a8c589818ea75b6d5a3076ab1f2cc90b9c40696f4879290c5a8c8e181ee775", size = 28595, upload-time = "2026-09-15T08:36:57.974Z" }, -] - -[[package]] -name = "py7zr" -version = "1.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backports-zstd" }, - { name = "brotli", marker = "platform_python_implementation == 'CPython'" }, - { name = "brotlicffi", marker = "platform_python_implementation == 'PyPy'" }, - { name = "inflate64" }, - { name = "multivolumefile" }, - { name = "psutil", marker = "sys_platform != 'cygwin'" }, - { name = "pybcj" }, - { name = "pycryptodomex" }, - { name = "pyppmd" }, - { name = "texttable" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/59/3f/ac248f25f3901d3d6cf80ac99d5bac6fd1e5e6543c1812c79b02b6074c60/py7zr-1.1.3.tar.gz", hash = "sha256:8d51894abb38355bf14881088bb97f01fe4cb5b14ebe22f66d6297668c7e1a74", size = 71695, upload-time = "2026-06-19T09:28:29.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/40/dc7f804abd0ea167ea8d4d9b3f5455321aaf671d040fcef647e9e952e714/py7zr-1.1.3-py3-none-any.whl", hash = "sha256:17934a35089e026dec6c72ee275d9b841e646881ef822d618e805c3006661d9a", size = 72242, upload-time = "2026-06-19T09:28:27.512Z" }, -] - -[[package]] -name = "pybcj" -version = "1.0.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/14/c9/e2de4f98c21f2ff9e58047395b6a8e17701a0f2977f0e63f80948f61647f/pybcj-1.0.8.tar.gz", hash = "sha256:6818270692912d47e81ccd777b7d0ed2ad3a19ef7aaead3735b38048c80bf368", size = 31763, upload-time = "2026-07-16T22:47:24.705Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/32/63b96b702fc4848834ce552302fbdccad92f67886eaf3fbbed530569f4a1/pybcj-1.0.8-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:748af4545040da68dfae8cdf9f902ac7c29f209aaf6450a970abe0302d1b0f58", size = 32798, upload-time = "2026-07-16T22:46:40.083Z" }, - { url = "https://files.pythonhosted.org/packages/a2/21/0b501901021499e41afe7d0148c94ee26dcf045d3e1389bf1b42a61d11a6/pybcj-1.0.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:190e21c3bdb6b28c63999889668a3f80f5d7eb13b957f2dfda53c9ddeb3d946a", size = 24673, upload-time = "2026-07-16T22:46:41.288Z" }, - { url = "https://files.pythonhosted.org/packages/b3/97/c05d5800d742f5162bd06ef790dfcf73128431328f16abe7a0e2109d8ca5/pybcj-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:384b25dfe66868329a3872c6eae668033e009e4e86754eba3a70b656c9eb4208", size = 24958, upload-time = "2026-07-16T22:46:42.469Z" }, - { url = "https://files.pythonhosted.org/packages/05/0f/9db13432a6f8878f90c108a68fda46e832eea53585cb5dee8bdbc9472467/pybcj-1.0.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b480efcd66cfb4533ede6c3d67224ff477c8dd661613826edff9c95921a562d6", size = 53297, upload-time = "2026-07-16T22:46:43.93Z" }, - { url = "https://files.pythonhosted.org/packages/76/59/5629811698e6cfaea6cf159cf86fa471904ceb60448dc8b80233bba2e392/pybcj-1.0.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a50b58f9b9d9978c55e3219b6816a1021a778b028a810b36aa754407591cb5ae", size = 52332, upload-time = "2026-07-16T22:46:45.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/0a/c5bd982957b5fd785f02dffcb3f6f45a93143e1e8ccc8224e19b3915fc84/pybcj-1.0.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1997badf9e59221470f84e193ac8c1bab158acfe6b058ffdc28c331807967ff9", size = 51192, upload-time = "2026-07-16T22:46:46.674Z" }, - { url = "https://files.pythonhosted.org/packages/7a/79/1d421d2d45e565a0f8eab8ce6f795e31beca96f658c1e483dac64a466a28/pybcj-1.0.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f0818cf48fbfddbae487536f3fcd69cfadfb7293630ab8658b66e89a0836d6a", size = 50849, upload-time = "2026-07-16T22:46:48.006Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c3/daddd6edc01689b1cb75733bd3098231bae20c9e77d7022dc0b1c40e56fd/pybcj-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:26e3e4d287f306996902289fc0e88b36d0fc817802f4fc4537b40a6518726585", size = 24956, upload-time = "2026-07-16T22:46:49.159Z" }, - { url = "https://files.pythonhosted.org/packages/a7/fc/9c668e2cd1432f7ede31bc131ab336c8602659374bc731bfaca6cf208bbe/pybcj-1.0.8-cp312-cp312-win_arm64.whl", hash = "sha256:82d2e46eff6c3fe8d16f6515f9ab0598eb960af9a2218907c5e0e9f993d7a666", size = 24251, upload-time = "2026-07-16T22:46:50.33Z" }, - { url = "https://files.pythonhosted.org/packages/25/c1/9d8822f1b97813f21b5907b351edb0f607c9736e21637812edf407a2dae6/pybcj-1.0.8-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f0e496c49dce294fb41a2221e7b3f96156bb940d8cfe359d441909f1cf241ea", size = 32799, upload-time = "2026-07-16T22:46:51.625Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8b/248154c815e683c0f991c471bedb7476d7c91fd8a56918f1ef66d2341ea5/pybcj-1.0.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0657c9534d480a43b11743cad4209d140d09d417adaf19213ff395686ed08812", size = 24673, upload-time = "2026-07-16T22:46:52.799Z" }, - { url = "https://files.pythonhosted.org/packages/e0/f7/f168c7b091fc41fc7ef6d9ed6cde2ec10bb4814f14ad21b493881476636e/pybcj-1.0.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:784443d0c075871689692679ef01cfe85eca789cc8d091f842fa02f62dead9d5", size = 24955, upload-time = "2026-07-16T22:46:54.069Z" }, - { url = "https://files.pythonhosted.org/packages/cc/a1/7b802ee7c7730521a102dadbdf944943795b3e9ca3ca665dbbdd1213f781/pybcj-1.0.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9195ce382f21e13df7ecc0df8fb42c5f391a7ac516066af16b023e6bd2f86046", size = 53344, upload-time = "2026-07-16T22:46:55.272Z" }, - { url = "https://files.pythonhosted.org/packages/4b/cf/c4b0248051cdb7af7f38223db63427175e755db1f1a7905e781e923f6d53/pybcj-1.0.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e5e67ec32176985bbf941185319a3fe385527b6826d457a8ec67dd43b5f0b5fa", size = 52410, upload-time = "2026-07-16T22:46:56.541Z" }, - { url = "https://files.pythonhosted.org/packages/52/77/9afed44c590a19940e04fdf38b7f55e7db3a8b35e3f6320340d99be25ca8/pybcj-1.0.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:20b657e4fe3e701a0f64a7c1224694820687334b17593d74a878f7124f10b24f", size = 51282, upload-time = "2026-07-16T22:46:57.748Z" }, - { url = "https://files.pythonhosted.org/packages/10/4b/7eaf8cf0ad598cc2035a24186a3e2bf45ec798279dad58484d0eb940d4b3/pybcj-1.0.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5b4c66b5358cd241b6ff81a8fafd5806e816d378c810a796ad51865be5533f7", size = 50935, upload-time = "2026-07-16T22:46:58.94Z" }, - { url = "https://files.pythonhosted.org/packages/39/f4/04d46f5059af00d2fae6086a7f47ebc23e553900ca75ea2a2e276d3a436c/pybcj-1.0.8-cp313-cp313-win_amd64.whl", hash = "sha256:e47d9c655d276200e5d08f7b550097dacc826364b44931f0fbdc0da2e4bdf448", size = 24956, upload-time = "2026-07-16T22:47:00.127Z" }, - { url = "https://files.pythonhosted.org/packages/63/cc/387af6844cdc53be20a9271998bc4a15a96360f06ce8829193d4a45f6b19/pybcj-1.0.8-cp313-cp313-win_arm64.whl", hash = "sha256:f561988aef99f07e991d0708ca6fe86e766d545c0a20e58bdbd38615a615e419", size = 24249, upload-time = "2026-07-16T22:47:01.28Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pycryptodomex" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c9/85/e24bf90972a30b0fcd16c73009add1d7d7cd9140c2498a68252028899e41/pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da", size = 4922157, upload-time = "2025-05-17T17:23:41.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/00/10edb04777069a42490a38c137099d4b17ba6e36a4e6e28bdc7470e9e853/pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886", size = 2498764, upload-time = "2025-05-17T17:22:21.453Z" }, - { url = "https://files.pythonhosted.org/packages/6b/3f/2872a9c2d3a27eac094f9ceaa5a8a483b774ae69018040ea3240d5b11154/pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d", size = 1643012, upload-time = "2025-05-17T17:22:23.702Z" }, - { url = "https://files.pythonhosted.org/packages/70/af/774c2e2b4f6570fbf6a4972161adbb183aeeaa1863bde31e8706f123bf92/pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa", size = 2187643, upload-time = "2025-05-17T17:22:26.37Z" }, - { url = "https://files.pythonhosted.org/packages/de/a3/71065b24cb889d537954cedc3ae5466af00a2cabcff8e29b73be047e9a19/pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8", size = 2273762, upload-time = "2025-05-17T17:22:28.313Z" }, - { url = "https://files.pythonhosted.org/packages/c9/0b/ff6f43b7fbef4d302c8b981fe58467b8871902cdc3eb28896b52421422cc/pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5", size = 2313012, upload-time = "2025-05-17T17:22:30.57Z" }, - { url = "https://files.pythonhosted.org/packages/02/de/9d4772c0506ab6da10b41159493657105d3f8bb5c53615d19452afc6b315/pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314", size = 2186856, upload-time = "2025-05-17T17:22:32.819Z" }, - { url = "https://files.pythonhosted.org/packages/28/ad/8b30efcd6341707a234e5eba5493700a17852ca1ac7a75daa7945fcf6427/pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006", size = 2347523, upload-time = "2025-05-17T17:22:35.386Z" }, - { url = "https://files.pythonhosted.org/packages/0f/02/16868e9f655b7670dbb0ac4f2844145cbc42251f916fc35c414ad2359849/pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462", size = 2272825, upload-time = "2025-05-17T17:22:37.632Z" }, - { url = "https://files.pythonhosted.org/packages/ca/18/4ca89ac737230b52ac8ffaca42f9c6f1fd07c81a6cd821e91af79db60632/pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328", size = 1772078, upload-time = "2025-05-17T17:22:40Z" }, - { url = "https://files.pythonhosted.org/packages/73/34/13e01c322db027682e00986873eca803f11c56ade9ba5bbf3225841ea2d4/pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708", size = 1803656, upload-time = "2025-05-17T17:22:42.139Z" }, - { url = "https://files.pythonhosted.org/packages/54/68/9504c8796b1805d58f4425002bcca20f12880e6fa4dc2fc9a668705c7a08/pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4", size = 1707172, upload-time = "2025-05-17T17:22:44.704Z" }, - { url = "https://files.pythonhosted.org/packages/dd/9c/1a8f35daa39784ed8adf93a694e7e5dc15c23c741bbda06e1d45f8979e9e/pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6", size = 2499240, upload-time = "2025-05-17T17:22:46.953Z" }, - { url = "https://files.pythonhosted.org/packages/7a/62/f5221a191a97157d240cf6643747558759126c76ee92f29a3f4aee3197a5/pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545", size = 1644042, upload-time = "2025-05-17T17:22:49.098Z" }, - { url = "https://files.pythonhosted.org/packages/8c/fd/5a054543c8988d4ed7b612721d7e78a4b9bf36bc3c5ad45ef45c22d0060e/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587", size = 2186227, upload-time = "2025-05-17T17:22:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a9/8862616a85cf450d2822dbd4fff1fcaba90877907a6ff5bc2672cafe42f8/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c", size = 2272578, upload-time = "2025-05-17T17:22:53.676Z" }, - { url = "https://files.pythonhosted.org/packages/46/9f/bda9c49a7c1842820de674ab36c79f4fbeeee03f8ff0e4f3546c3889076b/pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c", size = 2312166, upload-time = "2025-05-17T17:22:56.585Z" }, - { url = "https://files.pythonhosted.org/packages/03/cc/870b9bf8ca92866ca0186534801cf8d20554ad2a76ca959538041b7a7cf4/pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003", size = 2185467, upload-time = "2025-05-17T17:22:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/96/e3/ce9348236d8e669fea5dd82a90e86be48b9c341210f44e25443162aba187/pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744", size = 2346104, upload-time = "2025-05-17T17:23:02.112Z" }, - { url = "https://files.pythonhosted.org/packages/a5/e9/e869bcee87beb89040263c416a8a50204f7f7a83ac11897646c9e71e0daf/pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd", size = 2271038, upload-time = "2025-05-17T17:23:04.872Z" }, - { url = "https://files.pythonhosted.org/packages/8d/67/09ee8500dd22614af5fbaa51a4aee6e342b5fa8aecf0a6cb9cbf52fa6d45/pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c", size = 1771969, upload-time = "2025-05-17T17:23:07.115Z" }, - { url = "https://files.pythonhosted.org/packages/69/96/11f36f71a865dd6df03716d33bd07a67e9d20f6b8d39820470b766af323c/pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9", size = 1803124, upload-time = "2025-05-17T17:23:09.267Z" }, - { url = "https://files.pythonhosted.org/packages/f9/93/45c1cdcbeb182ccd2e144c693eaa097763b08b38cded279f0053ed53c553/pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51", size = 1707161, upload-time = "2025-05-17T17:23:11.414Z" }, -] - -[[package]] -name = "pydantic" -version = "2.13.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "pydantic-core" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/53/ef/fc4f868f4e2cee79f863883abffceff107875f569b848507319842d2a681/pydantic-2.13.5.tar.gz", hash = "sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08", size = 845750, upload-time = "2026-08-28T14:04:00.916Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/47/c95ffc2009878c7aac0c5e08528022dcb885933252a88b5f170058014464/pydantic-2.13.5-py3-none-any.whl", hash = "sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73", size = 472589, upload-time = "2026-08-28T14:03:59.136Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.46.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/af/f9/8a06bea35ef8daf588f707784c973a7046e0034c8d8cfb08828eeffb8b75/pydantic_core-2.46.5.tar.gz", hash = "sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc", size = 472262, upload-time = "2026-08-28T10:01:31.677Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3f/76358795aa7a8c6d4f36e2cb828ad1c90ee118e1393a9281664f5aade9d4/pydantic_core-2.46.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b9fe6fb92520e3fd61f2e49000b6911b188824f089b75973ea06d6267f0b476d", size = 2076516, upload-time = "2026-08-28T09:58:21.576Z" }, - { url = "https://files.pythonhosted.org/packages/db/50/26b091836076ce4cb2fac264186936acc069e0595772cfd02a563bc4761a/pydantic_core-2.46.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a39ac25a9a2fa4072efdb429833c4a4c8009a51ff9eea3eeae131713cd27991e", size = 1922874, upload-time = "2026-08-28T09:58:23.766Z" }, - { url = "https://files.pythonhosted.org/packages/09/f0/2a8ce3849e299d44e2d2c196b6082643a3235565a735cb51db7a6261f614/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fdc8b93a41521988916eeaa271173fcca7fa0803d62f87675aac8dcec1c8e29", size = 1951772, upload-time = "2026-08-28T09:58:25.435Z" }, - { url = "https://files.pythonhosted.org/packages/87/46/ac0dc8bdd9e6048183a14eb127764e7ad9240021c17513074a4711b0e31e/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b98134087d9de723658d17a42c7d0da8d6e2ef08015dee7dc93889047315f5e4", size = 2031832, upload-time = "2026-08-28T09:58:27.102Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c2/339de5bef7be36301a2231eaa52e62163742c2281f11b5f4892bc79785cd/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e652ab17569c94bff5475520f907b7148b8c24036a8ebbe5cf7cf7493d28579a", size = 2208645, upload-time = "2026-08-28T09:58:28.948Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a0/9ff22b797724262da14427abaed4dd1d864a139693fc5e7809114376a716/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d925f3d9afd05a8c0fb3a1031463a8d59ebe5e2afad297e29c78be19e13b4e62", size = 2265935, upload-time = "2026-08-28T09:58:30.625Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a4/eb9409ec0736e50aa70a412f16c204ed149516846912f7e6724d4c73ee53/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2", size = 2066284, upload-time = "2026-08-28T09:58:32.289Z" }, - { url = "https://files.pythonhosted.org/packages/c0/02/7f6156ffc926857f1c37c07d9a388682865a81830ab6a1b637082c25e399/pydantic_core-2.46.5-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:816ff0a6550ffc06c098ccd2e0698600f9aa7da192a79eaa6f9af504a35db869", size = 2105889, upload-time = "2026-08-28T09:58:33.986Z" }, - { url = "https://files.pythonhosted.org/packages/92/b1/e781d357ebe09fc929f995700f1b3503e8897f1cece183ecb1300d4d67e9/pydantic_core-2.46.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c7ea57fc63aa7da93a1bd2d644e6577befae10c52c4e36377635eea1056a74f5", size = 2158006, upload-time = "2026-08-28T09:58:35.647Z" }, - { url = "https://files.pythonhosted.org/packages/70/0a/644597d84ab400e50609c192120b85c9681c22d3a20461b9060a79be0a7a/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:efd62a42486f1bda5d24cb4f63d15a3c7768375fe83d36f9417b4ad7a2fb20b3", size = 2158408, upload-time = "2026-08-28T09:58:37.38Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ee/ca3b7b3a4b3769ffe9ce9432a7c9be755de9593a46d3b0d54d0409323e44/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2bc9419666990c06d7397831f2126a1ecc3594aaa3ff7de5bf2d066802f4e07b", size = 2309609, upload-time = "2026-08-28T09:58:39.22Z" }, - { url = "https://files.pythonhosted.org/packages/ce/52/39fa1f451486019524ca685020390e7ca351832fd874530ba30c8628e6dc/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:18a09e1e1011b462f2e32774f25859ef1223d5c2b0546a633cf56654710721e0", size = 2342618, upload-time = "2026-08-28T09:58:40.89Z" }, - { url = "https://files.pythonhosted.org/packages/81/5e/468fc630568c61dcef3cd47ad32ffbeed9af643f49208d1ea86ab4f890c4/pydantic_core-2.46.5-cp312-cp312-win32.whl", hash = "sha256:5cb482e9e84c851f4e623fe4acc1ced89168cf1fe18f7089db4548c8f5bbb65b", size = 1939475, upload-time = "2026-08-28T09:58:42.591Z" }, - { url = "https://files.pythonhosted.org/packages/cf/c9/4c19f41b84cf6b622a72fbeed7665b25d47a187d68d47d0d430c07f23268/pydantic_core-2.46.5-cp312-cp312-win_amd64.whl", hash = "sha256:5e81740c09e310f5aa5cbd3e434a01c154d4bef93241c7877b39f211d2b78ba8", size = 2043140, upload-time = "2026-08-28T09:58:44.272Z" }, - { url = "https://files.pythonhosted.org/packages/af/dd/0c1a050299147c746e5256db16d645ab5efd4f78c59937d581a0524e74a2/pydantic_core-2.46.5-cp312-cp312-win_arm64.whl", hash = "sha256:f7b0ec93a2893de856652154d73b7ba622f26fa97726487dcac373de5f4c6084", size = 1997729, upload-time = "2026-08-28T09:58:46.13Z" }, - { url = "https://files.pythonhosted.org/packages/f5/37/5abe39a8372a61d3dc3c1338fc504281c01b32fdb3169cd7187153b56d3e/pydantic_core-2.46.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0", size = 2075885, upload-time = "2026-08-28T09:58:47.856Z" }, - { url = "https://files.pythonhosted.org/packages/21/43/6323b1f8b217780454c61304bcd2b38ae4762f50754414124603ccc90bb2/pydantic_core-2.46.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff", size = 1922768, upload-time = "2026-08-28T09:58:49.58Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a3/c05ca796e1197618a774b01e596aeedfefc2f7d8c01ae3054e910b120e8a/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:193375f3548919d3f0b60936ca113ada3e38f264f91b9b8e0508efaad57be931", size = 1951241, upload-time = "2026-08-28T09:58:51.511Z" }, - { url = "https://files.pythonhosted.org/packages/68/32/33bc39ac705c52cffc908e8389f9754fdb208aea5c69cceddf4eb3ce99af/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:79bdfa52f843137045b2d081cc05c120ba6665d29b7559c2c47690906f39279f", size = 2031975, upload-time = "2026-08-28T09:58:53.166Z" }, - { url = "https://files.pythonhosted.org/packages/b0/70/2333e885c0f6a67bc105c5916965dac9b57f2718ee20d81d1a06a4ebdc13/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:24922243639cbdac66c75fcb6fd6495a9cb52b213d62f9a0d16f0310b1ff8038", size = 2208542, upload-time = "2026-08-28T09:58:55.017Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ea/296debfb4264207bbda5936133892e027c0a58875ad53ebd512fba8ec3a2/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76fe65e607be28c7fd4d56fc3c42b1583aa058ce3408b7ad0fd540171d31f9f", size = 2264692, upload-time = "2026-08-28T09:58:56.767Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f2/9e4de77a6271e07a76d2d58b11c091a979c191ed2939bf80067568b369d2/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f7b393a8b3da82f5c1fc0751e6d01ac6c55b93c18226a60bdfba4a724efafd1", size = 2066633, upload-time = "2026-08-28T09:58:58.531Z" }, - { url = "https://files.pythonhosted.org/packages/8d/db/f9e9d0c97445987b2084823d5c240de88087338f04fc2cfaa2df186b8049/pydantic_core-2.46.5-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:7ac031912d54f3d83ef3b3eb98dfabc1608802e2202263d25957eeed40b94761", size = 2105235, upload-time = "2026-08-28T09:59:00.421Z" }, - { url = "https://files.pythonhosted.org/packages/07/c5/79169b047b3b2c3e99e04bc76372af9637e0bf6db638274fa927df96369e/pydantic_core-2.46.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:837b396ca3d7b74091ca623f6cbd8351bd42d670a79c2683e79fb089f06a2de5", size = 2157367, upload-time = "2026-08-28T09:59:02.442Z" }, - { url = "https://files.pythonhosted.org/packages/26/b5/ba6057afb7c291bd449f51b867f95aef2072941c4ce4e5c31d6ffd132d3b/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:5ee239d575f80b08eca11f6e20f90c4c695de7825c67eefe6091fbf20dda648e", size = 2158420, upload-time = "2026-08-28T09:59:04.2Z" }, - { url = "https://files.pythonhosted.org/packages/6e/28/2057abecaafdc22912afa819603a51f0a62d40643b7c4871c51721fea9be/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e80675d75ae2cd14372cb65cad5400d9347a3d3f6c13000183f22dfd027283ed", size = 2309588, upload-time = "2026-08-28T09:59:06.048Z" }, - { url = "https://files.pythonhosted.org/packages/71/9d/881156dc404e27479c4246128d73538464cab4a239bec61995e227644c30/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c4b71f10dd532fb7a5cbc8f58707779e64f03a258c2bf8bfbaecfcd9970b519", size = 2341866, upload-time = "2026-08-28T09:59:08.539Z" }, - { url = "https://files.pythonhosted.org/packages/5a/38/d66f443a259f84d13babdceae568e572b0ed26da17ca5d0a649ebb110a67/pydantic_core-2.46.5-cp313-cp313-win32.whl", hash = "sha256:97bf8de4d541598c94a59344eeb988a94c08ff76b5723c41f6567ec18c7892ea", size = 1938580, upload-time = "2026-08-28T09:59:10.402Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1e/1d5371213f4cc9a7ed70c0bfcc7911de22311ee99a662a56077d7292d2ac/pydantic_core-2.46.5-cp313-cp313-win_amd64.whl", hash = "sha256:15f4a94963c95accac15b7b657bb177d3ad82bb90b0d0526d9a9b85079925db5", size = 2041980, upload-time = "2026-08-28T09:59:12.396Z" }, - { url = "https://files.pythonhosted.org/packages/5a/48/4222d90b1c67568bace4dec6dca6271449c66de3595d72b6d098f5fde597/pydantic_core-2.46.5-cp313-cp313-win_arm64.whl", hash = "sha256:d22a945598fb91236b4dd793a6e42e4f3dd7740bb5aace5ebd7d4c08d13bb575", size = 1997213, upload-time = "2026-08-28T09:59:14.245Z" }, - { url = "https://files.pythonhosted.org/packages/df/dd/053c2e4303f791f3b8f8a14ab0b22008e8eb21d868c0c90b4f9be705b76a/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:013d6f3483d81e02e7c328831808f336c8596ee33b4bd4026b9ffb1e960b8942", size = 2062540, upload-time = "2026-08-28T10:01:00.318Z" }, - { url = "https://files.pythonhosted.org/packages/d7/dd/a18df751a5e37dd51bfad7f68e766999125bebe68c9e1d10a493ad01bd63/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:e9c134bb666dd54b778b9fc0d2b50cbb7f979b9e3716f26a88c9ab3b6fc1dd0f", size = 1902040, upload-time = "2026-08-28T10:01:02.529Z" }, - { url = "https://files.pythonhosted.org/packages/b7/13/01d40f9d07ce8a779fd6e0bd8ad4fba91309500dd67b869e2e219d261a6d/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:347ec774390c87326a2e4929d58d3f7e8763a104d5d35f4cd595a4c952366433", size = 1967479, upload-time = "2026-08-28T10:01:05.004Z" }, - { url = "https://files.pythonhosted.org/packages/fa/04/c81d4841331c2178b6fb09ae225425e110ed72d990c9fe556c4ec03d1013/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e24d8f05fa2d28513d94e877e9c75ad66175376209b3977f916e240e623193c", size = 2111034, upload-time = "2026-08-28T10:01:07.345Z" }, -] - -[[package]] -name = "pydata-sphinx-theme" -version = "0.19.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "accessible-pygments" }, - { name = "babel" }, - { name = "beautifulsoup4" }, - { name = "docutils" }, - { name = "pygments" }, - { name = "sphinx" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/39/7e/e3defb93f30557ae825a3b3fbc2c6728301e5e9505a85e00d8503345c42c/pydata_sphinx_theme-0.19.0.tar.gz", hash = "sha256:148ba092bd6937b3321385dc482cc69a29ad2ede36547b4f010ade782bc6a062", size = 5004933, upload-time = "2026-06-15T09:31:02.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/77/bdb5a4c0e8e33c08f31fd175a5af0bc5e29f1b43ffa9e963f4c4f9bfbc97/pydata_sphinx_theme-0.19.0-py3-none-any.whl", hash = "sha256:5d7dfe3beb0facc88b5d78ff4a4c948f214cc0e03aae27e7fc58286e963b588b", size = 6201132, upload-time = "2026-06-15T09:30:59.966Z" }, -] - -[[package]] -name = "pygments" -version = "2.21.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, -] - -[[package]] -name = "pykwalify" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docopt" }, - { name = "python-dateutil" }, - { name = "ruamel-yaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/77/2d6849510dbfce5f74f1f69768763630ad0385ad7bb0a4f39b55de3920c7/pykwalify-1.8.0.tar.gz", hash = "sha256:796b2ad3ed4cb99b88308b533fb2f559c30fa6efb4fa9fda11347f483d245884", size = 62462, upload-time = "2020-12-30T22:31:10.745Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/fd/ac2161cce19fd67a18c269073f8e86292b5511acec6f8ef6eab88615d032/pykwalify-1.8.0-py2.py3-none-any.whl", hash = "sha256:731dfa87338cca9f559d1fca2bdea37299116e3139b73f78ca90a543722d6651", size = 24860, upload-time = "2020-12-30T22:31:09.09Z" }, -] - -[[package]] -name = "pyparsing" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/11/b213bebff182584360cb8d17c72c1677fec5c5c228de439e63bcf8ab1c8f/pyparsing-3.3.3.tar.gz", hash = "sha256:928ae7e20211f3b6f3915a72f06a0cfd29ab9d24279dd6346b6b1a7146397d36", size = 1050487, upload-time = "2026-09-20T20:59:05.609Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl", hash = "sha256:ece8c00a69cf01b45d0b1dedabb469c90d8caf996d4fda40f147627a122849a4", size = 126420, upload-time = "2026-09-20T20:59:04.025Z" }, -] - -[[package]] -name = "pypeline-runner" -version = "1.39.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "poks" }, - { name = "py-app-dev" }, - { name = "pyyaml" }, - { name = "typer" }, - { name = "west" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/41/bedb03c38505e88b7fbca3dc8fe6f2ec3c3733b08b0db7967fa2779087ac/pypeline_runner-1.39.0.tar.gz", hash = "sha256:fd387cf5a70f8b41b3363dc8064077ad28b903295913dc427acf06c73b7f2ad9", size = 40523, upload-time = "2026-09-04T15:10:55.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/80/1343cbc76ebe62a004576df9bca5ff31bf2765d70b9915f5d6288cd04b48/pypeline_runner-1.39.0-py3-none-any.whl", hash = "sha256:a6993ca2410e02c7e68004879f4b50fa64b1cc6f9c2fb6187837d96e81d89e01", size = 48884, upload-time = "2026-09-04T15:10:53.824Z" }, -] - -[[package]] -name = "pypeline-semantic-release" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "py-app-dev" }, - { name = "pypeline-runner" }, - { name = "python-semantic-release" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/f5/c54da09b40ebee03f0401778d4a83743e5807ce321e892dc4f96fe038b2a/pypeline_semantic_release-0.5.1.tar.gz", hash = "sha256:b1e5bd84143f9d0250da5e3b78c900587535d8a4419390898e93b2fdb0fee016", size = 16505, upload-time = "2026-04-21T05:35:29.442Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/50/584083a8dccd9010a977d5ea39fc9830bf5374421ee32459b5b821b66836/pypeline_semantic_release-0.5.1-py3-none-any.whl", hash = "sha256:9ef3602f4659f600b243a09d243cb3c861c5446eacbf6210bc79065efed0781b", size = 15443, upload-time = "2026-04-21T05:35:27.73Z" }, -] - -[[package]] -name = "pyppmd" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/d7/803232913cab9163a1a97ecf2236cd7135903c46ac8d49613448d88e8759/pyppmd-1.3.1.tar.gz", hash = "sha256:ced527f08ade4408c1bfc5264e9f97ffac8d221c9d13eca4f35ec1ec0c7b6b2e", size = 1351815, upload-time = "2025-11-27T22:08:44.175Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/89/696046e53c7aea98bb563aed3f15c3e2fa20c33e3d6c9de3c20992c586cc/pyppmd-1.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3faa58ab2ebe3b13ec23b1904639d687fb727270d2962fd2d239ca00fd6eb865", size = 77796, upload-time = "2025-11-27T22:07:50.362Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c5/a708cdb58e76889bc65201eda12211486548ffe00ecddc5dfbdce6d4d252/pyppmd-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27703f041ee96912a5410fd3ce31c5cde32f9323bd67f72f100bd960ee67bf13", size = 48185, upload-time = "2025-11-27T22:07:51.736Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/f299535c18009addb866d89a7044f1086e4eecf50542e57ff5bb15840195/pyppmd-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e773d8353b36f7e7973a43526993fb276b98a97839cb5dc8f4e6465ad873f41a", size = 48496, upload-time = "2025-11-27T22:07:52.866Z" }, - { url = "https://files.pythonhosted.org/packages/aa/9d/4a59b73ea8e305f9192ee26ceb7c3d57e17ccb9bcca0e99ef335db29fcf7/pyppmd-1.3.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:37b1883accf840cb0b711785d353f8548853a1401d381da007c0aec362f3ffac", size = 142620, upload-time = "2025-11-27T22:07:54.411Z" }, - { url = "https://files.pythonhosted.org/packages/88/d7/fe32c2a4f8539365e6292aed25545830a5e718a510cdb4caddd6fd8d8056/pyppmd-1.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bd6d179ad39b6191ca0cbe62fb9592f33f49277b4384ad7bc5eb0e6ca27ebee", size = 144306, upload-time = "2025-11-27T22:07:55.727Z" }, - { url = "https://files.pythonhosted.org/packages/20/20/e3907cf263b58f4ed4c5315bc1ac91721c85332fd0d73a89e3e2752904ef/pyppmd-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:806cf8d33606e44bf5ff5786c57891f57993f1eef1c763da3c58ea97de3a13c8", size = 139522, upload-time = "2025-11-27T22:07:57.034Z" }, - { url = "https://files.pythonhosted.org/packages/0a/03/22d5f93251e481f23bf741dde7f59cfc3e315f60b32b63f215a2d7bb8944/pyppmd-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1826cbce9a2c944aa08df79310a7e6d4a61fd20636b6dff64a77ea4bc43da30f", size = 142340, upload-time = "2025-11-27T22:07:58.49Z" }, - { url = "https://files.pythonhosted.org/packages/34/88/38d36c1394d5e331db9708dae08863c87c059bd5b6e43b20234be02a0503/pyppmd-1.3.1-cp312-cp312-win32.whl", hash = "sha256:d3ff96671319318d941dd34300d641745048e8a3251b077bddf98652d6ddc513", size = 42102, upload-time = "2025-11-27T22:08:00.124Z" }, - { url = "https://files.pythonhosted.org/packages/ee/29/1398c6f67dfb66367babeed2980caee837d0a488705e3dff7ff159e017e7/pyppmd-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:c8c1ad39e7ebde71bf5a54cf61f489bf4790f1dd0beb70dc2e8f5ad3329d7ca7", size = 46957, upload-time = "2025-11-27T22:08:01.587Z" }, - { url = "https://files.pythonhosted.org/packages/d2/41/ee3193c16472a5a1c5f1965abb8fa87a7ad455c39688e9a0c2d87d6a7027/pyppmd-1.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:391b2bf76d7dc45b343781754d0b734dcbf539b92667986a343f5488c4bf9ca0", size = 45214, upload-time = "2025-11-27T22:08:02.721Z" }, - { url = "https://files.pythonhosted.org/packages/f0/01/7cc3854b0e1304b90d2435e946db5e1c42e103adf840a68400002fc838b4/pyppmd-1.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4b4edb3e9619fd0bc39c1a07eb03e8731db833a93b23134f36c7ef581a94b37a", size = 77800, upload-time = "2025-11-27T22:08:03.856Z" }, - { url = "https://files.pythonhosted.org/packages/dd/02/caf6305224b9432ea7373670d101392f620c1ce741d6f95458b1fb9a16a4/pyppmd-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b8b5c813e462c91048b88e2adfbcc0c69f2c905f70097001d32066f86f675bd4", size = 48195, upload-time = "2025-11-27T22:08:05.323Z" }, - { url = "https://files.pythonhosted.org/packages/45/82/8c5d3f0f738a3cf5c209d1471efda105a16417c0aaa91a2ad39efb3d4efb/pyppmd-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e8d372d9fac382183e0371cf0c2d736b494b1857a1befe98d563342b1205265b", size = 48482, upload-time = "2025-11-27T22:08:06.441Z" }, - { url = "https://files.pythonhosted.org/packages/76/9c/09ab8b4f00473f210284dacf837cae8355ad900f3fd8fd98b2bba12de4fa/pyppmd-1.3.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b765ae21f7ed2f4ea8f32bfd9e3a4a8d738e73fc8f8dcddec9cbe2c898d60be", size = 142828, upload-time = "2025-11-27T22:08:07.611Z" }, - { url = "https://files.pythonhosted.org/packages/64/fd/673d429df719affa1445611288aab6111c0bc87e51eb4677fe2421a1dd64/pyppmd-1.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd00522ddfcc292304577386b6c217758c0c10e1fb9ce7877ad7d3b7b821a808", size = 144502, upload-time = "2025-11-27T22:08:08.869Z" }, - { url = "https://files.pythonhosted.org/packages/cf/0f/64dad213f56eaf23210ac7d3a5dedb19ad8f186f8b8682f65e7d51d9fa4a/pyppmd-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3de62099ff2ca876c2d39bc547bcba6f7b878988663abd782a5bad4edac3bb44", size = 139718, upload-time = "2025-11-27T22:08:10.114Z" }, - { url = "https://files.pythonhosted.org/packages/74/61/bd5dbe8d7374748687d48f0bc44e6b930470e09552b1bbc65ef899ab673e/pyppmd-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:011f845de195d60fe973a635a1f4be981b7d80f357a8acb1b2d83bdf5087c808", size = 142578, upload-time = "2025-11-27T22:08:11.388Z" }, - { url = "https://files.pythonhosted.org/packages/42/ae/0af00a9b1cd59b6821eb182413033d5ddbe05dfcde7511e3a6b1a5b83f6d/pyppmd-1.3.1-cp313-cp313-win32.whl", hash = "sha256:7d61bd01f25289b6ae54832db4254602fb0c6d105f6e6bf0aee39b803b698b98", size = 42102, upload-time = "2025-11-27T22:08:13.297Z" }, - { url = "https://files.pythonhosted.org/packages/6c/6d/311d8faca142f39b4d158c94ed7eb237a197d41dd7eb67f53442a7904e04/pyppmd-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:df8d84ab72381058a964ba66e5e81ed52dbd0b5ad734a5ef8353452983506098", size = 46962, upload-time = "2025-11-27T22:08:14.759Z" }, - { url = "https://files.pythonhosted.org/packages/bb/42/24b59b6bb73a3fec96c7f81521d146c2ac89ffcb89cf9e848f62e0660381/pyppmd-1.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:124a04aab6936ba011f9ad57067798c7f052fdb1848b0cc4318606eea55475e6", size = 45209, upload-time = "2025-11-27T22:08:15.933Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-decouple" -version = "3.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/97/373dcd5844ec0ea5893e13c39a2c67e7537987ad8de3842fe078db4582fa/python-decouple-3.8.tar.gz", hash = "sha256:ba6e2657d4f376ecc46f77a3a615e058d93ba5e465c01bbe57289bfb7cce680f", size = 9612, upload-time = "2023-03-01T19:38:38.143Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/d4/9193206c4563ec771faf2ccf54815ca7918529fe81f6adb22ee6d0e06622/python_decouple-3.8-py3-none-any.whl", hash = "sha256:d0d45340815b25f4de59c974b855bb38d03151d81b037d9e3f463b0c9f8cbd66", size = 9947, upload-time = "2023-03-01T19:38:36.015Z" }, -] - -[[package]] -name = "python-gitlab" -version = "5.6.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, - { name = "requests-toolbelt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/08/d35d0f28549c43611e942f39b6321a7b35c02189d5badceefe601c0207ce/python_gitlab-5.6.0.tar.gz", hash = "sha256:bc531e8ba3e5641b60409445d4919ace68a2c18cb0ec6d48fbced6616b954166", size = 396148, upload-time = "2025-01-28T16:33:39.136Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/5e/2e1ed7145835afaad87664b2f675a1d7b6e1211ad6ecfe57e200ae45f0bd/python_gitlab-5.6.0-py3-none-any.whl", hash = "sha256:68980cd70929fc7f8f06d8a7b09bd046a6b79e1995c19d61249f046005099100", size = 148836, upload-time = "2025-01-28T16:33:36.818Z" }, -] - -[[package]] -name = "python-magic" -version = "0.4.24" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.13'", -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/70/76b185393fecf78f81c12f9dc7b1df814df785f6acb545fc92b016e75a7e/python-magic-0.4.24.tar.gz", hash = "sha256:de800df9fb50f8ec5974761054a708af6e4246b03b4bdaee993f948947b0ebcf", size = 17295, upload-time = "2021-06-03T13:49:24.116Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/99/c89223c6547df268596899334ee77b3051f606077317023617b1c43162fb/python_magic-0.4.24-py2.py3-none-any.whl", hash = "sha256:4fec8ee805fea30c07afccd1592c0f17977089895bdfaae5fec870a84e997626", size = 12785, upload-time = "2021-06-03T13:49:22.828Z" }, -] - -[[package]] -name = "python-magic" -version = "0.4.27" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.13'", -] -sdist = { url = "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b", size = 14677, upload-time = "2022-06-07T20:16:59.508Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3", size = 13840, upload-time = "2022-06-07T20:16:57.763Z" }, -] - -[[package]] -name = "python-semantic-release" -version = "9.21.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "click-option-group" }, - { name = "deprecated" }, - { name = "dotty-dict" }, - { name = "gitpython" }, - { name = "importlib-resources" }, - { name = "jinja2" }, - { name = "pydantic" }, - { name = "python-gitlab" }, - { name = "requests" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "tomlkit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1d/2b/8395fa3b7bd31fd4d0a4181ad660b2f8ee14961334945d3bfc6e73dec21a/python_semantic_release-9.21.2.tar.gz", hash = "sha256:84d4f90f77528bcc0cbdcf3bc262172e3a4eeb7670b82fa45b4c859455e9cdf2", size = 308976, upload-time = "2026-07-04T01:11:42.085Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/95/9059bf101c4147dd18c65f9323b57c2a3e1e152ff12a6a316ab7562d79bb/python_semantic_release-9.21.2-py3-none-any.whl", hash = "sha256:856b13c025aadf52180d5704477c2e3582fc0d5f6b2746641d3006b9eed4a0b4", size = 132839, upload-time = "2026-07-04T01:11:40.502Z" }, -] - -[[package]] -name = "python-slugify" -version = "9.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "text-unidecode" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/ca/5740963aa82f2c9dd025d71f1b6cce2159545f9e60dcb86a447d1cf92252/python_slugify-9.1.0.tar.gz", hash = "sha256:3f02e8a0639c61e37eca5a783aae09112796d15d331ee3d5372241b61ee05f1c", size = 63567, upload-time = "2026-09-18T22:30:42.72Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/f1/e450f6f6eb1217a6dc5f015589a21566839297920304961a7c2230477837/python_slugify-9.1.0-py3-none-any.whl", hash = "sha256:ab2d1d0e7ad5fa46b3756f0f3fcfb37d6d5baa006ccd1f22c2481a6ab53b8c12", size = 15641, upload-time = "2026-09-18T22:30:41.612Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "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", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "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", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "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", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "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", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "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", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "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", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, -] - -[[package]] -name = "requests" -version = "2.34.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, -] - -[[package]] -name = "requests-file" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/5dc70102e4d337063452c82e1f0d95e39abfe67aa222ed8a5ddeb9df8de8/requests_file-3.0.1.tar.gz", hash = "sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576", size = 6967, upload-time = "2025-10-20T18:56:42.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/d5/de8f089119205a09da657ed4784c584ede8381a0ce6821212a6d4ca47054/requests_file-3.0.1-py2.py3-none-any.whl", hash = "sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2", size = 4514, upload-time = "2025-10-20T18:56:41.184Z" }, -] - -[[package]] -name = "requests-toolbelt" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, -] - -[[package]] -name = "rich" -version = "14.3.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/67/cae617f1351490c25a4b8ac3b8b63a4dda609295d8222bad12242dfdc629/rich-14.3.4.tar.gz", hash = "sha256:817e02727f2b25b40ef56f5aa2217f400c8489f79ca8f46ea2b70dd5e14558a9", size = 230524, upload-time = "2026-04-11T02:57:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/76/6d163cfac87b632216f71879e6b2cf17163f773ff59c00b5ff4900a80fa3/rich-14.3.4-py3-none-any.whl", hash = "sha256:07e7adb4690f68864777b1450859253bed81a99a31ac321ac1817b2313558952", size = 310480, upload-time = "2026-04-11T02:57:47.484Z" }, -] - -[[package]] -name = "roman-numerals" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, -] - -[[package]] -name = "roman-numerals-py" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "roman-numerals" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/de96fca640f4f656eb79bbee0e79aeec52e3e0e359f8a3e6a0d366378b64/roman_numerals_py-4.1.0.tar.gz", hash = "sha256:f5d7b2b4ca52dd855ef7ab8eb3590f428c0b1ea480736ce32b01fef2a5f8daf9", size = 4274, upload-time = "2025-12-17T18:25:41.153Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/2c/daca29684cbe9fd4bc711f8246da3c10adca1ccc4d24436b17572eb2590e/roman_numerals_py-4.1.0-py3-none-any.whl", hash = "sha256:553114c1167141c1283a51743759723ecd05604a1b6b507225e91dc1a6df0780", size = 4547, upload-time = "2025-12-17T18:25:40.136Z" }, -] - -[[package]] -name = "rpds-py" -version = "2026.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691, upload-time = "2026-06-30T07:15:14.96Z" }, - { url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542, upload-time = "2026-06-30T07:15:16.267Z" }, - { url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180, upload-time = "2026-06-30T07:15:17.62Z" }, - { url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067, upload-time = "2026-06-30T07:15:18.952Z" }, - { url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509, upload-time = "2026-06-30T07:15:20.434Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754, upload-time = "2026-06-30T07:15:21.831Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189, upload-time = "2026-06-30T07:15:23.371Z" }, - { url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750, upload-time = "2026-06-30T07:15:24.659Z" }, - { url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576, upload-time = "2026-06-30T07:15:25.987Z" }, - { url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807, upload-time = "2026-06-30T07:15:27.356Z" }, - { url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187, upload-time = "2026-06-30T07:15:28.931Z" }, - { url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030, upload-time = "2026-06-30T07:15:30.553Z" }, - { url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185, upload-time = "2026-06-30T07:15:32.027Z" }, - { url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394, upload-time = "2026-06-30T07:15:33.359Z" }, - { url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753, upload-time = "2026-06-30T07:15:34.778Z" }, - { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" }, - { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" }, - { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" }, - { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" }, - { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" }, - { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" }, - { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" }, - { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" }, - { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" }, - { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" }, - { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" }, - { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" }, - { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" }, -] - -[[package]] -name = "ruamel-yaml" -version = "0.19.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[package]] -name = "smmap" -version = "5.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, -] - -[[package]] -name = "snowballstemmer" -version = "3.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/f8/0a71edf031f03c40db17503cb8ca78a69a171254e568e7db241b0ab57ea1/snowballstemmer-3.1.1.tar.gz", hash = "sha256:e07bbc54a0d798fe6010a12398422e62a8bfbba95c394fd0956ef58cb4d3e260", size = 123314, upload-time = "2026-06-03T00:56:40.194Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/07/2ebca9b11fb9be7340a818d8d6f63feaebb146be2c4afbd6061701d6df6e/snowballstemmer-3.1.1-py3-none-any.whl", hash = "sha256:7e207fa178741da09cdee59d3ecec3827ad5f92b1fc5c9ff3755b639f71f5752", size = 104164, upload-time = "2026-06-03T00:56:38.614Z" }, -] - -[[package]] -name = "soupsieve" -version = "2.9.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/69/99/a6ca3beb3ccacb41fb3321d8a60e5566f9e6467601ef8eba6a17e1b89778/soupsieve-2.9.2.tar.gz", hash = "sha256:4a55d8cf158a9c2e587fa4922f1bbb91d68ac829e2d6f25403a85747c71daf74", size = 122445, upload-time = "2026-08-07T00:57:24.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl", hash = "sha256:8089a26fd974ca7a1f30276d3d8492ab266ab15af581642dfe8aa162e0c1c823", size = 37370, upload-time = "2026-08-07T00:57:23.524Z" }, -] - -[[package]] -name = "sphinx" -version = "8.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "roman-numerals-py" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, -] - -[[package]] -name = "sphinx-book-theme" -version = "1.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydata-sphinx-theme" }, - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/89/69/61dfa3b3851610b5f45960737bd99f8c5b2d70ba73f9ac84a527e0c564ae/sphinx_book_theme-1.1.3.tar.gz", hash = "sha256:1f25483b1846cb3d353a6bc61b3b45b031f4acf845665d7da90e01ae0aef5b4d", size = 434230, upload-time = "2024-06-12T14:11:22.128Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/80/90574e2e82c955b9c6f6b77f7badb2cf2ef4ef77599e4343cced2d098681/sphinx_book_theme-1.1.3-py3-none-any.whl", hash = "sha256:a554a9a7ac3881979a87a2b10f633aa2a5706e72218a10f71be38b3c9e831ae9", size = 430129, upload-time = "2024-06-12T14:11:20.002Z" }, -] - -[[package]] -name = "sphinx-codelinks" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "comment-parser", version = "1.2.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, - { name = "comment-parser", version = "1.2.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "docutils" }, - { name = "giturlparse" }, - { name = "ignore-python" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "pygments" }, - { name = "sphinx" }, - { name = "sphinx-needs" }, - { name = "tree-sitter" }, - { name = "tree-sitter-bash" }, - { name = "tree-sitter-c-sharp" }, - { name = "tree-sitter-cpp" }, - { name = "tree-sitter-go" }, - { name = "tree-sitter-json" }, - { name = "tree-sitter-python" }, - { name = "tree-sitter-rust" }, - { name = "tree-sitter-yaml" }, - { name = "typer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/30/ae/bf4b642c72b7d383723ea9ef1830d5fef5d5505165fa83e3ee1c547aea5e/sphinx_codelinks-1.4.0.tar.gz", hash = "sha256:bf8a5f88ad4747f4964d4e4da23999a4608db6a86e4935151c17b56338d6f50d", size = 47372, upload-time = "2026-07-30T16:09:57.83Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/52/95e8e7020619e8750fbb6ebfa4470d6c5ff2d0adebd37fcbc777065f510b/sphinx_codelinks-1.4.0-py3-none-any.whl", hash = "sha256:5cdc712a327b6ba09ea56836392cc750beaf157a342f0853ee0a773b887a7534", size = 54875, upload-time = "2026-07-30T16:09:56.623Z" }, -] - -[[package]] -name = "sphinx-copybutton" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, -] - -[[package]] -name = "sphinx-data-viewer" -version = "0.1.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/3a/7aeeb805327af5b0e6f072312f9bc660615045e791a5a8f7258276fdddd1/sphinx_data_viewer-0.1.5.tar.gz", hash = "sha256:a7d5e58613562bb745380bfe61ca8b69997998167fd6fa9aea55606c9a4b17e4", size = 103260, upload-time = "2024-08-28T10:34:21.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/60/5e902d53a5eb3ec7166a8eda602bd78d4b8671a73917567edc8f13a3b366/sphinx_data_viewer-0.1.5-py3-none-any.whl", hash = "sha256:b74b1d304c505c464d07c7b225ed0d84ea02dcc88bc1c49cdad7c2275fbbdad4", size = 8215, upload-time = "2024-08-28T10:34:20.136Z" }, -] - -[[package]] -name = "sphinx-design" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689, upload-time = "2024-08-02T13:48:44.277Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338, upload-time = "2024-08-02T13:48:42.106Z" }, -] - -[[package]] -name = "sphinx-mounts" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ignore-python" }, - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/06/85e09e90824dbf9b81c4f60fe156855d13249c1f1f7b208452b75d8fdc34/sphinx_mounts-0.2.0.tar.gz", hash = "sha256:7c19e867b463bccbff35281e2c6c20dfaa75d4fa88eef6abe97c9c371df6914b", size = 98441, upload-time = "2026-08-27T11:42:44.383Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/4c/c92fe023dec5204dae85690fc110b47d0c6a0fe9e57c2f8b8d82228ef168/sphinx_mounts-0.2.0-py3-none-any.whl", hash = "sha256:9e43cba60834bb648a7d3f0f43bb3fbbf9743e69933a89081a90a30d6d42d230", size = 100201, upload-time = "2026-08-27T11:42:43.045Z" }, -] - -[[package]] -name = "sphinx-needs" -version = "8.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonschema-rs" }, - { name = "minijinja" }, - { name = "requests" }, - { name = "requests-file" }, - { name = "sphinx" }, - { name = "sphinx-data-viewer" }, - { name = "sphinxcontrib-jquery" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/c2/9db8259372467a85da7d07aa9f642d292f56feadebb37caab73683bbcfed/sphinx_needs-8.5.0.tar.gz", hash = "sha256:42d327baaf39f5041f692cb85609ef7023c0a73b688cde5803db444e577bb63d", size = 28245371, upload-time = "2026-09-03T11:42:25.929Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/96/e0a0b6fdf37460b7aaf8fb887922138aa23bbef7635ddaf09fb64823927a/sphinx_needs-8.5.0-py3-none-any.whl", hash = "sha256:aa7548d1ec33cd66cd0223cf8d5c0a934dc4c67d67ad471b5351b817d5f02750", size = 2767023, upload-time = "2026-09-03T11:42:23.717Z" }, -] - -[[package]] -name = "sphinx-new-tab-link" -version = "0.8.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, - { name = "sphinxcontrib-extdevhelper-kasane" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/b9/ee505f904abefa6b574736c93813275a22821933cf2293c83893107eecb6/sphinx_new_tab_link-0.8.2.tar.gz", hash = "sha256:8a339fbfcb1712db8eb3d503c61b86da11a52f1216bd308d6f6dcec6139dd8ea", size = 7820, upload-time = "2026-08-26T13:08:21.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/67/ff27727d2a27dcd8e7c5a3fc27102aacd2063dbb33364d4ad245bd915266/sphinx_new_tab_link-0.8.2-py3-none-any.whl", hash = "sha256:c347d2a228d24f522d8b2c7e9aa453fcea87cfc0a98f50617a4dac702e9e3f9f", size = 6154, upload-time = "2026-08-26T13:08:19.713Z" }, -] - -[[package]] -name = "sphinx-test-reports" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lxml" }, - { name = "packaging" }, - { name = "sphinx" }, - { name = "sphinx-needs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/67/e1/1e1fe898f7ec7ce651ca8738548459a721fdb9d170c4a96f3faccdce9cb0/sphinx_test_reports-1.4.0.tar.gz", hash = "sha256:8a7918946453802380a352b4685f3bdb59efd98cb026921145381c5f558c0a4b", size = 20125, upload-time = "2026-06-16T19:58:05.682Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/b3/283af1e4ad11c88aee669cc170849b028170ec56b6bb6e24e62db3fbdae6/sphinx_test_reports-1.4.0-py3-none-any.whl", hash = "sha256:1ac7af2bc437a4ff3da580d8f7b49eef8f534d4973b8880880833751fa96a794", size = 27336, upload-time = "2026-06-16T19:58:04.399Z" }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, -] - -[[package]] -name = "sphinxcontrib-datatemplates" -version = "0.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "defusedxml" }, - { name = "pyyaml" }, - { name = "sphinx" }, - { name = "sphinxcontrib-runcmd" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/9e/8ac54a6a3e7a00339f417568899b64a0c0d622429db73cc1a28c8122c8e2/sphinxcontrib.datatemplates-0.11.0.tar.gz", hash = "sha256:793222e803430076341509cc167f8d715830b05e418c885313101d60fd442557", size = 30996, upload-time = "2023-12-21T14:59:10.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/8d/7a7dd95ad1eedec8dc770570c8b1f3dc1d13357383635607b6629ccf329c/sphinxcontrib.datatemplates-0.11.0-py3-none-any.whl", hash = "sha256:88d02f5edab32b88211ebb72a90553e3676a5737877bad1de412f84058ac282e", size = 12493, upload-time = "2023-12-21T14:59:07.96Z" }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, -] - -[[package]] -name = "sphinxcontrib-extdevhelper-kasane" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/18/ac3c641c56ea92a66c311353833e004884560fb67545055ec3c2829855db/sphinxcontrib_extdevhelper_kasane-0.2.1.tar.gz", hash = "sha256:97576f7f09216bc57e54d44a1e92091bafb56cfa90dc26dad7508df158edbc6f", size = 4514, upload-time = "2025-10-08T12:30:23.574Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/52/d513a1fa3d6d66564d1f5f442d085def8c3eb19f695d2e7fc5e2f0ac1bdb/sphinxcontrib_extdevhelper_kasane-0.2.1-py3-none-any.whl", hash = "sha256:dfa63809d1c4b3cab62aae74854dcb1a2c7d797cf52bb496e9806d5af032cdf9", size = 3936, upload-time = "2025-10-08T12:30:22.337Z" }, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, -] - -[[package]] -name = "sphinxcontrib-jquery" -version = "4.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331, upload-time = "2023-03-14T15:01:01.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104, upload-time = "2023-03-14T15:01:00.356Z" }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, -] - -[[package]] -name = "sphinxcontrib-mermaid" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "pyyaml" }, - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8c/87/06537dff20aa57df2d0f2afa87edff4afa85dec6f57f03f7acc24f83ff88/sphinxcontrib_mermaid-2.1.1.tar.gz", hash = "sha256:f8d7f1b2f0ad69b7ebdd5cbf532fb4ee107893b5373bc2d0e9634089c0b2cfee", size = 22353, upload-time = "2026-09-01T16:10:23.671Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/6a/5bf2222b8084c7a7bc3ec23d470c91e3e03e82e4ce0bbacaa61f456ce7c1/sphinxcontrib_mermaid-2.1.1-py3-none-any.whl", hash = "sha256:3b9ac1a8e0b5154dec3c0acf9b5c54dd9ba58540e416f20858c300814c25648f", size = 16368, upload-time = "2026-09-01T16:10:22.377Z" }, -] - -[[package]] -name = "sphinxcontrib-plantuml" -version = "0.31" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/08/8f/f26dd61b92176c4cba6b7898ca2875e7dfc29263d8e1b3b63d6acf81434f/sphinxcontrib_plantuml-0.31.tar.gz", hash = "sha256:fd74752f8ea070e641c3f8a402fccfa1d4a4056e0967b56033d2a76282d9f956", size = 15736, upload-time = "2025-09-03T11:11:35.028Z" } - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, -] - -[[package]] -name = "sphinxcontrib-runcmd" -version = "0.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a2/03/6eb30814c9839f36131284a46ec9fc39d7bd356078648bc7125d5d1c05e8/sphinxcontrib-runcmd-0.2.0.tar.gz", hash = "sha256:3551c389d9c5fe82d693c7222feb9658b1a1a5a1abcb0063e8385e5528c64c76", size = 5376, upload-time = "2018-11-08T20:19:34.32Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/d9/67a79080b5d9fcb367470af9e525a9c53122e95744665de09462dcd676d8/sphinxcontrib_runcmd-0.2.0-py2.py3-none-any.whl", hash = "sha256:7b739b68e27210b4c7c12ba16e5b3da7b313c49991401f896d29bea0f0771934", size = 6043, upload-time = "2018-11-08T20:19:33.17Z" }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, -] - -[[package]] -name = "spl-core" -version = "8.9.0" -source = { git = "https://github.com/useblocks/spl-core.git?rev=010727d08b27ca73994772444d25d7bc6e028c31#010727d08b27ca73994772444d25d7bc6e028c31" } -dependencies = [ - { name = "clanguru" }, - { name = "cookiecutter" }, - { name = "gcovr" }, - { name = "hammocking" }, - { name = "junitparser" }, - { name = "kconfiglib" }, - { name = "mlx-traceability" }, - { name = "myst-parser" }, - { name = "py-app-dev" }, - { name = "pydata-sphinx-theme" }, - { name = "pypeline-runner" }, - { name = "pypeline-semantic-release" }, - { name = "sphinx" }, - { name = "sphinx-book-theme" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design" }, - { name = "sphinx-needs" }, - { name = "sphinx-new-tab-link" }, - { name = "sphinx-test-reports" }, - { name = "sphinxcontrib-datatemplates" }, - { name = "sphinxcontrib-mermaid" }, - { name = "sphinxcontrib-plantuml" }, - { name = "typer" }, -] - -[[package]] -name = "spled" -version = "0.0.1" -source = { virtual = "." } -dependencies = [ - { name = "myst-parser" }, - { name = "object-deps-report" }, - { name = "poks" }, - { name = "pypeline-runner" }, - { name = "pypeline-semantic-release" }, - { name = "pytest" }, - { name = "sphinx-codelinks" }, - { name = "sphinx-mounts" }, - { name = "spl-core" }, -] - -[package.metadata] -requires-dist = [ - { name = "myst-parser", specifier = ">=4.0,<5" }, - { name = "object-deps-report", specifier = ">=0,<1" }, - { name = "poks", specifier = ">=0.9.4,<1" }, - { name = "pypeline-runner", specifier = ">=1.33,<2" }, - { name = "pypeline-semantic-release", specifier = ">=0,<1" }, - { name = "pytest", specifier = ">=8,<9" }, - { name = "sphinx-codelinks", specifier = ">=1.2,<2" }, - { name = "sphinx-mounts", specifier = ">=0.2,<1" }, - { name = "spl-core", git = "https://github.com/useblocks/spl-core.git?rev=010727d08b27ca73994772444d25d7bc6e028c31" }, -] - -[[package]] -name = "text-unidecode" -version = "1.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885, upload-time = "2019-08-30T21:36:45.405Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" }, -] - -[[package]] -name = "texttable" -version = "1.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/dc/0aff23d6036a4d3bf4f1d8c8204c5c79c4437e25e0ae94ffe4bbb55ee3c2/texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638", size = 12831, upload-time = "2023-10-03T09:48:12.272Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917", size = 10768, upload-time = "2023-10-03T09:48:10.434Z" }, -] - -[[package]] -name = "tomlkit" -version = "0.13.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, -] - -[[package]] -name = "tree-sitter" -version = "0.25.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/7c/0350cfc47faadc0d3cf7d8237a4e34032b3014ddf4a12ded9933e1648b55/tree-sitter-0.25.2.tar.gz", hash = "sha256:fe43c158555da46723b28b52e058ad444195afd1db3ca7720c59a254544e9c20", size = 177961, upload-time = "2025-09-25T17:37:59.751Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/9e/20c2a00a862f1c2897a436b17edb774e831b22218083b459d0d081c9db33/tree_sitter-0.25.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ddabfff809ffc983fc9963455ba1cecc90295803e06e140a4c83e94c1fa3d960", size = 146941, upload-time = "2025-09-25T17:37:34.813Z" }, - { url = "https://files.pythonhosted.org/packages/ef/04/8512e2062e652a1016e840ce36ba1cc33258b0dcc4e500d8089b4054afec/tree_sitter-0.25.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c0c0ab5f94938a23fe81928a21cc0fac44143133ccc4eb7eeb1b92f84748331c", size = 137699, upload-time = "2025-09-25T17:37:36.349Z" }, - { url = "https://files.pythonhosted.org/packages/47/8a/d48c0414db19307b0fb3bb10d76a3a0cbe275bb293f145ee7fba2abd668e/tree_sitter-0.25.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd12d80d91d4114ca097626eb82714618dcdfacd6a5e0955216c6485c350ef99", size = 607125, upload-time = "2025-09-25T17:37:37.725Z" }, - { url = "https://files.pythonhosted.org/packages/39/d1/b95f545e9fc5001b8a78636ef942a4e4e536580caa6a99e73dd0a02e87aa/tree_sitter-0.25.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b43a9e4c89d4d0839de27cd4d6902d33396de700e9ff4c5ab7631f277a85ead9", size = 635418, upload-time = "2025-09-25T17:37:38.922Z" }, - { url = "https://files.pythonhosted.org/packages/de/4d/b734bde3fb6f3513a010fa91f1f2875442cdc0382d6a949005cd84563d8f/tree_sitter-0.25.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbb1706407c0e451c4f8cc016fec27d72d4b211fdd3173320b1ada7a6c74c3ac", size = 631250, upload-time = "2025-09-25T17:37:40.039Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/5f654994f36d10c64d50a192239599fcae46677491c8dd53e7579c35a3e3/tree_sitter-0.25.2-cp312-cp312-win_amd64.whl", hash = "sha256:6d0302550bbe4620a5dc7649517c4409d74ef18558276ce758419cf09e578897", size = 127156, upload-time = "2025-09-25T17:37:41.132Z" }, - { url = "https://files.pythonhosted.org/packages/67/23/148c468d410efcf0a9535272d81c258d840c27b34781d625f1f627e2e27d/tree_sitter-0.25.2-cp312-cp312-win_arm64.whl", hash = "sha256:0c8b6682cac77e37cfe5cf7ec388844957f48b7bd8d6321d0ca2d852994e10d5", size = 113984, upload-time = "2025-09-25T17:37:42.074Z" }, - { url = "https://files.pythonhosted.org/packages/8c/67/67492014ce32729b63d7ef318a19f9cfedd855d677de5773476caf771e96/tree_sitter-0.25.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0628671f0de69bb279558ef6b640bcfc97864fe0026d840f872728a86cd6b6cd", size = 146926, upload-time = "2025-09-25T17:37:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/4e/9c/a278b15e6b263e86c5e301c82a60923fa7c59d44f78d7a110a89a413e640/tree_sitter-0.25.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f5ddcd3e291a749b62521f71fc953f66f5fd9743973fd6dd962b092773569601", size = 137712, upload-time = "2025-09-25T17:37:44.039Z" }, - { url = "https://files.pythonhosted.org/packages/54/9a/423bba15d2bf6473ba67846ba5244b988cd97a4b1ea2b146822162256794/tree_sitter-0.25.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd88fbb0f6c3a0f28f0a68d72df88e9755cf5215bae146f5a1bdc8362b772053", size = 607873, upload-time = "2025-09-25T17:37:45.477Z" }, - { url = "https://files.pythonhosted.org/packages/ed/4c/b430d2cb43f8badfb3a3fa9d6cd7c8247698187b5674008c9d67b2a90c8e/tree_sitter-0.25.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b878e296e63661c8e124177cc3084b041ba3f5936b43076d57c487822426f614", size = 636313, upload-time = "2025-09-25T17:37:46.68Z" }, - { url = "https://files.pythonhosted.org/packages/9d/27/5f97098dbba807331d666a0997662e82d066e84b17d92efab575d283822f/tree_sitter-0.25.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d77605e0d353ba3fe5627e5490f0fbfe44141bafa4478d88ef7954a61a848dae", size = 631370, upload-time = "2025-09-25T17:37:47.993Z" }, - { url = "https://files.pythonhosted.org/packages/d4/3c/87caaed663fabc35e18dc704cd0e9800a0ee2f22bd18b9cbe7c10799895d/tree_sitter-0.25.2-cp313-cp313-win_amd64.whl", hash = "sha256:463c032bd02052d934daa5f45d183e0521ceb783c2548501cf034b0beba92c9b", size = 127157, upload-time = "2025-09-25T17:37:48.967Z" }, - { url = "https://files.pythonhosted.org/packages/d5/23/f8467b408b7988aff4ea40946a4bd1a2c1a73d17156a9d039bbaff1e2ceb/tree_sitter-0.25.2-cp313-cp313-win_arm64.whl", hash = "sha256:b3f63a1796886249bd22c559a5944d64d05d43f2be72961624278eff0dcc5cb8", size = 113975, upload-time = "2025-09-25T17:37:49.922Z" }, -] - -[[package]] -name = "tree-sitter-bash" -version = "0.25.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/0e/f0108be910f1eef6499eabce517e79fe3b12057280ed398da67ce2426cba/tree_sitter_bash-0.25.1.tar.gz", hash = "sha256:bfc0bdaa77bc1e86e3c6652e5a6e140c40c0a16b84185c2b63ad7cd809b88f14", size = 419703, upload-time = "2025-12-02T17:01:08.849Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/8e/37e7364d9c9c58da89e05c510671d8c45818afd7b31c6939ab72f8dc6c04/tree_sitter_bash-0.25.1-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:0e6235f59e366d220dde7d830196bed597d01e853e44d8ccd1a82c5dd2500acf", size = 194160, upload-time = "2025-12-02T17:00:59.047Z" }, - { url = "https://files.pythonhosted.org/packages/23/bb/2d2cfbb1f89aaeb1ec892624f069d92d058d06bb66f16b9ec9fb5873ab60/tree_sitter_bash-0.25.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:f4a34a6504c7c5b2a9b8c5c4065531dea19ca2c35026e706cf2eeeebe2c92512", size = 202659, upload-time = "2025-12-02T17:01:00.275Z" }, - { url = "https://files.pythonhosted.org/packages/25/f0/1bb25519be27460255d3899db677313cfa1e6306988fbf456a3d7e211bbb/tree_sitter_bash-0.25.1-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e76c4cfb20b076552406782b7f8c2a3946835993df0a44df006de54b7030c7dc", size = 230596, upload-time = "2025-12-02T17:01:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/d7/22/9f70bc3d3b942ab9fc0f89c1dc9e087519a3a94f64ae6b7377aae3a7a0f0/tree_sitter_bash-0.25.1-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3f484c4bb8796cde7a87ca351e6116f09653edac0eb3c6d238566359dd28b117", size = 231981, upload-time = "2025-12-02T17:01:02.859Z" }, - { url = "https://files.pythonhosted.org/packages/7a/c3/f1540e42cd41b323c6821e45e52e1aed6ed386209aad52db996f05703963/tree_sitter_bash-0.25.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5e76af6df46d958c7f5b6d5884c9743218e3902a00ccb493ec92728b1084430b", size = 228364, upload-time = "2025-12-02T17:01:03.997Z" }, - { url = "https://files.pythonhosted.org/packages/f7/a0/c3050a6277dfcac8c480f514dc4fe49f3f65f0eac68b4702cbaca2584e85/tree_sitter_bash-0.25.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a3332d71c7b7d5f78259b19d02d0ea111fcb82b72712ee4a93aaa5b226d3f0a8", size = 230074, upload-time = "2025-12-02T17:01:05.05Z" }, - { url = "https://files.pythonhosted.org/packages/71/0f/203fe6b27211387f4b9ba8c4a321567ca4ded2624dae6ccdbd2b6e940e17/tree_sitter_bash-0.25.1-cp310-abi3-win_amd64.whl", hash = "sha256:52a6802d9218f86278aa3e8b459c3abdad67eed0fde1f9f13aca5b6c634217a6", size = 195574, upload-time = "2025-12-02T17:01:06.412Z" }, - { url = "https://files.pythonhosted.org/packages/47/75/4ca1a9fabd8fb5aea78cea70f7837ce4dbf2afae115f62051e5fa99cba1c/tree_sitter_bash-0.25.1-cp310-abi3-win_arm64.whl", hash = "sha256:59115057ec2bae319e8082ff29559861045002964c3431ccb0fc92aa4bc9bccb", size = 191196, upload-time = "2025-12-02T17:01:07.486Z" }, -] - -[[package]] -name = "tree-sitter-c-sharp" -version = "0.23.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/fb/7e2962bc1901daf264e7ce263b168e0139304a5f8f66c9b2baf20e550f87/tree_sitter_c_sharp-0.23.5.tar.gz", hash = "sha256:2635c7d5ec93e59f2e831b571bed99c4cc68a5d183a0994020aa769e1b990a71", size = 1147914, upload-time = "2026-04-14T16:11:22.441Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/c4/86d8d469400a856757a464a6ac01af97d8cdacbb595e62bdb98bf1e9db90/tree_sitter_c_sharp-0.23.5-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:61e1981cf21b09ee547b9c4c68e64fb4394325f8fc8d5f6d50d41471eba923ea", size = 333658, upload-time = "2026-04-14T16:11:11.288Z" }, - { url = "https://files.pythonhosted.org/packages/c8/13/593c8603f834eaf15082b81e079289fc9f062b4c0ab5b9489134084eec06/tree_sitter_c_sharp-0.23.5-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a75994a11f6fed3f5b8c36ad6a00e5dc43205bd912c43af3a2a54fdf649664eb", size = 376296, upload-time = "2026-04-14T16:11:12.972Z" }, - { url = "https://files.pythonhosted.org/packages/41/5a/a8855cbb5bbab28adb29c2c7f0e7be5a9f1d21450c13b3c3e613190d9b8c/tree_sitter_c_sharp-0.23.5-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aa88a780204cd153c4c1ae2d59c654cee1402212fa0d069823d6d34301587438", size = 358333, upload-time = "2026-04-14T16:11:14.214Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c8/e0f391e343f5424d0627e3b6886c77baeb1249a3f10986be00b0b64ecdab/tree_sitter_c_sharp-0.23.5-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea38fb095d85d360dc5a0bec2fa605e496228876f798c9e089d5f0e72bcef46", size = 359448, upload-time = "2026-04-14T16:11:15.419Z" }, - { url = "https://files.pythonhosted.org/packages/6f/fc/10f807ac79f928241c5e0d827fdaf91e97dfba662fc7e07d7bd664140ec1/tree_sitter_c_sharp-0.23.5-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:05a9256415e7f24d4f133133794a9c224c60d19f677a04e2f6a94c25090b6d65", size = 358144, upload-time = "2026-04-14T16:11:17.087Z" }, - { url = "https://files.pythonhosted.org/packages/de/2a/6c3e12ef0cf09138717fcc02e1de8b76a3928d1bed65c7e3c2bd3172bcef/tree_sitter_c_sharp-0.23.5-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8636dc70b5a373c35c1036ed5de98e801f2e4d105ae41e2e20b6804c36e3bf33", size = 357525, upload-time = "2026-04-14T16:11:18.214Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e0/bd287b092d611df95a9149117fd27b5947ce75527113d6898a4b4e2c8858/tree_sitter_c_sharp-0.23.5-cp310-abi3-win_amd64.whl", hash = "sha256:41a28cfa3d9ea50f5629e44550a03188c8fbd5079803dfc03554b6fd594b33fa", size = 338756, upload-time = "2026-04-14T16:11:19.661Z" }, - { url = "https://files.pythonhosted.org/packages/7f/fb/114ff43fdd256d0befed32f77c1dadee9517867181c70794571f718ed05c/tree_sitter_c_sharp-0.23.5-cp310-abi3-win_arm64.whl", hash = "sha256:2de4ebf95ddc2e92cd3105c8a8e0e7ec646bc82f52bfaf2f3acec0fa2401ec09", size = 337260, upload-time = "2026-04-14T16:11:20.849Z" }, -] - -[[package]] -name = "tree-sitter-cpp" -version = "0.23.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/2c/4dd63d705a8933543cad9b92ff31be849b164fec91a6eb63475ebc9ce668/tree_sitter_cpp-0.23.4.tar.gz", hash = "sha256:6a59c4cebb1ad1dc2e8d586cf8a72b39d21b8108b7b139d089719e81a339e41d", size = 940358, upload-time = "2024-11-11T06:59:24.934Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/ac/11d56670f7b048362db872ca866fd00ba2002a322ab179f047b7c0fb2910/tree_sitter_cpp-0.23.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aacb1759f0efd9dbc25bd8ee88184a340483018869f75412d9c3bc32c039a520", size = 287861, upload-time = "2024-11-11T06:59:15.005Z" }, - { url = "https://files.pythonhosted.org/packages/12/1c/0337c016bdc00a77a3326d12f10ee836401dd28f27db6fd5b7734bfb21ed/tree_sitter_cpp-0.23.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc3c404d9f0cbd87951213a85440afbf4c31e718f8d907fa9ee12bea4b8d276f", size = 315513, upload-time = "2024-11-11T06:59:16.679Z" }, - { url = "https://files.pythonhosted.org/packages/b3/7b/dd38c049b10ed7fda118b903a1d28a8b55a36b98c30606ef90e8f374c6de/tree_sitter_cpp-0.23.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc43ddf1279d5d5a4ef190373f4cb16522801bec4492bcd4754edf2aeba2b7b", size = 334813, upload-time = "2024-11-11T06:59:18.253Z" }, - { url = "https://files.pythonhosted.org/packages/6a/4d/23e390234d2acd351f5563b1079c515d7c1fe13ddb7392cee543be74dda3/tree_sitter_cpp-0.23.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:773d2cafc08bbc0f998687fa33f42f378c1a371cdb582870c4d13abb06092706", size = 316110, upload-time = "2024-11-11T06:59:19.823Z" }, - { url = "https://files.pythonhosted.org/packages/32/c7/b94a7e0e803af9d3bd4608fb4f0cfb2e9e233abaf0a38c928bfb0b1a025d/tree_sitter_cpp-0.23.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:247d127f0eb6574b0f6b30c0151e0bd0774e2e7acf9c558bdf9fbb8adc2e80c0", size = 308242, upload-time = "2024-11-11T06:59:21.466Z" }, - { url = "https://files.pythonhosted.org/packages/37/7e/909e52b3dec09c475140b0e175511e275d0d00ba2dbd7c68102d377ae0f6/tree_sitter_cpp-0.23.4-cp39-abi3-win_amd64.whl", hash = "sha256:68606a45bea92669d155399e1239f771a7767d8683cd8f8e30e7d813107030ca", size = 290997, upload-time = "2024-11-11T06:59:22.432Z" }, - { url = "https://files.pythonhosted.org/packages/d4/6a/65435d4d1f4c735be7ffe52d7c2e7b8a7f7c2790343a2719c60c548611c8/tree_sitter_cpp-0.23.4-cp39-abi3-win_arm64.whl", hash = "sha256:712f84f18be94cbe2a148fa4fdf40fcf4a8c25a8f7670efb9f8a47ddec2fc281", size = 288203, upload-time = "2024-11-11T06:59:23.404Z" }, -] - -[[package]] -name = "tree-sitter-go" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/05/727308adbbc79bcb1c92fc0ea10556a735f9d0f0a5435a18f59d40f7fd77/tree_sitter_go-0.25.0.tar.gz", hash = "sha256:a7466e9b8d94dda94cae8d91629f26edb2d26166fd454d4831c3bf6dfa2e8d68", size = 93890, upload-time = "2025-08-29T06:20:25.044Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/aa/0984707acc2b9bb461fe4a41e7e0fc5b2b1e245c32820f0c83b3c602957c/tree_sitter_go-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b852993063a3429a443e7bd0aa376dd7dd329d595819fabf56ac4cf9d7257b54", size = 47117, upload-time = "2025-08-29T06:20:14.286Z" }, - { url = "https://files.pythonhosted.org/packages/32/16/dd4cb124b35e99239ab3624225da07d4cb8da4d8564ed81d03fcb3a6ba9f/tree_sitter_go-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:503b81a2b4c31e302869a1de3a352ad0912ccab3df9ac9950197b0a9ceeabd8f", size = 48674, upload-time = "2025-08-29T06:20:17.557Z" }, - { url = "https://files.pythonhosted.org/packages/86/fb/b30d63a08044115d8b8bd196c6c2ab4325fb8db5757249a4ef0563966e2e/tree_sitter_go-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04b3b3cb4aff18e74e28d49b716c6f24cb71ddfdd66768987e26e4d0fa812f74", size = 66418, upload-time = "2025-08-29T06:20:18.345Z" }, - { url = "https://files.pythonhosted.org/packages/26/21/d3d88a30ad007419b2c97b3baeeef7431407faf9f686195b6f1cad0aedf9/tree_sitter_go-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:148255aca2f54b90d48c48a9dbb4c7faad6cad310a980b2c5a5a9822057ed145", size = 72006, upload-time = "2025-08-29T06:20:19.14Z" }, - { url = "https://files.pythonhosted.org/packages/cd/d0/0dd6442353ced8a88bbda9e546f4ea29e381b59b5a40b122e5abb586bb6c/tree_sitter_go-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4d338116cdf8a6c6ff990d2441929b41323ef17c710407abe0993c13417d6aad", size = 70603, upload-time = "2025-08-29T06:20:21.544Z" }, - { url = "https://files.pythonhosted.org/packages/01/e2/ee5e09f63504fc286539535d374d2eaa0e7d489b80f8f744bb3962aff22a/tree_sitter_go-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5608e089d2a29fa8d2b327abeb2ad1cdb8e223c440a6b0ceab0d3fa80bdeebae", size = 66088, upload-time = "2025-08-29T06:20:22.336Z" }, - { url = "https://files.pythonhosted.org/packages/6e/b6/d9142583374720e79aca9ccb394b3795149a54c012e1dfd80738df2d984e/tree_sitter_go-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:30d4ada57a223dfc2c32d942f44d284d40f3d1215ddcf108f96807fd36d53022", size = 48152, upload-time = "2025-08-29T06:20:23.089Z" }, - { url = "https://files.pythonhosted.org/packages/9e/00/9a2638e7339236f5b01622952a4d71c1474dd3783d1982a89555fc1f03b1/tree_sitter_go-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:d5d62362059bf79997340773d47cc7e7e002883b527a05cca829c46e40b70ded", size = 46752, upload-time = "2025-08-29T06:20:24.235Z" }, -] - -[[package]] -name = "tree-sitter-json" -version = "0.24.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/29/e92df6dca3a6b2ab1c179978be398059817e1173fbacd47e832aaff3446b/tree_sitter_json-0.24.8.tar.gz", hash = "sha256:ca8486e52e2d261819311d35cf98656123d59008c3b7dcf91e61d2c0c6f3120e", size = 8155, upload-time = "2024-11-11T06:05:00.667Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/41/84866232980fb3cf0cff46f5af2dbb9bfa3324b32614c6a9af3d08926b72/tree_sitter_json-0.24.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:59ac06c6db1877d0e2076bce54a5fddcdd2fc38ca778905662e80fa9ffcea2ab", size = 8718, upload-time = "2024-11-11T06:04:49.779Z" }, - { url = "https://files.pythonhosted.org/packages/5c/31/102c15948d97b135611d6a995c97a3933c0e9745f25737723977f58e142c/tree_sitter_json-0.24.8-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:62b4c45b561db31436a81a3f037f71ec29049f4fc9bf5269b6ec3ebaaa35a1cd", size = 9163, upload-time = "2024-11-11T06:04:51.275Z" }, - { url = "https://files.pythonhosted.org/packages/28/64/aa44ea2f3d2e76ec086ce83902eb26b2ed0a92d3fd5e2714c9cb007e90d1/tree_sitter_json-0.24.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8627f7d375fda9fc193ebee368c453f374f65c2f25c58b6fea4e6b49a7fccbc", size = 17726, upload-time = "2024-11-11T06:04:52.732Z" }, - { url = "https://files.pythonhosted.org/packages/77/08/10001992526670e0d6f24c571b179f0ece90e5e014a4b98a3ce076884f32/tree_sitter_json-0.24.8-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85cca779872f7278f3a74eb38533d34b9c4de4fd548615e3361fa64fe350ad0a", size = 17236, upload-time = "2024-11-11T06:04:54.189Z" }, - { url = "https://files.pythonhosted.org/packages/92/64/908e9e0bd84fe3c81c564115d3bbe0e49b0e152784bbaf153d749d00bbe6/tree_sitter_json-0.24.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:deeb45850dcc52990fbb52c80196492a099e3fa3512d928a390a91cf061068cc", size = 16071, upload-time = "2024-11-11T06:04:55.628Z" }, - { url = "https://files.pythonhosted.org/packages/53/df/31daab1eedb445bef208a04fc35428de3afe2b37075fec84d7737e1c69de/tree_sitter_json-0.24.8-cp39-abi3-win_amd64.whl", hash = "sha256:e4849a03cd7197267b2688a4506a90a13568a8e0e8588080bd0212fcb38974e3", size = 11457, upload-time = "2024-11-11T06:04:57.698Z" }, - { url = "https://files.pythonhosted.org/packages/6c/3d/902d2f3125b6b90cebf404b63ca775bc6d82071ccc76c0d10fabfeb2febe/tree_sitter_json-0.24.8-cp39-abi3-win_arm64.whl", hash = "sha256:591e0096c882d12668b88f30d3ca6f85b9db3406910eaaab6afb6b17d65367dd", size = 10174, upload-time = "2024-11-11T06:04:59.309Z" }, -] - -[[package]] -name = "tree-sitter-python" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b8/8b/c992ff0e768cb6768d5c96234579bf8842b3a633db641455d86dd30d5dac/tree_sitter_python-0.25.0.tar.gz", hash = "sha256:b13e090f725f5b9c86aa455a268553c65cadf325471ad5b65cd29cac8a1a68ac", size = 159845, upload-time = "2025-09-11T06:47:58.159Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/64/a4e503c78a4eb3ac46d8e72a29c1b1237fa85238d8e972b063e0751f5a94/tree_sitter_python-0.25.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:14a79a47ddef72f987d5a2c122d148a812169d7484ff5c75a3db9609d419f361", size = 73790, upload-time = "2025-09-11T06:47:47.652Z" }, - { url = "https://files.pythonhosted.org/packages/e6/1d/60d8c2a0cc63d6ec4ba4e99ce61b802d2e39ef9db799bdf2a8f932a6cd4b/tree_sitter_python-0.25.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:480c21dbd995b7fe44813e741d71fed10ba695e7caab627fb034e3828469d762", size = 76691, upload-time = "2025-09-11T06:47:49.038Z" }, - { url = "https://files.pythonhosted.org/packages/aa/cb/d9b0b67d037922d60cbe0359e0c86457c2da721bc714381a63e2c8e35eba/tree_sitter_python-0.25.0-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:86f118e5eecad616ecdb81d171a36dde9bef5a0b21ed71ea9c3e390813c3baf5", size = 108133, upload-time = "2025-09-11T06:47:50.499Z" }, - { url = "https://files.pythonhosted.org/packages/40/bd/bf4787f57e6b2860f3f1c8c62f045b39fb32d6bac4b53d7a9e66de968440/tree_sitter_python-0.25.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be71650ca2b93b6e9649e5d65c6811aad87a7614c8c1003246b303f6b150f61b", size = 110603, upload-time = "2025-09-11T06:47:51.985Z" }, - { url = "https://files.pythonhosted.org/packages/5d/25/feff09f5c2f32484fbce15db8b49455c7572346ce61a699a41972dea7318/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6d5b5799628cc0f24691ab2a172a8e676f668fe90dc60468bee14084a35c16d", size = 108998, upload-time = "2025-09-11T06:47:53.046Z" }, - { url = "https://files.pythonhosted.org/packages/75/69/4946da3d6c0df316ccb938316ce007fb565d08f89d02d854f2d308f0309f/tree_sitter_python-0.25.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:71959832fc5d9642e52c11f2f7d79ae520b461e63334927e93ca46cd61cd9683", size = 107268, upload-time = "2025-09-11T06:47:54.388Z" }, - { url = "https://files.pythonhosted.org/packages/ed/a2/996fc2dfa1076dc460d3e2f3c75974ea4b8f02f6bc925383aaae519920e8/tree_sitter_python-0.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:9bcde33f18792de54ee579b00e1b4fe186b7926825444766f849bf7181793a76", size = 76073, upload-time = "2025-09-11T06:47:55.773Z" }, - { url = "https://files.pythonhosted.org/packages/07/19/4b5569d9b1ebebb5907d11554a96ef3fa09364a30fcfabeff587495b512f/tree_sitter_python-0.25.0-cp310-abi3-win_arm64.whl", hash = "sha256:0fbf6a3774ad7e89ee891851204c2e2c47e12b63a5edbe2e9156997731c128bb", size = 74169, upload-time = "2025-09-11T06:47:56.747Z" }, -] - -[[package]] -name = "tree-sitter-rust" -version = "0.24.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/87/75cbd22b927267d310f76cca1ab3c1d9d41035dfa3eb9cc95f96ee199440/tree_sitter_rust-0.24.2.tar.gz", hash = "sha256:54fb02a5911e345308b405174465112479f56dc39e3f1e7744d7568595f00db9", size = 339341, upload-time = "2026-03-27T21:08:55.629Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/24/2b2d33af5e27c84a4fde4e8cd2594bb4ab1e1cf48756a9f40dadc84956cc/tree_sitter_rust-0.24.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3620cfd12340efa43082d45df76349ff511893a9c361da2f8d6d51e307020a59", size = 129507, upload-time = "2026-03-27T21:08:47.585Z" }, - { url = "https://files.pythonhosted.org/packages/78/2a/cf39f881a545360b5a86bb1accba1f4acc713daab01fb9edd35b6e84f473/tree_sitter_rust-0.24.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:01a46622735498493f29f3e628a90de95c96a07bfbeb88996243eb986b1cee36", size = 136812, upload-time = "2026-03-27T21:08:48.761Z" }, - { url = "https://files.pythonhosted.org/packages/ca/45/a051bbd3045a61182dde25b93ae9a33d2677c935b16952283e12eaf46051/tree_sitter_rust-0.24.2-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e033c5a93b57c88e0a835880de39fc802909ff69f57aaff6000211c196ea5190", size = 164706, upload-time = "2026-03-27T21:08:49.605Z" }, - { url = "https://files.pythonhosted.org/packages/b5/f6/a5a146df5c0a5daea3ffcd5d7245775fe7f084357770d5a313dd6245ae78/tree_sitter_rust-0.24.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d76d1208c3638b871236090759dfc13d478921320653a6c9da5336e7c58f65a", size = 170310, upload-time = "2026-03-27T21:08:50.424Z" }, - { url = "https://files.pythonhosted.org/packages/95/a8/f85b1ca75e01361ca5f92d226593ca4857cea49551b9f6c8fa6fc08ea917/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:87930163a462408c49ab62c667e74029bc26b4cc7123dd1bdc7352215786c64a", size = 168668, upload-time = "2026-03-27T21:08:51.404Z" }, - { url = "https://files.pythonhosted.org/packages/a2/e1/3519f866a4679ca36acd9f5a06a779ecb8a92b18887c5546458d521df557/tree_sitter_rust-0.24.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da2b86099028fd42c6cd32878b7b16b01f8aac0f7b0e98742b7fa6bc3cf09b89", size = 162403, upload-time = "2026-03-27T21:08:52.588Z" }, - { url = "https://files.pythonhosted.org/packages/34/71/7ef609894dbfe5699eb16f7471f9b8af1d958d8ba3e29c238d7607e8cb47/tree_sitter_rust-0.24.2-cp39-abi3-win_amd64.whl", hash = "sha256:4529c125d928882ddfb879fdc6bc0704913261ecc078b6fa7902559e0daf200d", size = 129422, upload-time = "2026-03-27T21:08:54.031Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d8/050a781172745bc345f98abb7c56e72022ea0790f8e793de981c83c2ef15/tree_sitter_rust-0.24.2-cp39-abi3-win_arm64.whl", hash = "sha256:66ba90f61bd54f4c4f5d30434957daf64507c16b0313df76becb37d63f70a227", size = 128245, upload-time = "2026-03-27T21:08:54.803Z" }, -] - -[[package]] -name = "tree-sitter-yaml" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/b6/941d356ac70c90b9d2927375259e3a4204f38f7499ec6e7e8a95b9664689/tree_sitter_yaml-0.7.2.tar.gz", hash = "sha256:756db4c09c9d9e97c81699e8f941cb8ce4e51104927f6090eefe638ee567d32c", size = 84882, upload-time = "2025-10-07T14:40:36.071Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/29/c0b8dbff302c49ff4284666ffb6f2f21145006843bb4c3a9a85d0ec0b7ae/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:7e269ddcfcab8edb14fbb1f1d34eed1e1e26888f78f94eedfe7cc98c60f8bc9f", size = 43898, upload-time = "2025-10-07T14:40:29.486Z" }, - { url = "https://files.pythonhosted.org/packages/18/0d/15a5add06b3932b5e4ce5f5e8e179197097decfe82a0ef000952c8b98216/tree_sitter_yaml-0.7.2-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:0807b7966e23ddf7dddc4545216e28b5a58cdadedcecca86b8d8c74271a07870", size = 44691, upload-time = "2025-10-07T14:40:30.369Z" }, - { url = "https://files.pythonhosted.org/packages/72/92/c4b896c90d08deb8308fadbad2210fdcc4c66c44ab4292eac4e80acb4b61/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1a5c60c98b6c4c037aae023569f020d0c489fad8dc26fdfd5510363c9c29a41", size = 91430, upload-time = "2025-10-07T14:40:31.16Z" }, - { url = "https://files.pythonhosted.org/packages/89/59/61f1fed31eb6d46ff080b8c0d53658cf29e10263f41ef5fe34768908037a/tree_sitter_yaml-0.7.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88636d19d0654fd24f4f242eaaafa90f6f5ebdba8a62e4b32d251ed156c51a2a", size = 92428, upload-time = "2025-10-07T14:40:31.954Z" }, - { url = "https://files.pythonhosted.org/packages/e3/62/a33a04d19b7f9a0ded780b9c9fcc6279e37c5d00b89b00425bb807a22cc2/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d2e8f0bb14aa4537320952d0f9607eef3021d5aada8383c34ebeece17db1e06", size = 90580, upload-time = "2025-10-07T14:40:33.037Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e7/9525defa7b30792623f56b1fba9bbba361752348875b165b8975b87398fd/tree_sitter_yaml-0.7.2-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:74ca712c50fc9d7dbc68cb36b4a7811d6e67a5466b5a789f19bf8dd6084ef752", size = 90455, upload-time = "2025-10-07T14:40:33.778Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d6/8d1e1ace03db3b02e64e91daf21d1347941d1bbecc606a5473a1a605250d/tree_sitter_yaml-0.7.2-cp310-abi3-win_amd64.whl", hash = "sha256:7587b5ca00fc4f9a548eff649697a3b395370b2304b399ceefa2087d8a6c9186", size = 45514, upload-time = "2025-10-07T14:40:34.562Z" }, - { url = "https://files.pythonhosted.org/packages/d8/c7/dcf3ea1c4f5da9b10353b9af4455d756c92d728a8f58f03c480d3ef0ead5/tree_sitter_yaml-0.7.2-cp310-abi3-win_arm64.whl", hash = "sha256:f63c227b18e7ce7587bce124578f0bbf1f890ac63d3e3cd027417574273642c4", size = 44065, upload-time = "2025-10-07T14:40:35.337Z" }, -] - -[[package]] -name = "typer" -version = "0.26.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "rich" }, - { name = "shellingham" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/ed/ef06584ccdd5c410df0837951ecd7e15d9a6144ea1bd4c73cecab1a89891/typer-0.26.7.tar.gz", hash = "sha256:e314a34c617e419c091b2830dda3ea1f257134ff593061a8f5b9717ab8dddb3a", size = 201709, upload-time = "2026-06-03T07:18:06.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/24/25/2201973529af2c954de0bb725323c3aaed6d7f0ceee8f550dec9185df013/typer-0.26.7-py3-none-any.whl", hash = "sha256:5c87cfbc5d34491c5346ebf49c23e18d56ccb863268d3a8d592b26087c2f5e58", size = 122456, upload-time = "2026-06-03T07:18:05.732Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/26/b09b8010994eccc3c09092e6b34058f36a460eea2d4c3e8b910c695975a0/typing_inspection-0.4.4.tar.gz", hash = "sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47", size = 76928, upload-time = "2026-08-12T12:37:25.997Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl", hash = "sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147", size = 14750, upload-time = "2026-08-12T12:37:24.648Z" }, -] - -[[package]] -name = "tzdata" -version = "2026.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/31/3d74fa778a63b98b7374323befcc0be5ab3bd94afd4096a0124e7379152c/tzdata-2026.4.tar.gz", hash = "sha256:f1b8bd365d8d210c55353f4d7f8d6d8561c0ba50d704b700d195a9424bba0d79", size = 199350, upload-time = "2026-09-12T12:56:03.251Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/bc/8737e8d54cf51106118039b83f485a4783112fab49ea9d044b234978a46e/tzdata-2026.4-py2.py3-none-any.whl", hash = "sha256:c2169a8b0a7a5e9674da5a135ccdfb2b3e671b333ed9fed17b41f73c34476e81", size = 347494, upload-time = "2026-09-12T12:56:01.67Z" }, -] - -[[package]] -name = "urllib3" -version = "2.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" }, -] - -[[package]] -name = "west" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama" }, - { name = "packaging" }, - { name = "pykwalify" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/54/8e/ddb81e2635e58d156092556934a2cffe245978c91e4a08e11d72591f46e4/west-1.5.0.tar.gz", hash = "sha256:7088fe0e9afe0719ebee95c51c529149f7bcfc919d83a8206d35fa9c683ed0a5", size = 149326, upload-time = "2025-09-23T14:45:52.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/1d/e50fb71de492abb549917fdbf13c4a8a8459af9d78a2fa47905804905c36/west-1.5.0-py3-none-any.whl", hash = "sha256:f71411d11ed9ed00847405c6a83600374c2e0cf8676ed5af5d8b572f92c4765a", size = 106991, upload-time = "2025-09-23T14:45:50.661Z" }, -] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, -] - -[[package]] -name = "wrapt" -version = "2.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/42/a6/6375d56c44d590ef24acf0f8f5bf7ed768ff7a510b959306ec412611e90f/wrapt-2.4.1.tar.gz", hash = "sha256:fd6390aab9e8aa40c52eff3c180f098e8d9f5894b1fd4c4fd2c207067b33ed16", size = 164597, upload-time = "2026-09-10T23:12:16.811Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/1f/a2f3225c5ecf522684c1d051aea8ce8253f240e55be75826b787777afd6c/wrapt-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7e86fbc2ac8a363ea04abf631fad82720e16b17a25020f32dbe9b24a2ed2b0e3", size = 98890, upload-time = "2026-09-10T23:10:07.876Z" }, - { url = "https://files.pythonhosted.org/packages/68/6c/eb45660fd4d92cce11ec923f55bb2e647a6c18d30e53734eb07a3c530e31/wrapt-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24389748f0b9d5b67e478fad4fc8b3f1108422ef80716e48eead6cebcebbff08", size = 98742, upload-time = "2026-09-10T23:10:09.236Z" }, - { url = "https://files.pythonhosted.org/packages/a3/4f/17a89a580cb0082e61b8375074d5c9e5d38e4aa83ee19b6174ee472d17c2/wrapt-2.4.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30d11c289b013bf384ff1a1a6553f150d0b855901708a9bef667a5680f8247c9", size = 236301, upload-time = "2026-09-10T23:10:11.039Z" }, - { url = "https://files.pythonhosted.org/packages/01/ca/4700eb008a34bf02de328806ddde15fc84c8d1e65d3dcafb92a935a50319/wrapt-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9356dbb59199a0e4709de35fa4a1ac1a88ef6da99711a397f5b009233faff326", size = 237805, upload-time = "2026-09-10T23:10:12.594Z" }, - { url = "https://files.pythonhosted.org/packages/75/5d/26c1740299b29e190d5f4b4a99eb001401042a9d9ab338e3f8e1ce140ecb/wrapt-2.4.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8342f332dada211f64b74609e332d727b13315e9a83177f7918bf68c59f815f2", size = 217037, upload-time = "2026-09-10T23:10:14.028Z" }, - { url = "https://files.pythonhosted.org/packages/3a/55/ec72991153a2ae8b40238bc44cec7c3ddf7706ef6e2d314b0c6f5c7febce/wrapt-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2f86e328c482bc5383b4eda5094be0bed3617fc3076aa9225ff1a9eb6372de9b", size = 234659, upload-time = "2026-09-10T23:10:15.384Z" }, - { url = "https://files.pythonhosted.org/packages/51/32/7cfa1e070dcda76ea56a3e252341cba1cd1e9412baf23cd7adfebf1114e2/wrapt-2.4.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4dc92697444ee380544fbb43c86612d8486529aadf917c22b5524141d4af074c", size = 214590, upload-time = "2026-09-10T23:10:16.744Z" }, - { url = "https://files.pythonhosted.org/packages/79/10/248841cb30107f6f32c53a02662e1c3e0c7c06bea0b8ebfaaee94885dcee/wrapt-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edd03758a7578526642508b8833d43496fdfba0f64e0025dfca153a7c1777735", size = 225103, upload-time = "2026-09-10T23:10:18.346Z" }, - { url = "https://files.pythonhosted.org/packages/90/02/5b2bf7b35b008a39939a2908e85dba3b867596e535fc9f12ddf3ba1fcaf6/wrapt-2.4.1-cp312-cp312-win32.whl", hash = "sha256:5d83e412665aeb1e854eefbf1564d0d67872d9994b502a0bce96e6ff7f4970b7", size = 93441, upload-time = "2026-09-10T23:10:19.81Z" }, - { url = "https://files.pythonhosted.org/packages/d4/2a/47be56772bfb07ef242d6e924049688e38384af2b2fc99b0f31180988448/wrapt-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:b4e7efdd476ac631a0181551fd9aace844765ea3ce2b5133b194fae4421e8ad0", size = 98808, upload-time = "2026-09-10T23:10:21.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ec/40e4c9735626afd1dc0eeb310310278361f192800503cda0f5b3d8d24db4/wrapt-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:38819761401baa2d11916d7265b82f23265f8fe5a31c431dd7c24a8863c65f88", size = 95240, upload-time = "2026-09-10T23:10:22.39Z" }, - { url = "https://files.pythonhosted.org/packages/5c/1a/9b5aa3c2391aa6d00fffa085c2219e8fc91cd4d2b9b080d2b4e4b6b93f42/wrapt-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55f36bb1461f93beaf18d818e568b5de343dd8fade7456773d201a38ee723bd3", size = 98597, upload-time = "2026-09-10T23:10:23.723Z" }, - { url = "https://files.pythonhosted.org/packages/fe/07/dc98150c2f9ee5b5fcbd841765e178ea6cc6c43733ab8d1f5181a4fb9f3d/wrapt-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:53e15cd74bd6b84d7fa90b93dda7334d85f4641fae97632de8aa61d268dfd145", size = 98842, upload-time = "2026-09-10T23:10:25.109Z" }, - { url = "https://files.pythonhosted.org/packages/08/c2/0e772a570e8d75c1b3ec45930a3023492fa68223f8f5e3485af4844c10c3/wrapt-2.4.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:be6cdd7121adc89a6f52e3c2f4e26a2d4dcdc1c0fde47e3156234db8939e4cdc", size = 234642, upload-time = "2026-09-10T23:10:26.644Z" }, - { url = "https://files.pythonhosted.org/packages/76/25/4ce4d02dd95ff9ed972a2fc396d04fec636daa5a4ac18cc73a3dc20aa6c3/wrapt-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03b5598edd435373278731d0d53449ce7a9626bc48d5548e4a71124ee3e526a1", size = 235484, upload-time = "2026-09-10T23:10:28.098Z" }, - { url = "https://files.pythonhosted.org/packages/fd/80/436ef1df620ef8edd9ef057b4d55a0cd704435ff66852a0ef26cbaa02a58/wrapt-2.4.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fdc997819a6df4c65bdb0a1c5601c98b479ee34cc2f94ffa98a767034ad6366d", size = 214371, upload-time = "2026-09-10T23:10:29.841Z" }, - { url = "https://files.pythonhosted.org/packages/6a/2c/cc5b7503843399087db3106a7cf60d60d0900f69539e96148b9fff116291/wrapt-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3bfc6907ebed560d2d3f677c3b17bf6199679163b6c6e475035c9dca497d1697", size = 232347, upload-time = "2026-09-10T23:10:31.511Z" }, - { url = "https://files.pythonhosted.org/packages/86/fb/17668b1ca572c44b458c09d76064d35f5f57d5ac7629248871604bef816c/wrapt-2.4.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a0c3b217332cf0c4df085fec41c126bf7507d6c1ca0efb3ba8d2b3fd234d4e73", size = 212792, upload-time = "2026-09-10T23:10:32.968Z" }, - { url = "https://files.pythonhosted.org/packages/b8/2b/0c5de10d7259e07c478c44bf2fbe179c6878727d09edf0632b97884801db/wrapt-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a23b89621cfeb3329b1a290596bf402e61d7d5a647a65ca8ea735e48771b71d4", size = 223585, upload-time = "2026-09-10T23:10:34.43Z" }, - { url = "https://files.pythonhosted.org/packages/aa/36/013dce1c687f8f1c87b1e78f403d04c80ae9b2ae77de4ddba16069a3e7d1/wrapt-2.4.1-cp313-cp313-win32.whl", hash = "sha256:bc67d4872af5ab2dc1b88904097b92ac00e7658fdf010dee36807807fe882ac4", size = 93425, upload-time = "2026-09-10T23:10:35.901Z" }, - { url = "https://files.pythonhosted.org/packages/b7/1d/d53bcf5910209a45191c8bc173ff0e00830416547d8038772dc444932ee0/wrapt-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:1fe758b9c2d49138231ec3efabd106fec665f86fec50b3d02d7edd75f08a69ca", size = 98569, upload-time = "2026-09-10T23:10:37.316Z" }, - { url = "https://files.pythonhosted.org/packages/e8/1f/759d0c522918f9dfb620136622d8e1ce619570adda0de8fa2cfbb55cbb86/wrapt-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c974c36e8205255a3947dad9c2fe000431b57dd522946e56c192174a7f92a0f", size = 95272, upload-time = "2026-09-10T23:10:38.657Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a2/edcfc8d9a30375791b775715f8501364588f65b494ec4f6930568a19e765/wrapt-2.4.1-py3-none-any.whl", hash = "sha256:1e84ec5d89a0a07a0ef6bcd343f5c8ecdc95601d71de3058cdc63274e86c193c", size = 75317, upload-time = "2026-09-10T23:12:14.82Z" }, -] - -[[package]] -name = "zstandard" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, - { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, - { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, - { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, - { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, - { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, - { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, - { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, - { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, - { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, - { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, - { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, - { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, - { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, - { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, - { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" }, - { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" }, - { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" }, - { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" }, - { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" }, - { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" }, - { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" }, - { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" }, - { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" }, - { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" }, - { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" }, - { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" }, - { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" }, -] From 464cdb119b93c59b7d1555ca4b6bf0523d50949e Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:51:34 +0200 Subject: [PATCH 02/24] docs: highlight the JSON blocks with comments as json Pygments knows no jsonc lexer, so Sphinx warned twice. Its JSON lexer accepts // and /* */ comments, and ubc has no lexer check. --- doc/cross-platform-alignment-proposal.md | 2 +- doc/devcontainer-and-bootstrap-design.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/cross-platform-alignment-proposal.md b/doc/cross-platform-alignment-proposal.md index 0e29922..5df628e 100644 --- a/doc/cross-platform-alignment-proposal.md +++ b/doc/cross-platform-alignment-proposal.md @@ -123,7 +123,7 @@ toolchain layer; drop SPLed's `docker-compose` indirection unless multi-service Shared skeleton (identical in both repos except the `Dockerfile` toolchain layer and extensions): -```jsonc +```json { "name": "", "build": { "dockerfile": "Dockerfile" }, diff --git a/doc/devcontainer-and-bootstrap-design.md b/doc/devcontainer-and-bootstrap-design.md index 7458191..7984a49 100644 --- a/doc/devcontainer-and-bootstrap-design.md +++ b/doc/devcontainer-and-bootstrap-design.md @@ -207,7 +207,7 @@ the symptom (poks downloads timing out) points nowhere near `devcontainer.json`. `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` for build-time egress (apt/pipx/uv). Rather than baking proxy config into a portable image, pass them as build args on demand: -```jsonc +```json // devcontainer.json build.args — only for a cold build behind the proxy: "HTTP_PROXY": "${localEnv:HTTP_PROXY}", "HTTPS_PROXY": "${localEnv:HTTPS_PROXY}", From cbef21a455b49d33a19ef5ba2c93c89ee795d3c3 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:51:34 +0200 Subject: [PATCH 03/24] docs: give every page a place in the navigation The three developer notes are orphan: true, which both readers honour. The requirements traceability matrix joins the start page's toctree, because it is product documentation. Four 'not in any toctree' warnings go in each reader. --- doc/cross-platform-alignment-proposal.md | 4 ++++ doc/devcontainer-and-bootstrap-design.md | 4 ++++ doc/testing_strategy.md | 4 ++++ index.md | 1 + 4 files changed, 13 insertions(+) diff --git a/doc/cross-platform-alignment-proposal.md b/doc/cross-platform-alignment-proposal.md index 5df628e..2e6ba49 100644 --- a/doc/cross-platform-alignment-proposal.md +++ b/doc/cross-platform-alignment-proposal.md @@ -1,3 +1,7 @@ +--- +orphan: true +--- + # Cross-Platform Alignment Proposal: `hammocking` & `SPLed` **Goal:** Develop and build both projects consistently under **Windows**, **Linux**, and inside a diff --git a/doc/devcontainer-and-bootstrap-design.md b/doc/devcontainer-and-bootstrap-design.md index 7984a49..3a68231 100644 --- a/doc/devcontainer-and-bootstrap-design.md +++ b/doc/devcontainer-and-bootstrap-design.md @@ -1,3 +1,7 @@ +--- +orphan: true +--- + # SPLed DevContainer, DevPod & Bare-Linux Bootstrap — Design **Status:** implemented and validated (see § Validation). diff --git a/doc/testing_strategy.md b/doc/testing_strategy.md index ae3b53e..138d843 100644 --- a/doc/testing_strategy.md +++ b/doc/testing_strategy.md @@ -1,3 +1,7 @@ +--- +orphan: true +--- + # Testing Strategy This document describes the testing strategy for the SPLED project, including diff --git a/index.md b/index.md index 10fbc63..fa6be49 100644 --- a/index.md +++ b/index.md @@ -10,6 +10,7 @@ doc/customer_requirements/index doc/software_architecture/index doc/sw_requirements/index doc/components/index +doc/results/index ``` ````{if} var.build_config.target == "reports" From d610d5b532de4be056ab0cc39b34cb7682fafcb0 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:51:34 +0200 Subject: [PATCH 04/24] feat(needs): declare the fields the imported needs carry The ubConnect ReqIF export carries configuration, origin, priority and reqif_uuid, and the CSV import carries level. Undeclared, Sphinx warned and dropped them, and ubc dropped them without a warning. --- ubproject.toml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ubproject.toml b/ubproject.toml index df7065d..6854b46 100644 --- a/ubproject.toml +++ b/ubproject.toml @@ -174,6 +174,41 @@ nullable = true [needs.fields.image.schema] type = "string" +[needs.fields.configuration] +description = "Configuration, from the ubConnect ReqIF export" +nullable = true + +[needs.fields.configuration.schema] +type = "string" + +[needs.fields.origin] +description = "Origin, from the ubConnect ReqIF export" +nullable = true + +[needs.fields.origin.schema] +type = "string" + +[needs.fields.priority] +description = "Priority, from the ubConnect ReqIF export" +nullable = true + +[needs.fields.priority.schema] +type = "string" + +[needs.fields.reqif_uuid] +description = "ReqIF identifier, from the ubConnect ReqIF export" +nullable = true + +[needs.fields.reqif_uuid.schema] +type = "string" + +[needs.fields.level] +description = "Hierarchy level, from the CSV import" +nullable = true + +[needs.fields.level.schema] +type = "integer" + [codelinks] outdir = "output" set_remote_url = true From d84d0521ec739d8bdd1ca8601b0326713c0fbcd0 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:51:34 +0200 Subject: [PATCH 05/24] chore(ubc): ignore tabs in the generated source listings clanguru documents declarations from system headers in the test listings, and their tabs raised 25 ubc warnings; the SPLed sources contain none. lint.per-file-ignores scopes the ignore to the listings until clanguru documents only the file's own declarations. --- ubproject.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ubproject.toml b/ubproject.toml index 6854b46..65ed620 100644 --- a/ubproject.toml +++ b/ubproject.toml @@ -209,6 +209,13 @@ nullable = true [needs.fields.level.schema] type = "integer" +# clanguru documents declarations from system headers in the test listings, and +# their tabs trip ubc. Ignored for the generated listings only, until clanguru +# documents just the declarations of the file itself. The key is spelled with +# hyphens: ubc silently ignores `per_file_ignores`. +[lint.per-file-ignores] +"generated/**/__source_docs/**" = ["source.tab_in_line"] + [codelinks] outdir = "output" set_remote_url = true From d5f4fb15f439ea2925750b7bcd4337521689f561 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:51:34 +0200 Subject: [PATCH 06/24] fix(brightness_controller): implement SWDD_BC-203 in the automatic branch only The runnable's SWIMPL_BC-003c sits outside any #ifdef, yet implemented SWDD_BC-203, which exists only with automatic brightness adjustment: a dead link in Sleep and IDEA/Sloemada. The link moves to a need inside the automatic branch. Upstream has the same modelling bug in the @rst block of SWIMPL_BC-003 (since b593419); this commit is kept separate so it can go upstream on its own. --- components/brightness_controller/src/brightness_controller.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/brightness_controller/src/brightness_controller.c b/components/brightness_controller/src/brightness_controller.c index c5dc26f..5b507a1 100644 --- a/components/brightness_controller/src/brightness_controller.c +++ b/components/brightness_controller/src/brightness_controller.c @@ -53,11 +53,12 @@ static brightness_t manualBrightnessAdjustment(void) #endif /* CONFIG_BRIGHTNESS_ADJUSTMENT_AUTOMATIC */ -// @need Brightness Controller runnable, SWIMPL_BC-003c, impl, [SWDD_BC-200, SWDD_BC-202, SWDD_BC-203], [REQ_46, REQ_47, REQ_48] +// @need Brightness Controller runnable, SWIMPL_BC-003c, impl, [SWDD_BC-200, SWDD_BC-202], [REQ_46, REQ_47, REQ_48] void brightnessController(void) { brightness_t brightnessValue = 0; #ifdef CONFIG_BRIGHTNESS_ADJUSTMENT_AUTOMATIC + // @need Automatic brightness adjustment in the runnable, SWIMPL_BC-004c, impl, [SWDD_BC-203], [REQ_48] static BrightnessAdjustmentData data = { .ticksCounter = 0, .period = BRIGHTNESS_PERIOD_TICKS, From db9e968e283a7f002721271bbd4d10ba13bbc3a1 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:51:34 +0200 Subject: [PATCH 07/24] docs(agents): a condition that cannot be evaluated warns sphinx-needs warns for an {if} block and sphinx-mounts for a rule, and both leave the content out. AGENTS.md said this happens silently. --- AGENTS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 62524b2..4df6f00 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -189,9 +189,9 @@ that `tools/variant_data.py` writes, exposed as `var.*`. The governing rule: > **Everything a condition may name has to be IN the variant data file.** A key that only `conf.py` knows is invisible to ubCode, `ubc` and a reviewer's -editor, so their view of the project silently disagrees with the build — -silently, because a condition a tool cannot evaluate gates content **off** -rather than failing. That is why `conf.py` does not touch the file at all: +editor, so their view of the project disagrees with the build. A condition a +tool cannot evaluate gates its content **off**: the reader warns and carries on +without it. That is why `conf.py` does not touch the file at all: sphinx-needs reads it, and which cell a build reads is a command-line override (`-D needs_variant_data_file=...`), exactly as for `ubc`. @@ -249,8 +249,8 @@ project shows lives in the tree or under `generated/`. The `{if}` directive takes a real Python expression, so a bare `var.features.BLINKING` is enough. A `variant_sources` condition uses a restricted grammar that needs `== True`. And a condition that cannot be -evaluated **excludes** what it gates, so a typo silently shrinks the document -set rather than failing loudly — which is what `test_ubproject_config.py` is +evaluated **excludes** what it gates: Sphinx warns (sphinx-needs for an `{if}` +block, sphinx-mounts for a rule) and builds on without that content — which is what `test_ubproject_config.py` is for. ### Checking with the other reader From 68259771375c9b0effec14fab5d3d78bdb0a2e98 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:53:24 +0200 Subject: [PATCH 08/24] docs(variants): trace code to the design, per variant A new use case for one-line implementation needs: where they live, how the component page shows them, and how #ifdef branches make a link hold only in some variants, with the compile database caveats. Two troubleshooting rows and a rule of thumb join it; use cases 17 and 18 become 18 and 19. --- VARIANTS.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/VARIANTS.md b/VARIANTS.md index d501d4c..383904c 100644 --- a/VARIANTS.md +++ b/VARIANTS.md @@ -27,7 +27,8 @@ document is a template, and nothing in `conf.py` or CMake decides what a page co | include a document only when its component is in the variant | [12](#12-include-a-document-only-when-its-component-is-in-the-variant) | | put one document in different places per variant | [13](#13-place-one-document-differently-per-variant) | | add a component, a feature or a variant | [14](#14-add-a-component), [15](#15-add-or-change-a-feature), [16](#16-add-a-variant) | -| check my work, or find out why something is missing | [17](#17-run-the-checks), [18](#18-when-something-looks-wrong) | +| link code to the design, per variant | [17](#17-trace-code-to-the-design-per-variant) | +| check my work, or find out why something is missing | [18](#18-run-the-checks), [19](#19-when-something-looks-wrong) | New to SPLed? Go through use cases 1 to 8 once, in order. @@ -341,7 +342,7 @@ A `reports` build adds test results and coverage. Gate the section on the target ````` The component pages end with a block like this one. The `generated/…` names are the same in every -variant ([18](#18-when-something-looks-wrong) explains the `generated` link). +variant ([19](#19-when-something-looks-wrong) explains the `generated` link). ### 12. Include a document only when its component is in the variant @@ -406,7 +407,9 @@ without a warning. ### 14. Add a component 1. Add `spl_add_component(components/)` to the `parts.cmake` of every variant that has it. -2. Write its documentation in `components//doc/index.md`. +2. Write its documentation in `components//doc/index.md`. If its sources implement design + needs, give that page a `## Traceability` section with a `src-trace` block + ([17](#17-trace-code-to-the-design-per-variant)). 3. Add one rule to `ubproject.toml`, next to the others: copy the `auto_off` rule from [12](#12-include-a-document-only-when-its-component-is-in-the-variant) and change its paths. 4. Add one line to the toctree in `doc/components/index.md`: `/components//doc/index`. @@ -454,9 +457,45 @@ CMake sees the same symbol as `NIGHT_MODE="True"` for conditional components; se No rule and no document has to change. The rules gate on components, so the new variant gets its documents from its `parts.cmake`. +### 17. Trace code to the design, per variant + +An implementation need lives in the source file, as a one-line comment right above the code it +describes: + +```c +// @need Periodic Brightness Adjustment, SWIMPL_BC-001c, impl, [SWDD_BC-100, SWDD_BC-102], [REQ_46, REQ_48] +``` + +The fields are the title, the ID, the type, the design needs it implements and the requirements it +fulfills. Each component's page shows its needs with one `src-trace` block, in a `## Traceability` +section: + +````text +```{src-trace} +:project: components +:directory: brightness_controller/src +``` +```` + +Both readers read the same comments, so the needs are part of the `docs` build, and ubCode shows +them without building anything. Each need links to its line on GitHub. Test specifications stay in +`@rst` blocks in the test sources. + +**The preprocessor decides per variant.** A need inside an `#ifdef` branch exists only in the +variants that compile that branch. A link that holds only in some variants therefore belongs to a +need inside that branch. `SWDD_BC-203` exists only with automatic brightness adjustment, so the +runnable's automatic branch carries `SWIMPL_BC-004c` for it, and the runnable's own need +`SWIMPL_BC-003c` does not link it. + +codelinks takes the branches from `build/compile_commands.json`, which VS Code writes when it +configures a CMake build (`cmake.copyCompileCommands`). Without that file, codelinks treats every +`#ifdef` as false, and Sphinx warns. A `test` kit database does not help yet: libclang rejects its +`-save-temps` option, so codelinks skips the file and its needs disappear. Configure a `prod` kit +build for the editor until codelinks drops that option. + ## Check -### 17. Run the checks +### 18. Run the checks ```bash python -m pytest -m docs # the documentation gate @@ -473,7 +512,7 @@ python -m pytest test/test_ubproject_config.py test/test_variant_data.py # rul - No compiler is needed. The tests regenerate `build/variants/`, and restore the `generated` link when they have pointed it elsewhere. -### 18. When something looks wrong +### 19. When something looks wrong | What you see | What to do | | --- | --- | @@ -482,6 +521,8 @@ python -m pytest test/test_ubproject_config.py test/test_variant_data.py # rul | A change to a file under `build/` is gone | `build/` is generated and rewritten on every run. Change `KConfig`, a `config.txt` or a `parts.cmake` instead, or try the change on a copy ([7](#7-try-a-change-without-touching-the-product)). | | `python tools/variant_data.py --all --check` fails | The variant data is older than its sources. Run `python tools/variant_data.py --all`. | | A documentation build stops with `SPL_SPHINX_BINARY_DIR … leads to …, but this build writes to …` | `generated` is a link (a junction on Windows) to the build directory configured last. It gives every generated page the same name in every variant. Since another build was configured, it leads elsewhere: configure this build again, then build its documentation. | +| A link from a need in the code is dead in one variant | The need sits outside the `#ifdef` that decides the link. Give the link to a need inside the branch that implements it ([17](#17-trace-code-to-the-design-per-variant)). | +| Sphinx warns `compile_commands … is not a readable file` | codelinks has no compile database and treats every `#ifdef` as false. Configure the variant's `prod` kit in VS Code, which writes `build/compile_commands.json` ([17](#17-trace-code-to-the-design-per-variant)). | | ubCode shows no test results or coverage | That is expected. Those pages exist only after a CMake build of the `reports` target, and ubCode does not follow the `generated` link. | ## Rules of thumb @@ -492,6 +533,8 @@ python -m pytest test/test_ubproject_config.py test/test_variant_data.py # rul gate on the variant name. - No Jinja in documents, no conditions in toctree entries, and no variant selection in `conf.py`. Select a variant with `-D` for Sphinx and `-c` for ubc. +- A link that holds only in some variants belongs to a need inside the `#ifdef` branch that + implements it. - Do not edit `build/` or `generated/`. - After changing a variant's sources, regenerate and run `python -m pytest -m docs`. From dd8216eed934c87bc84d5e693e23348430c3359c Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:55:29 +0200 Subject: [PATCH 09/24] refactor: define implementation needs as one-line comments Move the ten `.. impl::` needs out of their `@rst`/`@endrst` blocks in light_controller, flight_controller, main_control_knob and power_signal_processing and into `// @need` one-line markers, matching the style brightness_controller already used. Titles, ids and the implements/fulfills links are unchanged. ubproject.toml replaces the per-component codelinks project with one shared `components` project rooted at `components/`; each component page traces its own source through it with a `src-trace` block. The source listings no longer define needs, so the integration suite's second listings stop duplicating them. --- components/brightness_controller/doc/index.md | 3 +- .../examples/flight_controller/doc/index.md | 7 +++ .../flight_controller/src/flight_controller.c | 16 +----- components/light_controller/doc/index.md | 7 +++ .../light_controller/src/light_controller.c | 52 +++---------------- components/main_control_knob/doc/index.md | 7 +++ .../main_control_knob/src/main_control_knob.c | 9 +--- .../power_signal_processing/doc/index.md | 7 +++ .../src/power_signal_processing.c | 10 +--- ubproject.toml | 24 ++++----- 10 files changed, 52 insertions(+), 90 deletions(-) diff --git a/components/brightness_controller/doc/index.md b/components/brightness_controller/doc/index.md index f2b9408..554587e 100644 --- a/components/brightness_controller/doc/index.md +++ b/components/brightness_controller/doc/index.md @@ -66,7 +66,8 @@ The Brightness Controller is use the RTE interface `RteSetBrightnessAdjustmentCo ## Traceability ```{src-trace} -:project: brightness_controller +:project: components +:directory: brightness_controller ``` ````{if} var.build_config.target == "reports" diff --git a/components/examples/flight_controller/doc/index.md b/components/examples/flight_controller/doc/index.md index c468209..35403b4 100644 --- a/components/examples/flight_controller/doc/index.md +++ b/components/examples/flight_controller/doc/index.md @@ -91,3 +91,10 @@ flowchart TD off_course -- "False" --> FALSE ``` + +## Traceability + +```{src-trace} +:project: components +:directory: examples/flight_controller +``` diff --git a/components/examples/flight_controller/src/flight_controller.c b/components/examples/flight_controller/src/flight_controller.c index 31a6825..b2432e6 100644 --- a/components/examples/flight_controller/src/flight_controller.c +++ b/components/examples/flight_controller/src/flight_controller.c @@ -5,13 +5,7 @@ // See this youtube video for more information: // https://www.youtube.com/watch?v=k0_PF8MtEEo -/** - * @rst - * .. impl:: Abort decision logic - * :id: SWIMPL_FC-001 - * :implements: SWDD_FC-100, SWDD_FC-101, SWDD_FC-102 - * @endrst - */ +// @need Abort decision logic, SWIMPL_FC-001, impl, [SWDD_FC-100, SWDD_FC-101, SWDD_FC-102], [] SPLE_TESTABLE_STATIC bool_t CheckAbort(bool_t abort_commanded, bool_t valid_abort_command, bool_t off_course) { bool_t result; @@ -26,13 +20,7 @@ SPLE_TESTABLE_STATIC bool_t CheckAbort(bool_t abort_commanded, bool_t valid_abor return result; } -/** - * @rst - * .. impl:: Flight Controller's main runnable - * :id: SWIMPL_FC-002 - * :implements: SWDD_FC-103, SWDD_FC-200, SWDD_FC-201, SWDD_FC-202, SWDD_FC-203, SWDD_FC-204 - * @endrst - */ +// @need Flight Controller's main runnable, SWIMPL_FC-002, impl, [SWDD_FC-103, SWDD_FC-200, SWDD_FC-201, SWDD_FC-202, SWDD_FC-203, SWDD_FC-204], [] void flightController(void) { /* Get all relevant signals */ diff --git a/components/light_controller/doc/index.md b/components/light_controller/doc/index.md index fe28c88..90dca9c 100644 --- a/components/light_controller/doc/index.md +++ b/components/light_controller/doc/index.md @@ -111,6 +111,13 @@ stateDiagram-v2 ``` ```` +## Traceability + +```{src-trace} +:project: components +:directory: light_controller +``` + ````{if} var.build_config.target == "reports" ## Verification diff --git a/components/light_controller/src/light_controller.c b/components/light_controller/src/light_controller.c index 8e23b39..466ddb9 100644 --- a/components/light_controller/src/light_controller.c +++ b/components/light_controller/src/light_controller.c @@ -19,14 +19,8 @@ typedef enum COLOR_VAL_PURPLE } LightColor; +// @need Light state, SWIMPL_LC-001, impl, [SWDD_LC-100], [REQ_42] /** - * @rst - * .. impl:: Light state - * :id: SWIMPL_LC-001 - * :implements: SWDD_LC-100 - * :fulfills: REQ_42 - * @endrst - * * @enum LightState * @brief Represents the states of the light. */ @@ -73,13 +67,7 @@ static const uint8_t light_colors_count = (uint8_t)((uint32_t)sizeof(light_color static brightness_t getBrightnessValue(void) { #ifdef CONFIG_BRIGHTNESS_ADJUSTMENT_ENABLED - /** - * @rst - * .. impl:: Variable brightness - * :id: SWIMPL_LC-005 - * :implements: SWDD_LC-204 - * @endrst - */ + // @need Variable brightness, SWIMPL_LC-005, impl, [SWDD_LC-204], [] return RteGetBrightnessValue(); #else return 128; @@ -118,14 +106,7 @@ static RGBColor getRGBColorWithBrightness(LightColor colorEnum, brightness_t bri return color; } -/** - * @rst - * .. impl:: Turn light off - * :id: SWIMPL_LC-002 - * :implements: SWDD_LC-102 - * :fulfills: REQ_43 - * @endrst - */ +// @need Turn light off, SWIMPL_LC-002, impl, [SWDD_LC-102], [REQ_43] static void turnLightOff(void) { #ifdef CONFIG_BLINKING @@ -134,14 +115,7 @@ static void turnLightOff(void) RteSetLightValue(OFF_COLOR); } -/** - * @rst - * .. impl:: Turn light on - * :id: SWIMPL_LC-003 - * :implements: SWDD_LC-102 - * :fulfills: REQ_43 - * @endrst - */ +// @need Turn light on, SWIMPL_LC-003, impl, [SWDD_LC-102], [REQ_43] static void turnLightOn(void) { // Used to iterate through the light colors @@ -173,15 +147,7 @@ static void turnLightOn(void) } #ifdef CONFIG_BLINKING -/** - * @rst - * - * .. impl:: Calculate blink period - * :id: SWIMPL_LC-004 - * :implements: SWDD_LC-101 - * :fulfills: REQ_44 - * @endrst - */ +// @need Calculate blink period, SWIMPL_LC-004, impl, [SWDD_LC-101], [REQ_44] SPLE_TESTABLE_STATIC percentage_t calculateBlinkPeriod(percentage_t mainKnobValue) { // Calculate blink period based on main knob value @@ -194,14 +160,8 @@ SPLE_TESTABLE_STATIC percentage_t calculateBlinkPeriod(percentage_t mainKnobValu } #endif +// @need Light Controller's main function, SWIMPL_LC-006, impl, [SWDD_LC-100], [REQ_44] /** - * @rst - * .. impl:: Light Controller's main function - * :id: SWIMPL_LC-006 - * :implements: SWDD_LC-100 - * :fulfills: REQ_44 - * @endrst - * * @brief Controls the light state. * * Uses a state machine to determine the light state based on several inputs, diff --git a/components/main_control_knob/doc/index.md b/components/main_control_knob/doc/index.md index 58b399f..0290e22 100644 --- a/components/main_control_knob/doc/index.md +++ b/components/main_control_knob/doc/index.md @@ -52,6 +52,13 @@ The main control knob component uses the RTE interface `RteGetMainKnobValue()` t The main control knob component uses the RTE interface `RteSetMainKnobValue()` to set the new value of the main control knob. ``` +## Traceability + +```{src-trace} +:project: components +:directory: main_control_knob +``` + ````{if} var.build_config.target == "reports" ## Verification diff --git a/components/main_control_knob/src/main_control_knob.c b/components/main_control_knob/src/main_control_knob.c index 60623a2..b769b0c 100644 --- a/components/main_control_knob/src/main_control_knob.c +++ b/components/main_control_knob/src/main_control_knob.c @@ -10,14 +10,7 @@ #define MIN_KNOB_VALUE 0u #define MAX_KNOB_VALUE 100u -/** - * @rst - * .. impl:: Main Control Knob's main function - * :id: SWIMPL_MCK-001 - * :implements: SWDD_MCK-100, SWDD_MCK-101, SWDD_MCK-200, SWDD_MCK-201, SWDD_MCK-202, SWDD_MCK-203 - * :fulfills: REQ_50 - * @endrst - */ +// @need Main Control Knob's main function, SWIMPL_MCK-001, impl, [SWDD_MCK-100, SWDD_MCK-101, SWDD_MCK-200, SWDD_MCK-201, SWDD_MCK-202, SWDD_MCK-203], [REQ_50] void mainControlKnob(void) { percentage_t currentValue = RteGetMainKnobValue(); diff --git a/components/power_signal_processing/doc/index.md b/components/power_signal_processing/doc/index.md index 4908ca8..fb1cb05 100644 --- a/components/power_signal_processing/doc/index.md +++ b/components/power_signal_processing/doc/index.md @@ -109,6 +109,13 @@ graph TD ``` ```` +## Traceability + +```{src-trace} +:project: components +:directory: power_signal_processing +``` + ````{if} var.build_config.target == "reports" ## Verification diff --git a/components/power_signal_processing/src/power_signal_processing.c b/components/power_signal_processing/src/power_signal_processing.c index 0ae932c..9f553c1 100644 --- a/components/power_signal_processing/src/power_signal_processing.c +++ b/components/power_signal_processing/src/power_signal_processing.c @@ -6,15 +6,7 @@ #include "power_signal_processing.h" #include "rte.h" -/** - * @rst - * - * .. impl:: Power signal processing - * :id: SWIMPL_PSP-001 - * :implements: SWDD_PSP-001, SWDD_PSP-002, SWDD_PSP-003 - * :fulfills: REQ_38 - * @endrst - */ +// @need Power signal processing, SWIMPL_PSP-001, impl, [SWDD_PSP-001, SWDD_PSP-002, SWDD_PSP-003], [REQ_38] void powerSignalProcessing(void) { // Check if "P" key was pressed diff --git a/ubproject.toml b/ubproject.toml index 65ed620..a2cbf2b 100644 --- a/ubproject.toml +++ b/ubproject.toml @@ -220,43 +220,43 @@ type = "integer" outdir = "output" set_remote_url = true -[codelinks.projects.brightness_controller] +[codelinks.projects.components] remote_url_pattern = "https://github.com/avengineers/SPLed/blob/{commit}/{path}#L{line}" -[codelinks.projects.brightness_controller.source_discover] -src_dir = "components/brightness_controller/src" -include = ["**/*.c", "**/*.h"] +[codelinks.projects.components.source_discover] +src_dir = "components" +include = ["**/src/**/*.c", "**/src/**/*.h"] comment_type = "cpp" -[codelinks.projects.brightness_controller.analyse] +[codelinks.projects.components.analyse] get_oneline_needs = true get_need_id_refs = false get_rst = false -[codelinks.projects.brightness_controller.analyse.preprocessor] +[codelinks.projects.components.analyse.preprocessor] compile_commands = "build/compile_commands.json" -[codelinks.projects.brightness_controller.analyse.oneline_comment_style] +[codelinks.projects.components.analyse.oneline_comment_style] start_sequence = "@need" end_sequence = "\n" field_split_char = "," -[[codelinks.projects.brightness_controller.analyse.oneline_comment_style.needs_fields]] +[[codelinks.projects.components.analyse.oneline_comment_style.needs_fields]] name = "title" -[[codelinks.projects.brightness_controller.analyse.oneline_comment_style.needs_fields]] +[[codelinks.projects.components.analyse.oneline_comment_style.needs_fields]] name = "id" -[[codelinks.projects.brightness_controller.analyse.oneline_comment_style.needs_fields]] +[[codelinks.projects.components.analyse.oneline_comment_style.needs_fields]] name = "type" default = "impl" -[[codelinks.projects.brightness_controller.analyse.oneline_comment_style.needs_fields]] +[[codelinks.projects.components.analyse.oneline_comment_style.needs_fields]] name = "implements" type = "list[str]" default = [] -[[codelinks.projects.brightness_controller.analyse.oneline_comment_style.needs_fields]] +[[codelinks.projects.components.analyse.oneline_comment_style.needs_fields]] name = "fulfills" type = "list[str]" default = [] From 218247ebb4d64bf819856442fd610ecf998533c0 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 10:59:24 +0200 Subject: [PATCH 10/24] docs(variants): name the component directory in src-trace The shared codelinks project matches its include patterns below :directory:, so the block names the component's directory, not its src directory. --- VARIANTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VARIANTS.md b/VARIANTS.md index 383904c..70578cc 100644 --- a/VARIANTS.md +++ b/VARIANTS.md @@ -473,7 +473,7 @@ section: ````text ```{src-trace} :project: components -:directory: brightness_controller/src +:directory: brightness_controller ``` ```` From e0bebae6da5bb680ca47f8524842b58e4da7f2ec Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 12:51:34 +0200 Subject: [PATCH 11/24] fix(csv): put the requirement images into the needs' content Need templates are Sphinx-only, so ubc showed the requirements without their images and with different content. The image is now part of the imported content, and the template, its option and the `image` field go. --- doc/sw_requirements/index.md | 1 - doc/ubconnect/csv/data.csv | 18 +++++++++++++----- doc/ubconnect/csv/needs.json | 8 ++------ needs_templates/csv_image.need | 9 --------- ubproject.toml | 7 ------- 5 files changed, 15 insertions(+), 28 deletions(-) delete mode 100644 needs_templates/csv_image.need diff --git a/doc/sw_requirements/index.md b/doc/sw_requirements/index.md index 50621bb..e744b91 100644 --- a/doc/sw_requirements/index.md +++ b/doc/sw_requirements/index.md @@ -4,5 +4,4 @@ ``` ```{needimport} ../ubconnect/csv/needs.json -:template: csv_image ``` diff --git a/doc/ubconnect/csv/data.csv b/doc/ubconnect/csv/data.csv index da30645..055180e 100644 --- a/doc/ubconnect/csv/data.csv +++ b/doc/ubconnect/csv/data.csv @@ -1,5 +1,13 @@ -level,type,id,text,image,status,implements,satisfies -1,req,REQ_CSV_1,Text Req 1,,new,, -2,req,REQ_CSV_2,Text Req 2,picture/pic1.jpg,open,,REQ_CSV_1 -2,req,REQ_CSV_3,Text Req 3,picture/pic1.jpg,open,,REQ_CSV_1|REQ_CSV_2 -1,spec,ARCH_CSV_1,Text Arch 1,,open,REQ_CSV_1|REQ_CSV_2|REQ_CSV_3, +level,type,id,text,status,implements,satisfies +1,req,REQ_CSV_1,Text Req 1,new,, +2,req,REQ_CSV_2,"Text Req 2 + +```{image} /doc/ubconnect/csv/picture/pic1.jpg +:width: 300px +```",open,,REQ_CSV_1 +2,req,REQ_CSV_3,"Text Req 3 + +```{image} /doc/ubconnect/csv/picture/pic1.jpg +:width: 300px +```",open,,REQ_CSV_1|REQ_CSV_2 +1,spec,ARCH_CSV_1,Text Arch 1,open,REQ_CSV_1|REQ_CSV_2|REQ_CSV_3, diff --git a/doc/ubconnect/csv/needs.json b/doc/ubconnect/csv/needs.json index e4c840b..76d9be8 100644 --- a/doc/ubconnect/csv/needs.json +++ b/doc/ubconnect/csv/needs.json @@ -18,7 +18,6 @@ "status": "new", "links": [], "level": 1, - "image": "", "implements": "", "links_back": [ "REQ_CSV_2", @@ -29,13 +28,12 @@ "id": "REQ_CSV_2", "type": "req", "title": "", - "content": "Text Req 2", + "content": "Text Req 2\n\n```{image} /doc/ubconnect/csv/picture/pic1.jpg\n:width: 300px\n```", "status": "open", "links": [ "REQ_CSV_1" ], "level": 2, - "image": "picture/pic1.jpg", "implements": "", "links_back": [ "REQ_CSV_3" @@ -45,14 +43,13 @@ "id": "REQ_CSV_3", "type": "req", "title": "", - "content": "Text Req 3", + "content": "Text Req 3\n\n```{image} /doc/ubconnect/csv/picture/pic1.jpg\n:width: 300px\n```", "status": "open", "links": [ "REQ_CSV_1", "REQ_CSV_2" ], "level": 2, - "image": "picture/pic1.jpg", "implements": "" }, "ARCH_CSV_1": { @@ -63,7 +60,6 @@ "status": "open", "links": [], "level": 1, - "image": "", "implements": [ "REQ_CSV_1", "REQ_CSV_2", diff --git a/needs_templates/csv_image.need b/needs_templates/csv_image.need deleted file mode 100644 index 720712a..0000000 --- a/needs_templates/csv_image.need +++ /dev/null @@ -1,9 +0,0 @@ -{# Place the original content here #} -{{content}} - -{% if image %} - -```{image} /doc/ubconnect/csv/{{ image }} -:width: 300px -``` -{% endif %} diff --git a/ubproject.toml b/ubproject.toml index a2cbf2b..af5ac47 100644 --- a/ubproject.toml +++ b/ubproject.toml @@ -167,13 +167,6 @@ style = "node" # --- end vendored ----------------------------------------------------------- -[needs.fields.image] -description = "Image associated with the need" -nullable = true - -[needs.fields.image.schema] -type = "string" - [needs.fields.configuration] description = "Configuration, from the ubConnect ReqIF export" nullable = true From f4396249132024f2879b6f0c49d44b2fbc559204 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 12:55:32 +0200 Subject: [PATCH 12/24] feat: select a variant with generated files instead of a link Selecting a variant -- CMake configure, or tools/variant_data.py on its own -- now writes every file that decides what ubCode, ubc and the Sphinx build show, and nothing is maintained by hand per component: - ubproject.variants.toml, the document rules: one per component with a doc/index page, gated on membership of the variant's component list from parts.cmake, plus the scope rules of a per-component report. The same for every variant, at the project root because sphinx-mounts reads rule patterns relative to their file. - build/selection.toml, the selected build: its cell's variant data file and a mount of its build directory at `generated`, gated on the build's own variant and kit. It replaces the `generated` symlink or junction and build/autoconf.json. - /selection/[/].toml, the same for every documentation run spl-core starts (SPL_SPHINX_OPTIONS), so the reports of two builds can be built side by side; a component report's file also names its root document. - component cells, build/variants////.json, with build_config.scope and build_config.component, so ubCode builds a per-component report from the same files. ubproject.toml extends the rules file, which extends the selection. conf.py hands the selection's keys to sphinx-needs and sphinx-mounts, which read one TOML file each and do not follow `extend`, and stops with a clear error when nothing is selected. The components page globs every component's documentation. --- .gitignore | 2 + .vscode/settings.json | 3 +- CMakeLists.txt | 45 ++--- conf.py | 149 ++++++++-------- doc/component_report.md | 18 +- doc/components/index.md | 33 ++-- tools/variant_data.py | 376 +++++++++++++++++++++++++++++----------- ubproject.toml | 161 +++-------------- 8 files changed, 417 insertions(+), 370 deletions(-) diff --git a/.gitignore b/.gitignore index dda3759..012f760 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ # Windows without Developer Mode, a copy) of the configured variant's build # output directory. Generated output, never committed. /generated +# The document rules tools/variant_data.py generates (see ubproject.toml). +/ubproject.variants.toml # Output directory of test results /test/output diff --git a/.vscode/settings.json b/.vscode/settings.json index fb6ee1a..7ee109d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,7 +20,8 @@ // the edit is the only guard rail that also applies to an assistant. "files.readonlyInclude": { "build/**": true, - "generated/**": true + "generated/**": true, + "ubproject.variants.toml": true }, "cmake.buildDirectory": "${workspaceFolder}/build/${variant:variant}/${buildKit}/${buildType}", "cmake.copyCompileCommands": "${workspaceFolder}/build/compile_commands.json", diff --git a/CMakeLists.txt b/CMakeLists.txt index 632614c..89c53c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,11 +49,12 @@ include("${SPL_CORE_DIR}/spl.cmake") # Variant data: one generation step, everything downstream declarative. # # tools/variant_data.py writes build/variants///.json for -# every variant, plus build/autoconf.json as the "current" pointer and a -# `generated` symlink to this build directory. It is invoked here so that -# configuring in the IDE keeps the pointer in step with whatever CMake Tools -# selected -- the same role cmake.copyCompileCommands plays for the compilation -# database. +# the variant (and one per component report), the document rules +# ubproject.variants.toml, and the selection of this build: build/selection.toml +# for the IDE and a bare run, and /selection/... for every documentation +# run spl-core starts here. Configuring a build is selecting it, so configuring in +# the IDE keeps the selection in step with whatever CMake Tools selected. A test +# kit build shows its reports, a prod kit build its design documentation. # # It is deliberately a standalone script and not CMake code. KConfig is pure # Python, while the top-level project() call above demands a C toolchain before @@ -74,11 +75,16 @@ set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${_SPLED_VARIANT_DATA_SCRIPT} ${CMAKE_SOURCE_DIR}/variants/${VARIANT}/parts.cmake ) +if(BUILD_KIT STREQUAL test) + set(_spled_selected_target reports) +else() + set(_spled_selected_target docs) +endif() execute_process( COMMAND "${_VENV_PYTHON}" ${_SPLED_VARIANT_DATA_SCRIPT} --variant ${VARIANT} --kit ${BUILD_KIT} - --target docs + --target ${_spled_selected_target} --current --build-dir ${CMAKE_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} @@ -94,21 +100,20 @@ endif() # it has to be set before parts.cmake adds the components, which is where # spl-core computes the paths it writes for Sphinx. # -# The variant data file for each build shape. spl-core hands it to sphinx-build -# as `-D needs_variant_data_file=`, so a reports build evaluates its fences -# against the reports cell. `build/autoconf.json`, the pointer ubCode and a bare -# sphinx-build read, always holds the docs cell, so the IDE sees the report -# fences as a clean false. -set(SPL_VARIANT_DATA_FILE_DOCS ${CMAKE_SOURCE_DIR}/build/variants/${VARIANT}/${BUILD_KIT}/docs.json) -set(SPL_VARIANT_DATA_FILE_REPORTS ${CMAKE_SOURCE_DIR}/build/variants/${VARIANT}/${BUILD_KIT}/reports.json) +# Every documentation run spl-core starts names its own selection file, which +# tools/variant_data.py has just written for this build: the variant data file of +# its shape (and component), and the mount of this build directory. conf.py reads +# it, and ubc takes the same file with `-c "$(cat )"`. No run depends on +# which build was configured last, so the reports of two builds can be built side +# by side. +set(SPL_SPHINX_OPTIONS -D spl_selection=${CMAKE_BINARY_DIR}/selection/@SHAPE@.toml) +set(SPL_SPHINX_COMPONENT_OPTIONS -D spl_selection=${CMAKE_BINARY_DIR}/selection/@COMPONENT_PATH@/@SHAPE@.toml) -# Sphinx reaches this build directory through `generated`, the link -# tools/variant_data.py has just pointed at it. The generated report pages are -# therefore named `generated/...` whatever the variant, kit or build type, and -# the report sections name them directly instead of globbing `/build/**`. -# spl-core writes each coverage report next to its page under that name, and -# stops a documentation build whose link has since been re-pointed at another -# build directory. +# The selection mounts this build directory at `generated`, so every page +# spl-core generates is named `generated/...` whatever the variant, kit or build +# type, and the report sections name them directly instead of globbing +# `/build/**`. spl-core writes each coverage report next to its page under that +# name. `generated` does not exist on disk: it is a mount, not a link. set(SPL_SPHINX_BINARY_DIR ${CMAKE_SOURCE_DIR}/generated) # No document is rendered through Jinja, so the source listings clanguru diff --git a/conf.py b/conf.py index d949510..8b48c3f 100644 --- a/conf.py +++ b/conf.py @@ -2,6 +2,10 @@ """Configuration""" import datetime import os +import tomllib +from pathlib import Path + +from sphinx.errors import ConfigError from spl_core.report_generation.spl_sphinx import SplSphinx from spl_core.report_generation.spl_html_settings import html_theme, html_show_sourcelink, html_theme_options, html_sidebars, html_last_updated_fmt # noqa: F401 @@ -26,35 +30,38 @@ ".git", "**/test_results.rst", # We renamed this file, but nobody deletes it. # Build output is never read where it lies. The one build a Sphinx run - # documents is reached through `generated`, the link tools/variant_data.py - # points at the configured build directory, and spl-core names every page - # it generates through that link (SPL_SPHINX_BINARY_DIR in CMakeLists.txt). - # Pruning `build` keeps every other variant's output out of the walk and - # leaves exactly one route to each generated page, which is the invariant - # test_ubproject_config.py guards: with two, every page exists twice. + # documents comes in through its mount at `generated` (the selection below), + # and spl-core names every page it generates there (SPL_SPHINX_BINARY_DIR in + # CMakeLists.txt). Pruning `build` keeps every other variant's output out of + # the walk and leaves exactly one route to each generated page: + # with two, every page exists twice. # # get_matching_files applies these to directories as well as files, so each # entry prunes the walk rather than filtering its result. "build", - # ...and, inside the configured build, the directories that hold no - # documents: CMake's own state and the HTML the builds write. + # ...and, inside the mounted build, the directories that hold no documents: + # CMake's own state and the HTML the builds write. "generated/CMakeFiles", "generated/**/CMakeFiles", "generated/**/html", ] -# The 150% source set: every hand-written document the product line has. +# The 150% source set: every document the product line has, hand-written and +# generated. The same set ubCode's parser includes name in ubproject.toml; the +# generated pages there are written relative to the mount, here by their names. # -# Which of them the build actually contains is decided by the -# [[source.variant_sources]] rules in ubproject.toml, against the variant data. -# Narrowing the set here as well would put a second, invisible gate in front of -# the declared one -- and an invisible gate is the thing this whole design -# exists to remove. +# Which of them a build actually contains is decided by the rules in +# ubproject.variants.toml, against the variant data. Narrowing the set here as +# well would put a second, invisible gate in front of the declared one -- and an +# invisible gate is the thing this whole design exists to remove. include_patterns = [ "index.md", "doc/**", "components/**/doc/**", "test/**/doc/**", + "generated/components/**/*.rst", + "generated/test/**/*.rst", + "generated/reports/*.rst", ] # configuration of built-in stuff ########################################### @@ -92,10 +99,11 @@ # spl-core's base configuration is vendored into it. needs_from_toml = "ubproject.toml" -# The same file, named again for the other extension that reads it. sphinx-mounts -# already defaults to this path; naming it makes the coupling visible in conf.py -# rather than resting on a library default. -sources_from_toml = "ubproject.toml" +# The document rules, generated by tools/variant_data.py, one per documented +# component. sphinx-mounts reads rules from one file, relative to that file, so +# they live in a file of their own at the project root; ubproject.toml reaches +# the same file through `extend`. +sources_from_toml = "ubproject.variants.toml" # Registers project Python that the configuration references by name. This is # the last thing in the needs model that ubCode cannot see, because it cannot @@ -103,64 +111,59 @@ needs_functions = SplSphinx.default_needs_functions needs_global_options = SplSphinx.default_needs_global_options -# variant data ############################################################## +# variant selection ######################################################### # -# ONE file, and nothing computed here. +# ONE file decides what a build shows, and nothing is computed here. # -# Everything a document's {if} directive or a condition in ubproject.toml may -# name -- the complete feature vector, the variant, the build kit, the build -# target, the component list -- is written by tools/variant_data.py and read -# from here verbatim. That is the whole point: ubCode, ubc and a reviewer's -# editor cannot execute this file, so anything synthesized here would be -# invisible to them and their view of the project would silently disagree with -# the build. The rule is "everything a condition may name has to be IN the -# file", and it only holds if this file adds nothing. +# tools/variant_data.py writes build/selection.toml whenever a variant is +# selected: the variant data file every condition is evaluated against, and, for +# a CMake build, the mount that brings that build's generated pages in at +# `generated`; for a per-component report also its root document. ubproject.toml +# reaches it through `extend` (by way of ubproject.variants.toml), which ubCode +# and ubc follow. sphinx-needs and sphinx-mounts each read one TOML file and do +# not follow `extend`, so this hands them the selection's keys: the same keys, +# from the same file, nothing added. # -# Which cell of the matrix a build reads is not decided here either. It is the -# sphinx-needs option `needs_variant_data_file`, and ubproject.toml sets it to -# the pointer `build/autoconf.json`, which is what the IDE and a bare -# `sphinx-build` read. A build that needs another cell overrides the option on -# the command line: +# The variant data file holds everything a document's {if} directive or a rule +# may name -- the complete feature vector, the variant, the build kit, the build +# target, the component list. ubCode, ubc and a reviewer's editor cannot execute +# this file, so anything synthesized here would be invisible to them, and their +# view of the project would disagree with the build. # -# sphinx-build -D needs_variant_data_file=build/variants/Sleep/test/docs.json ... +# Each run spl-core starts names its own build's file (`-D spl_selection=...`, +# SPL_SPHINX_OPTIONS in CMakeLists.txt), so two builds' reports can be built side +# by side. An explicit `-D needs_variant_data_file=...` or `-D root_doc=...` still +# wins over the file, the way `-c` does for ubc: # -# spl-core does exactly that for the shape it is building -# (SPL_VARIANT_DATA_FILE_DOCS and _REPORTS in CMakeLists.txt), and so do the -# tests. sphinx-needs keeps a command-line override even though needs_from_toml -# names the pointer, and it is the same key `ubc check -c` overrides, so both -# readers select a variant the same way. Nothing in this file could do it as -# robustly: sphinx-needs replaces conf.py's values with the TOML's and resolves -# the variant data in the very next config-inited handler, so an assignment is -# lost and a handler would have to slot in between the two. - -# build shape ############################################################### +# sphinx-build -D needs_variant_data_file=build/variants/Sleep/test/docs.json ... # -# The rest of this file is Sphinx plumbing, not variant data: which documents -# are in the source set and which one is the root. No other tool needs to -# decide these, and none of it may leak into `var.*`. - -_build_config = SplSphinx.get_default_html_context()["build_config"] - -# Two shapes of build use this configuration: the variant-wide one, and the -# per-component report that spl-core builds for a single component. This is the -# only build-shape decision left here, and it is a Sphinx one -- which document -# is the root -- not variant data. No other reader has to decide it. -if _build_config.get("component_info"): - root_doc = "doc/component_report" - exclude_patterns.append("index.md") - # A per-component report builds one component and stitches its own table of - # contents, so the project-wide variant rules do not apply to it. - sources_from_toml = None - include_patterns.extend(_build_config.get("include_patterns", [])) -else: - root_doc = "index" - exclude_patterns.append("doc/component_report.md") - # The pages spl-core generated for the configured build, under the - # `generated/` names it gave them. Only those: the hand-written component - # trees are in the source set already, and admitting them a second time would - # parse every need in them twice. - # - # Nothing here decides which of them a build reads. spl-core lists the report - # pages for the docs shape too, and the `generated/**` rule in ubproject.toml - # removes them there -- declared once, where ubCode reads it as well. - include_patterns.extend(pattern for pattern in _build_config.get("include_patterns", []) if pattern.startswith("generated/")) +# The handler runs at config-inited priority 5: before sphinx-needs loads its +# TOML (10) and resolves the variant data (11), and before sphinx-mounts loads +# its rules (400) and mounts (500). + +root_doc = "index" + + +def _load_selection(app, config): + selection_path = Path(app.confdir) / config.spl_selection + overrides = getattr(config, "overrides", {}) + if not selection_path.is_file(): + if "needs_variant_data_file" in overrides: + return + raise ConfigError( + f"No variant is selected: {selection_path} does not exist. Configure a CMake build, " + "or run `python tools/variant_data.py --all --current --variant --kit `." + ) + selection = tomllib.loads(selection_path.read_text(encoding="utf-8")) + variant_data_file = selection.get("needs", {}).get("variant_data_file") + if variant_data_file is not None and "needs_variant_data_file" not in overrides: + config.needs_variant_data_file = variant_data_file + root = selection.get("project", {}).get("root_doc") + if root is not None and "root_doc" not in overrides: + config.root_doc = root + config.mounts = selection.get("source", {}).get("mounts", []) + + +def setup(app): + app.add_config_value("spl_selection", "build/selection.toml", "env", types=(str,)) + app.connect("config-inited", _load_selection, priority=5) diff --git a/doc/component_report.md b/doc/component_report.md index ba070bd..2422dd5 100644 --- a/doc/component_report.md +++ b/doc/component_report.md @@ -1,19 +1,13 @@ # Software Component Report -**Variant:** {variant}`build_config.variant` +**Variant:** {variant}`build_config.variant` · **Component:** {variant}`build_config.component` This is the root document of the per-component report that spl-core builds for -a single component. The build configuration restricts the source set to that -one component, so the glob below resolves to exactly its design document, which -in turn groups that component's verification pages -- and names the component, -which is why this page does not. - -The component name is deliberately not read from `build_config.component_info`. -That key is written per BUILD, by spl-core, for one component; the variant data -every other reader has cannot contain it, because the generator does not know -which component a per-component report is for. A role naming it resolves inside -that one build and nowhere else, which is the divergence between readers this -project's configuration exists to prevent. +a single component. Its variant data has `build_config.scope = "component"`, and +the rules in ubproject.variants.toml leave that component's documents only, so +the glob below resolves to exactly its design document, which in turn groups the +component's verification pages. ubCode builds the same report from the same +files (build/selection.toml, or the build's `selection//reports.toml`). ```{toctree} :maxdepth: 2 diff --git a/doc/components/index.md b/doc/components/index.md index 9ca703b..93de37f 100644 --- a/doc/components/index.md +++ b/doc/components/index.md @@ -1,18 +1,12 @@ # Components -This page lists every component the product line has. It is a 150% view: the -entries are always all of them, and the ones the built variant does not contain -are removed from the build by the `[[source.variant_sources]]` rules in -`ubproject.toml`, each gated on membership of that variant's component list. - -So this page needs no templating and no generated content. Adding a component -to the report means adding it to a variant's `parts.cmake` and adding one rule -plus one line here — never editing a loop, and never editing anything under -`build/`. - -An entry naming a document the current variant excludes is reported as INFO by -both Sphinx and ubCode, by design: the 150% tree is the source of truth, and a -variant showing less of it is the normal case rather than an error. +This page lists the components of the variant. The toctree globs every +component's documentation, and the rules in `ubproject.variants.toml` remove the +ones the variant does not contain, each gated on membership of that variant's +component list. `tools/variant_data.py` derives the rules from where each +component's documentation lives, so adding a component means adding it to a +variant's `parts.cmake` and writing its documentation: nothing to edit here, +and nothing under `build/`. Each entry is a group. A component's own document is the entry point and carries the toctree for its verification pages, so unit test results and @@ -21,14 +15,9 @@ list. ```{toctree} :maxdepth: 2 +:glob: -/components/light_controller/doc/index -/components/main_control_knob/doc/index -/components/power_button/doc/index -/components/power_signal_processing/doc/index -/components/brightness_controller/doc/index -/components/auto_off/doc/index -/components/examples/hello_gmock/doc/index -/components/examples/flight_controller/doc/index -/test/spled_integration/doc/index +/components/*/doc/index +/components/examples/*/doc/index +/test/*/doc/index ``` diff --git a/tools/variant_data.py b/tools/variant_data.py index 012a14e..b2c869a 100644 --- a/tools/variant_data.py +++ b/tools/variant_data.py @@ -1,25 +1,40 @@ #!/usr/bin/env python3 -"""Write the variant data files that every documentation tool reads. +"""Write the files that decide what every documentation tool shows. -One generation step, one artefact per build shape, and nothing downstream needs -Python to decide anything: `ubproject.toml` conditions and the `{if}` directives -in the documents read these files, and so do `sphinx-build`, `ubc` and the -ubCode language server. The rule that follows from that is the only one worth -remembering here: +One generation step before any reader runs, and nothing downstream needs Python +to decide anything: the rules, the `{if}` directives in the documents and the +selected build all come out of the files written here, and `sphinx-build`, +`ubc` and the ubCode language server read the same ones. The rule that follows +from that is the only one worth remembering here: - everything a condition may name has to be IN the file. + everything a condition may name has to be IN the variant data file. A key that only `conf.py` knows is invisible to every other reader, and their -view of the project then silently disagrees with the build. That is why this -writes the complete feature vector -- including the booleans KConfig omits -- -plus the build shape, rather than leaving any of it to be synthesized later. +view of the project then disagrees with the build. That is why this writes the +complete feature vector -- including the booleans KConfig omits -- plus the +build shape, rather than leaving any of it to be synthesized later. Layout:: - build/variants///.json the matrix - build/variants/GENERATED marker; nobody edits generated output - build/autoconf.json the "current" pointer, one cell of the matrix - generated/ symlink to the current cell's build directory + build/variants///.json the matrix: one variant data file per cell + build/variants////.json the same, for one component's report + build/variants/GENERATED marker; nobody edits generated output + ubproject.variants.toml the document rules, one per documented component + build/selection.toml the selected build, for the IDE and a bare run + /selection/[/].toml the same for each run spl-core starts there + +Nothing needs maintaining by hand for a component. Its rule is derived from +where its documentation lives and gated on the variant's component list, which +comes from `parts.cmake`: adding a component is adding it to `parts.cmake` and +writing its documentation. + +The rules are the same for every variant, so they live in one file, at the +project root: sphinx-mounts reads rule patterns relative to the file that +declares them and refuses absolute or `..` ones. A selection file names a +variant data file and, for a CMake build, mounts that build's directory at +`generated`, where every page spl-core generates is named. ubproject.toml +reaches both files through `extend`; conf.py hands the selection's keys to +Sphinx, whose extensions each read one TOML file and do not follow `extend`. Nothing here needs a compiler. KConfig is pure Python, while CMake's top-level `project()` call demands a C toolchain before it will even configure -- so with @@ -43,6 +58,36 @@ from pathlib import Path from typing import Any +#: The generated rule file, at the project root (see the module docstring). +RULES_FILE = "ubproject.variants.toml" + +#: The selection the IDE and a bare `sphinx-build` or `ubc` read. +SELECTION_FILE = "build/selection.toml" + +#: The root document of a per-component report. +COMPONENT_ROOT_DOC = "doc/component_report" + +#: Rules written by hand, for the cases a component's membership does not decide +#: (VARIANTS.md, use case 13). Appended to the generated ones: ubproject.toml +#: cannot hold rules of its own, because `extend` replaces arrays rather than +#: merging them. +HAND_WRITTEN_RULES = "doc/variant_rules.toml" + +#: Where the generated pages of the selected build appear. spl-core names every +#: page it generates below it (SPL_SPHINX_BINARY_DIR in CMakeLists.txt). +MOUNT_AT = "generated" + +#: The pages a build directory contributes, relative to it: spl-core's report +#: pages and the clanguru listings. The same patterns as ubCode's rst parser +#: include in ubproject.toml; everything else in a build directory (CMake state, +#: HTML output, spl-core's navigation wrappers, fetched dependencies) stays out. +MOUNTED_PAGES = ("components/**/*.rst", "test/**/*.rst", "reports/*.rst") + +GENERATED_HEADER = ( + "# GENERATED by tools/variant_data.py -- do not edit. It is rewritten whenever a\n" + "# variant is selected, and whatever is changed here is lost.\n" +) + PROJECT_ROOT = Path(__file__).resolve().parent.parent #: Components a variant's `parts.cmake` adds only for the test build kit. @@ -185,8 +230,34 @@ def components(project_root: Path, variant: str, kit: str) -> list[str]: return result -def variant_data(project_root: Path, variant: str, kit: str, target: str) -> dict[str, Any]: - """One cell of the matrix: everything a condition anywhere may name.""" +def documented_components(project_root: Path) -> list[str]: + """Every component with documentation: a directory with a `doc/index` page. + + The same test spl-core applies before it creates a component's docs and + report builds. Components without documentation need no rule, because they + contribute no page. + """ + found: set[str] = set() + for top in ("components", "test"): + base = project_root / top + if not base.is_dir(): + continue + for index in base.rglob("index.*"): + if index.suffix in (".md", ".rst") and index.parent.name == "doc": + found.add(index.parent.parent.relative_to(project_root).as_posix()) + return sorted(found) + + +def variant_data( + project_root: Path, variant: str, kit: str, target: str, component: str | None = None +) -> dict[str, Any]: + """One cell of the matrix: everything a condition anywhere may name. + + `component` makes it the cell of that component's report. The component list + stays the variant's, so a block conditioned on another component's presence + reads the same in the component's report as in the variant's documentation; + only `scope` and `component` differ, and the rules gate the documents on them. + """ return { "features": features(project_root, variant), "build_config": { @@ -194,105 +265,192 @@ def variant_data(project_root: Path, variant: str, kit: str, target: str) -> dic "kit": kit, "target": target, "components": components(project_root, variant, kit), + "scope": "component" if component else "variant", + "component": component or "", }, } -def cell_path(project_root: Path, variant: str, kit: str, target: str) -> Path: - return project_root / "build" / "variants" / variant / kit / f"{target}.json" +def cell_path(project_root: Path, variant: str, kit: str, target: str, component: str | None = None) -> Path: + base = project_root / "build" / "variants" / variant / kit + return (base / component if component else base) / f"{target}.json" -def write_cell(project_root: Path, variant: str, kit: str, target: str, data: dict[str, Any]) -> Path: - path = cell_path(project_root, variant, kit, target) +def write_cell( + project_root: Path, variant: str, kit: str, target: str, data: dict[str, Any], component: str | None = None +) -> Path: + path = cell_path(project_root, variant, kit, target, component) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8") return path -def write_pointer(project_root: Path, data: dict[str, Any], build_dir: Path | None) -> None: - """Point `build/autoconf.json` and `generated` at one cell. +def reported_components(project_root: Path, variant: str, kit: str) -> list[str]: + """The components of this cell that get a per-component report.""" + documented = set(documented_components(project_root)) + return [component for component in components(project_root, variant, kit) if component in documented] + + +def cells(project_root: Path, variant: str) -> list[tuple[str, str, str | None]]: + """Every (kit, target, component) cell of one variant, the component ones included.""" + result: list[tuple[str, str, str | None]] = [] + for kit in KITS: + for target in TARGETS: + result.append((kit, target, None)) + result.extend((kit, target, component) for component in reported_components(project_root, variant, kit)) + return result - `ubproject.toml` names the data file as `variant_data_file`, so the pointer - is the variant the IDE shows. - `generated` is the configured build directory, and CMakeLists.txt hands it - to spl-core as SPL_SPHINX_BINARY_DIR. Every page spl-core generates is then - named `generated/...` in the Sphinx build, whatever the variant, kit or build - type, and the report sections name those pages directly. Re-pointing the link - is safe: spl-core stops a documentation build whose link no longer leads to - its own build directory, rather than letting it read another build's pages. +def rules_toml(project_root: Path) -> str: + """The document rules: which documents a variant data file admits. - The link sits at the project root rather than inside `build/`, because - `build` is pruned from the Sphinx walk and excluded by ubCode: each - generated page has to be reachable under exactly one name. + Rules are subtractive: a rule whose condition is FALSE removes the files it + names, a TRUE rule does nothing, so two rules naming the same file compose as + AND. Gated on membership of the variant's component list, never on the + variant's name: the product structure is stated once, in parts.cmake. - ubCode does not descend symlinked directories, so the IDE does not show the - generated pages. They are build output of the reports target, which is - Sphinx-only anyway. + A per-component report (`scope == "component"`) shows one component: its own + documentation and generated pages, under doc/component_report.md, and none of + the variant-wide documents. """ - pointer = project_root / "build" / "autoconf.json" - pointer.parent.mkdir(parents=True, exist_ok=True) - pointer.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8") + doc_dir = project_root / "doc" + variant_wide = ["index.md"] + for entry in sorted(doc_dir.iterdir()) if doc_dir.is_dir() else []: + if entry.name == Path(COMPONENT_ROOT_DOC).name + ".md": + continue + variant_wide.append(f"doc/{entry.name}/**" if entry.is_dir() else f"doc/{entry.name}") + variant_wide.append(f"{MOUNT_AT}/reports/**") - if build_dir is None: - return - - current = project_root / "generated" - _remove_link(current) - target = build_dir.resolve() - try: - current.symlink_to(target, target_is_directory=True) - return - except OSError: - pass - - # Windows refuses a symlink without Developer Mode, but creates a junction - # without any privilege, and Sphinx walks a junction like a directory. - try: - _create_junction(target, current) - return - except OSError: - pass - - # Neither could be created. A plain directory keeps the path present and - # says why it is empty; spl-core then stops the documentation builds with a - # message naming both paths, instead of building them without their reports. - current.mkdir(parents=True, exist_ok=True) - (current / "NO_SYMLINK").write_text( - "Neither a symlink nor a junction could be created here, so `generated`\n" - "is a plain directory.\n" - f"It should lead to: {target}\n" + def rule(condition: str, files: list[str], comment: str) -> str: + listed = "".join(f' "{pattern}",\n' for pattern in files) + return f"\n# {comment}\n[[source.variant_sources]]\nif = \"{condition}\"\nfiles = [\n{listed}]\n" + + text = GENERATED_HEADER + ( + "#\n" + "# Which documents a variant data file admits. The same for every variant; the\n" + "# selected build comes in through the `extend` below.\n" "\n" - "The documentation builds read the generated report pages through this\n" - "path, and spl-core stops them until it leads to the build directory. On\n" - "Windows, keep the checkout on an NTFS volume, or enable Developer Mode.\n", - encoding="utf-8", + f'extend = "{SELECTION_FILE}"\n' + ) + text += rule("var.build_config.scope == 'variant'", variant_wide, "The variant-wide documents.") + text += rule("var.build_config.scope == 'component'", [f"{COMPONENT_ROOT_DOC}.md"], "The root of a per-component report.") + for component in documented_components(project_root): + condition = ( + f"'{component}' in var.build_config.components and " + f"(var.build_config.scope == 'variant' or var.build_config.component == '{component}')" + ) + text += rule(condition, [f"{component}/doc/**", f"{MOUNT_AT}/{component}/**"], component) + hand_written = project_root / HAND_WRITTEN_RULES + if hand_written.is_file(): + import tomllib + + for entry in tomllib.loads(hand_written.read_text(encoding="utf-8")).get("source", {}).get("variant_sources", []): + if set(entry) != {"if", "files"} or not isinstance(entry["files"], list): + raise ValueError(f"{HAND_WRITTEN_RULES}: a rule has exactly `if` and `files`, got {sorted(entry)}") + text += rule(entry["if"].replace('"', "'"), entry["files"], f"from {HAND_WRITTEN_RULES}") + return text + + +def selection_toml( + variant_data_file: Path, variant: str, kit: str, build_dir: Path | None, root_doc: str | None = None +) -> str: + """One selection: which variant data file a reader evaluates, and which build it shows. + + The mount's condition names the build's own variant and kit. A reader pointed + at another variant's data then reads none of this build's pages, instead of + showing them as that variant's. + """ + text = GENERATED_HEADER + f'\n[needs]\nvariant_data_file = "{variant_data_file.resolve().as_posix()}"\n' + if root_doc: + text += f'\n[project]\nroot_doc = "{root_doc}"\n' + if build_dir is None: + text += "\n[source]\nmounts = []\n" + else: + condition = f'var.build_config.target == "reports" and var.build_config.variant == "{variant}" and var.build_config.kit == "{kit}"' + included = ", ".join(f'"{pattern}"' for pattern in MOUNTED_PAGES) + text += ( + "\n[[source.mounts]]\n" + f'dir = "{build_dir.resolve().as_posix()}"\n' + f'mount_at = "{MOUNT_AT}"\n' + f"include = [{included}]\n" + "gitignore = false\n" + f"if = '{condition}'\n" + ) + return text + + +def write_text_if_changed(path: Path, text: str) -> bool: + """Write only when the content changes, so a reader's cache stays valid.""" + if path.exists() and path.read_text(encoding="utf-8") == text: + return False + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(text, encoding="utf-8") + return True + + +def write_rules(project_root: Path) -> Path: + path = project_root / RULES_FILE + write_text_if_changed(path, rules_toml(project_root)) + return path + + +def write_selection(project_root: Path, variant: str, kit: str, target: str, build_dir: Path | None) -> None: + """Select one cell: build/selection.toml, and one file per spl-core run of the build. + + Every documentation run spl-core starts in the build names its own file + (`-D spl_selection=...`, SPL_SPHINX_OPTIONS in CMakeLists.txt), so two builds' + reports can be built side by side. build/selection.toml is what the IDE and a + bare run read; it is written last, so it names the build configured last. + """ + _remove_stale_selection(project_root) + if build_dir is not None: + runs = build_dir / "selection" + for shape in TARGETS: + write_text_if_changed( + runs / f"{shape}.toml", + selection_toml(cell_path(project_root, variant, kit, shape), variant, kit, build_dir), + ) + for component in reported_components(project_root, variant, kit): + write_text_if_changed( + runs / component / f"{shape}.toml", + selection_toml( + cell_path(project_root, variant, kit, shape, component), variant, kit, build_dir, COMPONENT_ROOT_DOC + ), + ) + write_text_if_changed( + project_root / SELECTION_FILE, + selection_toml(cell_path(project_root, variant, kit, target), variant, kit, build_dir), ) +def _remove_stale_selection(project_root: Path) -> None: + """Remove what earlier versions of this script wrote to select a variant. + + `build/autoconf.json` was the pointer, and `generated` a link (a junction on + Windows) to the configured build directory. Nothing reads either any more, + and a stale link would still lead Sphinx's walk into a build directory. + """ + pointer = project_root / "build" / "autoconf.json" + if pointer.is_file(): + pointer.unlink() + _remove_link(project_root / MOUNT_AT) + + def _remove_link(path: Path) -> None: - """Remove whatever `generated` was, without ever touching what it led to.""" + """Remove the `generated` link earlier versions made, never what it led to. + + A real directory is only removed when it is the placeholder those versions + left where no link could be made (it holds `NO_SYMLINK`); anything else there + is somebody's, and stays. + """ if path.is_symlink() or path.is_file(): path.unlink() elif path.is_junction(): path.rmdir() # removes the junction itself, not the directory it leads to - elif path.is_dir(): + elif path.is_dir() and (path / "NO_SYMLINK").is_file(): shutil.rmtree(path) -def _create_junction(target: Path, link: Path) -> None: - """Create a directory junction at `link` leading to `target`. - - Raises OSError where junctions do not exist, which is everywhere but Windows. - """ - try: - import _winapi # CPython's Windows API module - except ImportError as error: - raise OSError("directory junctions exist on Windows only") from error - - _winapi.CreateJunction(str(target), str(link)) - - #: Dropped into every directory this script owns. `build/` itself gets one too, #: because that is the directory somebody is most likely to open and edit in. GENERATED_MARKER = ( @@ -316,16 +474,20 @@ def _check(project_root: Path, selected: list[str]) -> int: meant to catch and report success. """ stale: list[str] = [] + expected_files: list[tuple[Path, str]] = [] for variant in selected: - for kit in KITS: - for target in TARGETS: - expected = json.dumps(variant_data(project_root, variant, kit, target), indent=2, sort_keys=True) + "\n" - path = cell_path(project_root, variant, kit, target) - rel = path.relative_to(project_root) - if not path.exists(): - stale.append(f"{rel}: missing") - elif path.read_text(encoding="utf-8") != expected: - stale.append(f"{rel}: stale") + for kit, target, component in cells(project_root, variant): + data = variant_data(project_root, variant, kit, target, component) + expected_files.append( + (cell_path(project_root, variant, kit, target, component), json.dumps(data, indent=2, sort_keys=True) + "\n") + ) + expected_files.append((project_root / RULES_FILE, rules_toml(project_root))) + for path, expected in expected_files: + rel = path.relative_to(project_root) + if not path.exists(): + stale.append(f"{rel}: missing") + elif path.read_text(encoding="utf-8") != expected: + stale.append(f"{rel}: stale") if stale: print("variant data is not up to date:", file=sys.stderr) @@ -334,7 +496,7 @@ def _check(project_root: Path, selected: list[str]) -> int: print("\nrun: python tools/variant_data.py --all", file=sys.stderr) return 1 - print(f"variant data up to date ({len(selected)} variants x {len(KITS)} kits x {len(TARGETS)} targets)") + print(f"variant data up to date ({len(expected_files) - 1} cells, and the rules)") return 0 @@ -348,12 +510,12 @@ def main(argv: list[str] | None = None) -> int: parser.add_argument( "--current", action="store_true", - help="also write build/autoconf.json and the `generated` link for the selected cell", + help=f"also select the cell: write {SELECTION_FILE}", ) parser.add_argument( "--build-dir", type=Path, - help="CMake binary dir of the selected cell; `generated` points at it", + help="CMake binary dir of the selected cell: mounted at `generated`, with one selection file per run", ) parser.add_argument( "--check", @@ -372,19 +534,21 @@ def main(argv: list[str] | None = None) -> int: if args.check: return _check(project_root, selected) + written = 0 for variant in selected: - for kit in KITS: - for target in TARGETS: - data = variant_data(project_root, variant, kit, target) - path = write_cell(project_root, variant, kit, target, data) - print(f"wrote {path.relative_to(project_root)}") + for kit, target, component in cells(project_root, variant): + data = variant_data(project_root, variant, kit, target, component) + write_cell(project_root, variant, kit, target, data, component) + written += 1 + print(f"wrote {written} variant data files") mark_generated(project_root) + print(f"wrote {write_rules(project_root).relative_to(project_root)}") if args.current or not args.all: - data = variant_data(project_root, args.variant or selected[0], args.kit, args.target) - write_pointer(project_root, data, args.build_dir) - print(f"current -> {args.variant or selected[0]} / {args.kit} / {args.target}") + variant = args.variant or selected[0] + write_selection(project_root, variant, args.kit, args.target, args.build_dir) + print(f"selected {variant} / {args.kit} / {args.target}" + (f" in {args.build_dir}" if args.build_dir else "")) return 0 diff --git a/ubproject.toml b/ubproject.toml index af5ac47..6413941 100644 --- a/ubproject.toml +++ b/ubproject.toml @@ -1,5 +1,12 @@ "$schema" = "https://ubcode.useblocks.com/ubproject.schema.json" +# The generated half of the configuration: the document rules, one per +# documented component, and through them the selected build (build/selection.toml). +# tools/variant_data.py writes both whenever a variant is selected, so nothing in +# this file has to change when a component is added. ubCode and ubc follow the +# `extend`; conf.py hands the same files to Sphinx, whose extensions do not. +extend = "ubproject.variants.toml" + # --------------------------------------------------------------------------- # The one configuration file. Both readers of this project's documentation read # it: ubCode and ubc directly, and the Sphinx build through `needs_from_toml` @@ -15,7 +22,8 @@ # # spl-core's base needs configuration is therefore vendored below rather than # referenced. test_ubproject_config.py fails if it drifts from what the -# installed spl-core ships, so vendoring cannot rot quietly. +# installed spl-core ships, so vendoring cannot rot quietly. The one `extend` +# left names files this project generates itself, which every reader is handed. # --------------------------------------------------------------------------- [server] @@ -41,18 +49,16 @@ include = [ ] # Every .rst in this project is generated: the clanguru source listings and the -# report pages spl-core writes into the configured build directory. The build -# tree itself is excluded below (`build/**`): indexing every variant and kit -# ever built is the problem that exclusion solves. +# report pages spl-core writes into the selected build directory, which the +# selection mounts at `generated` (build/selection.toml). The build tree itself +# is excluded below (`build/**`): indexing every variant and kit ever built is +# the problem that exclusion solves. # -# The include names `generated`, the link to the configured build through which -# the Sphinx build reads those pages (SPL_SPHINX_BINARY_DIR in CMakeLists.txt). -# For ubCode it is inert, because ubCode does not descend symlinked directories -# ("directories are skipped and symlinked directories are not descended"), so -# the IDE shows no generated page. They are output of the reports target, which -# stays Sphinx-only while its pages use the sphinx-test-reports directive. +# The patterns are written relative to the mounted directory, because that is +# what ubCode matches a parser include against inside a mount, not the mounted +# name. conf.py names the same pages as `generated/...` in `include_patterns`. [parse.parsers.rst] -include = ["generated/**/*.rst"] +include = ["components/**/*.rst", "test/**/*.rst", "reports/*.rst"] [source] exclude = [".bzr", ".direnv", ".eggs", ".git", ".git-rewrite", ".hg", ".svn", ".venv", ".vscode", "_build", "dist", "node_modules", "site-packages"] @@ -61,23 +67,14 @@ respect_gitignore = false # output. Without the exclude, ubCode indexes every variant and every kit ever # built, so need IDs appear many times over and the IDE disagrees with any one # build about what the project contains. conf.py prunes `build` from the Sphinx -# walk for the same reason, and reads the configured build through `generated`. +# walk for the same reason; the selected build comes in through its mount. # `extend_include` is deliberately absent: it is ignored in parser mode, so the # generated listings are named by [parse.parsers.rst] instead. extend_exclude = [ "build/**", - # The root document of spl-core's PER-COMPONENT report, a build shape - # ubCode never performs. conf.py excludes it from every variant-wide - # build, so Sphinx never sees it either; without this line ubCode indexed - # it and reported it as an orphan -- the two readers disagreeing about the - # document set, which is the one thing this configuration exists to - # prevent. `[parse.parsers.*]` has no `exclude`, and extend_exclude IS - # honoured in parser mode (unlike extend_include), so this is the knob. - "doc/component_report.md", ] [needs] -variant_data_file = "build/autoconf.json" src_trace_config_from_toml = "ubproject.toml" @@ -257,119 +254,11 @@ default = [] # --------------------------------------------------------------------------- # Variant-dependent documentation. # -# Two mechanisms, one data source. Both read the file named by -# `[needs] variant_data_file` above, which tools/variant_data.py writes, so -# ubCode, ubc and the Sphinx build decide every condition identically. -# -# `[[source.variant_sources]]` gates whole documents that live IN this tree, -# including the generated output, which the `generated` link brings into it. -# `[[source.mounts]]`, for trees outside it, is not needed. Blocks inside a -# document are gated by the {if} directive of Sphinx-Needs, which reads the -# same `var.*`. -# -# A condition may only name what the variant data file declares. Naming -# anything else makes it unevaluable, and an unevaluable condition EXCLUDES -# what it gates -- so a typo silently shrinks the document set rather than -# failing loudly. +# Nothing here. Which documents a variant has is decided by the rules in +# ubproject.variants.toml, which tools/variant_data.py derives from where each +# component's documentation lives, gated on the variant's component list from +# parts.cmake. Which build a reader shows is build/selection.toml: the variant +# data file it evaluates, and the build directory it mounts at `generated`. +# Blocks inside a document are gated by the {if} directive of Sphinx-Needs, +# which reads the same `var.*`. # --------------------------------------------------------------------------- - -# Rules are subtractive: a rule whose condition is FALSE removes the files it -# names, and a TRUE rule does nothing. Two rules naming the same file therefore -# compose as AND, which is what gates the generated report pages on both the -# build shape and the component being part of the variant. - -# Generated output belongs to the `reports` build shape only. -# -# `generated` is the configured variant's build directory, maintained by -# tools/variant_data.py. spl-core lists the report pages for the `docs` build -# as well, and conf.py passes them on for both shapes, so this rule is the one -# place that keeps them out of a `docs` build. Without it, every report page -# would be read and then left out of every toctree -- one orphan warning each. -[[source.variant_sources]] -if = 'var.build_config.target == "reports"' -files = ["generated/**"] - -# Component documentation, hand-written and generated alike. -# -# Gated on membership of the variant's component list, which the generator -# reads out of that variant's parts.cmake. That is the point: the product -# structure is stated once, in the file that already states it. Gating on the -# owning KConfig feature instead would be a second encoding of the same fact, -# free to drift -- and gating the integration suite on `variant == "Disco"`, as -# this did before, was exactly that drift made concrete: it claimed the suite -# for Disco's prod kit too, where parts.cmake does not add it. -# -# doc/components/index.md carries the full 150% toctree. An entry naming a -# document a rule excluded is reported as INFO by both tools, by design. - -[[source.variant_sources]] -if = "'components/light_controller' in var.build_config.components" -files = [ - "components/light_controller/doc/**", - "generated/components/light_controller/reports/**", - "generated/components/light_controller/__source_docs/**", -] - -[[source.variant_sources]] -if = "'components/main_control_knob' in var.build_config.components" -files = [ - "components/main_control_knob/doc/**", - "generated/components/main_control_knob/reports/**", - "generated/components/main_control_knob/__source_docs/**", -] - -[[source.variant_sources]] -if = "'components/power_button' in var.build_config.components" -files = [ - "components/power_button/doc/**", - "generated/components/power_button/reports/**", - "generated/components/power_button/__source_docs/**", -] - -[[source.variant_sources]] -if = "'components/power_signal_processing' in var.build_config.components" -files = [ - "components/power_signal_processing/doc/**", - "generated/components/power_signal_processing/reports/**", - "generated/components/power_signal_processing/__source_docs/**", -] - -[[source.variant_sources]] -if = "'components/brightness_controller' in var.build_config.components" -files = [ - "components/brightness_controller/doc/**", - "generated/components/brightness_controller/reports/**", - "generated/components/brightness_controller/__source_docs/**", -] - -[[source.variant_sources]] -if = "'components/auto_off' in var.build_config.components" -files = [ - "components/auto_off/doc/**", - "generated/components/auto_off/reports/**", - "generated/components/auto_off/__source_docs/**", -] - -[[source.variant_sources]] -if = "'test/spled_integration' in var.build_config.components" -files = [ - "test/spled_integration/doc/**", - "generated/test/spled_integration/reports/**", - "generated/test/spled_integration/__source_docs/**", -] - -[[source.variant_sources]] -if = "'components/examples/hello_gmock' in var.build_config.components" -files = [ - "components/examples/hello_gmock/doc/**", - "generated/components/examples/hello_gmock/reports/**", - "generated/components/examples/hello_gmock/__source_docs/**", -] - -[[source.variant_sources]] -if = "'components/examples/flight_controller' in var.build_config.components" -files = [ - "components/examples/flight_controller/doc/**", - "generated/components/examples/flight_controller/reports/**", - "generated/components/examples/flight_controller/__source_docs/**", -] From 5c4c6e82161a147d133a70765f715cc960dee5cf Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 12:55:32 +0200 Subject: [PATCH 13/24] feat: import the test results as needs in both readers SPL_TEST_RESULTS_AS_NEEDS: spl-core writes each component's test results as needs.json after the test run, and the results page imports it, with the test specifications' `results` links as needextend blocks. ubproject.toml declares the three types and their 14 fields, because no extension registers them any more; conf.py loads neither sphinx-test-reports nor the sple_tr_link needs function, which removes two configuration warnings. The `file` field has to be declared, because ubc drops an undeclared field of an imported need without a word. sphinx-codelinks registers a field of the same name, and sphinx-needs compares names only, so conf.py filters exactly that one warning. --- CMakeLists.txt | 5 ++ conf.py | 43 +++++++++++++---- ubproject.toml | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 89c53c4..d1b5237 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,11 @@ set(SPL_SPHINX_BINARY_DIR ${CMAKE_SOURCE_DIR}/generated) # generates need no `{% raw %}` armour. set(SPL_SOURCE_DOCS_JINJA_RAW_TAGS OFF) +# Test results as needs.json that every reader imports, instead of +# sphinx-test-reports' test-report directive, which only Sphinx knows. The +# results page also carries the `results` links of the test specifications. +set(SPL_TEST_RESULTS_AS_NEEDS ON) + # The object_deps_report extension is currently Windows-only: its index.cmake # hardcodes the runner as "object_deps_report.exe", which does not exist on # Linux/macOS (the console script is "object_deps_report"). Guard the include to diff --git a/conf.py b/conf.py index 8b48c3f..7d80267 100644 --- a/conf.py +++ b/conf.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """Configuration""" import datetime +import logging import os import tomllib from pathlib import Path @@ -80,15 +81,21 @@ html_logo = "doc/_figures/SPLED_logo.png" -# Get default SPL extensions and their configurations -extensions = [*SplSphinx.default_extensions, "sphinx_codelinks", "sphinx_mounts"] +# Get default SPL extensions and their configurations. The test results are +# needs.json that spl-core writes after each test run and both readers import +# (SPL_TEST_RESULTS_AS_NEEDS in CMakeLists.txt), so sphinx-test-reports' directive, +# which only Sphinx knows, is not loaded. +extensions = [ + *(extension for extension in SplSphinx.default_extensions if extension != "sphinxcontrib.test_reports"), + "sphinx_codelinks", + "sphinx_mounts", +] extension_configs = SplSphinx.default_extension_configs # sphinx-codelinks: extract one-line need definitions from source code comments src_trace_config_from_toml = "ubproject.toml" # Apply extension-specific configurations -tr_report_template = extension_configs["tr_report_template"] myst_enable_extensions = extension_configs["myst_enable_extensions"] source_suffix = extension_configs["source_suffix"] @@ -105,11 +112,31 @@ # the same file through `extend`. sources_from_toml = "ubproject.variants.toml" -# Registers project Python that the configuration references by name. This is -# the last thing in the needs model that ubCode cannot see, because it cannot -# run project functions; it goes away with sple_tr_link. -needs_functions = SplSphinx.default_needs_functions -needs_global_options = SplSphinx.default_needs_global_options +# The `results` links of the test specifications are data, written by spl-core's +# converter as needextend blocks on each results page, so no needs function and +# no computed default is registered: every need and every link is something +# ubCode reads as well. + +# One warning is dropped, and only that one. ubproject.toml has to declare the +# test results' `file` field, because ubc drops an undeclared field of an +# imported need without a word. sphinx-codelinks registers a field of the same +# name, and sphinx-needs compares field names only, so it warns +# "Duplicate need field 'file'" although both declarations agree. Sphinx can only +# suppress a whole warning type (`needs.config`), which would hide every other +# problem in the needs configuration; this filter matches the one message. +# Remove it once sphinx-needs accepts a matching declaration. +class _DropTheDuplicateFileFieldWarning(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + if getattr(record, "type", "") != "needs" or getattr(record, "subtype", "") != "config": + return True + try: + return "Duplicate need field 'file'" not in record.getMessage() + except Exception: + return True + + +for _handler in logging.getLogger("sphinx").handlers: + _handler.filters.insert(0, _DropTheDuplicateFileFieldWarning()) # variant selection ######################################################### # diff --git a/ubproject.toml b/ubproject.toml index 6413941..70d01db 100644 --- a/ubproject.toml +++ b/ubproject.toml @@ -164,6 +164,130 @@ style = "node" # --- end vendored ----------------------------------------------------------- +# Test results. spl-core converts each component's JUnit XML into needs.json +# after the test run (SPL_TEST_RESULTS_AS_NEEDS in CMakeLists.txt), with the +# types, IDs and fields sphinx-test-reports' test-report directive used to +# create, so both readers import them. Declared here because no extension +# registers them any more. +[[needs.types]] +directive = "testfile" +title = "Test-File" +prefix = "TF_" +color = "#ffffff" +style = "node" + +[[needs.types]] +directive = "testsuite" +title = "Test-Suite" +prefix = "TS_" +color = "#cccccc" +style = "folder" + +[[needs.types]] +directive = "testcase" +title = "Test-Case" +prefix = "TC_" +color = "#999999" +style = "rectangle" + +[needs.fields.suites] +description = "Test result: suites" +nullable = true + +[needs.fields.suites.schema] +type = "integer" + +[needs.fields.cases] +description = "Test result: cases" +nullable = true + +[needs.fields.cases.schema] +type = "integer" + +[needs.fields.passed] +description = "Test result: passed" +nullable = true + +[needs.fields.passed.schema] +type = "integer" + +[needs.fields.skipped] +description = "Test result: skipped" +nullable = true + +[needs.fields.skipped.schema] +type = "integer" + +[needs.fields.failed] +description = "Test result: failed" +nullable = true + +[needs.fields.failed.schema] +type = "integer" + +[needs.fields.errors] +description = "Test result: errors" +nullable = true + +[needs.fields.errors.schema] +type = "integer" + +[needs.fields.file] +description = "Test result: file" +nullable = true + +[needs.fields.file.schema] +type = "string" + +[needs.fields.suite] +description = "Test result: suite" +nullable = true + +[needs.fields.suite.schema] +type = "string" + +[needs.fields.case] +description = "Test result: case" +nullable = true + +[needs.fields.case.schema] +type = "string" + +[needs.fields.case_name] +description = "Test result: case_name" +nullable = true + +[needs.fields.case_name.schema] +type = "string" + +[needs.fields.case_parameter] +description = "Test result: case_parameter" +nullable = true + +[needs.fields.case_parameter.schema] +type = "string" + +[needs.fields.classname] +description = "Test result: classname" +nullable = true + +[needs.fields.classname.schema] +type = "string" + +[needs.fields.result] +description = "Test result: result" +nullable = true + +[needs.fields.result.schema] +type = "string" + +[needs.fields.time] +description = "Test result: time" +nullable = true + +[needs.fields.time.schema] +type = "string" + [needs.fields.configuration] description = "Configuration, from the ubConnect ReqIF export" nullable = true From d3df1b3c04b0b31e255cfde6888523907af51a35 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 12:55:32 +0200 Subject: [PATCH 14/24] feat: hand codelinks the selected build's compile database codelinks takes each variant's #ifdef branches from build/compile_commands.json, which only VS Code's CMake Tools used to write, and only when configuring there. tools/compile_commands.py copies the selected build's database there on every build and for every documentation run, without the test kit's -save-temps, with which libclang cannot load a file: codelinks skipped the file and its needs disappeared. The IDE's own copy goes, because it would bring the option back. --- .vscode/settings.json | 1 - CMakeLists.txt | 31 +++++++++++++ tools/compile_commands.py | 92 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 tools/compile_commands.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 7ee109d..5da3c73 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,7 +24,6 @@ "ubproject.variants.toml": true }, "cmake.buildDirectory": "${workspaceFolder}/build/${variant:variant}/${buildKit}/${buildType}", - "cmake.copyCompileCommands": "${workspaceFolder}/build/compile_commands.json", "cmake.configureSettings": { "BUILD_KIT": "${buildKit}", "CMAKE_MESSAGE_LOG_LEVEL": "STATUS", diff --git a/CMakeLists.txt b/CMakeLists.txt index d1b5237..48419d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,3 +145,34 @@ endif() # Include variant specific parts/components definitions include(${PROJECT_SOURCE_DIR}/variants/${VARIANT}/parts.cmake) + +# codelinks reads the selected build's compile database from a fixed path, +# build/compile_commands.json, to take each variant's #ifdef branches. +# tools/compile_commands.py copies it there without `-save-temps`, which libclang +# rejects, and only for the selected build. CMake writes the database at the end +# of configuring, so this runs with every build; after a configure alone, build +# just this target. +add_custom_target(spled_codelinks_compile_commands ALL + COMMAND "${_VENV_PYTHON}" ${CMAKE_SOURCE_DIR}/tools/compile_commands.py --build-dir ${CMAKE_BINARY_DIR} + COMMENT "Handing codelinks the compile database of this build" + VERBATIM +) + +# Every documentation run reads the database, so it waits for the copy. spl-core +# creates those targets at the end of configuring, in a deferred call of its own, +# so this one is deferred as well and runs after it. +function(_spled_documentation_runs_need_the_database) + foreach(_target IN ITEMS docs reports) + if(TARGET ${_target}) + add_dependencies(${_target} spled_codelinks_compile_commands) + endif() + endforeach() + foreach(_component IN LISTS COMPONENT_NAMES) + foreach(_suffix IN ITEMS _docs _report) + if(TARGET ${_component}${_suffix}) + add_dependencies(${_component}${_suffix} spled_codelinks_compile_commands) + endif() + endforeach() + endforeach() +endfunction() +cmake_language(DEFER CALL _spled_documentation_runs_need_the_database) diff --git a/tools/compile_commands.py b/tools/compile_commands.py new file mode 100644 index 0000000..0d9b4f2 --- /dev/null +++ b/tools/compile_commands.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Hand codelinks the selected build's compile database. + +codelinks evaluates each source file's `#ifdef` branches with the build's compile +database, so a variant's one-line needs are the ones that variant compiles. Both +readers take the database from one fixed path, `build/compile_commands.json` +(ubproject.toml), because the path cannot follow the selection: `extend` and +ubc's `-c` both replace a codelinks project's whole table rather than one key in +it. CMake writes the database only into its build directory, so this copies it +to that path -- for the selected build only, the one `build/selection.toml` +mounts, so building another variant leaves the editor's database alone. + +The test kit compiles with `-save-temps`, which libclang cannot load a file with: +codelinks would skip every file, and its needs would disappear. The copy leaves +that option out. Once codelinks drops it itself, the filter can go. + +Run by the `spled_codelinks_compile_commands` target (CMakeLists.txt) on every +build, and after a configure without a build: + + cmake --build --target spled_codelinks_compile_commands +""" + +from __future__ import annotations + +import argparse +import json +import re +import sys +import tomllib +from pathlib import Path +from typing import Any + +PROJECT_ROOT = Path(__file__).resolve().parent.parent + +#: Where codelinks reads the database (ubproject.toml). +TARGET = "build/compile_commands.json" + +#: The selection whose build directory is the one to copy from. +SELECTION = "build/selection.toml" + +#: `-save-temps` and `-save-temps=obj|cwd`, as a whole token. +_SAVE_TEMPS = re.compile(r"(? Path | None: + """The build directory the selection mounts, or None if it names no build.""" + path = project_root / SELECTION + if not path.is_file(): + return None + mounts = tomllib.loads(path.read_text(encoding="utf-8")).get("source", {}).get("mounts", []) + return Path(mounts[0]["dir"]).resolve() if mounts else None + + +def without_save_temps(entries: list[dict[str, Any]]) -> list[dict[str, Any]]: + result = [] + for entry in entries: + entry = dict(entry) + if "command" in entry: + entry["command"] = _SAVE_TEMPS.sub("", entry["command"]).rstrip() + if "arguments" in entry: + entry["arguments"] = [argument for argument in entry["arguments"] if not _SAVE_TEMPS.fullmatch(argument)] + result.append(entry) + return result + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--project-root", type=Path, default=PROJECT_ROOT) + parser.add_argument("--build-dir", type=Path, required=True, help="CMake binary dir whose database to copy") + args = parser.parse_args(argv) + + project_root: Path = args.project_root.resolve() + build_dir: Path = args.build_dir.resolve() + if selected_build_dir(project_root) != build_dir: + print(f"{build_dir} is not the selected build; {TARGET} is left as it is") + return 0 + source = build_dir / "compile_commands.json" + if not source.is_file(): + print(f"{source} does not exist yet; {TARGET} is left as it is") + return 0 + + text = json.dumps(without_save_temps(json.loads(source.read_text(encoding="utf-8"))), indent=2) + "\n" + target = project_root / TARGET + if not target.is_file() or target.read_text(encoding="utf-8") != text: + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(text, encoding="utf-8") + print(f"wrote {TARGET} from {source}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 3b630bd0f5369684e4b2d51c64b377c0d9a43738 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 12:55:45 +0200 Subject: [PATCH 15/24] docs: give every generated page a place in the navigation Each component's Verification toctree names its source listings as well, and the two example components, which have tests but had no Verification section, get one. spl-core's wrapper pages were the only route to the listings, and SPLed links no wrapper: Sphinx reported the wrappers and ubCode the listings as in no toctree. The wrappers stay out of both readers now, because the mount admits only the report pages and listings. --- components/auto_off/doc/index.md | 1 + components/brightness_controller/doc/index.md | 1 + .../examples/flight_controller/doc/index.md | 15 +++++++++++++++ components/examples/hello_gmock/doc/index.md | 15 +++++++++++++++ components/light_controller/doc/index.md | 1 + components/main_control_knob/doc/index.md | 1 + components/power_button/doc/index.md | 1 + components/power_signal_processing/doc/index.md | 1 + test/spled_integration/doc/index.md | 1 + 9 files changed, 37 insertions(+) diff --git a/components/auto_off/doc/index.md b/components/auto_off/doc/index.md index f8cb62b..3ecc418 100644 --- a/components/auto_off/doc/index.md +++ b/components/auto_off/doc/index.md @@ -132,6 +132,7 @@ Key press detection and timer reset occur within one execution cycle (CONFIG_OS_ /generated/components/auto_off/reports/unit_test_spec /generated/components/auto_off/reports/unit_test_results /generated/components/auto_off/reports/coverage +/generated/components/auto_off/__source_docs/index ``` ```` diff --git a/components/brightness_controller/doc/index.md b/components/brightness_controller/doc/index.md index 554587e..6ea40b1 100644 --- a/components/brightness_controller/doc/index.md +++ b/components/brightness_controller/doc/index.md @@ -80,6 +80,7 @@ The Brightness Controller is use the RTE interface `RteSetBrightnessAdjustmentCo /generated/components/brightness_controller/reports/unit_test_spec /generated/components/brightness_controller/reports/unit_test_results /generated/components/brightness_controller/reports/coverage +/generated/components/brightness_controller/__source_docs/index ``` ```` diff --git a/components/examples/flight_controller/doc/index.md b/components/examples/flight_controller/doc/index.md index 35403b4..c2b5b82 100644 --- a/components/examples/flight_controller/doc/index.md +++ b/components/examples/flight_controller/doc/index.md @@ -98,3 +98,18 @@ flowchart TD :project: components :directory: examples/flight_controller ``` + +````{if} var.build_config.target == "reports" + +## Verification + +```{toctree} +:maxdepth: 1 + +/generated/components/examples/flight_controller/reports/unit_test_spec +/generated/components/examples/flight_controller/reports/unit_test_results +/generated/components/examples/flight_controller/reports/coverage +/generated/components/examples/flight_controller/__source_docs/index +``` + +```` diff --git a/components/examples/hello_gmock/doc/index.md b/components/examples/hello_gmock/doc/index.md index 73ba058..10cf431 100644 --- a/components/examples/hello_gmock/doc/index.md +++ b/components/examples/hello_gmock/doc/index.md @@ -95,3 +95,18 @@ My component sets the data by calling an interface that requires a pointer to be Verify that the set interface is called with the expected value through the pointer. ``` + +````{if} var.build_config.target == "reports" + +## Verification + +```{toctree} +:maxdepth: 1 + +/generated/components/examples/hello_gmock/reports/unit_test_spec +/generated/components/examples/hello_gmock/reports/unit_test_results +/generated/components/examples/hello_gmock/reports/coverage +/generated/components/examples/hello_gmock/__source_docs/index +``` + +```` diff --git a/components/light_controller/doc/index.md b/components/light_controller/doc/index.md index 90dca9c..97bd429 100644 --- a/components/light_controller/doc/index.md +++ b/components/light_controller/doc/index.md @@ -128,6 +128,7 @@ stateDiagram-v2 /generated/components/light_controller/reports/unit_test_spec /generated/components/light_controller/reports/unit_test_results /generated/components/light_controller/reports/coverage +/generated/components/light_controller/__source_docs/index ``` ```` diff --git a/components/main_control_knob/doc/index.md b/components/main_control_knob/doc/index.md index 0290e22..656116e 100644 --- a/components/main_control_knob/doc/index.md +++ b/components/main_control_knob/doc/index.md @@ -69,6 +69,7 @@ The main control knob component uses the RTE interface `RteSetMainKnobValue()` t /generated/components/main_control_knob/reports/unit_test_spec /generated/components/main_control_knob/reports/unit_test_results /generated/components/main_control_knob/reports/coverage +/generated/components/main_control_knob/__source_docs/index ``` ```` diff --git a/components/power_button/doc/index.md b/components/power_button/doc/index.md index bb4fb55..8c3eac1 100644 --- a/components/power_button/doc/index.md +++ b/components/power_button/doc/index.md @@ -80,6 +80,7 @@ stateDiagram-v2 /generated/components/power_button/reports/unit_test_spec /generated/components/power_button/reports/unit_test_results /generated/components/power_button/reports/coverage +/generated/components/power_button/__source_docs/index ``` ```` diff --git a/components/power_signal_processing/doc/index.md b/components/power_signal_processing/doc/index.md index fb1cb05..ddefd3a 100644 --- a/components/power_signal_processing/doc/index.md +++ b/components/power_signal_processing/doc/index.md @@ -126,6 +126,7 @@ graph TD /generated/components/power_signal_processing/reports/unit_test_spec /generated/components/power_signal_processing/reports/unit_test_results /generated/components/power_signal_processing/reports/coverage +/generated/components/power_signal_processing/__source_docs/index ``` ```` diff --git a/test/spled_integration/doc/index.md b/test/spled_integration/doc/index.md index 3d7394c..40dfdd4 100644 --- a/test/spled_integration/doc/index.md +++ b/test/spled_integration/doc/index.md @@ -10,6 +10,7 @@ /generated/test/spled_integration/reports/unit_test_spec /generated/test/spled_integration/reports/unit_test_results /generated/test/spled_integration/reports/coverage +/generated/test/spled_integration/__source_docs/index ``` ```` From 5076fdc07aa365899dbd97a2f84e16b693c8a02d Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Tue, 29 Sep 2026 12:55:45 +0200 Subject: [PATCH 16/24] docs: describe the generated selection AGENTS.md and VARIANTS.md describe what the selection step generates and how each reader takes it: the rules file, the selection and the per-run files, the component reports, the one-line needs and the compile database, the test results as needs, and the one gap left, ubCode not mounting a directory inside the project. Adding a component is adding it to parts.cmake and writing its documentation; hand-written special-case rules go into doc/variant_rules.toml. --- AGENTS.md | 188 +++++++++++++++++++++++++++++++--------------------- README.md | 2 + VARIANTS.md | 143 ++++++++++++++++++++++++--------------- 3 files changed, 204 insertions(+), 129 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4df6f00..7e579a0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -66,7 +66,8 @@ mode. `pyproject.toml` currently pins a **commit of the useblocks fork** of spl-core (branch `feat/configurable-docs-pipeline`, based on spl-core 8.9.0). It carries the documentation changes this project relies on: `SPL_SOURCE_DOCS_JINJA_RAW_TAGS`, -`SPL_VARIANT_DATA_FILE_DOCS` / `_REPORTS`, `SPL_SPHINX_BINARY_DIR` and +`SPL_SPHINX_BINARY_DIR`, `SPL_SPHINX_OPTIONS` / `SPL_SPHINX_COMPONENT_OPTIONS`, +`SPL_TEST_RESULTS_AS_NEEDS` with its JUnit converter, and `KConfig.declared_boolean_symbols()`. Pinning a commit keeps every build on the same code. Switch back to a PyPI release once upstream spl-core has released them. @@ -124,7 +125,7 @@ CI runs on **GitHub Actions** (`.github/workflows/ci.yml`) for every push/PR to Jobs: - `determine-gate` — computes the `gate_*` quality-gate marker once (by event/branch) and shares it with all four jobs via `needs`. -- `documentation` (`ubuntu-24.04`) — the compiler-free gate: a Python and the locked dependencies, then `pytest -m "docs and "`. It generates the variant data for **every** variant and builds each one's documents, where the build jobs only cover the variants they build. No poks, no scoop, no cross-compiler, so it is also the fastest signal in the workflow. +- `documentation` (`ubuntu-24.04`) — the documentation gate: a Python and the locked dependencies, then `pytest -m "docs and "`. It configures **every** variant and kit (the runner's gcc, g++, cmake and ninja; no poks, no scoop, no compiling), so codelinks takes each variant's `#ifdef` branches, and builds each one's documents in both readers in strict mode, comparing their needs. It is also the fastest signal in the workflow. - `test-on-windows` (`windows-2025`) — `build.ps1 -install` then `-selftests -marker `. - `test-on-linux` (`ubuntu-24.04`) — bare-runner path: `bootstrap_ubuntu.sh` + `bootstrap_python.sh`, then `build.sh --install` and `--selftests --marker `. - `test-devcontainer` (`ubuntu-24.04`) — builds `.devcontainer/` via `devcontainers/ci` (which runs `onCreateCommand`, i.e. `build.sh --install`) and runs `build.sh --selftests --marker ` inside the container. @@ -183,41 +184,59 @@ clanguru wrap their code in Jinja `{% raw %}` markers for projects that do render through Jinja; `CMakeLists.txt` turns that off (`SPL_SOURCE_DOCS_JINJA_RAW_TAGS`), so no `source-read` handler exists at all. -Everything variant-dependent is decided from **one file**: the variant data -that `tools/variant_data.py` writes, exposed as `var.*`. The governing rule: +### What decides what a reader shows + +A selection step runs before any reader: CMake configure, or +`tools/variant_data.py` on its own. It writes every file that decides what +ubCode, `ubc` and the Sphinx build show, and each reader takes them from there. +Nothing is decided on the command line or in `conf.py`, and nothing per +component is maintained by hand. + +| File | Holds | Read by | +| --- | --- | --- | +| `build/variants///.json` | the variant data, `var.*`; one cell per variant, kit and build shape | both, through the selection | +| `build/variants////.json` | the same, for one component's report (`scope = "component"`) | both, through that report's selection | +| `ubproject.variants.toml` (project root, git-ignored) | one document rule per documented component, derived from where its documentation lives | ubCode through `extend` in ubproject.toml; sphinx-mounts through `sources_from_toml` | +| `build/selection.toml` | the selected build: its cell's variant data file and the mount of its build directory at `generated` | ubCode through the rules file's `extend`; Sphinx through conf.py | +| `/selection/[/].toml` | the same, for each documentation run spl-core starts in that build | Sphinx with `-D spl_selection=`, ubc with `-c "$(cat )"` | + +The governing rule is unchanged: > **Everything a condition may name has to be IN the variant data file.** A key that only `conf.py` knows is invisible to ubCode, `ubc` and a reviewer's editor, so their view of the project disagrees with the build. A condition a tool cannot evaluate gates its content **off**: the reader warns and carries on -without it. That is why `conf.py` does not touch the file at all: -sphinx-needs reads it, and which cell a build reads is a command-line override -(`-D needs_variant_data_file=...`), exactly as for `ubc`. +without it. That is why `conf.py` computes nothing: it hands the selection's +keys to the two Sphinx extensions that cannot follow `extend`, and that is all. -### The three mechanisms +### The mechanisms -**Whole documents: `[[source.variant_sources]]` in `ubproject.toml`.** One rule -per component, gated on membership of the variant's component list: +**Whole documents: the generated rules.** `tools/variant_data.py` writes one rule +per component that has a `doc/index` page, gated on membership of the variant's +component list: ```toml [[source.variant_sources]] -if = "'components/auto_off' in var.build_config.components" +if = "'components/auto_off' in var.build_config.components and (var.build_config.scope == 'variant' or var.build_config.component == 'components/auto_off')" files = [ "components/auto_off/doc/**", - "generated/components/auto_off/reports/**", - "generated/components/auto_off/__source_docs/**", + "generated/components/auto_off/**", ] ``` Membership, never identity. The component list comes from that variant's `parts.cmake`, so the product structure is stated once, in the file that already -states it. **Never gate on the variant name** — that is a second encoding of the -same fact, free to drift. +states it. **Never gate a document on the variant name** — that is a second +encoding of the same fact, free to drift. Rules are *subtractive*: a FALSE rule +removes the files it names, a TRUE rule does nothing. Two more rules decide the +shape of the report: the variant-wide documents (`index.md`, `doc/`) exist only +in a variant's report, `doc/component_report.md` only in a component's. -Rules are *subtractive*: a FALSE rule removes the files it names, a TRUE rule -does nothing. Two rules naming the same file therefore compose as AND, which is -how generated output is gated on both the build shape and the component. +The rules are the same for every variant, and they live in a file of their own +at the project root because sphinx-mounts reads rule patterns relative to the +file that declares them and refuses absolute or `..` ones. Do not edit the file: +change the generator. **Blocks inside a document: the `{if}` directive of Sphinx-Needs.** Four-backtick fence, condition as the argument: @@ -231,8 +250,38 @@ fence, condition as the argument: Content behind a false condition is never parsed, so its needs never enter the traceability data. -**External trees: `[[source.mounts]]`.** Currently unused. Everything this -project shows lives in the tree or under `generated/`. +**The generated pages: a mount of the selected build.** The selection mounts the +build directory at `generated`, where spl-core names every page it generates +(`SPL_SPHINX_BINARY_DIR` in CMakeLists.txt): + +```toml +[[source.mounts]] +dir = "/build/Disco/test/Debug" +mount_at = "generated" +include = ["components/**/*.rst", "test/**/*.rst", "reports/*.rst"] +gitignore = false +if = 'var.build_config.target == "reports" and var.build_config.variant == "Disco" and var.build_config.kit == "test"' +``` + +The mount's condition does name a variant: the build's own. A reader pointed at +another variant's data then reads none of this build's pages, instead of showing +them as that variant's. No link, no junction, no copy: each build keeps its +pages in its own directory, so two builds' reports can be built side by side, +and `ubc diff -c "$(cat build/Spa/test/Debug/selection/reports.toml)"` compares +the selected build with another. + +**Implementation needs in the code: one-line comments.** `// @need , <id>, +impl, [<implements>], [<fulfills>]` above the code, shown on the component's page +by `src-trace` (`:project: components`, `:directory: <component>`). codelinks +takes the `#ifdef` branches from `build/compile_commands.json`, which +`tools/compile_commands.py` copies from the selected build without the +`-save-temps` libclang rejects. + +**Test results: needs.json.** spl-core converts each component's JUnit XML into +`unit_test_results.needs.json` after the test run (`SPL_TEST_RESULTS_AS_NEEDS`) +and writes the page that imports it, with the test specifications' `results` +links as `needextend` blocks. Both readers import them; sphinx-test-reports and +the `sple_tr_link` needs function are gone. ### What the data holds @@ -243,23 +292,25 @@ project shows lives in the tree or under `generated/`. | `var.build_config.kit` | `prod` or `test` | | `var.build_config.target` | `docs` or `reports` | | `var.build_config.components` | the variant's component list, from `parts.cmake` | +| `var.build_config.scope` | `variant`, or `component` for a per-component report | +| `var.build_config.component` | that component's path, empty for a variant | ### Two differences between the engines The `{if}` directive takes a real Python expression, so a bare -`var.features.BLINKING` is enough. A `variant_sources` condition uses a -restricted grammar that needs `== True`. And a condition that cannot be -evaluated **excludes** what it gates: Sphinx warns (sphinx-needs for an `{if}` -block, sphinx-mounts for a rule) and builds on without that content — which is what `test_ubproject_config.py` is -for. +`var.features.BLINKING` is enough. A rule condition uses a restricted grammar +that needs `== True`. And a condition that cannot be evaluated **excludes** what +it gates: Sphinx warns (sphinx-needs for an `{if}` block, sphinx-mounts for a +rule) and builds on without that content — which is what +`test_ubproject_config.py` is for. ### Checking with the other reader -The Sphinx build is only half the story: the point of keeping everything in the -variant data file is that a reader which never runs `conf.py` decides the same -things. `ubc` is that reader, and `pytest -m docs -k ubc` proves it — per -variant, it asserts that ubCode removes **exactly** the component documents the -variant's component list omits. +The Sphinx build is only half the story: the point of generating everything into +files is that a reader which never runs `conf.py` decides the same things. `ubc` +is that reader, and `pytest -m docs -k ubc` proves it — per variant, it asserts +that ubCode removes **exactly** the component documents the variant's component +list omits. `ubc` ships inside the ubCode VS Code extension and is on neither PyPI nor npm, so there is no install step this repository can own. The tests find it on @@ -271,73 +322,62 @@ export UBC="$HOME/.vscode/extensions/useblocks.ubcode-0.35.0-darwin-arm64/server pytest -m docs -k ubc ``` -To look at one variant by hand, override the data file rather than switching -the project: +To look at another cell without switching the project, override the data file, +or hand a build's own selection file over whole: ```bash ubc check -c "needs.variant_data_file = 'build/variants/Sleep/test/docs.json'" +ubc check -c "$(cat build/Spa/test/Debug/selection/reports.toml)" +sphinx-build -b html -D needs_variant_data_file=build/variants/Sleep/test/docs.json . out +sphinx-build -b html -D spl_selection=build/Spa/test/Debug/selection/reports.toml . out ``` -Two things to know about `ubproject.toml` when editing it. Configuring parsers +Three things to know about `ubproject.toml` when editing it. Configuring parsers puts ubCode in **parser mode**, where the document set comes from each `[parse.parsers.*].include` and `[source] extend_include` is *ignored* — so the parser includes have to stay in step with `include_patterns` in `conf.py`, or -the two readers are looking at different files. And `[[source.variant_sources]]` -rules are implemented as exclusions, which is why a rule that cannot be -evaluated removes what it gates. +the two readers are looking at different files. The rst include is written +relative to the mounted build directory, because that is what ubCode matches it +against. And `extend` merges tables key by key but replaces arrays, so the +committed file declares no rules and no mounts: they would replace the generated +ones. + +**Known gap.** ubCode does not mount a directory inside the project root, and +every CMake build lives under `build/`: until ubCode can, the IDE and `ubc` show +no generated page (test results, test specifications, listings). Sphinx reads +them. Everything else — the documents, the rules, the implementation needs — +agrees need for need. ### Adding a component 1. Add it to the variant's `parts.cmake`. -2. Add one `[[source.variant_sources]]` rule in `ubproject.toml`. -3. Add one line to the 150% toctree in `doc/components/index.md`. +2. Write its documentation in `<component>/doc/index.md`, with a `## Traceability` + section (`src-trace`) and a `## Verification` section inside + `{if} var.build_config.target == "reports"`, as the other components do. -Nothing is generated, no loop is edited, and nothing under `build/` is touched. +The next selection writes its rule, and the glob toctree in +`doc/components/index.md` lists it. Nothing else is edited, and nothing under +`build/` is touched. ### Generated output -`build/` and `generated/` are output. **Nobody edits them — not a person, not an -assistant.** The editor is configured to refuse it (`files.readonlyInclude`) and -`build/variants/GENERATED` says so on disk. - -`generated/` is the configured variant's build directory: a symlink (a junction -on Windows without Developer Mode) that `tools/variant_data.py` points at the -build CMake configures. `CMakeLists.txt` hands it to spl-core as -`SPL_SPHINX_BINARY_DIR`, so every page spl-core generates is named through it: -`generated/components/<c>/reports/coverage`, whatever the variant, kit or build -type. The report sections therefore name their pages directly, spl-core writes -each coverage report next to its page, and `SplBuild` finds the report -artifacts where the pages landed. - -The Sphinx build reads generated pages **only** through `generated`: `conf.py` -prunes `build` from its walk, so each page has exactly one name. -`test_generated_is_the_only_route_to_the_generated_pages` guards that. Because -the link is re-pointed on every configure, spl-core stops a documentation build -whose link leads to another build directory. Configure that build again first. - -ubCode does not descend the link, so the IDE shows no generated page. They are -output of the reports target, which stays Sphinx-only while its pages use the -sphinx-test-reports directive. +`build/`, `generated/` and `ubproject.variants.toml` are output. **Nobody edits +them — not a person, not an assistant.** The editor is configured to refuse it +(`files.readonlyInclude`), and `build/variants/GENERATED` and the files' own +headers say so. Regenerate without a compiler — KConfig is pure Python, and CMake's top-level `project()` call demands a C toolchain before it will configure at all: ```bash -python tools/variant_data.py --all # the whole matrix -python tools/variant_data.py --variant Sleep --kit test # ...and point at one cell -python tools/variant_data.py --all --check # CI: regenerate and diff -``` - -Preview a variant by pointing the build at its cell. It is a command-line -override of the same key `ubc check -c` overrides, and the way spl-core selects -the cell for the docs and reports targets; `conf.py` has no say in it: - -```bash -sphinx-build -b html -D needs_variant_data_file=build/variants/Sleep/test/docs.json . out +python tools/variant_data.py --all # the matrix and the rules +python tools/variant_data.py --all --current --variant Sleep --kit test # ...and select a cell +python tools/variant_data.py --all --check # CI: regenerate and diff ``` -Without the override, a build reads the pointer `build/autoconf.json`, which -exists once CMake or `tools/variant_data.py --variant ... --kit ...` has written it. +Selecting a cell this way writes `build/selection.toml` without a mount, so the +readers show the cell's documents and no generated page. Configuring a CMake +build selects that build, mount included. ## Project-Specific Conventions diff --git a/README.md b/README.md index 37abc7a..59cdc0b 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,8 @@ For the full testing strategy, marker definitions, and the gate assignment matri ## Variants and their documentation What a variant contains, and what its documentation shows, is decided by data, not by templates. +Selecting a variant generates everything the readers need, so nothing is maintained per component: +adding one is adding it to a variant's `parts.cmake` and writing its documentation. [VARIANTS.md](VARIANTS.md) walks through it by use case: switching the variant ubCode shows, previewing any variant, comparing two variants, trying a change without touching the product, and writing documentation that depends on the variant. diff --git a/VARIANTS.md b/VARIANTS.md index 70578cc..f5ff412 100644 --- a/VARIANTS.md +++ b/VARIANTS.md @@ -8,7 +8,10 @@ One set of documents describes every variant, and **data decides what one varian document is a template, and nothing in `conf.py` or CMake decides what a page contains: - a **variant data file** per variant says which features are on and which components are in; -- **conditions** in `ubproject.toml` and in the documents read that file; +- **conditions** read that file: in the documents, and in **rules** that `tools/variant_data.py` + generates from where each component's documentation lives, so no rule is written by hand; +- a **selection** file says which variant data file, and which build's generated pages, the editor + and a plain build show; - **Sphinx** and **ubCode**, with its command-line tool `ubc`, read the same files, so the editor shows what the build produces. @@ -41,19 +44,24 @@ flowchart LR C["variants/Sleep/config.txt<br/>the features of a variant"] P["variants/Sleep/parts.cmake<br/>the components of a variant"] end - G(["tools/variant_data.py"]) + G(["tools/variant_data.py<br/>run by CMake configure"]) subgraph gen["Generated, never edited"] V["build/variants/Sleep/test/docs.json<br/>one file per variant, kit and target"] - A["build/autoconf.json<br/>the variant you work on"] + RU["ubproject.variants.toml<br/>one rule per component"] + A["build/selection.toml<br/>the build you work on"] end - R["ubproject.toml and documents<br/>hold the conditions"] + R["documents<br/>hold the {if} conditions"] U["ubCode and ubc"] S["sphinx-build"] K --> G C --> G P --> G G --> V + G --> RU G --> A + A -->|names| V + RU --> U + RU --> S A --> U A --> S R --> U @@ -71,8 +79,9 @@ flowchart LR | **Component** | A folder under `components/`, plus the integration suite `test/spled_integration`. A variant lists its components in its `parts.cmake`. | | **Kit** | `prod`, the product, or `test`, which adds unit tests, coverage and, for Disco, the integration suite. | | **Target** | `docs`, the documents, or `reports`, the documents plus test results and coverage. | -| **Variant data file** | `build/variants/<variant>/<kit>/<target>.json`, one per combination: 5 variants × 2 kits × 2 targets = 20 files. | -| **Pointer** | `build/autoconf.json`, a copy of the variant data file you are working on. ubCode and a plain `sphinx-build` read it. | +| **Variant data file** | `build/variants/<variant>/<kit>/<target>.json`, one per combination: 5 variants × 2 kits × 2 targets = 20 files, plus one per component report under `<kit>/<component>/`. | +| **Rules** | `ubproject.variants.toml`, generated: one rule per component, which leaves the component's documents out of a variant that does not contain it. | +| **Selection** | `build/selection.toml`, generated when a variant is selected: which variant data file the editor and a plain `sphinx-build` read, and, for a CMake build, its generated pages, mounted at `generated`. | | **150 % documentation** | Every document of every variant lives in the tree. Conditions remove what one variant does not have. | | **`var.*`** | How a condition reads the variant data: `var.features.BLINKING`, `var.build_config.components`. | @@ -84,7 +93,9 @@ flowchart LR { "build_config": { "components": ["components/platform_types", "components/rte", "…", "components/brightness_controller", "components/auto_off"], + "component": "", "kit": "test", + "scope": "variant", "target": "docs", "variant": "Sleep" }, @@ -103,6 +114,8 @@ flowchart LR the ones that are off, so a condition on it always has an answer. - `build_config.components` is the variant's component list, read from its `parts.cmake`. - `build_config.variant`, `kit` and `target` say which combination the file describes. +- `build_config.scope` is `variant`, or `component` in the file of a per-component report, whose + `build_config.component` names the component. Everything under `build/` is generated, and nobody edits it; VS Code opens it read-only. To change a variant, change its sources ([15](#15-add-or-change-a-feature), [16](#16-add-a-variant)), or @@ -139,29 +152,31 @@ All commands below run from the repository root, with the virtualenv activated. python tools/variant_data.py --all ``` -- This writes all 20 variant data files under `build/variants/`, from `KConfig`, each variant's - `config.txt` and each variant's `parts.cmake`. KConfig is pure Python, so it needs neither a - compiler nor CMake. +- This writes the variant data files under `build/variants/`, from `KConfig`, each variant's + `config.txt` and each variant's `parts.cmake`, and the rules file `ubproject.variants.toml`. + KConfig is pure Python, so it needs neither a compiler nor CMake. - Run it again whenever `KConfig`, a `config.txt` or a `parts.cmake` changes. `python tools/variant_data.py --all --check` tells you whether the files are up to date; CI runs it. - Configuring a CMake build, with the build scripts or the CMake extension, writes the files of the - variant it configures, and makes that variant the current one ([4](#4-switch-the-variant-your-editor-shows)). + variant it configures, and selects that build ([4](#4-switch-the-variant-your-editor-shows)). ## Explore ### 3. See which variant you are working on -The pointer says it: +The selection says it: ```bash -python -c "import json; c = json.load(open('build/autoconf.json'))['build_config']; print(c['variant'], c['kit'], c['target'])" +python -c "import tomllib; print(tomllib.load(open('build/selection.toml', 'rb'))['needs']['variant_data_file'])" ``` ```text -Disco test docs +/home/you/SPLed/build/variants/Disco/test/reports.json ``` +If the selection also mounts a build, its `[[source.mounts]]` entry names the build directory. + The documentation says it as well: its start page shows **Variant: Disco**. The page takes that from the variant data file ([10](#10-print-a-variant-value-in-the-text)). @@ -175,14 +190,13 @@ from the variant data file ([10](#10-print-a-variant-value-in-the-text)). python tools/variant_data.py --variant Sleep --kit test --current ``` -Both copy the chosen variant's `docs` file to `build/autoconf.json`, so ubCode and a plain -`sphinx-build` now read Sleep. If the editor still shows the previous variant, reload the window -(*Command Palette → Developer: Reload Window*). +Both write `build/selection.toml`, which names the chosen variant's `docs` file, so ubCode and a +plain `sphinx-build` now read Sleep. If the editor still shows the previous variant, reload the +window (*Command Palette → Developer: Reload Window*). - The kit defaults to `prod`. Give `--kit test` for the test kit. -- Configuring a CMake build makes the same switch, to the variant you configure. -- The pointer always holds a `docs` file, so in the editor the report-only sections are inactive - ([11](#11-show-a-section-only-in-the-reports-build)). +- Configuring a CMake build selects that build: a `test` kit build its `reports` file and its + generated pages, mounted at `generated`; a `prod` kit build its `docs` file. ### 5. Look at another variant without switching @@ -207,8 +221,18 @@ You do not have to switch to look at a variant. Point the reader at its file ins rule that removed it. `ubc check` also lists the project's other warnings, so its exit code says nothing about the variant. -`-D` for Sphinx and `-c` for ubc override the same setting, `variant_data_file`. spl-core passes -the same `-D` when CMake builds the `docs` and `reports` targets. +`-D` for Sphinx and `-c` for ubc override the same setting, `variant_data_file`. + +To look at another **build**, with its generated pages, hand the readers that build's own +selection file. CMake writes one per documentation run into the build directory: + +```bash +python -m sphinx -b html -D spl_selection=build/Spa/test/Debug/selection/reports.toml . build/docs/Spa +ubc check -c "$(cat build/Spa/test/Debug/selection/reports.toml)" +``` + +spl-core does the same for every `docs` and `reports` run it starts, so the reports of two builds +can be built side by side. ### 6. See what differs between two variants @@ -231,6 +255,13 @@ Build both variants as in [5](#5-look-at-another-variant-without-switching), the The output lists every need that is new, removed or changed. For Disco against Sleep, it starts with `New need SWDD_AO-100`, the first of the auto-off requirements that only Sleep has. +- **Builds.** Every build keeps its own report pages and `needs.json` + (`build/<variant>/<kit>/<type>/reports/html/needs.json`), so two builds compare with `ubc diff -n`, + or directly with the other build's selection file: + + ```bash + ubc diff -c "$(cat build/Spa/test/Debug/selection/reports.toml)" + ``` ### 7. Try a change without touching the product @@ -271,9 +302,10 @@ use one of these, from the quickest to the most complete: ([12](#12-include-a-document-only-when-its-component-is-in-the-variant)). An entry for a component the variant does not have is reported as `toctree.variant_excluded`, with the rule that removed the document. -- **No generated report pages.** Test results and coverage exist only after a CMake build of the - `reports` target. ubCode does not follow the `generated` link to them, and the pointer holds a - `docs` file anyway. +- **No generated pages yet.** Test results, test specifications and source listings exist after a + CMake build of the test kit, and the selection mounts them at `generated`. Sphinx reads them; + ubCode does not yet, because it does not mount a directory that lies inside the project, and + every build lives under `build/`. ubCode's side is described in [ubCode's guide to variants](https://ubcode.useblocks.com/usage/variants.html). @@ -342,30 +374,31 @@ A `reports` build adds test results and coverage. Gate the section on the target ````` The component pages end with a block like this one. The `generated/…` names are the same in every -variant ([19](#19-when-something-looks-wrong) explains the `generated` link). +variant: the selection mounts the build directory there. ### 12. Include a document only when its component is in the variant -Whole documents are gated in `ubproject.toml`, with one rule per component: +You do not write anything for it. `tools/variant_data.py` generates one rule per component that has +a `doc/index` page, into `ubproject.variants.toml`: ```toml [[source.variant_sources]] -if = "'components/auto_off' in var.build_config.components" +if = "'components/auto_off' in var.build_config.components and (var.build_config.scope == 'variant' or var.build_config.component == 'components/auto_off')" files = [ "components/auto_off/doc/**", - "generated/components/auto_off/reports/**", - "generated/components/auto_off/__source_docs/**", + "generated/components/auto_off/**", ] ``` - When the condition is false, the files are left out before anything is read: no page, no needs, and no warning about a page that no toctree lists. -- The components page, `doc/components/index.md`, lists every component anyway. Sphinx and ubCode - report an entry for a left-out document as information. +- The components page, `doc/components/index.md`, globs every component's documentation, so it + lists exactly the variant's components. +- The second half of the condition is for the per-component report, which shows one component. - **Gate on the component list, never on the variant name.** The list comes from the variant's `parts.cmake`, so the product structure is written down once. `test_no_rule_names_a_variant_by_name` fails if a rule names a variant. -- Rule conditions use a smaller grammar than `{if}`. A boolean needs `== True`, as in +- Rule conditions, such as those of use case 13, use a smaller grammar than `{if}`. A boolean needs `== True`, as in `if = "var.features.AUTO_OFF == True"`. `and`, `or`, `not`, `==`, `!=`, `<`, `>`, `in` and `not in` work. A condition outside the grammar stops the Sphinx build. A condition that names an unknown key is reported, and removes its files. @@ -387,7 +420,8 @@ changes its place per variant. If one ever has to: ``` ```` 3. Gate the two pages with a rule and its **negation**, so that every variant has exactly one of - them: + them. Hand-written rules go into `doc/variant_rules.toml`, which `tools/variant_data.py` adds to + the generated ones; `ubproject.toml` cannot hold rules, because `extend` replaces an array: ```toml [[source.variant_sources]] @@ -409,15 +443,13 @@ without a warning. 1. Add `spl_add_component(components/<name>)` to the `parts.cmake` of every variant that has it. 2. Write its documentation in `components/<name>/doc/index.md`. If its sources implement design needs, give that page a `## Traceability` section with a `src-trace` block - ([17](#17-trace-code-to-the-design-per-variant)). -3. Add one rule to `ubproject.toml`, next to the others: copy the `auto_off` rule from - [12](#12-include-a-document-only-when-its-component-is-in-the-variant) and change its paths. -4. Add one line to the toctree in `doc/components/index.md`: `/components/<name>/doc/index`. -5. Regenerate the variant data: `python tools/variant_data.py --all`. - -Nothing is generated by hand, no loop is edited, and nothing under `build/` is touched. -`test_every_component_document_is_gated_by_a_rule` fails if step 3 is missing. To have the report -tasks in VS Code offer the component, add it to the `component` input in `.vscode/tasks.json`. For + ([17](#17-trace-code-to-the-design-per-variant)), and end it with a `## Verification` section + for the reports, as the other components do ([11](#11-show-a-section-only-in-the-reports-build)). +3. Regenerate: `python tools/variant_data.py --all`, or configure a build. + +That is all. The generator writes the component's rule, and the glob toctree in +`doc/components/index.md` lists its page. No rule and no toctree line is written by hand, and +nothing under `build/` is touched. To have the report tasks in VS Code offer the component, add it to the `component` input in `.vscode/tasks.json`. For the code side of a component, see [AGENTS.md](AGENTS.md#spl-specific-cmake-patterns). ### 15. Add or change a feature @@ -487,11 +519,11 @@ need inside that branch. `SWDD_BC-203` exists only with automatic brightness adj runnable's automatic branch carries `SWIMPL_BC-004c` for it, and the runnable's own need `SWIMPL_BC-003c` does not link it. -codelinks takes the branches from `build/compile_commands.json`, which VS Code writes when it -configures a CMake build (`cmake.copyCompileCommands`). Without that file, codelinks treats every -`#ifdef` as false, and Sphinx warns. A `test` kit database does not help yet: libclang rejects its -`-save-temps` option, so codelinks skips the file and its needs disappear. Configure a `prod` kit -build for the editor until codelinks drops that option. +codelinks takes the branches from `build/compile_commands.json`. Every build of the selected CMake +build copies its compile database there (`tools/compile_commands.py`), without the `-save-temps` +option of the `test` kit, which libclang rejects. After configuring without building, run +`cmake --build <build dir> --target spled_codelinks_compile_commands`. Without the file, codelinks +treats every `#ifdef` as false, and Sphinx warns. ## Check @@ -509,21 +541,21 @@ python -m pytest test/test_ubproject_config.py test/test_variant_data.py # rul - The ubc tests find `ubc` through the `UBC` environment variable, on the `PATH` or in the ubCode extension folder, and are skipped when it is not there. CI installs ubc and fails instead of skipping. -- No compiler is needed. The tests regenerate `build/variants/`, and restore the `generated` link - when they have pointed it elsewhere. +- The tests regenerate `build/variants/` and the rules, and restore `build/selection.toml` when + they have selected another cell. ### 19. When something looks wrong | What you see | What to do | | --- | --- | | A block or a page is missing in one variant | Its condition is false there, or it names a key that is not in the variant data file. Look the key up in `build/variants/<variant>/<kit>/docs.json`, check the spelling, and look for `'if' directive expression failed` or a rule warning in the build output. In a rule, a boolean needs `== True`. | -| The editor shows another variant than you expect | Look at the pointer ([3](#3-see-which-variant-you-are-working-on)). Configuring a CMake build switches it too. Switch back ([4](#4-switch-the-variant-your-editor-shows)) and reload the window. | +| The editor shows another variant than you expect | Look at the selection ([3](#3-see-which-variant-you-are-working-on)). Configuring a CMake build selects that build. Switch back ([4](#4-switch-the-variant-your-editor-shows)) and reload the window. | +| Sphinx stops with `No variant is selected` | `build/selection.toml` does not exist yet. Configure a build, or select a cell ([4](#4-switch-the-variant-your-editor-shows)). ubc stops for the same reason, with `Configuration file could not be loaded`. | | A change to a file under `build/` is gone | `build/` is generated and rewritten on every run. Change `KConfig`, a `config.txt` or a `parts.cmake` instead, or try the change on a copy ([7](#7-try-a-change-without-touching-the-product)). | | `python tools/variant_data.py --all --check` fails | The variant data is older than its sources. Run `python tools/variant_data.py --all`. | -| A documentation build stops with `SPL_SPHINX_BINARY_DIR … leads to …, but this build writes to …` | `generated` is a link (a junction on Windows) to the build directory configured last. It gives every generated page the same name in every variant. Since another build was configured, it leads elsewhere: configure this build again, then build its documentation. | | A link from a need in the code is dead in one variant | The need sits outside the `#ifdef` that decides the link. Give the link to a need inside the branch that implements it ([17](#17-trace-code-to-the-design-per-variant)). | -| Sphinx warns `compile_commands … is not a readable file` | codelinks has no compile database and treats every `#ifdef` as false. Configure the variant's `prod` kit in VS Code, which writes `build/compile_commands.json` ([17](#17-trace-code-to-the-design-per-variant)). | -| ubCode shows no test results or coverage | That is expected. Those pages exist only after a CMake build of the `reports` target, and ubCode does not follow the `generated` link. | +| Sphinx warns `compile_commands … is not a readable file` | codelinks has no compile database and treats every `#ifdef` as false. Build the selected build, or its `spled_codelinks_compile_commands` target ([17](#17-trace-code-to-the-design-per-variant)). | +| ubCode shows no test results or listings | That is expected for now: ubCode does not mount a directory inside the project, and the build lives under `build/` ([8](#8-know-what-ubcode-shows-you)). Sphinx shows them. | ## Rules of thumb @@ -532,10 +564,11 @@ python -m pytest test/test_ubproject_config.py test/test_variant_data.py # rul - Gate whole documents on components, blocks on features, and report sections on the target. Never gate on the variant name. - No Jinja in documents, no conditions in toctree entries, and no variant selection in `conf.py`. - Select a variant with `-D` for Sphinx and `-c` for ubc. + Select a variant by configuring a build or with `tools/variant_data.py --current`; look at + another one with `-D` for Sphinx and `-c` for ubc. - A link that holds only in some variants belongs to a need inside the `#ifdef` branch that implements it. -- Do not edit `build/` or `generated/`. +- Do not edit `build/`, `generated/` or `ubproject.variants.toml`. - After changing a variant's sources, regenerate and run `python -m pytest -m docs`. The design behind all this is described under From 40a8046c68e80f707b4a09f43413a9f62c05a353 Mon Sep 17 00:00:00 2001 From: Marco Heinemann <marco.heinemann@useblocks.com> Date: Tue, 29 Sep 2026 12:55:45 +0200 Subject: [PATCH 17/24] ci: gate the documentation on both readers, strictly For every variant and kit, test_docs_gate.py configures the cell's build (nothing is compiled), builds the documents with sphinx-build -W and with ubc, and compares the two needs.json files need by need. What the readers may differ in is named, with its reason, in docs_exceptions.toml: today REQ_37 and REQ_58, the untitled needs of the upstream export, and ubc's two warnings about them. Both build times go into the job summary. The documentation job installs Ninja and has the time to configure the cells. --- .github/workflows/ci.yml | 24 +++-- test/docs_exceptions.toml | 18 ++++ test/test_docs_gate.py | 179 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 test/docs_exceptions.toml create mode 100644 test/test_docs_gate.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0690f69..c4b8d07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,18 +42,19 @@ jobs: echo "marker=$marker" >> "$GITHUB_OUTPUT" documentation: - name: Documentation (no compiler) + name: Documentation runs-on: ubuntu-24.04 - timeout-minutes: 15 + timeout-minutes: 30 needs: determine-gate - # The documentation is derivable from the sources alone: KConfig is pure - # Python and the documents are text. Only CMake's top-level project() - # call needs a C toolchain, and nothing here goes through it. So this - # gate installs a Python and the locked dependencies, and nothing else -- - # no poks, no scoop, no cross-compiler. It is the fastest signal in the - # workflow and it covers every variant, where the build jobs cover the - # variants they build. + # The documentation of every variant and kit, in both readers, in strict + # mode, with their needs compared (test/test_docs_gate.py). Nothing is + # compiled, but each cell is CONFIGURED: codelinks takes a variant's + # #ifdef branches from its build's compile database, which only CMake + # writes, and CMake's top-level project() call wants a C/C++ toolchain. + # The runner's gcc, g++, cmake and ninja are enough -- no poks, no scoop, + # no cross-compiler. It covers every variant, where the build jobs cover + # the variants they build. steps: - name: Checkout Code uses: actions/checkout@v6 @@ -71,6 +72,11 @@ jobs: poetry config virtualenvs.in-project true poetry install --no-root + # cmake and gcc are part of the runner image; Ninja is the generator + # the builds use. + - name: Install Ninja + run: sudo apt-get install -y --no-install-recommends ninja-build + # `ubc` is the second reader. Without it the parity tests skip, and # a guarantee that only holds on a developer machine is not a # guarantee -- so the gate installs it and CI_REQUIRE_UBC turns a diff --git a/test/docs_exceptions.toml b/test/docs_exceptions.toml new file mode 100644 index 0000000..0e6d0c2 --- /dev/null +++ b/test/docs_exceptions.toml @@ -0,0 +1,18 @@ +# What the two readers of the documentation may differ in -- and nothing else. +# +# test_docs_gate.py builds every variant and kit with Sphinx and with ubc and +# fails on any warning and any difference in the needs that is not listed here. +# Every entry names its reason; remove it the day the reason is gone. + +[[needs]] +id = "REQ_37" +reason = "Imported from the ubConnect ReqIF export without a title (upstream data). Sphinx imports it with an empty title; ubc refuses an untitled need." + +[[needs]] +id = "REQ_58" +reason = "As REQ_37." + +[[ubc_warnings]] +code = "needs.import" +count = 2 +reason = "ubc's warnings about REQ_37 and REQ_58, see above." diff --git a/test/test_docs_gate.py b/test/test_docs_gate.py new file mode 100644 index 0000000..1fb3f60 --- /dev/null +++ b/test/test_docs_gate.py @@ -0,0 +1,179 @@ +"""The documentation gate: every variant and kit, both readers, strict, the same needs. + +For each cell this selects the build CMake configures for it -- configure only, +nothing is compiled -- so codelinks evaluates that build's `#ifdef` branches with +its compile database. Then it builds the documents with `sphinx-build -W` and with +ubc, and compares the two needs.json files need by need: IDs, types, titles, +fields and links. Whatever the two readers may differ in is listed, with its +reason, in docs_exceptions.toml; any other warning or difference fails. + +Both build times go into the job summary on CI (`GITHUB_STEP_SUMMARY`), so the +speed of the second reader is visible next to the first. + +The configure needs CMake, Ninja and a C/C++ compiler: on CI the runner's, here +whatever is on PATH. Without them the tests skip, and on CI (`CI` is set) they +fail instead, because a gate that only runs on a developer machine is none. +""" + +from __future__ import annotations + +import json +import os +import platform +import shutil +import subprocess +import sys +import time +import tomllib +from pathlib import Path + +import pytest + +PROJECT_ROOT = Path(__file__).resolve().parent.parent + +VARIANTS = ["Base/Dev", "Disco", "IDEA/Sloemada", "Sleep", "Spa"] +KITS = ["prod", "test"] + +pytestmark = [ + pytest.mark.docs, + pytest.mark.gate_develop_pr, + pytest.mark.gate_develop_push, + pytest.mark.gate_develop_nightly, + pytest.mark.gate_release_pr, + pytest.mark.gate_release, +] + +EXCEPTIONS = tomllib.loads((PROJECT_ROOT / "test" / "docs_exceptions.toml").read_text(encoding="utf-8")) +EXCEPTED_NEEDS = {entry["id"] for entry in EXCEPTIONS.get("needs", [])} +EXCEPTED_UBC_WARNINGS = {entry["code"]: entry["count"] for entry in EXCEPTIONS.get("ubc_warnings", [])} + +#: Build times per cell, for the job summary. +TIMINGS: list[tuple[str, str, float, float, int]] = [] + + +def _find_ubc() -> str | None: + for candidate in (os.environ.get("UBC"), shutil.which("ubc")): + if candidate and Path(candidate).is_file(): + return candidate + for extension in sorted((Path.home() / ".vscode" / "extensions").glob("useblocks.ubcode-*"), reverse=True): + for name in ("ubc", "ubc.exe"): + candidate = extension / "server" / "cli" / name + if candidate.is_file(): + return str(candidate) + return None + + +def _require(what: str, available: bool) -> None: + if available: + return + if os.environ.get("CI"): + pytest.fail(f"{what} is required on CI") + pytest.skip(f"{what} is not available") + + +@pytest.fixture(scope="module", autouse=True) +def _restore_the_developers_selection(): + """The gate selects every cell in turn; what the developer had selected comes back.""" + saved = {} + for name in ("build/selection.toml", "build/compile_commands.json"): + path = PROJECT_ROOT / name + saved[path] = path.read_bytes() if path.is_file() else None + yield + for path, content in saved.items(): + if content is None: + path.unlink(missing_ok=True) + else: + path.write_bytes(content) + if os.environ.get("GITHUB_STEP_SUMMARY") and TIMINGS: + with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary: + summary.write("### Documentation build time per cell\n\n| Variant | Kit | Sphinx | ubc | Needs |\n| --- | --- | ---: | ---: | ---: |\n") + for variant, kit, sphinx_s, ubc_s, needs in TIMINGS: + summary.write(f"| {variant} | {kit} | {sphinx_s:.2f} s | {ubc_s:.2f} s | {needs} |\n") + + +def _select_build(variant: str, kit: str) -> Path: + """Configure the cell's build, which selects it, and hand codelinks its database.""" + build_dir = PROJECT_ROOT / "build" / variant / kit / "Debug" + command = [ + "cmake", "-S", str(PROJECT_ROOT), "-B", str(build_dir), "-G", "Ninja", + f"-DVARIANT={variant}", f"-DBUILD_KIT={kit}", "-DCMAKE_BUILD_TYPE=Debug", + ] + if kit == "test": + toolchain = "toolchain.cmake" if platform.system() == "Windows" else "toolchain_linux.cmake" + command.append(f"-DCMAKE_TOOLCHAIN_FILE={PROJECT_ROOT / 'tools' / 'toolchains' / 'gcc' / toolchain}") + configured = subprocess.run(command, cwd=PROJECT_ROOT, capture_output=True, text=True) + assert configured.returncode == 0, f"configuring {variant}/{kit} failed:\n{configured.stdout[-3000:]}\n{configured.stderr[-3000:]}" + copied = subprocess.run( + ["cmake", "--build", str(build_dir), "--target", "spled_codelinks_compile_commands"], + cwd=PROJECT_ROOT, capture_output=True, text=True, + ) + assert copied.returncode == 0, f"copying the compile database of {variant}/{kit} failed:\n{copied.stdout[-3000:]}" + return build_dir + + +def _needs(path: Path) -> dict[str, dict]: + data = json.loads(path.read_text(encoding="utf-8")) + return data["versions"][data.get("current_version", "")]["needs"] + + +def _compared_fields() -> list[str]: + config = tomllib.loads((PROJECT_ROOT / "ubproject.toml").read_text(encoding="utf-8"))["needs"] + return ["type", "title", "status", "tags", *config.get("links", {}), *config.get("fields", {})] + + +def _normalized(value): + if value in (None, "", [], {}): + return None + return sorted(value) if isinstance(value, list) else value + + +@pytest.mark.parametrize("kit", KITS) +@pytest.mark.parametrize("variant", VARIANTS) +def test_both_readers_build_the_same_documentation_strictly(variant: str, kit: str, tmp_path: Path) -> None: + ubc = _find_ubc() + _require("ubc", ubc is not None) + _require("CMake and Ninja", bool(shutil.which("cmake") and shutil.which("ninja"))) + + build_dir = _select_build(variant, kit) + selection = build_dir / "selection" / "docs.toml" + + started = time.perf_counter() + sphinx = subprocess.run( + [sys.executable, "-m", "sphinx", "-W", "--keep-going", "-q", "-b", "html", + "-D", f"spl_selection={selection}", "-d", str(tmp_path / "doctrees"), str(PROJECT_ROOT), str(tmp_path / "sphinx")], + cwd=PROJECT_ROOT, capture_output=True, text=True, env={**os.environ, "VARIANT": variant}, + ) + sphinx_seconds = time.perf_counter() - started + warnings = [line for line in sphinx.stderr.splitlines() if "WARNING" in line or "ERROR" in line] + assert sphinx.returncode == 0 and not warnings, f"{variant}/{kit}: Sphinx is not clean in strict mode:\n" + "\n".join(warnings or [sphinx.stderr[-3000:]]) + + override = selection.read_text(encoding="utf-8") + checked = subprocess.run([ubc, "check", "--no-cache", "--output-format", "json", "-c", override], cwd=PROJECT_ROOT, capture_output=True, text=True) + counts: dict[str, int] = {} + for diagnostic in json.loads(checked.stdout or "{}").get("diagnostics", []): + if diagnostic["severity"] != "info": + counts[diagnostic["code"]] = counts.get(diagnostic["code"], 0) + 1 + unexpected = {code: count for code, count in counts.items() if EXCEPTED_UBC_WARNINGS.get(code) != count} + assert not unexpected, f"{variant}/{kit}: ubc reports findings docs_exceptions.toml does not name: {unexpected}" + + started = time.perf_counter() + built = subprocess.run([ubc, "build", "html", "--no-cache", "--deny", "none", "-o", str(tmp_path / "ubc"), "-c", override], cwd=PROJECT_ROOT, capture_output=True, text=True) + ubc_seconds = time.perf_counter() - started + assert built.returncode == 0, f"{variant}/{kit}: ubc build html failed:\n{built.stdout[-3000:]}\n{built.stderr[-3000:]}" + + sphinx_needs = _needs(tmp_path / "sphinx" / "needs.json") + ubc_needs = _needs(tmp_path / "ubc" / "needs.json") + TIMINGS.append((variant, kit, sphinx_seconds, ubc_seconds, len(sphinx_needs))) + + only_sphinx = sorted(set(sphinx_needs) - set(ubc_needs) - EXCEPTED_NEEDS) + only_ubc = sorted(set(ubc_needs) - set(sphinx_needs) - EXCEPTED_NEEDS) + assert not only_sphinx and not only_ubc, f"{variant}/{kit}: needs in one reader only: Sphinx {only_sphinx}, ubc {only_ubc}" + + fields = _compared_fields() + differences = [ + f"{need_id}.{field}: Sphinx {_normalized(sphinx_needs[need_id].get(field))!r}, ubc {_normalized(ubc_needs[need_id].get(field))!r}" + for need_id in sorted(set(sphinx_needs) & set(ubc_needs)) + for field in fields + if _normalized(sphinx_needs[need_id].get(field)) != _normalized(ubc_needs[need_id].get(field)) + ] + assert not differences, f"{variant}/{kit}: the readers disagree about {len(differences)} values:\n" + "\n".join(differences[:40]) From 8cd0dbe3bf863de798d9fa3b693453f640a83bdc Mon Sep 17 00:00:00 2001 From: Marco Heinemann <marco.heinemann@useblocks.com> Date: Tue, 29 Sep 2026 12:56:23 +0200 Subject: [PATCH 18/24] build: pin the spl-core commit that the generated selection needs useblocks/spl-core feat/docs-parity-sweep at 1181c0a adds SPL_SPHINX_OPTIONS and SPL_SPHINX_COMPONENT_OPTIONS (each run names its own selection file), lets the binary directory check pass for a mounted path, and writes the test results as needs (SPL_TEST_RESULTS_AS_NEEDS) with its JUnit converter. --- AGENTS.md | 2 +- poetry.lock | 6 +++--- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7e579a0..94a7999 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,7 +64,7 @@ build everyone gets. The override is for the span of one change, not a working mode. `pyproject.toml` currently pins a **commit of the useblocks fork** of spl-core -(branch `feat/configurable-docs-pipeline`, based on spl-core 8.9.0). It carries +(branch `feat/docs-parity-sweep`, stacked on `feat/configurable-docs-pipeline`, based on spl-core 8.9.0). It carries the documentation changes this project relies on: `SPL_SOURCE_DOCS_JINJA_RAW_TAGS`, `SPL_SPHINX_BINARY_DIR`, `SPL_SPHINX_OPTIONS` / `SPL_SPHINX_COMPONENT_OPTIONS`, `SPL_TEST_RESULTS_AS_NEEDS` with its JUnit converter, and diff --git a/poetry.lock b/poetry.lock index 4e4096a..ab4dd85 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4201,8 +4201,8 @@ typer = ">=0,<1" [package.source] type = "git" url = "https://github.com/useblocks/spl-core.git" -reference = "010727d08b27ca73994772444d25d7bc6e028c31" -resolved_reference = "010727d08b27ca73994772444d25d7bc6e028c31" +reference = "1181c0a636ac4e85ffab02d52ede21dc8cc7da45" +resolved_reference = "1181c0a636ac4e85ffab02d52ede21dc8cc7da45" [[package]] name = "text-unidecode" @@ -4802,4 +4802,4 @@ cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and pyt [metadata] lock-version = "2.1" python-versions = "<3.14,>=3.12" -content-hash = "c4fca35edababcd6d4b502ede808ffd05224e8d2fa83cda55228e9f69890d7f5" +content-hash = "59c523049113a748592982df85c52f241c1995f92f97b6605e5ee6892b273442" diff --git a/pyproject.toml b/pyproject.toml index 47f4a0a..b43e46c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ authors = [{ name = "Avengineers" }] requires-python = "<3.14,>=3.12" dependencies = [ - "spl-core @ git+https://github.com/useblocks/spl-core.git@010727d08b27ca73994772444d25d7bc6e028c31", + "spl-core @ git+https://github.com/useblocks/spl-core.git@1181c0a636ac4e85ffab02d52ede21dc8cc7da45", "pypeline-runner>=1.33,<2", "pytest>=8,<9", "myst-parser>=4.0,<5", From 3303b5a6d418469a65040c19d380e6b8397d5193 Mon Sep 17 00:00:00 2001 From: Marco Heinemann <marco.heinemann@useblocks.com> Date: Tue, 29 Sep 2026 12:58:28 +0200 Subject: [PATCH 19/24] feat: check the configuration whenever Sphinx reads it tools/config_checks.py holds the checks the tests ran: the vendored needs model against spl-core's, ubCode's parser includes against conf.py's include_patterns, what ubproject.toml must leave to the generated files, and the generated rules against the grammar both engines share. conf.py runs them at config-inited and reports each finding as a warning, so a drift fails the build under -W instead of waiting for someone to run the tests. --- conf.py | 25 +++++++++ tools/config_checks.py | 115 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 tools/config_checks.py diff --git a/conf.py b/conf.py index 7d80267..c6ce0e4 100644 --- a/conf.py +++ b/conf.py @@ -191,6 +191,31 @@ def _load_selection(app, config): config.mounts = selection.get("source", {}).get("mounts", []) +# configuration checks ###################################################### +# +# The checks test_ubproject_config.py runs, run again whenever Sphinx reads this +# configuration (tools/config_checks.py): the vendored needs model against +# spl-core's, the two readers' document sets against each other, and the +# generated rules against the shared grammar. A finding is a warning, so a drift +# fails the build under -W instead of waiting for someone to run the tests. + + +def _check_configuration(app, config): + import sys + + from sphinx.util import logging as sphinx_logging + + tools = str(Path(app.confdir) / "tools") + if tools not in sys.path: + sys.path.insert(0, tools) + import config_checks + + logger = sphinx_logging.getLogger(__name__) + for finding in config_checks.run_all(Path(app.confdir), list(config.include_patterns)): + logger.warning(f"configuration check: {finding}", type="spled", subtype="config") + + def setup(app): app.add_config_value("spl_selection", "build/selection.toml", "env", types=(str,)) app.connect("config-inited", _load_selection, priority=5) + app.connect("config-inited", _check_configuration, priority=900) diff --git a/tools/config_checks.py b/tools/config_checks.py new file mode 100644 index 0000000..60a8706 --- /dev/null +++ b/tools/config_checks.py @@ -0,0 +1,115 @@ +"""Checks that the configuration both readers read is still intact. + +conf.py runs them whenever Sphinx reads its configuration and reports each +finding as a warning, so a drift fails the build under `-W` instead of waiting +for someone to run the tests; test_ubproject_config.py calls the same functions. +needs-config-writer does the same for the configuration it writes +(`needscfg_warn_on_diff`). + +Each check returns a list of findings, one sentence each, and nothing when all +is well. +""" + +from __future__ import annotations + +import tomllib +from importlib.resources import files +from pathlib import Path +from typing import Any + +import variant_data + +#: The Markdown documents both readers read: ubCode's md parser include, and the +#: trees conf.py names in `include_patterns`. +MARKDOWN_INCLUDES = ["index.md", "doc/**/*.md", "components/**/doc/**/*.md", "test/**/doc/**/*.md"] +MARKDOWN_TREES = ["index.md", "doc/**", "components/**/doc/**", "test/**/doc/**"] + + +def spl_core_config() -> dict[str, Any]: + """The base configuration spl-core ships, as installed.""" + with files("spl_core.report_generation").joinpath("ubproject.toml").open("rb") as handle: + return tomllib.load(handle) + + +def vendored_needs_model(project: dict[str, Any], spl_core: dict[str, Any]) -> list[str]: + """spl-core's needs model is vendored into ubproject.toml; a copy has to stay a copy.""" + findings: list[str] = [] + expected_links = { + link["option"]: {"incoming": link["incoming"], "outgoing": link["outgoing"]} + for link in spl_core["needs"]["extra_links"] + } + if project["needs"].get("links") != expected_links: + findings.append("the link types in ubproject.toml differ from spl-core's extra_links") + fields = project["needs"].get("fields", {}) + for option in spl_core["needs"]["extra_options"]: + if option not in fields: + findings.append(f"spl-core declares the field {option!r}, ubproject.toml does not") + ours = {need_type["directive"]: need_type for need_type in project["needs"].get("types", [])} + for need_type in spl_core["needs"]["types"]: + if ours.get(need_type["directive"]) != need_type: + findings.append(f"the need type {need_type['directive']!r} differs from spl-core's, or is missing") + for key in ("exclude", "respect_gitignore"): + if project["source"].get(key) != spl_core["source"].get(key): + findings.append(f"[source] {key} differs from spl-core's") + return findings + + +def document_sets(project: dict[str, Any], include_patterns: list[str]) -> list[str]: + """ubCode's parser includes and conf.py's include_patterns name the same documents.""" + findings: list[str] = [] + parsers = project.get("parse", {}).get("parsers", {}) + if parsers.get("md", {}).get("include") != MARKDOWN_INCLUDES: + findings.append(f"the md parser include is not {MARKDOWN_INCLUDES}") + for tree in MARKDOWN_TREES: + if tree not in include_patterns: + findings.append(f"conf.py does not include {tree!r}, which the md parser reads") + if parsers.get("rst", {}).get("include") != list(variant_data.MOUNTED_PAGES): + findings.append(f"the rst parser include is not the mounted pages {list(variant_data.MOUNTED_PAGES)}") + for pattern in variant_data.MOUNTED_PAGES: + if f"{variant_data.MOUNT_AT}/{pattern}" not in include_patterns: + findings.append(f"conf.py does not include {variant_data.MOUNT_AT}/{pattern}, which the mount brings in") + return findings + + +def generated_half(project: dict[str, Any]) -> list[str]: + """What the generated files decide, ubproject.toml must not decide as well. + + `extend` merges tables key by key but replaces arrays: a rule or a mount in + ubproject.toml would replace every generated one, silently. + """ + findings: list[str] = [] + if project.get("extend") != variant_data.RULES_FILE: + findings.append(f"ubproject.toml has to extend {variant_data.RULES_FILE}, the generated rules") + if project.get("source", {}).get("variant_sources"): + findings.append(f"ubproject.toml declares rules; they would replace the generated ones (use {variant_data.HAND_WRITTEN_RULES})") + if project.get("source", {}).get("mounts"): + findings.append("ubproject.toml declares mounts; they would replace the selection's") + if "variant_data_file" in project.get("needs", {}): + findings.append("ubproject.toml names a variant data file; the selection names it") + return findings + + +def rule_grammar(project_root: Path) -> list[str]: + """Every generated rule is inside the grammar both engines share.""" + rules_file = project_root / variant_data.RULES_FILE + if not rules_file.is_file(): + return [] + from sphinx_mounts import variants + + findings: list[str] = [] + for rule in tomllib.loads(rules_file.read_text(encoding="utf-8")).get("source", {}).get("variant_sources", []): + try: + variants.validate(rule["if"]) + except Exception as error: # noqa: BLE001 - the finding names whatever the grammar refused + findings.append(f"the rule {rule['if']!r} is outside the shared grammar: {error}") + return findings + + +def run_all(project_root: Path, include_patterns: list[str]) -> list[str]: + project = tomllib.loads((project_root / "ubproject.toml").read_text(encoding="utf-8")) + return [ + *vendored_needs_model(project, spl_core_config()), + *document_sets(project, include_patterns), + *generated_half(project), + *rule_grammar(project_root), + ] From b4f38a482141de544218ef99dca40a113e18b87b Mon Sep 17 00:00:00 2001 From: Marco Heinemann <marco.heinemann@useblocks.com> Date: Tue, 29 Sep 2026 13:08:07 +0200 Subject: [PATCH 20/24] test: check the generated selection instead of the link The configuration tests read the generated rules and evaluate them, and the scope rules of a component report, with sphinx-mounts' own interpreter against the real cells; they check the one extend chain, the mount's condition and conf.py's source set by executing conf.py, and run the checks conf.py runs. The generator tests cover the component cells, the selection files with and without a build and for a component report, removing what earlier versions left, and hand-written rules. The documentation tests build through a selection file that mounts a fake build, where they used to re-point the link; ubCode reports a glob of the component toctree once, so the ubc comparison is made per glob. --- test/test_documentation.py | 429 +++++++++++++++++++--------------- test/test_ubproject_config.py | 228 +++++++++++++----- test/test_variant_data.py | 279 ++++++++++++++++------ 3 files changed, 627 insertions(+), 309 deletions(-) diff --git a/test/test_documentation.py b/test/test_documentation.py index 30dc194..2d790c9 100644 --- a/test/test_documentation.py +++ b/test/test_documentation.py @@ -13,16 +13,19 @@ - Sphinx builds the variant's documents without error; - the documents present are exactly the ones the variant's component list says, which is the property the whole variant-gating design exists to provide. + +And, with `ubc`, that a second reader which never runs conf.py decides the same +document set. """ import ast -import contextlib import json import os import re import shutil import subprocess import sys +from collections.abc import Iterator from pathlib import Path import pytest @@ -59,6 +62,15 @@ "components/examples/flight_controller": "components/examples/flight_controller/doc/index.html", } +#: The toctree globs of doc/components/index.md. ubCode reports a glob that +#: matched only documents a variant excludes, so these are what the parity +#: comparison has to evaluate. +COMPONENT_TOCTREE_GLOBS = ( + "components/*/doc/index", + "components/examples/*/doc/index", + "test/*/doc/index", +) + @pytest.fixture(scope="module") def all_variant_data() -> None: @@ -71,12 +83,49 @@ def all_variant_data() -> None: ) +@pytest.fixture(scope="module", autouse=True) +def selected_cell(all_variant_data: None) -> Iterator[None]: + """Select one cell for the readers that cannot take one on the command line. + + `ubc` loads the project through ubproject.toml -> ubproject.variants.toml -> + build/selection.toml, and refuses to load it when that file is missing. The + developer's own build/selection.toml -- which points at the build they + configured -- is put back byte for byte, because this test must not repoint + their IDE at a test selection. + """ + selection = PROJECT_ROOT / "build" / "selection.toml" + original = selection.read_bytes() if selection.is_file() else None + + subprocess.run( + [ + sys.executable, + str(PROJECT_ROOT / "tools" / "variant_data.py"), + "--all", + "--current", + "--variant", + "Disco", + "--kit", + "test", + ], + cwd=PROJECT_ROOT, + check=True, + capture_output=True, + ) + try: + yield + finally: + if original is None: + selection.unlink(missing_ok=True) + else: + selection.write_bytes(original) + + def _select_cell(variant: str, kit: str, target: str) -> list[str]: """The sphinx-build option that selects one cell of the matrix. A command-line override of `needs_variant_data_file`, which sphinx-needs keeps - even though needs_from_toml names the pointer. It is what spl-core passes for - the shape it builds, and the key `ubc check -c` overrides as well. + even though conf.py reads a selection. It is what spl-core passes for the + shape it builds, and the key `ubc check -c` overrides as well. """ return ["-D", f"needs_variant_data_file={PROJECT_ROOT / 'build' / 'variants' / variant / kit / f'{target}.json'}"] @@ -290,59 +339,85 @@ def test_ubc_finds_no_configuration_problem(all_variant_data: None) -> None: assert not problems, "; ".join(f"{d['code']}: {d['message']}" for d in problems) +def _glob_matches(pattern: str, document: str) -> bool: + """A path glob, where `*` does not cross `/` (Sphinx's and ubCode's rule).""" + regex = re.escape(pattern).replace(r"\*\*", ".*").replace(r"\*", "[^/]*") + return re.fullmatch(regex, document) is not None + + @needs_ubc @pytest.mark.parametrize("variant", VARIANTS) def test_ubc_excludes_exactly_what_sphinx_excludes(all_variant_data: None, variant: str) -> None: """The property the whole design exists to provide, proven across readers. - ubCode never runs conf.py. If it removes exactly the component documents - that the variant's component list omits -- the same set the Sphinx build - omits, asserted above -- then both readers are deciding from the same data - and agreeing. That is the claim; this is the test of it. + ubCode never runs conf.py. The component list is what gates the documents, + and doc/components/index.md globs its toctree. ubCode 0.35 reports a glob + that matched ONLY documents the variant excludes, with one example rather + than its members -- so the exact property is checked per glob: a glob is + reported exactly when every component it matches is missing from the + variant, and the example it names belongs to one of them. """ diagnostics = _ubc_check(variant, "test", "docs") - excluded_by_ubc = set() + reported_globs: set[str] = set() for diagnostic in diagnostics: if diagnostic["code"] != "toctree.variant_excluded": continue - match = re.search(r"toctree entry '([^']+)'", diagnostic["message"]) - assert match, diagnostic["message"] - excluded_by_ubc.add(match.group(1)) + message = diagnostic["message"] + glob = re.search(r"glob pattern '([^']+)'", message) + if glob: + reported_globs.add(glob.group(1)) + example = re.search(r"example '([^']+)'", message) + assert example, message + component = example.group(1).split("/doc/", 1)[0] + assert component in COMPONENT_DOCS, message + continue + entry = re.search(r"toctree entry '([^']+)'", message) + assert entry, message + component = entry.group(1).split("/doc/", 1)[0] + assert component in COMPONENT_DOCS, message components = set(variant_data.components(PROJECT_ROOT, variant, "test")) - expected = { - f"{component}/doc/index" for component in COMPONENT_DOCS if component not in components + covered = { + component + for glob in COMPONENT_TOCTREE_GLOBS + for component in COMPONENT_DOCS + if _glob_matches(glob, f"{component}/doc/index") } - - assert excluded_by_ubc == expected, ( - f"{variant}: ubCode and the component list disagree.\n" - f" ubCode excluded : {sorted(excluded_by_ubc)}\n" - f" expected : {sorted(expected)}" + assert covered == set(COMPONENT_DOCS), ( + "the toctree globs must name every documented component, or the parity " + f"check silently skips {sorted(set(COMPONENT_DOCS) - covered)}" ) + for glob in COMPONENT_TOCTREE_GLOBS: + matched = {component for component in COMPONENT_DOCS if _glob_matches(glob, f"{component}/doc/index")} + assert matched, f"the toctree glob {glob!r} matched no known component" + all_excluded = all(component not in components for component in matched) + assert (glob in reported_globs) == all_excluded, ( + f"{variant}: ubCode {'reported' if glob in reported_globs else 'did not report'} " + f"{glob!r}, but it matches " + ", ".join(sorted(matched)) + ) + + +def test_the_component_toctree_globs_are_the_ones_checked() -> None: + """The globs the parity test evaluates are the ones the page actually has.""" + page = (PROJECT_ROOT / "doc" / "components" / "index.md").read_text(encoding="utf-8") + for glob in COMPONENT_TOCTREE_GLOBS: + assert f"/{glob}" in page, f"doc/components/index.md no longer toctrees {glob}" + # --- the build shape must select its own cell ------------------------------- -def _build_with_spl_core_env(shape: str, out_dir: Path, tmp_path: Path, config: dict | None = None) -> subprocess.CompletedProcess: - """Build the way spl-core starts Sphinx, environment and options. +def _build_with_cell(shape: str, out_dir: Path) -> subprocess.CompletedProcess: + """Build the way spl-core starts Sphinx: one cell per build shape. - spl-core names the per-target config.json in SPHINX_BUILD_CONFIGURATION_FILE - and selects the variant data cell for the shape with - `-D needs_variant_data_file=`, which CMakeLists.txt configures through - SPL_VARIANT_DATA_FILE_DOCS and _REPORTS. The cell is what the - `{if} var.build_config.target` fences evaluate against. + conf.py reads the `-D needs_variant_data_file` override ahead of any + selection, and the cell is what the `{if} var.build_config.target` fences + evaluate against. """ - config_dir = tmp_path / "cfg" / shape - config_dir.mkdir(parents=True, exist_ok=True) - (config_dir / "config.json").write_text(json.dumps(config or {}), encoding="utf-8") - - env = { - **os.environ, - "SPHINX_BUILD_CONFIGURATION_FILE": str(config_dir / "config.json"), - "VARIANT": "Disco", - } + env = {**os.environ, "VARIANT": "Disco"} + env.pop("SPHINX_BUILD_CONFIGURATION_FILE", None) return subprocess.run( [sys.executable, "-m", "sphinx", "-b", "html", *_select_cell("Disco", "test", shape), str(PROJECT_ROOT), str(out_dir)], cwd=PROJECT_ROOT, @@ -359,21 +434,22 @@ def _cmake_set(cmake: str, variable: str) -> str: return match.group(1).strip() -def test_cmake_passes_spl_core_the_cell_for_each_shape() -> None: - """The helper mirrors a contract that lives in CMakeLists.txt. +def test_cmake_points_each_documentation_run_at_its_selection() -> None: + """spl-core hands every run its own build's selection file. - If the helper and the CMake configuration disagree, the tests would prove - the behaviour of an environment spl-core never builds in. This reads the + `-D spl_selection=<build>/selection/<shape>.toml` is what makes a run read + the cell of its shape (and, for a component, its own component's cell) + without depending on which build was configured last. This reads the `set(...)` calls rather than searching for the variable name, so a stray mention in a comment cannot satisfy it. """ cmake = (PROJECT_ROOT / "CMakeLists.txt").read_text(encoding="utf-8") - expected = { - "SPL_VARIANT_DATA_FILE_DOCS": "${CMAKE_SOURCE_DIR}/build/variants/${VARIANT}/${BUILD_KIT}/docs.json", - "SPL_VARIANT_DATA_FILE_REPORTS": "${CMAKE_SOURCE_DIR}/build/variants/${VARIANT}/${BUILD_KIT}/reports.json", - } - for variable, value in expected.items(): - assert _cmake_set(cmake, variable) == value, f"CMakeLists.txt must pass {variable}={value}" + assert _cmake_set(cmake, "SPL_SPHINX_OPTIONS") == ( + "-D spl_selection=${CMAKE_BINARY_DIR}/selection/@SHAPE@.toml" + ) + assert _cmake_set(cmake, "SPL_SPHINX_COMPONENT_OPTIONS") == ( + "-D spl_selection=${CMAKE_BINARY_DIR}/selection/@COMPONENT_PATH@/@SHAPE@.toml" + ) def test_the_reports_shape_reads_the_reports_cell(all_variant_data: None, tmp_path: Path) -> None: @@ -390,7 +466,7 @@ def test_the_reports_shape_reads_the_reports_cell(all_variant_data: None, tmp_pa """ for shape, expect_verification in (("reports", True), ("docs", False)): out = tmp_path / f"{shape}_html" - result = _build_with_spl_core_env(shape, out, tmp_path) + result = _build_with_cell(shape, out) assert result.returncode == 0, (result.stdout + result.stderr)[-2000:] page = (out / "components" / "light_controller" / "doc" / "index.html").read_text(encoding="utf-8") assert ("Verification" in page) is expect_verification, ( @@ -398,114 +474,101 @@ def test_the_reports_shape_reads_the_reports_cell(all_variant_data: None, tmp_pa ) -# --- the generated report pages -------------------------------------------- +# --- the generated report pages, through the mount ------------------------- #: The three pages spl-core writes per component at CONFIGURE time, and lists #: among its include patterns for BOTH build shapes. SPL_CORE_REPORT_PAGES = ("unit_test_spec", "unit_test_results", "coverage") -#: A fake build directory the `generated` link is pointed at for the duration of -#: a test. It is gitignored and pruned from the Sphinx walk, so the only route -#: to it is `generated` -- which is exactly the route under test. -FAKE_GENERATED_BUILD = PROJECT_ROOT / "build" / "_test_generated_build" - - -def _link(link: Path, target: Path) -> None: - """Create `generated` the way tools/variant_data.py does: a symlink, or a junction where Windows refuses one.""" - try: - link.symlink_to(target, target_is_directory=True) - except OSError: - variant_data._create_junction(target, link) - - -@contextlib.contextmanager -def _generated_points_at(fake_build: Path): - """Point `generated` at a fake build, then point it back where it was. - - Several tests need `generated` to lead somewhere the real build is not, so - they can assert on pages the real build does not contain. The link is - restored on the way out whatever happens, because leaving a developer's - pointer re-pointed at a removed test directory would break their next build - in a way they did not cause. It never calls variant_data.write_pointer: - that would overwrite build/autoconf.json, which belongs to the developer. - """ - generated = PROJECT_ROOT / "generated" - if generated.is_symlink() or generated.is_junction(): - # A junction's target can come back in the \\?\ form, which a new - # junction cannot be created from. - original: tuple[str, str | None] = ("link", os.readlink(generated).removeprefix("\\\\?\\")) - elif generated.is_dir(): - # A plain directory only ever holds the explanation variant_data.py - # writes when neither a symlink nor a junction could be created. - marker = generated / "NO_SYMLINK" - original = ("directory", marker.read_text(encoding="utf-8") if marker.is_file() else None) - else: - original = ("absent", None) - - variant_data._remove_link(generated) - shutil.rmtree(fake_build, ignore_errors=True) - fake_build.mkdir(parents=True, exist_ok=True) - _link(generated, fake_build) - - try: - yield generated - finally: - variant_data._remove_link(generated) - if original[0] == "link": - _link(generated, Path(original[1])) - elif original[0] == "directory": - generated.mkdir(parents=True, exist_ok=True) - if original[1] is not None: - (generated / "NO_SYMLINK").write_text(original[1], encoding="utf-8") - shutil.rmtree(fake_build, ignore_errors=True) - -def _fake_spl_core_report_tree(component: str) -> str: - """Write what spl-core's configure step writes, and the pattern naming it. +def _fake_spl_core_report_tree(build_dir: Path, component: str) -> None: + """Write what spl-core's configure step writes into a build directory. `component` is a path such as `components/light_controller`, so the pages - land where spl-core writes them and the returned include pattern is the - `generated/...` name it now passes for both build shapes. + land where spl-core writes them. The component's `__source_docs/index` and + the variant-wide `reports/coverage` are included because the pages link + them, and a report toctree that cannot resolve is the failure these tests + are watching for. """ - root = FAKE_GENERATED_BUILD / component / "reports" - root.mkdir(parents=True, exist_ok=True) + reports = build_dir / component / "reports" + reports.mkdir(parents=True, exist_ok=True) for page in SPL_CORE_REPORT_PAGES: - (root / f"{page}.rst").write_text( + (reports / f"{page}.rst").write_text( f"{page}\n{'=' * len(page)}\n\nGenerated by spl-core at configure time.\n", encoding="utf-8", ) - return f"generated/{component}/reports/**" + + listings = build_dir / component / "__source_docs" + listings.mkdir(parents=True, exist_ok=True) + (listings / "index.rst").write_text( + "Source Files\n============\n\nNo listing in this fixture.\n", + encoding="utf-8", + ) + + variant_reports = build_dir / "reports" + variant_reports.mkdir(parents=True, exist_ok=True) + (variant_reports / "coverage.rst").write_text( + "Coverage\n========\n\nVariant-wide coverage.\n", + encoding="utf-8", + ) + + +def _selection_file(tmp_path: Path, build_dir: Path, shape: str) -> Path: + """A selection naming a fake build, as `tools/variant_data.py` would write it.""" + selection = tmp_path / f"selection-{shape}.toml" + selection.write_text( + variant_data.selection_toml( + variant_data.cell_path(PROJECT_ROOT, "Disco", "test", shape), + "Disco", + "test", + build_dir, + ), + encoding="utf-8", + ) + return selection + + +def _build_with_selection(selection: Path, out_dir: Path) -> subprocess.CompletedProcess: + """Build with `-D spl_selection=<file>`, the way a build's own run does.""" + env = {**os.environ, "VARIANT": "Disco"} + env.pop("SPHINX_BUILD_CONFIGURATION_FILE", None) + return subprocess.run( + [sys.executable, "-m", "sphinx", "-b", "html", "-D", f"spl_selection={selection}", str(PROJECT_ROOT), str(out_dir)], + cwd=PROJECT_ROOT, + env=env, + capture_output=True, + text=True, + ) def test_the_docs_shape_does_not_read_the_generated_report_pages(all_variant_data: None, tmp_path: Path) -> None: - """spl-core lists the report pages for both shapes; the variant rule keeps - them out of a docs build. + """spl-core writes the report pages for both shapes; a docs build must not read them. They exist from configure time, and in a docs build the fences that would link them are false -- so reading them yields three documents per component - that no toctree references. The `if = 'var.build_config.target == "reports"'` - rule over `generated/**` in ubproject.toml is what removes them now, declared - once where ubCode reads it too, instead of a Sphinx-only filter. - - This needs no compiler: the only thing a CMake build contributes here is the - config.json, and that is three lines of JSON. + that no toctree references. The mount's condition, which names the build's + own target, is what keeps them out now: the selection is the same file a + reports build reads, and the condition gates the mount off. Declared once, + where ubCode reads it too, instead of a Sphinx-only filter. """ - with _generated_points_at(FAKE_GENERATED_BUILD): - pattern = _fake_spl_core_report_tree("components/light_controller") - out = tmp_path / "docs_html" - result = _build_with_spl_core_env("docs", out, tmp_path, {"target": "docs", "include_patterns": [pattern]}) - assert result.returncode == 0, (result.stdout + result.stderr)[-2000:] + build_dir = tmp_path / "build" / "Disco" / "test" / "Debug" + _fake_spl_core_report_tree(build_dir, "components/light_controller") + selection = _selection_file(tmp_path, build_dir, "docs") + + out = tmp_path / "docs_html" + result = _build_with_selection(selection, out) + assert result.returncode == 0, (result.stdout + result.stderr)[-2000:] - # Sphinx writes warnings to stderr, so scanning stdout alone made an - # earlier version of this test pass with the fix reverted. - log = result.stdout + result.stderr - offenders = [line for line in log.splitlines() if "generated/" in line and "WARNING" in line] - assert not offenders, "the docs shape read the generated report pages:\n" + "\n".join(offenders) + # Sphinx writes warnings to stderr, so scanning stdout alone made an + # earlier version of this test pass with the fix reverted. + log = result.stdout + result.stderr + offenders = [line for line in log.splitlines() if "generated/" in line and "WARNING" in line] + assert not offenders, "the docs shape read the generated report pages:\n" + "\n".join(offenders) - for page in SPL_CORE_REPORT_PAGES: - built = out / "generated" / "components" / "light_controller" / "reports" / f"{page}.html" - assert not built.is_file(), f"the docs shape built {page}" + for page in SPL_CORE_REPORT_PAGES: + built = out / "generated" / "components" / "light_controller" / "reports" / f"{page}.html" + assert not built.is_file(), f"the docs shape built {page}" def test_the_reports_shape_still_reads_them(all_variant_data: None, tmp_path: Path) -> None: @@ -514,27 +577,29 @@ def test_the_reports_shape_still_reads_them(all_variant_data: None, tmp_path: Pa The fixed `/generated/...` toctree names have to resolve, or Sphinx reports a nonexisting document and the page links nothing at all. """ - with _generated_points_at(FAKE_GENERATED_BUILD): - pattern = _fake_spl_core_report_tree("components/light_controller") - out = tmp_path / "reports_html" - result = _build_with_spl_core_env("reports", out, tmp_path, {"target": "reports", "include_patterns": [pattern]}) - assert result.returncode == 0, (result.stdout + result.stderr)[-2000:] + build_dir = tmp_path / "build" / "Disco" / "test" / "Debug" + _fake_spl_core_report_tree(build_dir, "components/light_controller") + selection = _selection_file(tmp_path, build_dir, "reports") - log = result.stdout + result.stderr - unresolved = [ - line - for line in log.splitlines() - if "nonexisting document" in line and "generated/components/light_controller/reports" in line - ] - assert not unresolved, "the report toctree did not resolve:\n" + "\n".join(unresolved) + out = tmp_path / "reports_html" + result = _build_with_selection(selection, out) + assert result.returncode == 0, (result.stdout + result.stderr)[-2000:] + + log = result.stdout + result.stderr + unresolved = [ + line + for line in log.splitlines() + if "nonexisting document" in line and "generated/components/light_controller/reports" in line + ] + assert not unresolved, "the report toctree did not resolve:\n" + "\n".join(unresolved) - for page in SPL_CORE_REPORT_PAGES: - assert (out / "generated" / "components" / "light_controller" / "reports" / f"{page}.html").is_file() + for page in SPL_CORE_REPORT_PAGES: + assert (out / "generated" / "components" / "light_controller" / "reports" / f"{page}.html").is_file() - document = (out / "components" / "light_controller" / "doc" / "index.html").read_text(encoding="utf-8") - for page in SPL_CORE_REPORT_PAGES: - href = f"generated/components/light_controller/reports/{page}.html" - assert href in document, f"the light_controller page does not link {page}" + document = (out / "components" / "light_controller" / "doc" / "index.html").read_text(encoding="utf-8") + for page in SPL_CORE_REPORT_PAGES: + href = f"generated/components/light_controller/reports/{page}.html" + assert href in document, f"the light_controller page does not link {page}" # --- the generated listings must not show their Jinja armour ---------------- @@ -546,8 +611,8 @@ def test_generated_source_listings_carry_no_jinja_markers(all_variant_data: None The global Jinja pass used to consume those markers. It is gone, so the flag is what keeps them out: CMakeLists.txt sets SPL_SOURCE_DOCS_JINJA_RAW_TAGS OFF, and spl-core then invokes clanguru without `--jinja-raw-tags`. This - generates the fixture the same way and builds it, so it tracks what the - build actually emits rather than a hand-written idea of it. + generates the fixture the same way and builds it through the mount, so it + tracks what the build actually emits rather than a hand-written idea of it. """ cmake = (PROJECT_ROOT / "CMakeLists.txt").read_text(encoding="utf-8") assert _cmake_set(cmake, "SPL_SOURCE_DOCS_JINJA_RAW_TAGS") == "OFF", ( @@ -561,37 +626,37 @@ def test_generated_source_listings_carry_no_jinja_markers(all_variant_data: None source = tmp_path / "sample.c" source.write_text("int add(int a, int b) { return a + b; }\n", encoding="utf-8") - with _generated_points_at(FAKE_GENERATED_BUILD): - component = "components/light_controller" - listing_dir = FAKE_GENERATED_BUILD / component / "__source_docs" - listing_dir.mkdir(parents=True, exist_ok=True) - listing = listing_dir / "sample_c.rst" - subprocess.run( - [clanguru, "docs", "--source-file", str(source), "--output-file", str(listing), "--format", "rst"], - check=True, - capture_output=True, - ) - assert "{% raw %}" not in listing.read_text(encoding="utf-8"), ( - "this is not what spl-core generates with the raw-tag flag off" - ) - (listing_dir / "index.rst").write_text( - "Source Files\n============\n\n.. toctree::\n :maxdepth: 1\n\n sample_c\n", - encoding="utf-8", - ) + build_dir = tmp_path / "build" / "Disco" / "test" / "Debug" + component = "components/light_controller" + _fake_spl_core_report_tree(build_dir, component) + listing_dir = build_dir / component / "__source_docs" + listing = listing_dir / "sample_c.rst" + subprocess.run( + [clanguru, "docs", "--source-file", str(source), "--output-file", str(listing), "--format", "rst"], + check=True, + capture_output=True, + ) + assert "{% raw %}" not in listing.read_text(encoding="utf-8"), ( + "this is not what spl-core generates with the raw-tag flag off" + ) + (listing_dir / "index.rst").write_text( + "Source Files\n============\n\n.. toctree::\n :maxdepth: 1\n\n sample_c\n", + encoding="utf-8", + ) - pattern = f"generated/{component}/__source_docs/**" - out = tmp_path / "html" - result = _build_with_spl_core_env("reports", out, tmp_path, {"target": "reports", "include_patterns": [pattern]}) - assert result.returncode == 0, (result.stdout + result.stderr)[-2000:] + selection = _selection_file(tmp_path, build_dir, "reports") + out = tmp_path / "html" + result = _build_with_selection(selection, out) + assert result.returncode == 0, (result.stdout + result.stderr)[-2000:] - page = out / "generated" / component / "__source_docs" / "sample_c.html" - assert page.is_file(), "the listing was not built" - rendered = page.read_text(encoding="utf-8") - for marker in ("{% raw %}", "{% endraw %}"): - assert marker not in rendered, f"{marker} reached the reader" - - # Syntax highlighting splits the code across spans, so assert on the - # text rather than the markup -- checking the raw HTML for a contiguous - # "int add" is how an earlier version of this test fooled itself. - text = re.sub(r"<[^>]+>", "", rendered) - assert "int" in text and "add" in text, "the listing did not contain the code" + page = out / "generated" / component / "__source_docs" / "sample_c.html" + assert page.is_file(), "the listing was not built" + rendered = page.read_text(encoding="utf-8") + for marker in ("{% raw %}", "{% endraw %}"): + assert marker not in rendered, f"{marker} reached the reader" + + # Syntax highlighting splits the code across spans, so assert on the text + # rather than the markup -- checking the raw HTML for a contiguous "int add" + # is how an earlier version of this test fooled itself. + text = re.sub(r"<[^>]+>", "", rendered) + assert "int" in text and "add" in text, "the listing did not contain the code" diff --git a/test/test_ubproject_config.py b/test/test_ubproject_config.py index 6431fba..94011d1 100644 --- a/test/test_ubproject_config.py +++ b/test/test_ubproject_config.py @@ -14,7 +14,6 @@ """ import json -import os import re import runpy import subprocess @@ -27,6 +26,9 @@ from sphinx.util.matching import Matcher PROJECT_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(PROJECT_ROOT / "tools")) + +import variant_data # noqa: E402 pytestmark = [ pytest.mark.unittests, @@ -54,15 +56,34 @@ def spl_core_config() -> dict: # --- the file has to stand on its own -------------------------------------- -def test_does_not_extend_anything(project_config: dict) -> None: - """No `extend`, because only one of the two readers implements it. +def test_ubproject_extends_the_generated_rules(project_config: dict) -> None: + """The committed configuration extends exactly the generated rules file. + + ubproject.toml has to keep its `[needs]` table complete for sphinx-needs, + which does not follow `extend`, but the document rules are generated and + live in a file of their own. This is the one `extend` left, and it names + only the file tools/variant_data.py writes. + """ + assert project_config["extend"] == "ubproject.variants.toml" + + +def test_the_generated_rules_extend_the_selection() -> None: + """ubproject.toml -> ubproject.variants.toml -> build/selection.toml.""" + rules = tomllib.loads((PROJECT_ROOT / "ubproject.variants.toml").read_text(encoding="utf-8")) + assert rules["extend"] == "build/selection.toml" - The path it used to name also had the Python minor version baked into it - and pointed at an interpreter that does not exist, so the base - configuration silently failed to resolve for ubCode while conf.py resolved - it a second time through importlib. + +def test_conf_py_names_both_generated_files_for_sphinx() -> None: + """sphinx-needs and sphinx-mounts read one TOML file each, no `extend`. + + conf.py hands each the file it needs: the complete needs model and the + generated rules. The selection it reads at config-inited defaults to + build/selection.toml, and `-D spl_selection=<file>` names a build's own. """ - assert "extend" not in project_config + conf = (PROJECT_ROOT / "conf.py").read_text(encoding="utf-8") + assert 'needs_from_toml = "ubproject.toml"' in conf + assert 'sources_from_toml = "ubproject.variants.toml"' in conf + assert 'app.add_config_value("spl_selection", "build/selection.toml", "env", types=(str,))' in conf def test_sphinx_reads_this_exact_file() -> None: @@ -126,22 +147,54 @@ def test_build_output_is_not_indexed(project_config: dict) -> None: assert "build/**" in project_config["source"]["extend_exclude"] -def test_the_per_component_report_root_is_hidden_from_ubcode(project_config: dict) -> None: - """conf.py excludes it from every variant build, so ubCode must too. +def _gated_patterns(rules: list[dict], data: dict) -> set[str]: + """The rule patterns whose condition is TRUE for this cell. - doc/component_report.md is the root document of spl-core's PER-COMPONENT - report -- a build shape ubCode never performs. conf.py drops it from the - variant-wide source set, so Sphinx never sees it; ubCode indexed it anyway - and reported it as an orphan. Four orphans in one reader and five in the - other is the two of them disagreeing about the document set, which is the - one thing this configuration exists to prevent. + Rules are subtractive: a pattern is admitted unless a rule naming it is + FALSE, so a pattern that appears in no TRUE rule is excluded. + """ + from sphinx_mounts import variants + + return { + pattern + for rule in rules + for pattern in rule["files"] + if variants.interpret(variants.validate(rule["if"]), data) + } + + +def test_the_scope_rules_split_variant_and_component_documents(rules: list[dict]) -> None: + """A variant shows the variant-wide documents and the components it has. - `[parse.parsers.*]` has no `exclude`, but `extend_exclude` is honoured in - parser mode -- unlike `extend_include` -- so that is where it belongs. + A component's report shows one component -- its documentation and generated + pages, under doc/component_report.md -- and none of the variant-wide + documents. The two facts are two rules over `var.build_config.scope`, + evaluated here with sphinx-mounts' own interpreter against the real + generated cells. """ - conf = (PROJECT_ROOT / "conf.py").read_text(encoding="utf-8") - assert 'exclude_patterns.append("doc/component_report.md")' in conf - assert "doc/component_report.md" in project_config["source"]["extend_exclude"] + variant_cell = _variant_data("Disco", "test", "reports") + component_cell = _component_variant_data("Disco", "test", "reports", "components/light_controller") + + variant_admitted = _gated_patterns(rules, variant_cell) + component_admitted = _gated_patterns(rules, component_cell) + + # The variant cell: the variant-wide documents and its components' pages. + assert "index.md" in variant_admitted + assert "doc/component_report.md" not in variant_admitted + assert "components/light_controller/doc/**" in variant_admitted + assert "generated/components/light_controller/**" in variant_admitted + assert "generated/reports/**" in variant_admitted + assert "components/auto_off/doc/**" not in variant_admitted, "Disco does not contain auto_off" + + # The component cell: exactly the one component, and none of the + # variant-wide documents. + assert "doc/component_report.md" in component_admitted + assert "components/light_controller/doc/**" in component_admitted + assert "generated/components/light_controller/**" in component_admitted + assert "index.md" not in component_admitted + assert "doc/components/**" not in component_admitted + assert "generated/reports/**" not in component_admitted + assert "components/auto_off/doc/**" not in component_admitted def test_extend_include_is_not_used_because_it_would_be_inert(project_config: dict, spl_core_config: dict) -> None: @@ -156,7 +209,9 @@ def test_extend_include_is_not_used_because_it_would_be_inert(project_config: di """ assert spl_core_config["source"]["extend_include"] == ["build/**/__source_docs/**"] assert "extend_include" not in project_config["source"] - assert project_config["parse"]["parsers"]["rst"]["include"] == ["generated/**/*.rst"] + # The generated listings are named by the rst parser instead, relative to the + # mounted directory (sphinx-mounts matches them inside the mount). + assert project_config["parse"]["parsers"]["rst"]["include"] == list(variant_data.MOUNTED_PAGES) def test_ubcode_and_sphinx_see_the_same_documents(project_config: dict) -> None: @@ -181,13 +236,30 @@ def test_ubcode_and_sphinx_see_the_same_documents(project_config: dict) -> None: for tree in ("index.md", "doc/**", "components/**/doc/**", "test/**/doc/**"): assert f'"{tree}"' in conf, f"conf.py does not include {tree}, which the md parser reads" + # The rst parser reads the mounted build, which Sphinx names `generated/`. + assert project_config["parse"]["parsers"]["rst"]["include"] == list(variant_data.MOUNTED_PAGES) + for pattern in project_config["parse"]["parsers"]["rst"]["include"]: + assert f'"generated/{pattern}"' in conf, ( + f"conf.py does not include generated/{pattern}, which the rst parser reads" + ) + # --- what the variant machinery needs -------------------------------------- -def test_variant_data_file_is_the_current_pointer(project_config: dict) -> None: - """What ubCode reads, and what a bare sphinx-build falls back to.""" - assert project_config["needs"]["variant_data_file"] == "build/autoconf.json" +def test_variant_data_is_named_by_the_selection_not_the_committed_config(project_config: dict) -> None: + """The committed configuration names no cell; a rendered selection does.""" + assert "variant_data_file" not in project_config.get("needs", {}) + + rendered = variant_data.selection_toml( + PROJECT_ROOT / "build" / "variants" / "Disco" / "test" / "reports.json", + "Disco", + "test", + None, + ) + selection = tomllib.loads(rendered) + assert selection["needs"]["variant_data_file"].endswith("build/variants/Disco/test/reports.json") + assert selection["source"]["mounts"] == [] def test_needs_json_is_written(project_config: dict) -> None: @@ -203,8 +275,10 @@ def test_needs_json_is_written(project_config: dict) -> None: @pytest.fixture(scope="module") -def rules(project_config: dict) -> list[dict]: - return project_config["source"]["variant_sources"] +def rules(generated_variant_data: None) -> list[dict]: + """The document rules, as tools/variant_data.py wrote them.""" + text = (PROJECT_ROOT / "ubproject.variants.toml").read_text(encoding="utf-8") + return tomllib.loads(text)["source"]["variant_sources"] @pytest.fixture(scope="module", autouse=True) @@ -228,6 +302,12 @@ def _variant_data(variant: str, kit: str, target: str) -> dict: return json.load(handle) +def _component_variant_data(variant: str, kit: str, target: str, component: str) -> dict: + path = PROJECT_ROOT / "build" / "variants" / variant / kit / component / f"{target}.json" + with path.open(encoding="utf-8") as handle: + return json.load(handle) + + def test_every_component_document_is_gated_by_a_rule(rules: list[dict]) -> None: """A hand-written component document with no rule is 150% in every variant. @@ -283,11 +363,12 @@ def test_rules_select_the_expected_documents(rules: list[dict], variant: str, ki from sphinx_mounts import variants data = _variant_data(variant, kit, "docs") - included: set[str] = set() + included = _gated_patterns(rules, data) excluded: set[str] = set() for rule in rules: ok = variants.interpret(variants.validate(rule["if"]), data) - (included if ok else excluded).update(rule["files"]) + if not ok: + excluded.update(rule["files"]) for pattern in expect_present: assert pattern in included, f"{variant}/{kit}: expected {pattern} to be included" @@ -296,20 +377,33 @@ def test_rules_select_the_expected_documents(rules: list[dict], variant: str, ki assert pattern in excluded, f"{variant}/{kit}: expected {pattern} to be excluded" -def test_generated_output_is_gated_on_the_reports_target(rules: list[dict]) -> None: - """A docs build must not read report pages it will not show. +def test_the_mount_condition_names_the_builds_own_cell(tmp_path: Path) -> None: + """The mount's condition does name a variant -- the build's own. - Rules are subtractive -- a FALSE rule removes the files it names, a TRUE - one does nothing -- so this rule and the per-component ones compose as AND. + A reader pointed at another variant's data then reads none of this build's + pages, instead of showing them as that variant's. `selection_toml` renders + the condition; this evaluates it against the real cells. """ from sphinx_mounts import variants - shape_rule = next(r for r in rules if "target" in r["if"]) - assert shape_rule["files"] == ["generated/**"] + build_dir = tmp_path / "build" / "Disco" / "test" / "Debug" + build_dir.mkdir(parents=True) + + rendered = variant_data.selection_toml( + PROJECT_ROOT / "build" / "variants" / "Disco" / "test" / "reports.json", + "Disco", + "test", + build_dir, + ) + mount = tomllib.loads(rendered)["source"]["mounts"][0] + assert mount["mount_at"] == variant_data.MOUNT_AT + assert mount["include"] == list(variant_data.MOUNTED_PAGES) + assert mount["gitignore"] is False - tree = variants.validate(shape_rule["if"]) + tree = variants.validate(mount["if"]) assert variants.interpret(tree, _variant_data("Disco", "test", "reports")) is True assert variants.interpret(tree, _variant_data("Disco", "test", "docs")) is False + assert variants.interpret(tree, _variant_data("Sleep", "test", "reports")) is False #: The report pages spl-core writes per component, and the names a report @@ -385,11 +479,14 @@ def test_report_sections_name_the_generated_pages() -> None: ) if component is not None: expected = {f"/generated/{component}/reports/{page}" for page in REPORT_PAGES} + expected.add(f"/generated/{component}/__source_docs/index") assert set(entries) == expected, ( f"{document.relative_to(PROJECT_ROOT)}: expected {sorted(expected)}, got {sorted(entries)}" ) - assert report_documents == 8, f"expected 8 report toctrees, found {report_documents}" + # Nine component documents (the two examples included) plus index.md's + # coverage toctree. + assert report_documents == 10, f"expected 10 report toctrees, found {report_documents}" def _excluded(matcher: Matcher, path: str) -> bool: @@ -407,7 +504,7 @@ def _excluded(matcher: Matcher, path: str) -> bool: return False -def test_generated_is_the_only_route_to_the_generated_pages(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, project_config: dict) -> None: +def test_generated_is_the_only_route_to_the_generated_pages(monkeypatch: pytest.MonkeyPatch) -> None: """With two routes, every generated page exists under two docnames. spl-core's include patterns name the build directory both ways in a SPLed @@ -415,27 +512,16 @@ def test_generated_is_the_only_route_to_the_generated_pages(tmp_path: Path, monk `generated/...` name. conf.py must forward only the `generated/` one and prune the whole `build` tree from the Sphinx walk, or each report page is discovered twice -- once per name -- and every link and need in it is - duplicated. This executes conf.py against a controlled configuration rather - than grepping its text, because the property is the resulting source set and - the resulting matcher. + duplicated. This executes conf.py rather than grepping its text, because the + property is the resulting source set and the resulting matcher. """ - config = { - "include_patterns": [ - "components/light_controller/doc/**", - "build/Disco/test/Debug/components/light_controller/reports/**", - "generated/components/light_controller/reports/**", - ], - } - config_file = tmp_path / "config.json" - config_file.write_text(json.dumps(config), encoding="utf-8") - - monkeypatch.setenv("SPHINX_BUILD_CONFIGURATION_FILE", str(config_file)) monkeypatch.setenv("VARIANT", "Disco") module = runpy.run_path(str(PROJECT_ROOT / "conf.py")) include_patterns = module["include_patterns"] - assert "generated/components/light_controller/reports/**" in include_patterns + for pattern in variant_data.MOUNTED_PAGES: + assert f"generated/{pattern}" in include_patterns assert not [pattern for pattern in include_patterns if pattern.startswith("build/")] matcher = Matcher(module["exclude_patterns"]) @@ -449,6 +535,38 @@ def test_generated_is_the_only_route_to_the_generated_pages(tmp_path: Path, monk cmake = (PROJECT_ROOT / "CMakeLists.txt").read_text(encoding="utf-8") assert re.search(r"set\(\s*SPL_SPHINX_BINARY_DIR\s+\$\{CMAKE_SOURCE_DIR\}/generated\s*\)", cmake), ( - "CMakeLists.txt must name the generated link as SPL_SPHINX_BINARY_DIR" + "CMakeLists.txt must mount the build at the stable generated/ name" ) - assert project_config["parse"]["parsers"]["rst"]["include"] == ["generated/**/*.rst"] + assert re.search( + r"set\(\s*SPL_SPHINX_OPTIONS\s+-D\s+spl_selection=\$\{CMAKE_BINARY_DIR\}/selection/@SHAPE@\.toml\s*\)", + cmake, + ), "CMakeLists.txt must point the variant-wide runs at this build's selection" + assert re.search( + r"set\(\s*SPL_SPHINX_COMPONENT_OPTIONS\s+-D\s+spl_selection=\$\{CMAKE_BINARY_DIR\}/selection/@COMPONENT_PATH@/@SHAPE@\.toml\s*\)", + cmake, + ), "CMakeLists.txt must point the per-component runs at this build's selection" + + +# --- the same checks, as conf.py runs them ------------------------------------ + + +def test_the_checks_conf_py_runs_find_nothing() -> None: + """conf.py runs tools/config_checks.py whenever Sphinx reads its configuration. + + The tests above are the detailed form of those checks; this one runs exactly + what a Sphinx build runs, with the include patterns conf.py really ends up + with, so a build under -W and this suite cannot disagree about whether the + configuration is intact. + """ + import config_checks # noqa: PLC0415 - tools/ is on sys.path, see above + + module = runpy.run_path(str(PROJECT_ROOT / "conf.py")) + assert config_checks.run_all(PROJECT_ROOT, list(module["include_patterns"])) == [] + + +def test_the_checks_notice_a_hand_written_rule(project_config: dict) -> None: + """A rule in ubproject.toml would replace every generated one: `extend` replaces arrays.""" + import config_checks # noqa: PLC0415 + + tampered = {**project_config, "source": {**project_config["source"], "variant_sources": [{"if": "True", "files": ["x"]}]}} + assert any("declares rules" in finding for finding in config_checks.generated_half(tampered)) diff --git a/test/test_variant_data.py b/test/test_variant_data.py index 1de89c8..4337f91 100644 --- a/test/test_variant_data.py +++ b/test/test_variant_data.py @@ -13,7 +13,9 @@ """ import json +import os import sys +import tomllib from pathlib import Path import pytest @@ -193,107 +195,240 @@ def test_every_cell_carries_the_whole_contract(variant: str, kit: str, target: s assert data["build_config"]["kit"] == kit assert data["build_config"]["target"] == target assert data["build_config"]["components"] + assert data["build_config"]["scope"] == "variant" + assert data["build_config"]["component"] == "" assert data["features"] # It has to survive the round trip to the file both tools read. assert json.loads(json.dumps(data)) == data -# --- the current-variant pointer ------------------------------------------- +@pytest.mark.parametrize("variant", ["Base/Dev", "Disco", "IDEA/Sloemada", "Sleep", "Spa"]) +@pytest.mark.parametrize("kit", variant_data.KITS) +@pytest.mark.parametrize("target", variant_data.TARGETS) +def test_every_component_cell_carries_the_whole_contract(variant: str, kit: str, target: str) -> None: + """A component's report gets the same complete contract, one scope deeper. + + The component list stays the variant's, so a block conditioned on another + component's presence reads the same in a component's report as in the + variant's documentation; only `scope` and `component` differ. + """ + documented = variant_data.reported_components(PROJECT_ROOT, variant, kit) + assert documented, f"{variant}/{kit} has no documented component to report on" + + for component in documented: + data = variant_data.variant_data(PROJECT_ROOT, variant, kit, target, component) + + assert set(data) == {"features", "build_config"} + assert data["build_config"]["variant"] == variant + assert data["build_config"]["kit"] == kit + assert data["build_config"]["target"] == target + assert data["build_config"]["scope"] == "component" + assert data["build_config"]["component"] == component + assert data["build_config"]["components"] == variant_data.components(PROJECT_ROOT, variant, kit) + assert data["features"] + assert json.loads(json.dumps(data)) == data + + +# --- the rendered selection ------------------------------------------------ + + +def _tmp_project(tmp_path: Path) -> Path: + """A small project root with a parts.cmake and two documented components.""" + root = tmp_path / "project" + parts = root / "variants" / "Test" + parts.mkdir(parents=True) + (parts / "parts.cmake").write_text( + "spl_add_component(components/a)\nspl_add_component(test/suite)\n", + encoding="utf-8", + ) + for component in ("components/a", "test/suite"): + doc = root / component / "doc" + doc.mkdir(parents=True) + (doc / "index.md").write_text(f"# {component}\n", encoding="utf-8") + return root + + +def test_selection_toml_without_a_build_dir_mounts_nothing(tmp_path: Path) -> None: + """Selecting a cell on its own shows its documents and no generated page.""" + cell = variant_data.cell_path(PROJECT_ROOT, "Disco", "test", "reports") + selection = tomllib.loads(variant_data.selection_toml(cell, "Disco", "test", None)) + assert selection["needs"]["variant_data_file"] == cell.resolve().as_posix() + assert selection["source"]["mounts"] == [] + assert "project" not in selection -def test_the_pointer_is_a_symlink_where_the_platform_allows_one(tmp_path: Path) -> None: - """A symlink is cheap and clear where the OS permits one.""" - build_dir = tmp_path / "build" / "V" / "test" / "Debug" + +def test_selection_toml_with_a_build_dir_mounts_it(tmp_path: Path) -> None: + """A CMake build mounts its own directory at the stable `generated` name.""" + build_dir = tmp_path / "build" / "Disco" / "test" / "Debug" build_dir.mkdir(parents=True) - (build_dir / "payload.txt").write_text("x", encoding="utf-8") + cell = variant_data.cell_path(PROJECT_ROOT, "Disco", "test", "reports") + + selection = tomllib.loads(variant_data.selection_toml(cell, "Disco", "test", build_dir)) + mount = selection["source"]["mounts"][0] - variant_data.write_pointer(tmp_path, {"features": {}, "build_config": {}}, build_dir) + assert mount["dir"] == build_dir.resolve().as_posix() + assert mount["mount_at"] == variant_data.MOUNT_AT + assert mount["include"] == list(variant_data.MOUNTED_PAGES) + assert mount["gitignore"] is False + assert "Disco" in mount["if"] and "test" in mount["if"] and "reports" in mount["if"] - generated = tmp_path / "generated" - assert generated.is_symlink() - assert (generated / "payload.txt").is_file() + +def test_selection_toml_can_name_a_component_root_doc(tmp_path: Path) -> None: + """A per-component report renders under its own root document.""" + cell = variant_data.cell_path(PROJECT_ROOT, "Disco", "test", "reports", "components/light_controller") + selection = tomllib.loads( + variant_data.selection_toml(cell, "Disco", "test", None, variant_data.COMPONENT_ROOT_DOC) + ) + assert selection["project"]["root_doc"] == variant_data.COMPONENT_ROOT_DOC -def test_a_refused_symlink_falls_back_to_a_junction(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: - """Windows without Developer Mode refuses a symlink but allows a junction. +def test_write_selection_writes_the_project_selection_and_every_run(tmp_path: Path) -> None: + """A selected build gets one selection file per spl-core documentation run. - The junction is the fallback that keeps Sphinx able to walk `generated` - without copying the binary directory. Only Windows takes this path, which is - why it needs a test rather than the platform nobody develops on finding out. + build/selection.toml is what the IDE and a bare run read; every shape and + every component of the build gets its own file, so two builds' reports can + be built side by side and each names the cell of its own shape. """ - build_dir = tmp_path / "build" / "V" / "test" / "Debug" - (build_dir / "CMakeFiles").mkdir(parents=True) - (build_dir / "CMakeFiles" / "huge.o").write_text("x" * 1000, encoding="utf-8") + root = _tmp_project(tmp_path) + build_dir = root / "build" / "Test" / "test" / "Debug" + build_dir.mkdir(parents=True) - calls: list[tuple[Path, Path]] = [] + variant_data.write_selection(root, "Test", "test", "reports", build_dir) - def refuse(*args, **kwargs): - raise OSError("symlinks not permitted") + selection = tomllib.loads((root / variant_data.SELECTION_FILE).read_text(encoding="utf-8")) + assert selection["needs"]["variant_data_file"] == variant_data.cell_path( + root, "Test", "test", "reports" + ).resolve().as_posix() - def fake_junction(target: Path, link: Path) -> None: - calls.append((target, link)) - link.mkdir(parents=True, exist_ok=True) + for shape in variant_data.TARGETS: + run = build_dir / "selection" / f"{shape}.toml" + assert run.is_file(), f"the variant-wide {shape} run has no selection" + assert tomllib.loads(run.read_text(encoding="utf-8"))["needs"]["variant_data_file"] == ( + variant_data.cell_path(root, "Test", "test", shape).resolve().as_posix() + ) - monkeypatch.setattr(Path, "symlink_to", refuse) - monkeypatch.setattr(variant_data, "_create_junction", fake_junction) + for component in variant_data.reported_components(root, "Test", "test"): + for shape in variant_data.TARGETS: + run = build_dir / "selection" / component / f"{shape}.toml" + assert run.is_file(), f"{component}'s {shape} run has no selection" + parsed = tomllib.loads(run.read_text(encoding="utf-8")) + assert parsed["needs"]["variant_data_file"] == ( + variant_data.cell_path(root, "Test", "test", shape, component).resolve().as_posix() + ) + assert parsed["project"]["root_doc"] == variant_data.COMPONENT_ROOT_DOC - variant_data.write_pointer(tmp_path, {"features": {}, "build_config": {}}, build_dir) - generated = tmp_path / "generated" - assert calls == [(build_dir.resolve(), generated)], "the junction must lead to the resolved build directory" - assert generated.is_dir() and not generated.is_symlink() - assert not (generated / "NO_SYMLINK").exists(), "the junction is a real link, not an explanation" - assert not (generated / "CMakeFiles").exists(), "the fallback copied the binary directory" +def test_selecting_removes_an_old_generated_symlink_without_touching_its_target(tmp_path: Path) -> None: + """The old link is removed; the build it led to is somebody's and stays.""" + root = _tmp_project(tmp_path) + target = tmp_path / "old_build" + target.mkdir() + (target / "keep.txt").write_text("keep", encoding="utf-8") + generated = root / "generated" + generated.symlink_to(target, target_is_directory=True) + variant_data.write_selection(root, "Test", "prod", "docs", None) -def test_neither_link_leaves_an_explained_directory(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: - """When both link types fail, the path still exists and says why. + assert not generated.exists(), "the stale link must be removed" + assert (target / "keep.txt").read_text(encoding="utf-8") == "keep", "the target must survive" - spl-core stops a documentation build whose `generated` does not lead to its - build directory, so the failure has to be a named one rather than a missing - path -- and copying the binary directory, as the old fallback did, would - leak build objects into the source tree without producing the reports. - """ - build_dir = tmp_path / "build" / "V" / "test" / "Debug" - (build_dir / "CMakeFiles").mkdir(parents=True) - (build_dir / "CMakeFiles" / "huge.o").write_text("x" * 1000, encoding="utf-8") - def refuse(*args, **kwargs): - raise OSError("no links here") +def test_selecting_removes_the_old_autoconf_pointer(tmp_path: Path) -> None: + """`build/autoconf.json` was the pointer; nothing reads it any more.""" + root = _tmp_project(tmp_path) + pointer = root / "build" / "autoconf.json" + pointer.parent.mkdir(parents=True) + pointer.write_text("{}", encoding="utf-8") + + variant_data.write_selection(root, "Test", "prod", "docs", None) + + assert not pointer.exists() - monkeypatch.setattr(Path, "symlink_to", refuse) - monkeypatch.setattr(variant_data, "_create_junction", refuse) - variant_data.write_pointer(tmp_path, {"features": {}, "build_config": {}}, build_dir) +def test_selecting_removes_a_no_symlink_placeholder(tmp_path: Path) -> None: + """A directory holding NO_SYMLINK is the old placeholder, and goes.""" + root = _tmp_project(tmp_path) + generated = root / "generated" + generated.mkdir() + (generated / "NO_SYMLINK").write_text("no link here\n", encoding="utf-8") - generated = tmp_path / "generated" - assert generated.is_dir() and not generated.is_symlink() - assert [p.name for p in generated.iterdir()] == ["NO_SYMLINK"] - assert str(build_dir.resolve()) in (generated / "NO_SYMLINK").read_text(encoding="utf-8") - assert not (generated / "CMakeFiles").exists(), "the fallback copied the binary directory" + variant_data.write_selection(root, "Test", "prod", "docs", None) + assert not generated.exists() -def test_repointing_generated_leaves_the_old_build_untouched(tmp_path: Path) -> None: - """Re-pointing the link must never touch the directory it used to lead to. - `_remove_link` removes the link itself, not its target, so switching from - one configured variant to another does not delete the previous build. A - `shutil.rmtree` on the resolved target would, silently, on every reconfigure. +def test_selecting_leaves_a_plain_generated_directory_alone(tmp_path: Path) -> None: + """A real directory somebody put there is not ours to remove.""" + root = _tmp_project(tmp_path) + generated = root / "generated" + generated.mkdir() + (generated / "somebody.txt").write_text("mine", encoding="utf-8") + + variant_data.write_selection(root, "Test", "prod", "docs", None) + + assert (generated / "somebody.txt").read_text(encoding="utf-8") == "mine" + + +# --- the generated rules --------------------------------------------------- + + +def _parsed_rules(root: Path) -> list[dict]: + return tomllib.loads(variant_data.rules_toml(root))["source"]["variant_sources"] + + +def test_rules_toml_covers_every_documented_component_exactly_once(tmp_path: Path) -> None: + """A hand-written component with no rule is 150% in every variant. + + The failure is additive and silent -- its documents appear in variants that + do not contain the component -- so each documented component has exactly one + rule naming it. """ - first = tmp_path / "build" / "first" - second = tmp_path / "build" / "second" - for build_dir, name in ((first, "first.txt"), (second, "second.txt")): - build_dir.mkdir(parents=True, exist_ok=True) - (build_dir / name).write_text(name, encoding="utf-8") - - variant_data.write_pointer(tmp_path, {"features": {}, "build_config": {}}, first) - generated = tmp_path / "generated" - assert (generated / "first.txt").is_file() - - variant_data.write_pointer(tmp_path, {"features": {}, "build_config": {}}, second) - assert generated.resolve() == second.resolve() - assert (generated / "second.txt").is_file() - - assert first.is_dir(), "the old build directory was removed" - assert (first / "first.txt").read_text(encoding="utf-8") == "first.txt" + root = _tmp_project(tmp_path) + rules = _parsed_rules(root) + documented = variant_data.documented_components(root) + assert documented == ["components/a", "test/suite"] + + for component in documented: + matching = [rule for rule in rules if f"'{component}'" in rule["if"]] + assert len(matching) == 1, f"{component} is gated by {len(matching)} rules" + files = matching[0]["files"] + assert f"{component}/doc/**" in files + assert f"{variant_data.MOUNT_AT}/{component}/**" in files + + +def test_every_generated_rule_condition_is_inside_the_grammar(tmp_path: Path) -> None: + """A condition outside the grammar is refused rather than evaluated.""" + from sphinx_mounts import variants + + for rule in _parsed_rules(_tmp_project(tmp_path)): + variants.validate(rule["if"]) + + +def test_hand_written_rules_are_appended(tmp_path: Path) -> None: + """Numbers a component's membership cannot decide come from a hand file.""" + root = _tmp_project(tmp_path) + (root / "doc").mkdir() + (root / variant_data.HAND_WRITTEN_RULES).write_text( + '[[source.variant_sources]]\nif = "var.build_config.scope == \'variant\'"\nfiles = ["extra/**"]\n', + encoding="utf-8", + ) + + rules = _parsed_rules(root) + assert rules[-1]["if"] == "var.build_config.scope == 'variant'" + assert rules[-1]["files"] == ["extra/**"] + + +def test_write_text_if_changed_does_not_rewrite_identical_content(tmp_path: Path) -> None: + """A reader's cache stays valid when the content has not changed.""" + path = tmp_path / "file.txt" + assert variant_data.write_text_if_changed(path, "same") is True + + os.utime(path, (1_000_000, 1_000_000)) + before = path.stat().st_mtime_ns + assert variant_data.write_text_if_changed(path, "same") is False + assert path.stat().st_mtime_ns == before, "identical content was rewritten" + + assert variant_data.write_text_if_changed(path, "different") is True + assert path.read_text(encoding="utf-8") == "different" From bfb7ab1558d1d438064f9340b39536fd757e2cee Mon Sep 17 00:00:00 2001 From: Marco Heinemann <marco.heinemann@useblocks.com> Date: Tue, 29 Sep 2026 13:11:30 +0200 Subject: [PATCH 21/24] fix(csv): let ubc show the images of the imported requirements ubc refuses an image in imported need content unless the import source is listed under [[needs.import_sources]] with allow_assets; since the image moved into the content, that is the CSV import. Sphinx needs no such permission and ignores the key. --- ubproject.toml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ubproject.toml b/ubproject.toml index 70d01db..a1769c0 100644 --- a/ubproject.toml +++ b/ubproject.toml @@ -115,6 +115,13 @@ outgoing = "verifies" incoming = "is tested by" outgoing = "tests" +# The CSV requirements carry their image in their content (doc/sw_requirements). +# ubc refuses an asset in imported content unless its source is listed here; +# Sphinx needs no such permission and ignores the key. +[[needs.import_sources]] +json_path = "doc/ubconnect/csv/needs.json" +allow_assets = true + [needs.fields.integrity] # spl-core declares this through the deprecated `extra_options` list. That form # implies an empty-string default, which is what every existing need carries, so From 65502771074e6258caaa6325a9e87f6c46c98a8d Mon Sep 17 00:00:00 2001 From: Marco Heinemann <marco.heinemann@useblocks.com> Date: Tue, 29 Sep 2026 13:11:30 +0200 Subject: [PATCH 22/24] test: run the documentation gate in a linked worktree too sphinx-codelinks does not find the git root where .git is a file, and warns once per source (codelinks.git_ref). The gate tolerates exactly that warning, and only in a linked worktree; a clone, as on CI, has no such warning. --- test/test_docs_gate.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/test_docs_gate.py b/test/test_docs_gate.py index 1fb3f60..4b85cef 100644 --- a/test/test_docs_gate.py +++ b/test/test_docs_gate.py @@ -145,7 +145,13 @@ def test_both_readers_build_the_same_documentation_strictly(variant: str, kit: s ) sphinx_seconds = time.perf_counter() - started warnings = [line for line in sphinx.stderr.splitlines() if "WARNING" in line or "ERROR" in line] - assert sphinx.returncode == 0 and not warnings, f"{variant}/{kit}: Sphinx is not clean in strict mode:\n" + "\n".join(warnings or [sphinx.stderr[-3000:]]) + if (PROJECT_ROOT / ".git").is_file(): + # A linked git worktree, where `.git` is a file: sphinx-codelinks does not + # find the git root there and warns once per source (codelinks.git_ref). + # A clone, as on CI, does not have the problem, so it is tolerated here only. + warnings = [line for line in warnings if "[codelinks.git_ref]" not in line] + tolerated_only = (PROJECT_ROOT / ".git").is_file() and not warnings + assert (sphinx.returncode == 0 or tolerated_only) and not warnings, f"{variant}/{kit}: Sphinx is not clean in strict mode:\n" + "\n".join(warnings or [sphinx.stderr[-3000:]]) override = selection.read_text(encoding="utf-8") checked = subprocess.run([ubc, "check", "--no-cache", "--output-format", "json", "-c", override], cwd=PROJECT_ROOT, capture_output=True, text=True) From 536f633a28dcdfa78280d194b81fe64d56dcf2cb Mon Sep 17 00:00:00 2001 From: Marco Heinemann <marco.heinemann@useblocks.com> Date: Tue, 29 Sep 2026 13:21:59 +0200 Subject: [PATCH 23/24] fix: show only its own component in a component report Two things let every component into a per-component report. A rule pattern without a slash matches at every depth, so the variant-wide rule's index.md also removed the component's own doc/index.md: the root index is no longer named, and is an orphan page, whose toctree entries a component report excludes anyway. And both readers match a rule's patterns against a mount's files relative to the mounted directory, so generated/<component>/** never narrowed the mounted build: each component's rule now names <component>/**, which covers its documentation in the tree and its pages in the build alike, and the variant's own pages are reports/**.\n\nA component report's links to the rest of the product dangle by design. Its selection file says so in ubc's terms (lint.ignore needs.dead_link), and conf.py suppresses sphinx-needs' needs.link_outgoing for that report. The rule tests now decide real paths with sphinx-mounts' own matcher, instead of comparing pattern strings, which is what let this through. --- AGENTS.md | 10 ++-- VARIANTS.md | 5 +- conf.py | 5 ++ index.md | 4 ++ test/test_ubproject_config.py | 93 +++++++++++++++++------------------ test/test_variant_data.py | 5 +- tools/variant_data.py | 20 ++++++-- 7 files changed, 78 insertions(+), 64 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 94a7999..9178b41 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -219,13 +219,13 @@ component list: ```toml [[source.variant_sources]] if = "'components/auto_off' in var.build_config.components and (var.build_config.scope == 'variant' or var.build_config.component == 'components/auto_off')" -files = [ - "components/auto_off/doc/**", - "generated/components/auto_off/**", -] +files = ["components/auto_off/**"] ``` -Membership, never identity. The component list comes from that variant's +The one pattern names the component's documentation in the tree and its +generated pages in a mounted build alike: both readers match a rule's patterns +against the project tree and against each mount's files, relative to the mounted +directory. Membership, never identity. The component list comes from that variant's `parts.cmake`, so the product structure is stated once, in the file that already states it. **Never gate a document on the variant name** — that is a second encoding of the same fact, free to drift. Rules are *subtractive*: a FALSE rule diff --git a/VARIANTS.md b/VARIANTS.md index f5ff412..119eaea 100644 --- a/VARIANTS.md +++ b/VARIANTS.md @@ -384,10 +384,7 @@ a `doc/index` page, into `ubproject.variants.toml`: ```toml [[source.variant_sources]] if = "'components/auto_off' in var.build_config.components and (var.build_config.scope == 'variant' or var.build_config.component == 'components/auto_off')" -files = [ - "components/auto_off/doc/**", - "generated/components/auto_off/**", -] +files = ["components/auto_off/**"] ``` - When the condition is false, the files are left out before anything is read: no page, no needs, diff --git a/conf.py b/conf.py index c6ce0e4..32e6de2 100644 --- a/conf.py +++ b/conf.py @@ -189,6 +189,11 @@ def _load_selection(app, config): if root is not None and "root_doc" not in overrides: config.root_doc = root config.mounts = selection.get("source", {}).get("mounts", []) + # A per-component report's links to the rest of the product dangle by design; + # its selection says so in ubc's terms, and this is the same statement for + # sphinx-needs, for that report only. + if "needs.dead_link" in selection.get("lint", {}).get("ignore", []): + config.suppress_warnings = [*config.suppress_warnings, "needs.link_outgoing"] # configuration checks ###################################################### diff --git a/index.md b/index.md index fa6be49..004b8a8 100644 --- a/index.md +++ b/index.md @@ -1,3 +1,7 @@ +--- +orphan: true +--- + # Variant Report **Variant:** {variant}`build_config.variant` diff --git a/test/test_ubproject_config.py b/test/test_ubproject_config.py index 94011d1..f69a5be 100644 --- a/test/test_ubproject_config.py +++ b/test/test_ubproject_config.py @@ -147,20 +147,22 @@ def test_build_output_is_not_indexed(project_config: dict) -> None: assert "build/**" in project_config["source"]["extend_exclude"] -def _gated_patterns(rules: list[dict], data: dict) -> set[str]: - """The rule patterns whose condition is TRUE for this cell. +def _removed_by_rules(rules: list[dict], data: dict, path: str) -> bool: + """Whether a document at `path` is removed for this cell. - Rules are subtractive: a pattern is admitted unless a rule naming it is - FALSE, so a pattern that appears in no TRUE rule is excluded. + Rules are subtractive: a path is removed when a rule whose condition is FALSE + has a pattern matching it. Patterns are matched the way both readers match + them (sphinx-mounts' own dialect): against paths in the project tree, and + against a mounted build's files, relative to the mounted directory. """ - from sphinx_mounts import variants + from sphinx_mounts import dialect, variants - return { - pattern + return any( + dialect.matches(pattern, path) for rule in rules + if not variants.interpret(variants.validate(rule["if"]), data) for pattern in rule["files"] - if variants.interpret(variants.validate(rule["if"]), data) - } + ) def test_the_scope_rules_split_variant_and_component_documents(rules: list[dict]) -> None: @@ -175,26 +177,27 @@ def test_the_scope_rules_split_variant_and_component_documents(rules: list[dict] variant_cell = _variant_data("Disco", "test", "reports") component_cell = _component_variant_data("Disco", "test", "reports", "components/light_controller") - variant_admitted = _gated_patterns(rules, variant_cell) - component_admitted = _gated_patterns(rules, component_cell) - - # The variant cell: the variant-wide documents and its components' pages. - assert "index.md" in variant_admitted - assert "doc/component_report.md" not in variant_admitted - assert "components/light_controller/doc/**" in variant_admitted - assert "generated/components/light_controller/**" in variant_admitted - assert "generated/reports/**" in variant_admitted - assert "components/auto_off/doc/**" not in variant_admitted, "Disco does not contain auto_off" + # The variant cell: the variant-wide documents and its components' pages, + # in the tree and in the mounted build (whose paths are relative to it). + for path in ("index.md", "doc/components/index.md", "components/light_controller/doc/index.md", + "components/light_controller/reports/coverage.rst", "reports/coverage.rst"): + assert not _removed_by_rules(rules, variant_cell, path), f"the variant removes {path}" + for path in ("doc/component_report.md", "components/auto_off/doc/index.md"): + assert _removed_by_rules(rules, variant_cell, path), f"the variant keeps {path}" # The component cell: exactly the one component, and none of the - # variant-wide documents. - assert "doc/component_report.md" in component_admitted - assert "components/light_controller/doc/**" in component_admitted - assert "generated/components/light_controller/**" in component_admitted - assert "index.md" not in component_admitted - assert "doc/components/**" not in component_admitted - assert "generated/reports/**" not in component_admitted - assert "components/auto_off/doc/**" not in component_admitted + # variant-wide documents. The root index.md is not named by any rule -- a + # pattern without a slash would match every index.md -- and is an orphan + # page there, whose toctree entries are all excluded. + for path in ("doc/component_report.md", "components/light_controller/doc/index.md", + "components/light_controller/reports/coverage.rst", "components/light_controller/__source_docs/index.rst"): + assert not _removed_by_rules(rules, component_cell, path), f"the component report removes {path}" + for path in ("doc/components/index.md", "doc/sw_requirements/index.md", "reports/coverage.rst", + "components/main_control_knob/doc/index.md", "components/main_control_knob/reports/coverage.rst", + "components/auto_off/doc/index.md"): + assert _removed_by_rules(rules, component_cell, path), f"the component report keeps {path}" + assert not _removed_by_rules(rules, component_cell, "index.md") + assert "orphan: true" in (PROJECT_ROOT / "index.md").read_text(encoding="utf-8").split("---")[1] def test_extend_include_is_not_used_because_it_would_be_inert(project_config: dict, spl_core_config: dict) -> None: @@ -315,10 +318,14 @@ def test_every_component_document_is_gated_by_a_rule(rules: list[dict]) -> None: every need in it would enter their traceability data. The failure is additive and silent, which is why it needs a test rather than a review. """ - gated = {pattern for rule in rules for pattern in rule["files"]} + from sphinx_mounts import dialect + + patterns = [pattern for rule in rules for pattern in rule["files"]] for doc_dir in sorted(PROJECT_ROOT.glob("components/**/doc")) + sorted(PROJECT_ROOT.glob("test/*/doc")): rel = doc_dir.relative_to(PROJECT_ROOT).as_posix() - assert f"{rel}/**" in gated, f"{rel} is not gated by any [[source.variant_sources]] rule" + assert any(dialect.matches(pattern, f"{rel}/index.md") for pattern in patterns), ( + f"{rel} is not gated by any generated rule" + ) def test_no_rule_names_a_variant_by_name(rules: list[dict]) -> None: @@ -349,32 +356,22 @@ def test_every_rule_condition_is_inside_the_grammar(rules: list[dict]) -> None: [ # Disco: BLINKING, so no brightness; no auto-off; integration suite in # the test kit only. - ("Disco", "test", ["components/light_controller/doc/**", "test/spled_integration/doc/**"], ["components/auto_off/doc/**", "components/brightness_controller/doc/**"]), - ("Disco", "prod", ["components/light_controller/doc/**"], ["test/spled_integration/doc/**", "components/auto_off/doc/**"]), + ("Disco", "test", ["components/light_controller/doc/index.md", "test/spled_integration/doc/index.md"], ["components/auto_off/doc/index.md", "components/brightness_controller/doc/index.md"]), + ("Disco", "prod", ["components/light_controller/doc/index.md"], ["test/spled_integration/doc/index.md", "components/auto_off/doc/index.md"]), # Sleep: manual brightness and auto-off, no integration suite. - ("Sleep", "test", ["components/auto_off/doc/**", "components/brightness_controller/doc/**"], ["test/spled_integration/doc/**"]), + ("Sleep", "test", ["components/auto_off/doc/index.md", "components/brightness_controller/doc/index.md"], ["test/spled_integration/doc/index.md"]), # Spa: brightness but no auto-off. - ("Spa", "test", ["components/brightness_controller/doc/**"], ["components/auto_off/doc/**"]), + ("Spa", "test", ["components/brightness_controller/doc/index.md"], ["components/auto_off/doc/index.md"]), # Base/Dev: the example components, none of the product ones. - ("Base/Dev", "test", ["components/examples/hello_gmock/doc/**"], ["components/light_controller/doc/**", "components/auto_off/doc/**"]), + ("Base/Dev", "test", ["components/examples/hello_gmock/doc/index.md"], ["components/light_controller/doc/index.md", "components/auto_off/doc/index.md"]), ], ) def test_rules_select_the_expected_documents(rules: list[dict], variant: str, kit: str, expect_present: list[str], expect_absent: list[str]) -> None: - from sphinx_mounts import variants - data = _variant_data(variant, kit, "docs") - included = _gated_patterns(rules, data) - excluded: set[str] = set() - for rule in rules: - ok = variants.interpret(variants.validate(rule["if"]), data) - if not ok: - excluded.update(rule["files"]) - - for pattern in expect_present: - assert pattern in included, f"{variant}/{kit}: expected {pattern} to be included" - assert pattern not in excluded, f"{variant}/{kit}: {pattern} is both included and excluded" - for pattern in expect_absent: - assert pattern in excluded, f"{variant}/{kit}: expected {pattern} to be excluded" + for path in expect_present: + assert not _removed_by_rules(rules, data, path), f"{variant}/{kit}: expected {path} to be included" + for path in expect_absent: + assert _removed_by_rules(rules, data, path), f"{variant}/{kit}: expected {path} to be excluded" def test_the_mount_condition_names_the_builds_own_cell(tmp_path: Path) -> None: diff --git a/test/test_variant_data.py b/test/test_variant_data.py index 4337f91..c7c4a9f 100644 --- a/test/test_variant_data.py +++ b/test/test_variant_data.py @@ -393,9 +393,8 @@ def test_rules_toml_covers_every_documented_component_exactly_once(tmp_path: Pat for component in documented: matching = [rule for rule in rules if f"'{component}'" in rule["if"]] assert len(matching) == 1, f"{component} is gated by {len(matching)} rules" - files = matching[0]["files"] - assert f"{component}/doc/**" in files - assert f"{variant_data.MOUNT_AT}/{component}/**" in files + # One pattern, matched in the tree and inside a mounted build alike. + assert matching[0]["files"] == [f"{component}/**"] def test_every_generated_rule_condition_is_inside_the_grammar(tmp_path: Path) -> None: diff --git a/tools/variant_data.py b/tools/variant_data.py index b2c869a..1ab7658 100644 --- a/tools/variant_data.py +++ b/tools/variant_data.py @@ -312,14 +312,23 @@ def rules_toml(project_root: Path) -> str: A per-component report (`scope == "component"`) shows one component: its own documentation and generated pages, under doc/component_report.md, and none of the variant-wide documents. + + Patterns follow gitignore rules, and both readers match them twice: against + the project tree, and against each mount's own files, relative to the mounted + directory. So `components/auto_off/**` names the component's documentation + in the tree and its generated pages in a mounted build alike, and + `reports/**` the variant's pages spl-core writes under `reports/`. A pattern + without a slash would match at every depth, which is why the root `index.md` + is not named: in a component's report it is an orphan page (its front matter + says so) whose every toctree entry is excluded. """ doc_dir = project_root / "doc" - variant_wide = ["index.md"] + variant_wide: list[str] = [] for entry in sorted(doc_dir.iterdir()) if doc_dir.is_dir() else []: if entry.name == Path(COMPONENT_ROOT_DOC).name + ".md": continue variant_wide.append(f"doc/{entry.name}/**" if entry.is_dir() else f"doc/{entry.name}") - variant_wide.append(f"{MOUNT_AT}/reports/**") + variant_wide.append("reports/**") def rule(condition: str, files: list[str], comment: str) -> str: listed = "".join(f' "{pattern}",\n' for pattern in files) @@ -339,7 +348,7 @@ def rule(condition: str, files: list[str], comment: str) -> str: f"'{component}' in var.build_config.components and " f"(var.build_config.scope == 'variant' or var.build_config.component == '{component}')" ) - text += rule(condition, [f"{component}/doc/**", f"{MOUNT_AT}/{component}/**"], component) + text += rule(condition, [f"{component}/**"], component) hand_written = project_root / HAND_WRITTEN_RULES if hand_written.is_file(): import tomllib @@ -362,7 +371,10 @@ def selection_toml( """ text = GENERATED_HEADER + f'\n[needs]\nvariant_data_file = "{variant_data_file.resolve().as_posix()}"\n' if root_doc: - text += f'\n[project]\nroot_doc = "{root_doc}"\n' + # A component's report shows one component, so its links to the rest of + # the product dangle by design. ubc's lint code says so; conf.py turns it + # into suppressing sphinx-needs' `needs.link_outgoing` for Sphinx. + text += f'\n[project]\nroot_doc = "{root_doc}"\n\n[lint]\nignore = ["needs.dead_link"]\n' if build_dir is None: text += "\n[source]\nmounts = []\n" else: From 9b3c644ceb1fcb702fcb1363a9066de90dd3929f Mon Sep 17 00:00:00 2001 From: Marco Heinemann <marco.heinemann@useblocks.com> Date: Tue, 29 Sep 2026 13:21:59 +0200 Subject: [PATCH 24/24] ci: gate the per-component reports as well Every component report spl-core builds, in the docs shape, in both readers, strictly, with the needs compared: 24 of them next to the 10 variant cells. Each run selects its cell first, because codelinks reads the selected build's compile database. --- test/test_docs_gate.py | 43 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/test/test_docs_gate.py b/test/test_docs_gate.py index 4b85cef..d91e1d5 100644 --- a/test/test_docs_gate.py +++ b/test/test_docs_gate.py @@ -127,15 +127,42 @@ def _normalized(value): return sorted(value) if isinstance(value, list) else value +def _component_reports() -> list[tuple[str, str]]: + """(variant, component) for every component report spl-core builds: the test kit's.""" + sys.path.insert(0, str(PROJECT_ROOT / "tools")) + import variant_data # noqa: PLC0415 + + return [(variant, component) for variant in VARIANTS for component in variant_data.reported_components(PROJECT_ROOT, variant, "test")] + + @pytest.mark.parametrize("kit", KITS) @pytest.mark.parametrize("variant", VARIANTS) def test_both_readers_build_the_same_documentation_strictly(variant: str, kit: str, tmp_path: Path) -> None: + _check_one_run(variant, kit, "docs.toml", tmp_path) + + +@pytest.mark.parametrize(("variant", "component"), _component_reports()) +def test_both_readers_build_the_same_component_report_strictly(variant: str, component: str, tmp_path: Path) -> None: + """spl-core's per-component report, in the docs shape, which upstream tests nowhere.""" + _check_one_run(variant, "test", f"{component}/docs.toml", tmp_path, label=f"{variant}/{component}") + + +#: The cell selected last. codelinks reads the selected build's compile database, +#: so a run of another cell selects that cell first. +_SELECTED: dict[str, tuple[str, str, Path]] = {} + + +def _check_one_run(variant: str, kit: str, selection_name: str, tmp_path: Path, label: str | None = None) -> None: ubc = _find_ubc() _require("ubc", ubc is not None) _require("CMake and Ninja", bool(shutil.which("cmake") and shutil.which("ninja"))) - build_dir = _select_build(variant, kit) - selection = build_dir / "selection" / "docs.toml" + current = _SELECTED.get("cell") + if current is None or current[:2] != (variant, kit): + _SELECTED["cell"] = (variant, kit, _select_build(variant, kit)) + build_dir = _SELECTED["cell"][2] + selection = build_dir / "selection" / selection_name + label = label or f"{variant}/{kit}" started = time.perf_counter() sphinx = subprocess.run( @@ -151,7 +178,7 @@ def test_both_readers_build_the_same_documentation_strictly(variant: str, kit: s # A clone, as on CI, does not have the problem, so it is tolerated here only. warnings = [line for line in warnings if "[codelinks.git_ref]" not in line] tolerated_only = (PROJECT_ROOT / ".git").is_file() and not warnings - assert (sphinx.returncode == 0 or tolerated_only) and not warnings, f"{variant}/{kit}: Sphinx is not clean in strict mode:\n" + "\n".join(warnings or [sphinx.stderr[-3000:]]) + assert (sphinx.returncode == 0 or tolerated_only) and not warnings, f"{label}: Sphinx is not clean in strict mode:\n" + "\n".join(warnings or [sphinx.stderr[-3000:]]) override = selection.read_text(encoding="utf-8") checked = subprocess.run([ubc, "check", "--no-cache", "--output-format", "json", "-c", override], cwd=PROJECT_ROOT, capture_output=True, text=True) @@ -160,20 +187,20 @@ def test_both_readers_build_the_same_documentation_strictly(variant: str, kit: s if diagnostic["severity"] != "info": counts[diagnostic["code"]] = counts.get(diagnostic["code"], 0) + 1 unexpected = {code: count for code, count in counts.items() if EXCEPTED_UBC_WARNINGS.get(code) != count} - assert not unexpected, f"{variant}/{kit}: ubc reports findings docs_exceptions.toml does not name: {unexpected}" + assert not unexpected, f"{label}: ubc reports findings docs_exceptions.toml does not name: {unexpected}" started = time.perf_counter() built = subprocess.run([ubc, "build", "html", "--no-cache", "--deny", "none", "-o", str(tmp_path / "ubc"), "-c", override], cwd=PROJECT_ROOT, capture_output=True, text=True) ubc_seconds = time.perf_counter() - started - assert built.returncode == 0, f"{variant}/{kit}: ubc build html failed:\n{built.stdout[-3000:]}\n{built.stderr[-3000:]}" + assert built.returncode == 0, f"{label}: ubc build html failed:\n{built.stdout[-3000:]}\n{built.stderr[-3000:]}" sphinx_needs = _needs(tmp_path / "sphinx" / "needs.json") ubc_needs = _needs(tmp_path / "ubc" / "needs.json") - TIMINGS.append((variant, kit, sphinx_seconds, ubc_seconds, len(sphinx_needs))) + TIMINGS.append((variant, label.split("/", 1)[1] if label != f"{variant}/{kit}" else kit, sphinx_seconds, ubc_seconds, len(sphinx_needs))) only_sphinx = sorted(set(sphinx_needs) - set(ubc_needs) - EXCEPTED_NEEDS) only_ubc = sorted(set(ubc_needs) - set(sphinx_needs) - EXCEPTED_NEEDS) - assert not only_sphinx and not only_ubc, f"{variant}/{kit}: needs in one reader only: Sphinx {only_sphinx}, ubc {only_ubc}" + assert not only_sphinx and not only_ubc, f"{label}: needs in one reader only: Sphinx {only_sphinx}, ubc {only_ubc}" fields = _compared_fields() differences = [ @@ -182,4 +209,4 @@ def test_both_readers_build_the_same_documentation_strictly(variant: str, kit: s for field in fields if _normalized(sphinx_needs[need_id].get(field)) != _normalized(ubc_needs[need_id].get(field)) ] - assert not differences, f"{variant}/{kit}: the readers disagree about {len(differences)} values:\n" + "\n".join(differences[:40]) + assert not differences, f"{label}: the readers disagree about {len(differences)} values:\n" + "\n".join(differences[:40])