From 418f46a6b1ef7a9a422d5784b7f9ca410d30de86 Mon Sep 17 00:00:00 2001 From: Dimitri Yatsenko Date: Sat, 17 Oct 2026 16:54:35 -0500 Subject: [PATCH 1/4] build(test): add hypothesis and register its profiles Add hypothesis to the test dependency group and to the test and dev extras, for the property tests of Diagram.expand. tests/conftest.py registers three profiles, selected with HYPOTHESIS_PROFILE (default: ci when CI is set, else dev): - dev: 50 examples, for fast local runs; - ci: 500 examples, derandomized, no deadline; - thorough: 2000 random examples, with failing examples saved under the gitignored .hypothesis/ directory. pixi.lock is re-solved. Besides hypothesis and sortedcontainers in the test and dev environments, it now records the test-py310 and test-py314 environments that pyproject.toml already declares, and packaging in the default environment; no existing package changes version. --- pixi.lock | 8173 +++++++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 3 + tests/conftest.py | 23 + 3 files changed, 7995 insertions(+), 204 deletions(-) diff --git a/pixi.lock b/pixi.lock index 0421929da..f84029955 100644 --- a/pixi.lock +++ b/pixi.lock @@ -105,6 +105,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8f/52/0634adaace9be2d8cac9ef78f05c47f3a675882e068438b9d7ec7ef0c13f/pandas-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl @@ -229,6 +230,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl @@ -308,6 +310,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl @@ -454,6 +457,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/cf/40/eed6741ee50c5687c951acff0edd5f95f42722bad81eedfaedb52f8e2de2/hypothesis-6.170.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/e5/ae/2ad30f4652712c82f1c23423d79136fbce338932ad166d70c1efb86a5998/identify-2.6.14-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl @@ -505,6 +509,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/f3/51/0489a6a5595b7760b5dbac0dd82852b510326e7d88d51dbffcd2e07e3ff3/ruff-0.14.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/2d/26b8b30067d94339afee62c3edc9b803a6eb9332f521ba77d8aaab5de873/testcontainers-4.14.2-py3-none-any.whl @@ -652,6 +657,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/7b/b4/9ae75c3a63a87ca454383c6305f33f25538cf1eccf29491ba768b80e894d/hypothesis-6.170.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl - pypi: https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl @@ -703,6 +709,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/9e/e9/08840ff5127916bb989c86f18924fd568938b06f58b60e206176f327c0fe/ruff-0.14.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl - pypi: https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/2d/26b8b30067d94339afee62c3edc9b803a6eb9332f521ba77d8aaab5de873/testcontainers-4.14.2-py3-none-any.whl @@ -804,6 +811,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7c/5b/cdd2c612277b7ac7ec8c0c9bc41812c43dc7b2d5f2b0897e15fdf5a1f915/fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c8/ba/e389f3ac83570fe4e84ff2f68c2296621151949f2fa2f44d9c047d7acf1d/hypothesis-6.170.1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl @@ -855,6 +863,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7d/f8/2be49047f929d6965401855461e697ab185e1a6a683d914c5c19c7962d9e/ruff-0.14.9-py3-none-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/2d/26b8b30067d94339afee62c3edc9b803a6eb9332f521ba77d8aaab5de873/testcontainers-4.14.2-py3-none-any.whl @@ -996,6 +1005,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/7a/34/259b28ea7a2a0c904b11cd36c79b8cef8019b26ee5dbe24e73b469dea347/greenlet-3.3.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/cf/40/eed6741ee50c5687c951acff0edd5f95f42722bad81eedfaedb52f8e2de2/hypothesis-6.170.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl @@ -1041,6 +1051,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/2d/26b8b30067d94339afee62c3edc9b803a6eb9332f521ba77d8aaab5de873/testcontainers-4.14.2-py3-none-any.whl @@ -1184,6 +1195,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/49/8d/9771d03e7a8b1ee456511961e1b97a6d77ae1dea4a34a5b98eee706689d3/greenlet-3.3.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/7b/b4/9ae75c3a63a87ca454383c6305f33f25538cf1eccf29491ba768b80e894d/hypothesis-6.170.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/05/aa/62893d6a591d337aa59dcc4c6f6c842f1fe20cd72c8c5c1f980255243252/ipython-9.7.0-py3-none-any.whl @@ -1229,6 +1241,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/2d/26b8b30067d94339afee62c3edc9b803a6eb9332f521ba77d8aaab5de873/testcontainers-4.14.2-py3-none-any.whl @@ -1326,6 +1339,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c8/ba/e389f3ac83570fe4e84ff2f68c2296621151949f2fa2f44d9c047d7acf1d/hypothesis-6.170.1-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/48/c5/d5e07995077e48220269c28a221e168c91123ad5ceee44d548f54a057fc0/ipython-9.6.0-py3-none-any.whl @@ -1371,6 +1385,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/2d/26b8b30067d94339afee62c3edc9b803a6eb9332f521ba77d8aaab5de873/testcontainers-4.14.2-py3-none-any.whl @@ -1384,6 +1399,1099 @@ environments: - pypi: https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl - pypi: ./ + test-py310: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-51.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.6-h3c89d7e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.17-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.90.0-h3d503d9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphviz-13.1.2-h87b6fe6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.6.0-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20260512-pl5321h373387f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.90.0-h569388d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.6.0-h23af247_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.6.0-h23af247_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpango-1.58.2-hce4e988_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpango-devel-1.58.2-h317d9d0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.59-h922cc85_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.62.4-ha427ee3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.1-h13e7031_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-h2f58602_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.22-h267e890_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-h7cc23a3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-h7cc23a3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.6-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - pypi: https://files.pythonhosted.org/packages/09/ab/c8f982c4dfaab49200e31820a4ffe6275356f7dcaaa1024735d8f8b4e048/aiobotocore-3.9.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5d/38/01f5fc732d5373d6c099db0343279cd4e6607287d336bab6ff6ae9085a0d/aiohttp-3.14.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/86/5363df11b86d02cf3662208e7406496327649cc90eb365bf6f4e8a54a41f/argon2_cffi_bindings-26.1.0-cp310-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/eb/c51d3b54dfaa9267a687359d946dc2a1ca6a27fb4d381773e59b7bd72bf4/botocore-1.43.106-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/11/2f/5abff74666f8388d2c9516c265f99c33484c827f7fcb3cd703c2f3cbb17e/cachebox-5.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/c4/9f/9f52d2886d52645987d603425482c63c5045a3005db1354a7097e5ed1ae9/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/29/4e/e1d38d27817d91776ca543d01276a67eace3df675a673cac32b12c167d58/coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/26/4a2bad8eb430d8d805a4642c4bff25103a37548d74ab346f8b1e024abcc5/deepdiff-9.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4d/dd/8bc67e7d5ffc1d88aa5af511189dfb76dc4e1e5b808fab186146ef2663e5/executing-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/ed/0d6d0d6467ae3d009fb82fb411867c7fcfeadbdd25602cab0d7a7963f400/faker-40.43.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/56/151b5e81d20c63834f48ad37a0cbbbe2f9b248e38f8d10387f0cf5219244/fonttools-4.65.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/94/5c/092682ae7ca1aadd44aa36b0bc35c48c9ed55f9ffebb5794980b1e2ae74b/greenlet-3.5.6-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/42/42/31e66ce21aa6ea030ace8874269e5a169b0c69d8a3043042e315bd64c6ad/hypothesis-6.168.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c0/56/4cc7fc9e9e3f38fd324f24f8afe0ad8bb5fa41283f37f1aaf9de0612c968/ipython-8.39.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/be/c7/71eb0d51cdcbb63b857c931aa4e9218b078b5d9f8ecd383b234f28fe7b0f/jedi-0.20.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/0e/9f394d693be549fa3fab62498c2778294595991047adc3dcf10aa99b91c0/kiwisolver-1.5.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fc/b7/d8bcec2626c35f96972bff656299fef4578113ea6193c8fdad324710410c/matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/53/f65ed68b96eea2b4f6fa4f9031bdfccd8f3097c429e3d371c545d1c40ca4/multidict-6.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4e/89/36f4cd76cf4baf05c50ababb976249153f18c959171c7f6ba09a6f217260/pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ac/09/cc33bbd5463749c116b62c204d88bed6c02a6cb901eac7adab0d38651b07/polars-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/88/e9fecfd49159da92f54ff2445883577a0f1bc195da53ecc9535c458d55dd/polars_runtime_32-2.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f0/51/5adee15e12a7e314cece4543fbf295b0fe1b504dc78b32dff4c8eb1e29b0/propcache-0.5.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/fd/96/ce330aa1959250e6fd254678e6489b3b39eb956669122ad4b45dde3b02c3/psycopg2_binary-2.9.13-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/57/5c/f8fc0eb2de03464a557d5a4d0c15e972d73362414696618833b771f7eddd/pyarrow-25.0.1-cp310-cp310-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/f6/1d91d5cbe2f3acd0ba6f596d54466ab8703485d4edf84ca28d9507db8d43/pycryptodome-4.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1b/1a/21a58791021397d9ca35295e3f4291c16e319155c5904334b89eb07e97e1/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/4b/0a906d8184f011ff8dbd4722743783867589b33269d2c5fff238d636fdcb/pymysql-1.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/ef/c66110d46fb800dda0bf33164182dfadabe26a90e4476844d502a23dca8e/pytz-2026.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/9c/15fb18bd708949dc953fe120be8d3c7a1b1b140c2592a9c81ce7961e9ecf/s3fs-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4e/1f/c4661f84df99587a216fd2f11c00a19776c4d67428dc084db6dd745a4c23/sqlalchemy-2.0.55-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/7e/424aac8b355597835deb333e757a0e94b5ccf38ad00f07fe6ed1f4e17c88/testcontainers-4.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/94/21/1e5995a1c920cce14e4bffae20c665ec10e7ed03ab25e006cd741092b718/tzdata-2026.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/db/6c/f940133c71427c208575910e981942bd78c98b1f7cd0d1425ca4b7457c04/wcwidth-0.9.2-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/84/d0/9c0409db3062fc7ef8347c578a251b0fe3471741871c3bc849ea07c98e98/wrapt-2.5.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/28/b9/7818ac6dec7fbcd16be2d19495807c01e2c38834e57d41c63356a75e786b/yarl-1.25.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: ./ + linux-aarch64: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-51.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/at-spi2-atk-2.38.0-h1f2db35_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/at-spi2-core-2.40.3-h1f2db35_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/atk-1.0-2.38.0-hedc4a1f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_10.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cairo-1.18.6-hca320e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/dbus-1.16.2-h71d469a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/epoxy-1.5.10-he30d5cf_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.18.3-ha4b22b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.14.3-h8af1aa0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fribidi-1.0.17-h5bc82ec_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gdk-pixbuf-2.44.8-hb26ce08_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glib-tools-2.90.1-hd8364ca_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/graphite2-1.3.15-h7ac5ae9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/graphviz-13.1.2-hdb06ba2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gtk3-3.24.52-h75d4e7a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gts-0.7.6-he293c15_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-14.6.0-h8af1aa0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/hicolor-icon-theme-0.17-h8af1aa0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-78.3-py311h3512406_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.22.2-h095d8e5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.2.0-h52b7260_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcups-2.3.3-h4f2b762_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-hfa851ae_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdrm-2.4.129-h5bc82ec_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20260512-pl5321hc48eb74_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-devel-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.8.5-h4154aff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.7.0-hdaad0be_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype-2.14.3-h8af1aa0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype6-2.14.3-h9cc7050_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-16.2.0-h205dda4_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-16.2.0-he9431aa_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgd-2.3.3-h88aa843_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-devel-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.90.1-h3584fe5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglvnd-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-devel-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libharfbuzz-14.6.0-h8f7ccb3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libharfbuzz-devel-14.6.0-h8f7ccb3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.18-h1ea5142_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.2.0-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.3-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpango-1.58.2-h1a0baab_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpango-devel-1.58.2-h12dfbc4_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpciaccess-0.19-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.59-hf9b7768_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/librsvg-2.62.4-ha90e91f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.54.1-h399dd60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-16.2.0-hef695bb_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-16.2.0-hdbbeba8_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.2-h45f9f85_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42.4-hd6fdeab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.6.0-ha2e29f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h3f04742_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.38-h80f16a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxkbcommon-1.13.2-he51e330_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-16-2.15.4-h2e5089f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.15.4-h92b9305_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.6-h2b6f883_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.5-he6ad1d5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pango-1.58.2-ha8baf52_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pcre2-10.47-he574923_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pixman-0.46.4-h7ac5ae9_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h5bc82ec_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.10.22-h4f76b5d_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.3-ha7194a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_hf03c496_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wayland-1.26.0-h637a836_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xkeyboard-config-2.48-h80f16a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libice-1.1.2-h80f16a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libsm-1.2.6-hf23e593_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libx11-1.8.13-h63a1b12_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h5bc82ec_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxcomposite-0.4.7-he30d5cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxcursor-1.2.3-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdamage-1.1.6-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h5bc82ec_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxext-1.3.7-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxfixes-6.0.2-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxi-1.8.3-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxinerama-1.1.6-hfae3067_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrandr-1.5.5-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrender-0.9.12-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxtst-1.2.6-h5bc82ec_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxxf86vm-1.1.7-he30d5cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-xorgproto-2025.1-h29ee22c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda + - pypi: https://files.pythonhosted.org/packages/09/ab/c8f982c4dfaab49200e31820a4ffe6275356f7dcaaa1024735d8f8b4e048/aiobotocore-3.9.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/61/5f/3271cad1b34347e8bf27abee20d501ebdc56cec97e8ae59cd3fabb23d917/aiohttp-3.14.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/e4/ad91d8297638aa2258aad4501c306aca99480dfe76ccd638173fa3702db9/argon2_cffi_bindings-26.1.0-cp310-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/eb/c51d3b54dfaa9267a687359d946dc2a1ca6a27fb4d381773e59b7bd72bf4/botocore-1.43.106-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/02/8ae1b63dbdebb2ebf600523f48b54e9bfb10db5a28551c3432346f49e1dd/cachebox-5.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/52/fc/e518013affcc43c9f919c3ba41bffe9b4ee4aceb0a6462a6243efcca5f2d/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/0e/19/14a8e44cbb2ad36ae62aaa03c5eec4a06a7f7df220ea3776af26927acbe2/coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/26/4a2bad8eb430d8d805a4642c4bff25103a37548d74ab346f8b1e024abcc5/deepdiff-9.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4d/dd/8bc67e7d5ffc1d88aa5af511189dfb76dc4e1e5b808fab186146ef2663e5/executing-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/ed/0d6d0d6467ae3d009fb82fb411867c7fcfeadbdd25602cab0d7a7963f400/faker-40.43.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f8/6a/36f465a1c277131f9569f6a56fe99cf135391b4860b9c4f4b23e5b1cd5df/fonttools-4.65.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1d/05/dc0d54e90af2f192724936799cee990687792ff54a4638d3ae0e0a0145c0/greenlet-3.5.6-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/53/1b/8257699b8456241b8348fe0071c29912aeeaf5d16ef97a45e9c1d3170ca6/hypothesis-6.168.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c0/56/4cc7fc9e9e3f38fd324f24f8afe0ad8bb5fa41283f37f1aaf9de0612c968/ipython-8.39.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/be/c7/71eb0d51cdcbb63b857c931aa4e9218b078b5d9f8ecd383b234f28fe7b0f/jedi-0.20.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/96/26efc04348c0f332b6e6c471dff132bb8b18b2df494a1b65826601674e62/kiwisolver-1.5.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/12/49/b78e214a527ea732033b7f4d37f7afb504d74ba9d134bd47938230dfb8b1/matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/33/51/0de5fc980df34d1ffed9ef68f60ce86c2d4baa3d422598a53f8730c56757/multidict-6.9.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/07/50/b5d688cc9c52d4482f3d5bcab6ce20bc2a74a85d2343841c907444a3be2c/pillow-12.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ac/09/cc33bbd5463749c116b62c204d88bed6c02a6cb901eac7adab0d38651b07/polars-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0d/0d/85e3ed90417996fc09770be91b39979074fe2978fc15b431bf8a9459760d/polars_runtime_32-2.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/d1/7fd3ffb5ca0e669a8fbf55d9fbb17c50ff5f44becfefcab27a9b9b67908f/propcache-0.5.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/ee/29/b41c62219b232c9631db1a8cfb99a93abb9afb8a00291579b830f756ce89/psycopg2_binary-2.9.13-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/34/e138b451fd3970a6eda4599f68ae3b2b32b661bc958de3239d54a0bf6575/pyarrow-25.0.1-cp310-cp310-manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/7e/e72be1be5879001d922ce66d54e940fdf1bf6ce19bd6521e130ad26fe58d/pycryptodome-4.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bf/be/f75637e222085d29a3076edbc5cfd0dd9b77d145e310ff7dc1150f07558a/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/4b/0a906d8184f011ff8dbd4722743783867589b33269d2c5fff238d636fdcb/pymysql-1.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/ef/c66110d46fb800dda0bf33164182dfadabe26a90e4476844d502a23dca8e/pytz-2026.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/9c/15fb18bd708949dc953fe120be8d3c7a1b1b140c2592a9c81ce7961e9ecf/s3fs-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d7/9f/65b06d212a305ac2a1ad6aebf45087c41e36b9416d07c981adb7438d095c/sqlalchemy-2.0.55-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/7e/424aac8b355597835deb333e757a0e94b5ccf38ad00f07fe6ed1f4e17c88/testcontainers-4.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/94/21/1e5995a1c920cce14e4bffae20c665ec10e7ed03ab25e006cd741092b718/tzdata-2026.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/f0/b8ef7758003d66b60f093695831a86dcc726aac01ee6446ffcbda27b61e3/wcwidth-0.9.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/a4/da/a295034b0ba4b4a66ac8c173312214845c406f681d7fbf636b57c6ff8f7f/wrapt-2.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/1d/fc/21d74789198ad68a23a209ab0d73fe5a52d39ebbd9945041e72827acdd77/yarl-1.25.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: ./ + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-51.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.6-h580cee5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.17-hb001647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.90.0-h06d1d6c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-13.1.2-hcd33d8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gts-0.7.6-he42f4ea_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.6.0-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.90.0-hccd281b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.6.0-hcda0f7c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.6.0-hcda0f7c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-hae62183_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpango-1.58.2-hc2ac4f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpango-devel-1.58.2-haa2d88b_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.59-hf5e6511_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.62.4-hc8ac265_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.1-hca69786_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hc6efcc1_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.22-hac0b6dc_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda + - pypi: https://files.pythonhosted.org/packages/09/ab/c8f982c4dfaab49200e31820a4ffe6275356f7dcaaa1024735d8f8b4e048/aiobotocore-3.9.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/dc/b7fefdd7dd64d080da2d69d609344191cc94b0e2eba70200c371014c9b36/aiohttp-3.14.5-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e7/d2/0ae991f1b2181e5be49007c574710a800ad36c2978683addb3e67c474e55/argon2_cffi_bindings-26.1.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/eb/c51d3b54dfaa9267a687359d946dc2a1ca6a27fb4d381773e59b7bd72bf4/botocore-1.43.106-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/98/8d/e0b13d9bfd43f295cce7824ebaac1970f818a7027c16f290de404934cafe/cachebox-5.2.3-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/98/77/46e87bcfc45d25ab4db7cfc9bb544bfa3ffd302289ed31ae93f5433eb899/charset_normalizer-3.5.2-cp310-cp310-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/d1/e1/285727a8a74d48de256e8605413ee4cf82301228253ea58e5ab4270138b0/coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/26/4a2bad8eb430d8d805a4642c4bff25103a37548d74ab346f8b1e024abcc5/deepdiff-9.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4d/dd/8bc67e7d5ffc1d88aa5af511189dfb76dc4e1e5b808fab186146ef2663e5/executing-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/ed/0d6d0d6467ae3d009fb82fb411867c7fcfeadbdd25602cab0d7a7963f400/faker-40.43.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/53/d9/1caaa015dd207da7ccd3feba87289997bd88334ea3e74a367cdc7b0e50a3/fonttools-4.65.0-cp310-cp310-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a1/60/90ccc9e18d831480920dc0f1d33a9af142e796d67dbe6a760e93d0122587/hypothesis-6.168.5-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c0/56/4cc7fc9e9e3f38fd324f24f8afe0ad8bb5fa41283f37f1aaf9de0612c968/ipython-8.39.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/be/c7/71eb0d51cdcbb63b857c931aa4e9218b078b5d9f8ecd383b234f28fe7b0f/jedi-0.20.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/30/ae/30b94088722e7bce8b821cd5ee935a87f80023e7243125f1accf98e39bf7/kiwisolver-1.5.1-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/bb/2f/127081eb83162053ebb9678ceac64220b93a663e0167432566e9c7c82aab/matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e0/89/b845535014a9ad50537ca50204aaf6351c5368822a3acd9da854fb2a5a34/multidict-6.9.1-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6b/ba/3762f376a2948e3036488d773a146e0ae6ecc2ca03ac20e2615bd0b2ba02/pillow-12.3.0-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ac/09/cc33bbd5463749c116b62c204d88bed6c02a6cb901eac7adab0d38651b07/polars-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5f/fd/2237bf53ffaff47cdf1edc6c10587a7a6444d4951150eeb08d84f3493ff8/polars_runtime_32-2.0.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ff/9a/6be90814d8762952594a9e380161802541bff690f3a2e011dcc28ec193ce/propcache-0.5.4-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/27/ef/e6f9280c2327e52f6c139ab549e57a21202b724384d4697dce5794c59f64/psycopg2_binary-2.9.13-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0a/3e/5cd70becb51e1d044c54ba5e627424a6e87df5b98008cbd22cc6abd409ca/pyarrow-25.0.1-cp310-cp310-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/22/5c/a8a903c48ab726e8a72d4ab750e0a7fe0ace0dcd6fb426ce5b004ff618c6/pycryptodome-4.0.1-cp39-abi3-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c1/17/6963f4ee55b7df7658f5c036c0f90baf8cd3d413f5d8b551f227e8467afc/pydantic_core-2.50.1-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/4b/0a906d8184f011ff8dbd4722743783867589b33269d2c5fff238d636fdcb/pymysql-1.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/ef/c66110d46fb800dda0bf33164182dfadabe26a90e4476844d502a23dca8e/pytz-2026.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/9c/15fb18bd708949dc953fe120be8d3c7a1b1b140c2592a9c81ce7961e9ecf/s3fs-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3a/df/221b08eb2c1f421624b92a6cb24fd606268bcf299b96bd73e3ad83951aa0/sqlalchemy-2.0.55-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/7e/424aac8b355597835deb333e757a0e94b5ccf38ad00f07fe6ed1f4e17c88/testcontainers-4.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/94/21/1e5995a1c920cce14e4bffae20c665ec10e7ed03ab25e006cd741092b718/tzdata-2026.5-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/07/cb6940e81134b7ed25fa312ee9ab536a63db0793b149f88a90e603ceace9/wcwidth-0.9.2-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/fe/13/38001295886db51429dd29882c9097fa56ff642f811439ca13998127d8d4/wrapt-2.5.1-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/ae/de/4204e6646e278b1cd4a9cf73ebe8b7cfcf16693f1249b324eb273d67602a/yarl-1.25.1-cp310-cp310-macosx_11_0_arm64.whl + - pypi: ./ + test-py314: + channels: + - url: https://conda.anaconda.org/conda-forge/ + indexes: + - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-51.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-core-2.40.3-h0630a04_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-h04ea711_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.6-h3c89d7e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.17-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.90.0-h3d503d9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/graphviz-13.1.2-h87b6fe6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.6.0-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20260512-pl5321h373387f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.90.0-h569388d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.6.0-h23af247_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.6.0-h23af247_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpango-1.58.2-hce4e988_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpango-devel-1.58.2-h317d9d0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.59-h922cc85_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.14.8-hd51c5da_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.62.4-ha427ee3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.1-h13e7031_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-h2f58602_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.8-hcd007b5_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-h7cc23a3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-h7cc23a3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.6-h7cc23a3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + - pypi: https://files.pythonhosted.org/packages/09/ab/c8f982c4dfaab49200e31820a4ffe6275356f7dcaaa1024735d8f8b4e048/aiobotocore-3.9.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a6/66/55a8904b3a129fafdf94f9cc0a2e4ca09a3c914650be52355db7ad0bbdb6/aiohttp-3.14.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6f/86/5363df11b86d02cf3662208e7406496327649cc90eb365bf6f4e8a54a41f/argon2_cffi_bindings-26.1.0-cp310-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/eb/c51d3b54dfaa9267a687359d946dc2a1ca6a27fb4d381773e59b7bd72bf4/botocore-1.43.106-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/78/c4/c9b3fa764ac5420a9e079ad53fa8840d4a26b74c4ccda56acbef49cf76ff/cachebox-5.2.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/18/24/bad3ac4271589df29cf5ce2f5ae490518a5739358052bd0d61209e6fea54/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/cf/8c/9677dad226e5aaac5b823d5b09d881ebc32ff1648af417597649841e516c/contourpy-1.4.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a8/d4/a306940c81c6ae759e82fff27d20b7fdc6896e422b821f51313cce212b6c/coverage-7.16.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/26/4a2bad8eb430d8d805a4642c4bff25103a37548d74ab346f8b1e024abcc5/deepdiff-9.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4d/dd/8bc67e7d5ffc1d88aa5af511189dfb76dc4e1e5b808fab186146ef2663e5/executing-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/ed/0d6d0d6467ae3d009fb82fb411867c7fcfeadbdd25602cab0d7a7963f400/faker-40.43.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/79/fe/fef04b2cc2930edba11095f9e9b5c2797f8594fc54316195cc39d3c3bc63/fonttools-4.67.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/85/41/4d999c28cad9f236f5a9a155b40c917ea32e3d961915c03b2b3ab8d120f0/hypothesis-6.170.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2d/1e/65b59cf518c106aa755e7f7da3099027738687a862ec785060702a481320/ipython-9.17.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/be/c7/71eb0d51cdcbb63b857c931aa4e9218b078b5d9f8ecd383b234f28fe7b0f/jedi-0.20.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a1/13/08188f0cafa3a800403e4ff62b9aad4e7a17f9c4c7e080dc8f18c64794cf/kiwisolver-1.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/29/5e/52f56f93d5b20815ee0ae352f7a38eff25580edb180b4fa7df87cfeba68f/matplotlib-3.11.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f8/e5/8d54118bc228e64e1087f1729647b93bbea225eda4a3f914663a6f410bca/multidict-6.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/7e/cd/fe58041e9011f307c490e3e17dd48cc516448f7c698a3f2d9d9d65d7e6a8/networkx-3.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/16/d5/928078d2b28f26829b138b4a6c3980045022fb409f570657a224ae60ef4e/numpy-2.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/ba/ffdcb19be4ff6bfe7d969e7cef2c567c633df5a3a1cc1053394ad053bca8/pandas-3.0.6-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1d/f2/1371707ce438ba9a43f9d24089828494ec6f9396e28cd7903aa704dd6306/pillow-13.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ac/09/cc33bbd5463749c116b62c204d88bed6c02a6cb901eac7adab0d38651b07/polars-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/88/e9fecfd49159da92f54ff2445883577a0f1bc195da53ecc9535c458d55dd/polars_runtime_32-2.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/96/d3/e97cd6f5de2176bd90ed4076c7a9b5e09d0f0b9687d00a576507988bb62c/propcache-0.5.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: 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 + - pypi: https://files.pythonhosted.org/packages/91/ad/7f52f92cc65c23778daff7eec4ee2099236694a0a4723a5f180d0708b607/psycopg2_binary-2.9.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/c1/0c1ff38ab7df1b2cf54cf0ad9f19a516c4e416c6c9b4c966cc2c9d587f77/pyarrow-26.0.0-cp314-cp314-manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ca/f6/1d91d5cbe2f3acd0ba6f596d54466ab8703485d4edf84ca28d9507db8d43/pycryptodome-4.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/45/35/38f6d6564fae57d9b12e5676dfa947e0e7d5c46dbeaa7eb3352261d6d299/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/53/f4/b987bf8c51e5b19a95fa66d1ee596074141e085d9c2ddf97920803c7029b/pydantic_settings-2.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/4b/0a906d8184f011ff8dbd4722743783867589b33269d2c5fff238d636fdcb/pymysql-1.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/9c/15fb18bd708949dc953fe120be8d3c7a1b1b140c2592a9c81ce7961e9ecf/s3fs-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/42/9e/fa478482503a69dfb3b0cb82f3f80fe3816026530dd34b5359e2acd9f30d/sqlalchemy-2.1.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/7e/424aac8b355597835deb333e757a0e94b5ccf38ad00f07fe6ed1f4e17c88/testcontainers-4.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/db/6c/f940133c71427c208575910e981942bd78c98b1f7cd0d1425ca4b7457c04/wcwidth-0.9.2-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/aa/a0/e784d7a9fd277a2ee395490ec4df96608b7fe218bb1ef7dced2a1caea490/wrapt-2.5.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/a2/be/415b00c0fe5a0615b062a456b26623d7ec91c2bee20faea1a14045aa0469/yarl-1.25.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: ./ + linux-aarch64: + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-51.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/at-spi2-atk-2.38.0-h1f2db35_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/at-spi2-core-2.40.3-h1f2db35_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/atk-1.0-2.38.0-hedc4a1f_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_10.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cairo-1.18.6-hca320e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/dbus-1.16.2-h71d469a_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/epoxy-1.5.10-he30d5cf_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.18.3-ha4b22b4_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.14.3-h8af1aa0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fribidi-1.0.17-h5bc82ec_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gdk-pixbuf-2.44.8-hb26ce08_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glib-tools-2.90.1-hd8364ca_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/graphite2-1.3.15-h7ac5ae9_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/graphviz-13.1.2-hdb06ba2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gtk3-3.24.52-h75d4e7a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gts-0.7.6-he293c15_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-14.6.0-h8af1aa0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/hicolor-icon-theme-0.17-h8af1aa0_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-78.3-py311h3512406_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.22.2-h095d8e5_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.2.0-h52b7260_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcups-2.3.3-h4f2b762_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-hfa851ae_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdrm-2.4.129-h5bc82ec_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20260512-pl5321hc48eb74_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-devel-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.8.5-h4154aff_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.7.0-hdaad0be_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype-2.14.3-h8af1aa0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype6-2.14.3-h9cc7050_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-16.2.0-h205dda4_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-16.2.0-he9431aa_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgd-2.3.3-h88aa843_12.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-devel-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.90.1-h3584fe5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglvnd-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-devel-1.7.0-hd24410f_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libharfbuzz-14.6.0-h8f7ccb3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libharfbuzz-devel-14.6.0-h8f7ccb3_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.18-h1ea5142_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.2.0-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.3-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libmpdec-4.0.0-he30d5cf_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpango-1.58.2-h1a0baab_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpango-devel-1.58.2-h12dfbc4_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpciaccess-0.19-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.59-hf9b7768_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpython-3.14.8-h150030a_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/librsvg-2.62.4-ha90e91f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.54.1-h399dd60_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-16.2.0-hef695bb_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-16.2.0-hdbbeba8_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.2-h45f9f85_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42.4-hd6fdeab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.6.0-ha2e29f5_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h3f04742_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxkbcommon-1.13.2-he51e330_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-16-2.15.4-h2e5089f_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.15.4-h92b9305_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.6-h2b6f883_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.5-he6ad1d5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pango-1.58.2-ha8baf52_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pcre2-10.47-he574923_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pixman-0.46.4-h7ac5ae9_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h5bc82ec_1004.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.14.8-hbec3b18_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.3-ha7194a6_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_hf03c496_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wayland-1.26.0-h637a836_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xkeyboard-config-2.48-h80f16a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libice-1.1.2-h80f16a2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libsm-1.2.6-hf23e593_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libx11-1.8.13-h63a1b12_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h5bc82ec_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxcomposite-0.4.7-he30d5cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxcursor-1.2.3-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdamage-1.1.6-h86ecc28_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h5bc82ec_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxext-1.3.7-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxfixes-6.0.2-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxi-1.8.3-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxinerama-1.1.6-hfae3067_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrandr-1.5.5-h5bc82ec_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrender-0.9.12-he30d5cf_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxtst-1.2.6-h5bc82ec_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxxf86vm-1.1.7-he30d5cf_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-xorgproto-2025.1-h29ee22c_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda + - pypi: https://files.pythonhosted.org/packages/09/ab/c8f982c4dfaab49200e31820a4ffe6275356f7dcaaa1024735d8f8b4e048/aiobotocore-3.9.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/19/b0/5aa186d56ce2334dabe29b70bd99dc8ae926ee44184c0de64a53d415a4f3/aiohttp-3.14.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/e4/ad91d8297638aa2258aad4501c306aca99480dfe76ccd638173fa3702db9/argon2_cffi_bindings-26.1.0-cp310-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/eb/c51d3b54dfaa9267a687359d946dc2a1ca6a27fb4d381773e59b7bd72bf4/botocore-1.43.106-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/0b/3eedaf9ea4b41c931f4340bfa42056efe2bb5fe3a79649d6c8a1dce585a5/cachebox-5.2.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/7f/72/9c5e7707b57c8ddfa9ddf7b0b1d009d7fbab9e9e887d5b721060f37e307d/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/35/da/5a6562febc994b2c4bf9d01e57a50458ef8a2051bb3e7b35a00333e0fc26/contourpy-1.4.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/b9/a3/d3d99d93b02517087aa05bc0cf2d04d372956b849e5443e059079901429b/coverage-7.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/26/4a2bad8eb430d8d805a4642c4bff25103a37548d74ab346f8b1e024abcc5/deepdiff-9.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4d/dd/8bc67e7d5ffc1d88aa5af511189dfb76dc4e1e5b808fab186146ef2663e5/executing-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/ed/0d6d0d6467ae3d009fb82fb411867c7fcfeadbdd25602cab0d7a7963f400/faker-40.43.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/1f/2c/b7f33fa3bd1e4afdf9bf93b760f22486350eda487ce76c47f5931f868957/fonttools-4.67.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3c/2d/4e06cae907df9b6dfea73f3497ae132b865d2e957a6df242baa4b33ec728/hypothesis-6.170.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2d/1e/65b59cf518c106aa755e7f7da3099027738687a862ec785060702a481320/ipython-9.17.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/be/c7/71eb0d51cdcbb63b857c931aa4e9218b078b5d9f8ecd383b234f28fe7b0f/jedi-0.20.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8a/3e/053bdc3c9abdb8f2606225eda398adca25c0c91ab90add8222a69db65ee0/kiwisolver-1.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/d4/42/81d5cba4bb39b41b0998efc880e129f2d7cdb94256ca49678fdcd9c21be5/matplotlib-3.11.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/97/555aab000e03ecba85c0a1837fd5674d8bf10949490df7800da3a40660ce/multidict-6.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/7e/cd/fe58041e9011f307c490e3e17dd48cc516448f7c698a3f2d9d9d65d7e6a8/networkx-3.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/d6/34b0a2b0741386a63025a65a2c09caaaaaad6d0ca95b66cd65c30dd7fcb5/numpy-2.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/f2/cc5f2adb8d6e86a85d9fb5128f8cf205a61189336f70d1f7faf0d1b53ec9/pandas-3.0.6-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/83/16/b603274617b3e6728da3a0133d4fba1ea8d7fbe0c4ea5fef9cbffa398b8b/pillow-13.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ac/09/cc33bbd5463749c116b62c204d88bed6c02a6cb901eac7adab0d38651b07/polars-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0d/0d/85e3ed90417996fc09770be91b39979074fe2978fc15b431bf8a9459760d/polars_runtime_32-2.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f1/74/70ac8430e28f21e442c7bcb964eb46c4363f6881ade4aa0e978bfd8d503a/propcache-0.5.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: 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 + - pypi: https://files.pythonhosted.org/packages/3d/2a/1a472059b198942d99651656e2bc610575584478bfe68d297ecabbd4887f/psycopg2_binary-2.9.13-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/bc/c90fcbbcf893631e23dab1b0fb3fa29a508a8614326571b03c0894eda00b/pyarrow-26.0.0-cp314-cp314-manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/7e/e72be1be5879001d922ce66d54e940fdf1bf6ce19bd6521e130ad26fe58d/pycryptodome-4.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a1/4e/5eb629f6efc2a27e421d789dd4bfacbb5f6d09c80c28b13d1e87d73163d5/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/53/f4/b987bf8c51e5b19a95fa66d1ee596074141e085d9c2ddf97920803c7029b/pydantic_settings-2.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/4b/0a906d8184f011ff8dbd4722743783867589b33269d2c5fff238d636fdcb/pymysql-1.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/9c/15fb18bd708949dc953fe120be8d3c7a1b1b140c2592a9c81ce7961e9ecf/s3fs-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/a1/01931ba38770f169ce70e4034b0dcf2c1a776fe7d8dde93a34b6355510b1/sqlalchemy-2.1.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/7e/424aac8b355597835deb333e757a0e94b5ccf38ad00f07fe6ed1f4e17c88/testcontainers-4.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/f0/b8ef7758003d66b60f093695831a86dcc726aac01ee6446ffcbda27b61e3/wcwidth-0.9.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/81/56/01ebc86b88056f5782b9d50f962fb398c6b82aa11efc98f50c64896d94e3/wrapt-2.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/e5/5f/ed1538bcd06009fe990d6d283dd7667f639e62a81e35c6d8c6ef6c08fb3c/yarl-1.25.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: ./ + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-51.0-unix_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/atk-1.0-2.38.0-hd03087b_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.6-h580cee5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/epoxy-1.5.10-hc919400_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.17-hb001647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.90.0-h06d1d6c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphviz-13.1.2-hcd33d8b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gts-0.7.6-he42f4ea_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.6.0-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.90.0-hccd281b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.6.0-hcda0f7c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.6.0-hcda0f7c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-hae62183_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpango-1.58.2-hc2ac4f2_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpango-devel-1.58.2-haa2d88b_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.59-hf5e6511_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.8-h281bc5c_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.62.4-hc8ac265_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.1-hca69786_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hc6efcc1_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.8-h0ae1c2c_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda + - pypi: https://files.pythonhosted.org/packages/09/ab/c8f982c4dfaab49200e31820a4ffe6275356f7dcaaa1024735d8f8b4e048/aiobotocore-3.9.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ce/b5/05b8ac39a76ff4bca89f42a4c2471560c71f894ff6e4bc16158c951874e3/aiohttp-3.14.5-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e7/d2/0ae991f1b2181e5be49007c574710a800ad36c2978683addb3e67c474e55/argon2_cffi_bindings-26.1.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a8/eb/c51d3b54dfaa9267a687359d946dc2a1ca6a27fb4d381773e59b7bd72bf4/botocore-1.43.106-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/fc/62/33aaade81b181d5191cc39c867c297aa7c65f3191aa9749bf99b77496b88/cachebox-5.2.3-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/7c/8a/40db9aa9f5907bb0e6f8b6d64064bf8852fb33d4b813ff9414911df7647c/charset_normalizer-3.5.2-cp314-cp314-macosx_10_15_universal2.whl + - pypi: https://files.pythonhosted.org/packages/04/5a/513484208742f65af2648c519f0b7ab034616e183c4403538024c136a1ab/contourpy-1.4.0-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/75/9e/e3785ba3ecba2bd11efc74bfe2801ca4b78c4480b15a375648d809a59da3/coverage-7.16.2-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/c7/26/4a2bad8eb430d8d805a4642c4bff25103a37548d74ab346f8b1e024abcc5/deepdiff-9.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/4d/dd/8bc67e7d5ffc1d88aa5af511189dfb76dc4e1e5b808fab186146ef2663e5/executing-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/81/ed/0d6d0d6467ae3d009fb82fb411867c7fcfeadbdd25602cab0d7a7963f400/faker-40.43.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/25/5b/c418f48918e40ef8c3f0f555567fe013c0c8058a8afa8040d6baeec80683/fonttools-4.67.0-cp314-cp314-macosx_10_15_universal2.whl + - pypi: https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0a/c1/f6441724a57c5b92f71451b243b682c016cbbfc728ba792154ecd7d00c6d/hypothesis-6.170.1-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/2d/1e/65b59cf518c106aa755e7f7da3099027738687a862ec785060702a481320/ipython-9.17.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/be/c7/71eb0d51cdcbb63b857c931aa4e9218b078b5d9f8ecd383b234f28fe7b0f/jedi-0.20.1-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e1/f4/b3007a3ed5c9be73f81161140684cf7d9bdb9c4b632f5f484d2a1c713fb9/kiwisolver-1.5.1-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/4c/a0/bba05f0a25bfeb0475c2f2a87958b31103aa6b7a5386017afb1a96212825/matplotlib-3.11.2-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f9/b4/8e6d950f02ca6ecc00ecf671f2ddb5dab7017671a8d197326f75621f5786/multidict-6.9.1-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/7e/cd/fe58041e9011f307c490e3e17dd48cc516448f7c698a3f2d9d9d65d7e6a8/networkx-3.7-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/f3/49/fee7587c33ee35f7977f9051d7f2023d4e7246d62710c80f20c2361ea232/numpy-2.5.4-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/35/61/47ae13476995cc8a40cd609e93e7cf11f273d8692925c2903cb6d38aa0d1/pandas-3.0.6-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b5/f4/0bf6f1ac9ba7ee2ffe9914aeb410a1035a0c1a3c42f122388d7fc950bc4b/pillow-13.0.0-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ac/09/cc33bbd5463749c116b62c204d88bed6c02a6cb901eac7adab0d38651b07/polars-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5f/fd/2237bf53ffaff47cdf1edc6c10587a7a6444d4951150eeb08d84f3493ff8/polars_runtime_32-2.0.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e2/22/b533b493d7025456f44518b33e53e000021a20fe7c27b88cf3d341df7186/propcache-0.5.4-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/0f/f4/e3a789de34c9ac25d20b25c2be583da16394a2ba0926da1c863653831f41/psycopg2_binary-2.9.13-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/8c/32/01858422a37f083911c2bb4d15cc32c5eeaa9d9b2bf5ddedee995a7146a6/pyarrow-26.0.0-cp314-cp314-macosx_12_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/22/5c/a8a903c48ab726e8a72d4ab750e0a7fe0ace0dcd6fb426ce5b004ff618c6/pycryptodome-4.0.1-cp39-abi3-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/94/40/15c06410c9b7b8da5805d27b64e09bd3f900e986728106db2689dbc51513/pydantic_core-2.50.1-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/53/f4/b987bf8c51e5b19a95fa66d1ee596074141e085d9c2ddf97920803c7029b/pydantic_settings-2.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/4b/0a906d8184f011ff8dbd4722743783867589b33269d2c5fff238d636fdcb/pymysql-1.2.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bc/9c/15fb18bd708949dc953fe120be8d3c7a1b1b140c2592a9c81ce7961e9ecf/s3fs-2026.9.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/01/b0/59b26375cd618e861a9295d855b866ca85be654d20ed47e494e33c3bf26e/sqlalchemy-2.1.5-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/00/7e/424aac8b355597835deb333e757a0e94b5ccf38ad00f07fe6ed1f4e17c88/testcontainers-4.15.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a0/07/cb6940e81134b7ed25fa312ee9ab536a63db0793b149f88a90e603ceace9/wcwidth-0.9.2-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/93/39/cca8afb80dbb9fce6103e59db507a9415291c4dbe97ea055875ff62901fb/wrapt-2.5.1-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/6e/bb/dda344765ffd3430afe1a1c66c866a57fae67786537d4f14607df6505ac1/yarl-1.25.1-cp314-cp314-macosx_11_0_arm64.whl + - pypi: ./ packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 @@ -1392,6 +2500,20 @@ packages: purls: [] size: 2562 timestamp: 1578324546067 +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + build_number: 20 + sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 + md5: a9f577daf3de00bca7c3c76c0ecbd1de + depends: + - __glibc >=2.17,<3.0.a0 + - libgomp >=7.5.0 + constrains: + - openmp_impl <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 28948 + timestamp: 1770939786096 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 build_number: 16 sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 @@ -1406,6 +2528,19 @@ packages: purls: [] size: 23621 timestamp: 1650670423406 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-20_gnu.conda + build_number: 20 + sha256: a2527b1d81792a0ccd2c05850960df119c2b6d8f5fdec97f2db7d25dc23b1068 + md5: 468fd3bb9e1f671d36c2cbc677e56f1d + depends: + - libgomp >=7.5.0 + constrains: + - openmp_impl <0.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 28926 + timestamp: 1770939656741 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2 build_number: 16 sha256: 3702bef2f0a4d38bd8288bbe54aace623602a1343c2cfbefd3fa188e015bebf0 @@ -1443,6 +2578,17 @@ packages: purls: [] size: 631452 timestamp: 1758743294412 +- conda: https://conda.anaconda.org/conda-forge/noarch/adwaita-icon-theme-51.0-unix_0.conda + sha256: def01b148aa54d4de29b7d4bf58d941c8699582201db14cf78c6c89fa1666434 + md5: 6f62266fb2bfc35a5071cfa1d6ffdcc9 + depends: + - __unix + - hicolor-icon-theme + - librsvg + license: LGPL-3.0-or-later OR CC-BY-SA-3.0 + purls: [] + size: 687517 + timestamp: 1790100765464 - pypi: https://files.pythonhosted.org/packages/16/54/a295bd8d7ac900c339b2c7024ed0ff9538afb60e92eb0979b8bb49deb20e/aiobotocore-3.3.0-py3-none-any.whl name: aiobotocore version: 3.3.0 @@ -1458,11 +2604,34 @@ packages: - wrapt>=1.10.10,<3.0.0 - httpx>=0.25.1,<0.29 ; extra == 'httpx' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/09/ab/c8f982c4dfaab49200e31820a4ffe6275356f7dcaaa1024735d8f8b4e048/aiobotocore-3.9.2-py3-none-any.whl + name: aiobotocore + version: 3.9.2 + sha256: 363b4892423b272eb84afbb58000da0be6ccd0cf8a68b98438c957d05b841efc + requires_dist: + - aiohttp>=3.14.0,<4.0.0 + - aioitertools>=0.5.1,<1.0.0 + - botocore>=1.43.101,<1.43.107 + - jmespath>=0.7.1,<2.0.0 + - multidict>=6.0.0,<7.0.0 + - python-dateutil>=2.1,<3.0.0 + - typing-extensions>=4.14.0,<5.0.0 ; python_full_version < '3.11' + - wrapt>=1.10.10,<3.0.0 + - anyio>=4.11.0,<5 ; extra == 'httpx' + - httpx>=0.25.1,<0.29 ; extra == 'httpx' + - anyio>=4.11.0,<5 ; extra == 'httpx2' + - httpx2>=2.0,<3.0.0 ; extra == 'httpx2' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl name: aiohappyeyeballs version: 2.6.1 sha256: f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8 requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl + name: aiohappyeyeballs + version: 2.7.1 + sha256: 9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl name: aiohttp version: 3.13.3 @@ -1517,32 +2686,151 @@ packages: - brotlicffi>=1.2 ; platform_python_implementation != 'CPython' and extra == 'speedups' - backports-zstd ; python_full_version < '3.14' and platform_python_implementation == 'CPython' and extra == 'speedups' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - name: aioitertools - version: 0.13.0 - sha256: 0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be - requires_dist: - - typing-extensions>=4.0 ; python_full_version < '3.10' - requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl - name: aiosignal - version: 1.4.0 - sha256: 053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e - requires_dist: - - frozenlist>=1.1.0 - - typing-extensions>=4.2 ; python_full_version < '3.13' - requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - name: annotated-types - version: 0.7.0 - sha256: 1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 +- pypi: https://files.pythonhosted.org/packages/19/b0/5aa186d56ce2334dabe29b70bd99dc8ae926ee44184c0de64a53d415a4f3/aiohttp-3.14.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: aiohttp + version: 3.14.5 + sha256: 1eb8167961ec4dfcc8cb9dd50bd0ee72519f7ef496be95203e49e27b01618382 requires_dist: - - typing-extensions>=4.0.0 ; python_full_version < '3.9' - requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl - name: argon2-cffi - version: 25.1.0 - sha256: fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741 + - aiohappyeyeballs>=2.5.0 + - aiosignal>=1.4.0 + - async-timeout>=4.0,<6.0 ; python_full_version < '3.11' + - attrs>=17.3.0 + - frozenlist>=1.1.1 + - multidict>=4.5,<8.0 + - propcache>=0.2.0 + - typing-extensions>=4.4 ; python_full_version < '3.13' + - yarl>=1.25.1,<2.0 + - aiodns>=3.3.0 ; sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotli>=1.2 ; platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotlicffi>=1.2 ; platform_python_implementation != 'CPython' and extra == 'speedups' + - backports-zstd ; python_full_version < '3.14' and platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/32/dc/b7fefdd7dd64d080da2d69d609344191cc94b0e2eba70200c371014c9b36/aiohttp-3.14.5-cp310-cp310-macosx_11_0_arm64.whl + name: aiohttp + version: 3.14.5 + sha256: 330900acd0dc4cb8b27f9c127fbaad770964845338493e7906ae3822e82dbf8d + requires_dist: + - aiohappyeyeballs>=2.5.0 + - aiosignal>=1.4.0 + - async-timeout>=4.0,<6.0 ; python_full_version < '3.11' + - attrs>=17.3.0 + - frozenlist>=1.1.1 + - multidict>=4.5,<8.0 + - propcache>=0.2.0 + - typing-extensions>=4.4 ; python_full_version < '3.13' + - yarl>=1.25.1,<2.0 + - aiodns>=3.3.0 ; sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotli>=1.2 ; platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotlicffi>=1.2 ; platform_python_implementation != 'CPython' and extra == 'speedups' + - backports-zstd ; python_full_version < '3.14' and platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/5d/38/01f5fc732d5373d6c099db0343279cd4e6607287d336bab6ff6ae9085a0d/aiohttp-3.14.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: aiohttp + version: 3.14.5 + sha256: f375db73a39f5cf83696d500e21a67f418dc9a988955756f254be8f03b7b3651 + requires_dist: + - aiohappyeyeballs>=2.5.0 + - aiosignal>=1.4.0 + - async-timeout>=4.0,<6.0 ; python_full_version < '3.11' + - attrs>=17.3.0 + - frozenlist>=1.1.1 + - multidict>=4.5,<8.0 + - propcache>=0.2.0 + - typing-extensions>=4.4 ; python_full_version < '3.13' + - yarl>=1.25.1,<2.0 + - aiodns>=3.3.0 ; sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotli>=1.2 ; platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotlicffi>=1.2 ; platform_python_implementation != 'CPython' and extra == 'speedups' + - backports-zstd ; python_full_version < '3.14' and platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/61/5f/3271cad1b34347e8bf27abee20d501ebdc56cec97e8ae59cd3fabb23d917/aiohttp-3.14.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: aiohttp + version: 3.14.5 + sha256: 0684952aeae1f5dbfe02d46039338513b94009baecd15d8e4098a357c4c4a2a6 + requires_dist: + - aiohappyeyeballs>=2.5.0 + - aiosignal>=1.4.0 + - async-timeout>=4.0,<6.0 ; python_full_version < '3.11' + - attrs>=17.3.0 + - frozenlist>=1.1.1 + - multidict>=4.5,<8.0 + - propcache>=0.2.0 + - typing-extensions>=4.4 ; python_full_version < '3.13' + - yarl>=1.25.1,<2.0 + - aiodns>=3.3.0 ; sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotli>=1.2 ; platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotlicffi>=1.2 ; platform_python_implementation != 'CPython' and extra == 'speedups' + - backports-zstd ; python_full_version < '3.14' and platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a6/66/55a8904b3a129fafdf94f9cc0a2e4ca09a3c914650be52355db7ad0bbdb6/aiohttp-3.14.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: aiohttp + version: 3.14.5 + sha256: 94684b879ac1d71e4238850c99b62dc1b28d9086b156a2555f082010b85a865c + requires_dist: + - aiohappyeyeballs>=2.5.0 + - aiosignal>=1.4.0 + - async-timeout>=4.0,<6.0 ; python_full_version < '3.11' + - attrs>=17.3.0 + - frozenlist>=1.1.1 + - multidict>=4.5,<8.0 + - propcache>=0.2.0 + - typing-extensions>=4.4 ; python_full_version < '3.13' + - yarl>=1.25.1,<2.0 + - aiodns>=3.3.0 ; sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotli>=1.2 ; platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotlicffi>=1.2 ; platform_python_implementation != 'CPython' and extra == 'speedups' + - backports-zstd ; python_full_version < '3.14' and platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ce/b5/05b8ac39a76ff4bca89f42a4c2471560c71f894ff6e4bc16158c951874e3/aiohttp-3.14.5-cp314-cp314-macosx_11_0_arm64.whl + name: aiohttp + version: 3.14.5 + sha256: c32e26310cc10e547f53cd13d39a369034f69dcb7d749d5cb0e5f67bc196b6ba + requires_dist: + - aiohappyeyeballs>=2.5.0 + - aiosignal>=1.4.0 + - async-timeout>=4.0,<6.0 ; python_full_version < '3.11' + - attrs>=17.3.0 + - frozenlist>=1.1.1 + - multidict>=4.5,<8.0 + - propcache>=0.2.0 + - typing-extensions>=4.4 ; python_full_version < '3.13' + - yarl>=1.25.1,<2.0 + - aiodns>=3.3.0 ; sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotli>=1.2 ; platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + - brotlicffi>=1.2 ; platform_python_implementation != 'CPython' and extra == 'speedups' + - backports-zstd ; python_full_version < '3.14' and platform_python_implementation == 'CPython' and sys_platform != 'android' and sys_platform != 'ios' and extra == 'speedups' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl + name: aioitertools + version: 0.13.0 + sha256: 0be0292b856f08dfac90e31f4739432f4cb6d7520ab9eb73e143f4f2fa5259be + requires_dist: + - typing-extensions>=4.0 ; python_full_version < '3.10' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl + name: aiosignal + version: 1.4.0 + sha256: 053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e + requires_dist: + - frozenlist>=1.1.0 + - typing-extensions>=4.2 ; python_full_version < '3.13' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl + name: annotated-types + version: 0.7.0 + sha256: 1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 + requires_dist: + - typing-extensions>=4.0.0 ; python_full_version < '3.9' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl + name: annotated-types + version: 0.8.0 + sha256: f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl + name: argon2-cffi + version: 25.1.0 + sha256: fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741 requires_dist: - argon2-cffi-bindings requires_python: '>=3.8' @@ -1570,6 +2858,30 @@ packages: - cffi>=1.0.1 ; python_full_version < '3.14' - cffi>=2.0.0b1 ; python_full_version >= '3.14' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/6f/86/5363df11b86d02cf3662208e7406496327649cc90eb365bf6f4e8a54a41f/argon2_cffi_bindings-26.1.0-cp310-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl + name: argon2-cffi-bindings + version: 26.1.0 + sha256: 27f1821903e2ceadcb88ec2b45ef190897b7682449c772f4d9b53e42c520cf29 + requires_dist: + - cffi>=1.0.1 ; python_full_version < '3.14' + - cffi>=2 ; python_full_version >= '3.14' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/7e/e4/ad91d8297638aa2258aad4501c306aca99480dfe76ccd638173fa3702db9/argon2_cffi_bindings-26.1.0-cp310-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl + name: argon2-cffi-bindings + version: 26.1.0 + sha256: 78de2d65e0b9ea7ce9d1b1c3e87297b2d7305a02c266ee2a2d6910daddd7ee69 + requires_dist: + - cffi>=1.0.1 ; python_full_version < '3.14' + - cffi>=2 ; python_full_version >= '3.14' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e7/d2/0ae991f1b2181e5be49007c574710a800ad36c2978683addb3e67c474e55/argon2_cffi_bindings-26.1.0-cp310-abi3-macosx_11_0_arm64.whl + name: argon2-cffi-bindings + version: 26.1.0 + sha256: 21ca0396fe5ec995dd54431c32698189666f9224810acfa752e50d2bd94d9df2 + requires_dist: + - cffi>=1.0.1 ; python_full_version < '3.14' + - cffi>=2 ; python_full_version >= '3.14' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl name: asttokens version: 3.0.0 @@ -1581,6 +2893,22 @@ packages: - pytest-cov ; extra == 'test' - pytest-xdist ; extra == 'test' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl + name: asttokens + version: 3.0.2 + sha256: 9da13157f5b28becde0bd374fc677dcd3c290614264eff096f167c469cd9f933 + requires_dist: + - astroid>=2,<5 ; extra == 'astroid' + - astroid>=2,<5 ; extra == 'test' + - pytest<9.0 ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-xdist ; extra == 'test' + requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl + name: async-timeout + version: 5.0.1 + sha256: 39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c + requires_python: '>=3.8' - conda: https://conda.anaconda.org/conda-forge/linux-64/at-spi2-atk-2.38.0-h0630a04_3.tar.bz2 sha256: 26ab9386e80bf196e51ebe005da77d57decf6d989b4f34d96130560bc133479c md5: 6b889f174df1e0f816276ae69281af4d @@ -1698,6 +3026,27 @@ packages: - urllib3>=1.25.4,!=2.2.0,<3 ; python_full_version >= '3.10' - awscrt==0.31.2 ; extra == 'crt' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/a8/eb/c51d3b54dfaa9267a687359d946dc2a1ca6a27fb4d381773e59b7bd72bf4/botocore-1.43.106-py3-none-any.whl + name: botocore + version: 1.43.106 + sha256: c1fb8818f9957cb5037f73db34ac1a12ba43562e31d6e7f9cb7e5fc64e1dba78 + requires_dist: + - jmespath>=0.7.1,<2.0.0 + - python-dateutil>=2.1,<3.0.0 + - urllib3>=1.25.4,!=2.2.0,<3 + - awscrt==0.36.0 ; extra == 'crt' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_10.conda + sha256: 1a0d382c515ebf55f8ee1f38c8b81bc95af5c2acc42ad53b66bc5df932032f96 + md5: e675fabcf81499adc7edf58124fb1e01 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 257808 + timestamp: 1785906269155 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda sha256: c30daba32ddebbb7ded490f0e371eae90f51e72db620554089103b4a6934b0d5 md5: 51a19bba1b8ebfb60df25cde030b7ebc @@ -1709,6 +3058,16 @@ packages: purls: [] size: 260341 timestamp: 1757437258798 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_10.conda + sha256: 24eacc8a20fd7c4616566178562bef7f9344eb4a8700cfc3180fa75a6ff9d39f + md5: fd544ef1c672d645bf78e5819cbb8f91 + depends: + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 194694 + timestamp: 1785906301397 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h4777abc_8.conda sha256: d2a296aa0b5f38ed9c264def6cf775c0ccb0f110ae156fcde322f3eccebf2e01 md5: 2921ac0b541bf37c69e66bd6d9a43bca @@ -1719,6 +3078,16 @@ packages: purls: [] size: 192536 timestamp: 1757437302703 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h4e30115_10.conda + sha256: 8ec22f0ba25cbfc2e64d70cf29459eccd7ffdf6436f6a6ff15bbfef799f7d4f6 + md5: b50612e7d190b8061ab4e7dc119cf4d5 + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + purls: [] + size: 124965 + timestamp: 1785906749812 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda sha256: b456200636bd5fecb2bec63f7e0985ad2097cf1b83d60ce0b6968dffa6d02aa1 md5: 58fd217444c2a5701a44244faf518206 @@ -1747,6 +3116,45 @@ packages: purls: [] size: 154402 timestamp: 1754210968730 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.7.22-hbd8a1cb_0.conda + sha256: 0a0544cf95f64394fe4959286f5c71f5444ad58feb0602e53becb27448d24da6 + md5: 0f51e2391ade309db462a55611263e9c + depends: + - __unix + license: ISC + purls: [] + size: 131780 + timestamp: 1784754889428 +- pypi: https://files.pythonhosted.org/packages/11/2f/5abff74666f8388d2c9516c265f99c33484c827f7fcb3cd703c2f3cbb17e/cachebox-5.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: cachebox + version: 5.2.3 + sha256: ad16d733219f4cab3eec6533af30ab7b9c919c6e3e22ad1ef4eb82629a62edef + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/78/c4/c9b3fa764ac5420a9e079ad53fa8840d4a26b74c4ccda56acbef49cf76ff/cachebox-5.2.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: cachebox + version: 5.2.3 + sha256: 1f4d2a80a5cd3380739c67f7d89e596634f5897b8d5a4a3dc1598312cb077535 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/98/8d/e0b13d9bfd43f295cce7824ebaac1970f818a7027c16f290de404934cafe/cachebox-5.2.3-cp310-cp310-macosx_11_0_arm64.whl + name: cachebox + version: 5.2.3 + sha256: e7f33d24e90dc8aa26762e25898c91a1223b66685420a28a3628fa2e006924f5 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/9e/0b/3eedaf9ea4b41c931f4340bfa42056efe2bb5fe3a79649d6c8a1dce585a5/cachebox-5.2.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: cachebox + version: 5.2.3 + sha256: 7df1735ca778480d51b8232fed397ffe3935158f20d34fb1c5ed171b53d5a6e2 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/bc/02/8ae1b63dbdebb2ebf600523f48b54e9bfb10db5a28551c3432346f49e1dd/cachebox-5.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: cachebox + version: 5.2.3 + sha256: 56cb03ec6289a2ac5daf7422d755683324f02d821bfa796087100df2a7ebd5de + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/fc/62/33aaade81b181d5191cc39c867c297aa7c65f3191aa9749bf99b77496b88/cachebox-5.2.3-cp314-cp314-macosx_11_0_arm64.whl + name: cachebox + version: 5.2.3 + sha256: dc6315902f2ef4afbf10bc8e08c54ff34de5ce124546b8e0016c9b0d327be21e + requires_python: '>=3.9' - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda sha256: 3bd6a391ad60e471de76c0e9db34986c4b5058587fbf2efa5a7f54645e28c2c7 md5: 09262e66b19567aff4f592fb53b28760 @@ -1773,6 +3181,33 @@ packages: purls: [] size: 978114 timestamp: 1741554591855 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.6-h3c89d7e_0.conda + sha256: 5d0efc6f9981468aca72897460d9b2d17b74dff72787f20a9d2330e214064515 + md5: 7920701f9abcb488411495742cf365a7 + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - icu >=78.3,<79.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libglib >=2.90.0,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libstdcxx >=15 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - pixman >=0.46.4,<1.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: LGPL-2.1-only or MPL-1.1 + purls: [] + size: 1007110 + timestamp: 1790045358757 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cairo-1.18.4-h83712da_0.conda sha256: 37cfff940d2d02259afdab75eb2dbac42cf830adadee78d3733d160a1de2cc66 md5: cd55953a67ec727db5dc32b167201aa6 @@ -1798,6 +3233,32 @@ packages: purls: [] size: 966667 timestamp: 1741554768968 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/cairo-1.18.6-hca320e2_0.conda + sha256: 8cb719a45d778510fe5636025fbeea72c3f0776d33ea914223d2cfff32bd6b40 + md5: c0ee439454f8af28da76d30564c378cc + depends: + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - icu >=78.3,<79.0a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libglib >=2.90.0,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libstdcxx >=15 + - libxcb >=1.17.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - pixman >=0.46.4,<1.0a0 + - xorg-libice >=1.1.2,<2.0a0 + - xorg-libsm >=1.2.6,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: LGPL-2.1-only or MPL-1.1 + purls: [] + size: 977435 + timestamp: 1790045314596 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.4-h6a3b0d2_0.conda sha256: 00439d69bdd94eaf51656fdf479e0c853278439d22ae151cabf40eb17399d95f md5: 38f6df8bc8c668417b904369a01ba2e2 @@ -1817,6 +3278,26 @@ packages: purls: [] size: 896173 timestamp: 1741554795915 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.6-h580cee5_0.conda + sha256: 32ad9b731d0b4c6dd82061201206164b07bd695bc695e5eb3addc0087ed54cca + md5: 0d25672f63cd1c9b08653a0becfbf31a + depends: + - __osx >=11.0 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - icu >=78.3,<79.0a0 + - libcxx >=21 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.90.0,<3.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + - pixman >=0.46.4,<1.0a0 + license: LGPL-2.1-only or MPL-1.1 + purls: [] + size: 844614 + timestamp: 1790045326889 - pypi: https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl name: certifi version: 2025.8.3 @@ -1827,6 +3308,11 @@ packages: version: 2025.10.5 sha256: 0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl + name: certifi + version: 2026.7.22 + sha256: 62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl name: cffi version: 2.0.0 @@ -1848,6 +3334,48 @@ packages: requires_dist: - pycparser ; implementation_name != 'PyPy' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + name: cffi + version: 2.1.1 + sha256: 58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1 + requires_dist: + - pycparser ; implementation_name != 'PyPy' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: cffi + version: 2.1.1 + sha256: 194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6 + requires_dist: + - pycparser ; implementation_name != 'PyPy' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl + name: cffi + version: 2.1.1 + sha256: ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b + requires_dist: + - pycparser ; implementation_name != 'PyPy' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + name: cffi + version: 2.1.1 + sha256: 5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9 + requires_dist: + - pycparser ; implementation_name != 'PyPy' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl + name: cffi + version: 2.1.1 + sha256: 661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72 + requires_dist: + - pycparser ; implementation_name != 'PyPy' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: cffi + version: 2.1.1 + sha256: b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96 + requires_dist: + - pycparser ; implementation_name != 'PyPy' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl name: cfgv version: 3.4.0 @@ -1868,6 +3396,36 @@ packages: version: 3.4.4 sha256: e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/18/24/bad3ac4271589df29cf5ce2f5ae490518a5739358052bd0d61209e6fea54/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: charset-normalizer + version: 3.5.2 + sha256: 34276fd796040bf0993ab33a369aa572e6979c7aab225a88893667ad8eac8f7a + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/52/fc/e518013affcc43c9f919c3ba41bffe9b4ee4aceb0a6462a6243efcca5f2d/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: charset-normalizer + version: 3.5.2 + sha256: 9373ad13ef0d2c0fb761e04e55bfdee5a08b52cef2c882c8fbe9935b1517152e + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/7c/8a/40db9aa9f5907bb0e6f8b6d64064bf8852fb33d4b813ff9414911df7647c/charset_normalizer-3.5.2-cp314-cp314-macosx_10_15_universal2.whl + name: charset-normalizer + version: 3.5.2 + sha256: fcff63213e8e6e47770541a4607175404f47cbb3ebea7b6058cc82d524a0e424 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/7f/72/9c5e7707b57c8ddfa9ddf7b0b1d009d7fbab9e9e887d5b721060f37e307d/charset_normalizer-3.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: charset-normalizer + version: 3.5.2 + sha256: 8d86d6fc60743dc916eb79e2eb1ec4818e21e427731543af40a3021851174a13 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/98/77/46e87bcfc45d25ab4db7cfc9bb544bfa3ffd302289ed31ae93f5433eb899/charset_normalizer-3.5.2-cp310-cp310-macosx_10_9_universal2.whl + name: charset-normalizer + version: 3.5.2 + sha256: 195c26fb65950f8fce54e26349852b7bdd7c5f120aeefbcc440b8a20faaed4a3 + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/c4/9f/9f52d2886d52645987d603425482c63c5045a3005db1354a7097e5ed1ae9/charset_normalizer-3.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: charset-normalizer + version: 3.5.2 + sha256: 8a893cc101149f80a653f82062ebc95b34525a2614382e1da5458fe7c6997249 + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/20/01/b394922252051e97aab231d416c86da3d8a6d781eeadcdca1082867de64e/codespell-2.4.1-py3-none-any.whl name: codespell version: 2.4.1 @@ -1891,6 +3449,81 @@ packages: - pytest-cov ; extra == 'types' - pytest-dependency ; extra == 'types' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl + name: contourpy + version: 1.3.2 + sha256: dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989 + requires_dist: + - numpy>=1.23 + - furo ; extra == 'docs' + - sphinx>=7.2 ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - bokeh ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==1.15.0 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - pytest-rerunfailures ; extra == 'test-no-images' + - pytest-xdist ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: contourpy + version: 1.3.2 + sha256: ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631 + requires_dist: + - numpy>=1.23 + - furo ; extra == 'docs' + - sphinx>=7.2 ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - bokeh ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==1.15.0 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - pytest-rerunfailures ; extra == 'test-no-images' + - pytest-xdist ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: contourpy + version: 1.3.2 + sha256: 9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d + requires_dist: + - numpy>=1.23 + - furo ; extra == 'docs' + - sphinx>=7.2 ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - bokeh ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==1.15.0 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - pytest-rerunfailures ; extra == 'test-no-images' + - pytest-xdist ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.10' - pypi: 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 name: contourpy version: 1.3.3 @@ -1966,10 +3599,91 @@ packages: - pytest-xdist ; extra == 'test-no-images' - wurlitzer ; extra == 'test-no-images' requires_python: '>=3.11' -- pypi: https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - name: coverage - version: 7.10.6 - sha256: 0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27 +- pypi: https://files.pythonhosted.org/packages/04/5a/513484208742f65af2648c519f0b7ab034616e183c4403538024c136a1ab/contourpy-1.4.0-cp314-cp314-macosx_11_0_arm64.whl + name: contourpy + version: 1.4.0 + sha256: 072a702e2e178f4fcf96f04775d0c059e4c917925abf0d3208ebb6865df3c23d + requires_dist: + - numpy>=2.0 + - myst-parser ; extra == 'docs' + - sphinx ; extra == 'docs' + - sphinx-book-theme ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - bokeh ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==2.1.0 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pre-commit ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - pytest-rerunfailures<16 ; extra == 'test-no-images' + - pytest-xdist ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/35/da/5a6562febc994b2c4bf9d01e57a50458ef8a2051bb3e7b35a00333e0fc26/contourpy-1.4.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl + name: contourpy + version: 1.4.0 + sha256: 404dbcd9233513dfd1323b66ea593fef90e899f658f6b8a9ed8e93bd0ca669db + requires_dist: + - numpy>=2.0 + - myst-parser ; extra == 'docs' + - sphinx ; extra == 'docs' + - sphinx-book-theme ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - bokeh ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==2.1.0 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pre-commit ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - pytest-rerunfailures<16 ; extra == 'test-no-images' + - pytest-xdist ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/cf/8c/9677dad226e5aaac5b823d5b09d881ebc32ff1648af417597649841e516c/contourpy-1.4.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: contourpy + version: 1.4.0 + sha256: e439ab450c93455feb0218532ded3e946ec7a9b5f459069f122cfa44b89229f6 + requires_dist: + - numpy>=2.0 + - myst-parser ; extra == 'docs' + - sphinx ; extra == 'docs' + - sphinx-book-theme ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - bokeh ; extra == 'bokeh' + - selenium ; extra == 'bokeh' + - contourpy[bokeh,docs] ; extra == 'mypy' + - bokeh ; extra == 'mypy' + - docutils-stubs ; extra == 'mypy' + - mypy==2.1.0 ; extra == 'mypy' + - types-pillow ; extra == 'mypy' + - contourpy[test-no-images] ; extra == 'test' + - matplotlib ; extra == 'test' + - pre-commit ; extra == 'test' + - pillow ; extra == 'test' + - pytest ; extra == 'test-no-images' + - pytest-cov ; extra == 'test-no-images' + - pytest-rerunfailures<16 ; extra == 'test-no-images' + - pytest-xdist ; extra == 'test-no-images' + - wurlitzer ; extra == 'test-no-images' + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/73/dd/508420fb47d09d904d962f123221bc249f64b5e56aa93d5f5f7603be475f/coverage-7.10.6-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: coverage + version: 7.10.6 + sha256: 0f3f56e4cb573755e96a16501a98bf211f100463d70275759e73f3cbc00d4f27 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.9' @@ -1987,6 +3701,48 @@ packages: requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/0e/19/14a8e44cbb2ad36ae62aaa03c5eec4a06a7f7df220ea3776af26927acbe2/coverage-7.16.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: coverage + version: 7.16.2 + sha256: 9fd670ac43b709c575aefc25bf52d8a598a3bc5017bddfd0a179152ab06a2deb + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/29/4e/e1d38d27817d91776ca543d01276a67eace3df675a673cac32b12c167d58/coverage-7.16.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: coverage + version: 7.16.2 + sha256: 191803c4996b499fcd78c2ad5e5f767dcc53cb4dc6de6d6a741b443a1821ef02 + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/75/9e/e3785ba3ecba2bd11efc74bfe2801ca4b78c4480b15a375648d809a59da3/coverage-7.16.2-cp314-cp314-macosx_11_0_arm64.whl + name: coverage + version: 7.16.2 + sha256: a2fac6895eb299a2e52d7bbb8fb3903502b9da8d3f5309ceb16ec40c646b58ee + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a8/d4/a306940c81c6ae759e82fff27d20b7fdc6896e422b821f51313cce212b6c/coverage-7.16.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: coverage + version: 7.16.2 + sha256: 35f37886699cb9abd29958247d718628d5bc6f39e623dff66a09e546c42a7e03 + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b9/a3/d3d99d93b02517087aa05bc0cf2d04d372956b849e5443e059079901429b/coverage-7.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: coverage + version: 7.16.2 + sha256: 0fd7a86fdda7cb6d616d178654bd0ad6bc0f3f33c2e478aa598500a1a9e34eda + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/d1/e1/285727a8a74d48de256e8605413ee4cf82301228253ea58e5ab4270138b0/coverage-7.16.2-cp310-cp310-macosx_11_0_arm64.whl + name: coverage + version: 7.16.2 + sha256: 40c0f00899fe6181ae7f434ceb200e51f5ee4b8ed10e3b5f0b605f0cae15da87 + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl name: cryptography version: 46.0.6 @@ -2077,6 +3833,60 @@ packages: - check-sdist ; extra == 'pep8test' - click>=8.0.1 ; extra == 'pep8test' requires_python: '>=3.8,!=3.9.0,!=3.9.1' +- pypi: https://files.pythonhosted.org/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl + name: cryptography + version: 50.0.2 + sha256: 4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856 + requires_dist: + - cffi>=2.0.0 ; platform_python_implementation != 'PyPy' + - typing-extensions>=4.13.2 ; python_full_version < '3.11' + - bcrypt>=3.1.5 ; extra == 'ssh' + requires_python: '>=3.9,!=3.9.0,!=3.9.1' +- pypi: https://files.pythonhosted.org/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl + name: cryptography + version: 50.0.2 + sha256: 0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2 + requires_dist: + - cffi>=2.0.0 ; platform_python_implementation != 'PyPy' + - typing-extensions>=4.13.2 ; python_full_version < '3.11' + - bcrypt>=3.1.5 ; extra == 'ssh' + requires_python: '>=3.9,!=3.9.0,!=3.9.1' +- pypi: https://files.pythonhosted.org/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl + name: cryptography + version: 50.0.2 + sha256: f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480 + requires_dist: + - cffi>=2.0.0 ; platform_python_implementation != 'PyPy' + - typing-extensions>=4.13.2 ; python_full_version < '3.11' + - bcrypt>=3.1.5 ; extra == 'ssh' + requires_python: '>=3.9,!=3.9.0,!=3.9.1' +- pypi: https://files.pythonhosted.org/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl + name: cryptography + version: 50.0.2 + sha256: f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5 + requires_dist: + - cffi>=2.0.0 ; platform_python_implementation != 'PyPy' + - typing-extensions>=4.13.2 ; python_full_version < '3.11' + - bcrypt>=3.1.5 ; extra == 'ssh' + requires_python: '>=3.9,!=3.9.0,!=3.9.1' +- pypi: https://files.pythonhosted.org/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl + name: cryptography + version: 50.0.2 + sha256: dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454 + requires_dist: + - cffi>=2.0.0 ; platform_python_implementation != 'PyPy' + - typing-extensions>=4.13.2 ; python_full_version < '3.11' + - bcrypt>=3.1.5 ; extra == 'ssh' + requires_python: '>=3.9,!=3.9.0,!=3.9.1' +- pypi: https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl + name: cryptography + version: 50.0.2 + sha256: fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb + requires_dist: + - cffi>=2.0.0 ; platform_python_implementation != 'PyPy' + - typing-extensions>=4.13.2 ; python_full_version < '3.11' + - bcrypt>=3.1.5 ; extra == 'ssh' + requires_python: '>=3.9,!=3.9.0,!=3.9.1' - pypi: https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl name: cycler version: 0.12.1 @@ -2092,13 +3902,14 @@ packages: requires_python: '>=3.8' - pypi: ./ name: datajoint - version: 2.2.0.dev0 - sha256: 48335cedf96fa3b5efd3ddf880bd5065813f2baea43cad01a2fddbba94e561ec + version: 2.3.5.dev8+ga00b89f55.d20261017 + sha256: a3817469ad3406f5f40d8d07d59760edf41b7a1e5da9a68eba4ca1045d357d28 requires_dist: - deepdiff - fsspec>=2023.1.0 - networkx - numpy + - packaging - pandas - pydantic-settings>=2.0.0 - pydot @@ -2108,6 +3919,7 @@ packages: - pyarrow>=14.0.0 ; extra == 'arrow' - adlfs>=2023.1.0 ; extra == 'azure' - codespell ; extra == 'dev' + - hypothesis ; extra == 'dev' - polars>=0.20.0 ; extra == 'dev' - pre-commit ; extra == 'dev' - pyarrow>=14.0.0 ; extra == 'dev' @@ -2119,6 +3931,7 @@ packages: - psycopg2-binary>=2.9.0 ; extra == 'postgres' - s3fs>=2023.1.0 ; extra == 's3' - faker ; extra == 'test' + - hypothesis ; extra == 'test' - ipython ; extra == 'test' - matplotlib ; extra == 'test' - polars>=0.20.0 ; extra == 'test' @@ -2131,7 +3944,7 @@ packages: - testcontainers[minio,mysql,postgres]>=4.0 ; extra == 'test' - ipython ; extra == 'viz' - matplotlib ; extra == 'viz' - requires_python: '>=3.10,<3.14' + requires_python: '>=3.10,<3.15' - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda sha256: 3b988146a50e165f0fa4e839545c679af88e4782ec284cc7b6d07dd226d6a068 md5: 679616eb5ad4e521c83da4650860aba7 @@ -2148,6 +3961,33 @@ packages: purls: [] size: 437860 timestamp: 1747855126005 +- conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-he8c428d_2.conda + sha256: 87a0a42d4ccc527597eff3234509dfb03e0d9bf67d5e2b6fc758e2ef389d8abe + md5: a6eb37b51be1a9a654dc3a8aca039b1a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libstdcxx >=15 + - libexpat >=2.8.1,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - libglib >=2.88.3,<3.0a0 + license: AFL-2.1 OR GPL-2.0-or-later + purls: [] + size: 449564 + timestamp: 1788197922783 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/dbus-1.16.2-h71d469a_2.conda + sha256: d35726cb8b091a239c96d631bdf4286455393e3274266f7102109a68c69c12f9 + md5: 2d4de8dd159984989d47c03fb5543a9f + depends: + - libstdcxx >=15 + - libgcc >=15 + - libexpat >=2.8.1,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - libglib >=2.88.3,<3.0a0 + license: AFL-2.1 OR GPL-2.0-or-later + purls: [] + size: 482209 + timestamp: 1788197942246 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/dbus-1.16.2-heda779d_0.conda sha256: 5c9166bbbe1ea7d0685a1549aad4ea887b1eb3a07e752389f86b185ef8eac99a md5: 9203b74bb1f3fa0d6f308094b3b44c1e @@ -2168,6 +4008,11 @@ packages: version: 5.2.1 sha256: d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl + name: decorator + version: 5.3.1 + sha256: f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/f7/e6/efe534ef0952b531b630780e19cabd416e2032697019d5295defc6ef9bd9/deepdiff-8.6.1-py3-none-any.whl name: deepdiff version: 8.6.1 @@ -2202,6 +4047,45 @@ packages: - pytest-cov~=6.0.0 ; extra == 'test' - python-dotenv~=1.0.0 ; extra == 'test' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/c7/26/4a2bad8eb430d8d805a4642c4bff25103a37548d74ab346f8b1e024abcc5/deepdiff-9.1.0-py3-none-any.whl + name: deepdiff + version: 9.1.0 + sha256: 80c0460e1993b04f6f0ca79abf25548b129fd218478c4ebb08f80560f5d10610 + requires_dist: + - cachebox>=5.2,<6 + - orderly-set>=5.5.0,<6 + - click~=8.3.1 ; extra == 'cli' + - pyyaml~=6.0.3 ; extra == 'cli' + - coverage~=7.13.5 ; extra == 'coverage' + - bump2version~=1.0.1 ; extra == 'dev' + - jsonpickle~=4.1.1 ; extra == 'dev' + - ipdb~=0.13.13 ; extra == 'dev' + - numpy~=2.4.3 ; python_full_version >= '3.14' and extra == 'dev' + - numpy~=2.2.0 ; python_full_version < '3.14' and extra == 'dev' + - python-dateutil~=2.9.0.post0 ; extra == 'dev' + - orjson~=3.11.7 ; extra == 'dev' + - tomli~=2.4.0 ; extra == 'dev' + - tomli-w~=1.2.0 ; extra == 'dev' + - pandas~=3.0.1 ; python_full_version >= '3.11' and extra == 'dev' + - pandas~=2.2.0 ; python_full_version < '3.11' and extra == 'dev' + - polars~=1.39.3 ; extra == 'dev' + - nox==2026.2.9 ; extra == 'dev' + - uuid6==2025.0.1 ; extra == 'dev' + - pytz ; extra == 'dev' + - flit-core==3.12.0 ; extra == 'dev' + - sphinx~=8.1.3 ; extra == 'docs' + - sphinx-sitemap~=2.9.0 ; extra == 'docs' + - sphinxemoji~=0.3.2 ; extra == 'docs' + - furo>=2024.8.6 ; extra == 'docs' + - orjson ; extra == 'optimize' + - flake8~=7.3.0 ; extra == 'static' + - flake8-pyproject~=1.2.4 ; extra == 'static' + - pydantic~=2.12.5 ; extra == 'static' + - pytest~=9.0.3 ; extra == 'test' + - pytest-benchmark~=5.2.3 ; extra == 'test' + - pytest-cov~=7.1.0 ; extra == 'test' + - python-dotenv~=1.2.2 ; extra == 'test' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl name: distlib version: 0.4.0 @@ -2224,6 +4108,24 @@ packages: - paramiko>=2.4.3 ; extra == 'ssh' - websocket-client>=1.3.0 ; extra == 'websockets' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl + name: docker + version: 7.2.0 + sha256: a3f45fdeb9165e2d25d9a1d02ddf3bc70fb572cf5ebbf9b58558c22caf29b71f + requires_dist: + - pywin32>=304 ; sys_platform == 'win32' + - requests>=2.26.0 + - urllib3>=1.26.0 + - coverage==7.2.7 ; extra == 'dev' + - pytest-cov==4.1.0 ; extra == 'dev' + - pytest-timeout==2.1.0 ; extra == 'dev' + - pytest==7.4.2 ; extra == 'dev' + - ruff==0.1.8 ; extra == 'dev' + - myst-parser==0.18.0 ; extra == 'docs' + - sphinx==5.1.1 ; extra == 'docs' + - paramiko>=2.4.3 ; extra == 'ssh' + - websocket-client>=1.3.0 ; extra == 'websockets' + requires_python: '>=3.8' - conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-h166bdaf_1.tar.bz2 sha256: 1e58ee2ed0f4699be202f23d49b9644b499836230da7dd5b2f63e6766acff89e md5: a089d06164afd2d511347d3f87214e0b @@ -2234,6 +4136,29 @@ packages: purls: [] size: 1440699 timestamp: 1648505042260 +- conda: https://conda.anaconda.org/conda-forge/linux-64/epoxy-1.5.10-hb03c661_2.conda + sha256: a5b51e491fec22bcc1765f5b2c8fff8a97428e9a5a7ee6730095fb9d091b0747 + md5: 057083b06ccf1c2778344b6dabace38b + depends: + - __glibc >=2.17,<3.0.a0 + - libdrm >=2.4.125,<2.5.0a0 + - libegl >=1.7.0,<2.0a0 + - libegl-devel + - libgcc >=14 + - libgl >=1.7.0,<2.0a0 + - libgl-devel + - libglx >=1.7.0,<2.0a0 + - libglx-devel + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + - xorg-libxfixes >=6.0.1,<7.0a0 + - xorg-libxxf86vm >=1.1.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 411735 + timestamp: 1758743520805 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/epoxy-1.5.10-he30d5cf_2.conda sha256: aa562cdd72d2d15b0f2ee4565c8e34f18b52f7135a3f3b1ce727c202425c3bec md5: 1c50e7c46ccefffe918ac974fa1a6752 @@ -2266,6 +4191,14 @@ packages: purls: [] size: 296347 timestamp: 1758743805063 +- pypi: https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl + name: exceptiongroup + version: 1.3.1 + sha256: a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 + requires_dist: + - typing-extensions>=4.6.0 ; python_full_version < '3.13' + - pytest>=6 ; extra == 'test' + requires_python: '>=3.7' - pypi: https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl name: executing version: 2.2.1 @@ -2279,6 +4212,20 @@ packages: - littleutils ; extra == 'tests' - rich ; python_full_version >= '3.11' and extra == 'tests' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/4d/dd/8bc67e7d5ffc1d88aa5af511189dfb76dc4e1e5b808fab186146ef2663e5/executing-2.3.0-py3-none-any.whl + name: executing + version: 2.3.0 + sha256: 736e859c9f8701f11fcf516856f26f562e04776387824b43a35a1dfe21c84122 + requires_dist: + - pysource-minimize>=0.10.1 ; extra == 'tests' + - asttokens>=2.1.0 ; extra == 'tests' + - ipython ; extra == 'tests' + - pytest ; extra == 'tests' + - coverage[toml] ; extra == 'tests' + - coverage-enable-subprocess ; extra == 'tests' + - littleutils ; extra == 'tests' + - rich ; python_full_version >= '3.11' and extra == 'tests' + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/f5/11/02ebebb09ff2104b690457cb7bc6ed700c9e0ce88cf581486bb0a5d3c88b/faker-37.8.0-py3-none-any.whl name: faker version: 37.8.0 @@ -2293,6 +4240,15 @@ packages: requires_dist: - tzdata requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/81/ed/0d6d0d6467ae3d009fb82fb411867c7fcfeadbdd25602cab0d7a7963f400/faker-40.43.0-py3-none-any.whl + name: faker + version: 40.43.0 + sha256: 9dd7c0ddfaf30c842b05502d3cf641c135e0120a3a19047008ba8525b72953ed + requires_dist: + - tzdata ; sys_platform == 'win32' + - tzdata ; extra == 'tzdata' + - pillow ; extra == 'image' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl name: filelock version: 3.19.1 @@ -2350,6 +4306,22 @@ packages: purls: [] size: 265599 timestamp: 1730283881107 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.3-h4db4eae_1.conda + sha256: 5a3eb10b18a97223ab06b3a7f0d7f56658db2f2800e2f2af95836fe3bf55ba63 + md5: 922776b528a470ab5afa81fd42abfa1d + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libuuid >=2.42.2,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 296288 + timestamp: 1786667377340 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.15.0-h8dda3cd_1.conda sha256: fe023bb8917c8a3138af86ef537b70c8c5d60c44f93946a87d1e8bb1a6634b55 md5: 112b71b6af28b47c624bcbeefeea685b @@ -2364,6 +4336,21 @@ packages: purls: [] size: 277832 timestamp: 1730284967179 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.18.3-ha4b22b4_1.conda + sha256: 5f7801b6044ff233943e68f5156d4987fe8447d1be9378fb5c67f7aa009a33e7 + md5: bb629904b3e9326efaa5c39c956f2b63 + depends: + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libuuid >=2.42.2,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 304159 + timestamp: 1786667327378 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.15.0-h1383a14_1.conda sha256: f79d3d816fafbd6a2b0f75ebc3251a30d3294b08af9bb747194121f5efa364bc md5: 7b29f48742cea5d1ccb5edd839cb5621 @@ -2377,6 +4364,21 @@ packages: purls: [] size: 234227 timestamp: 1730284037572 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.18.3-h81aa574_1.conda + sha256: 004570d35fb0eff73ce3ae49b209a47622d0c2e580dd0dc4c61d59626b386a09 + md5: 8ac8cc3fe744b484de4387703134d8b2 + depends: + - __osx >=11.0 + - libexpat >=2.8.1,<3.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libintl >=0.25.1,<1.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 265659 + timestamp: 1786667932256 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 sha256: a997f2f1921bb9c9d76e6fa2f6b408b7fa549edd349a77639c9fe7a23ea93e61 md5: fee5683a3f04bd15cbd8318b096a27ab @@ -2515,79 +4517,371 @@ packages: - skia-pathops>=0.5.0 ; extra == 'all' - uharfbuzz>=0.23.0 ; extra == 'all' requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda - sha256: bf8e4dffe46f7d25dc06f31038cacb01672c47b9f45201f065b0f4d00ab0a83e - md5: 4afc585cd97ba8a23809406cd8a9eda8 - depends: - - libfreetype 2.14.1 ha770c72_0 - - libfreetype6 2.14.1 h73754d4_0 - license: GPL-2.0-only OR FTL - purls: [] - size: 173114 - timestamp: 1757945422243 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.14.1-h8af1aa0_0.conda - sha256: 9f8de35e95ce301cecfe01bc9d539c7cc045146ffba55efe9733ff77ad1cfb21 - md5: 0c8f36ebd3678eed1685f0fc93fc2175 - depends: - - libfreetype 2.14.1 h8af1aa0_0 - - libfreetype6 2.14.1 hdae7a39_0 - license: GPL-2.0-only OR FTL - purls: [] - size: 173174 - timestamp: 1757945489158 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.1-hce30654_0.conda - sha256: 14427aecd72e973a73d5f9dfd0e40b6bc3791d253de09b7bf233f6a9a190fd17 - md5: 1ec9a1ee7a2c9339774ad9bb6fe6caec - depends: - - libfreetype 2.14.1 hce30654_0 - - libfreetype6 2.14.1 h6da58f4_0 - license: GPL-2.0-only OR FTL - purls: [] - size: 173399 - timestamp: 1757947175403 -- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda - sha256: 858283ff33d4c033f4971bf440cebff217d5552a5222ba994c49be990dacd40d - md5: f9f81ea472684d75b9dd8d0b328cf655 - depends: - - __glibc >=2.17,<3.0.a0 - - libgcc >=14 - license: LGPL-2.1-or-later - purls: [] - size: 61244 - timestamp: 1757438574066 -- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fribidi-1.0.16-he30d5cf_0.conda - sha256: 1bfcd715bcb49a0b22d5d1899a22c6ff884b06f8e141eb746f3949752469a422 - md5: f3ac54914f7d3e1d68cb8d891765e5f9 - depends: - - libgcc >=14 - license: LGPL-2.1-or-later - purls: [] - size: 62909 - timestamp: 1757438620177 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hc919400_0.conda - sha256: d856dc6744ecfba78c5f7df3378f03a75c911aadac803fa2b41a583667b4b600 - md5: 04bdce8d93a4ed181d1d726163c2d447 - depends: - - __osx >=11.0 - license: LGPL-2.1-or-later - purls: [] - size: 59391 - timestamp: 1757438897523 -- pypi: https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl - name: frozenlist - version: 1.8.0 - sha256: f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40 - requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - name: frozenlist - version: 1.8.0 - sha256: eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822 - requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - name: frozenlist - version: 1.8.0 - sha256: fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027 - requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/53/d9/1caaa015dd207da7ccd3feba87289997bd88334ea3e74a367cdc7b0e50a3/fonttools-4.65.0-cp310-cp310-macosx_10_9_universal2.whl + name: fonttools + version: 4.65.0 + sha256: 93a73af2075036d36d7fbf856779c56a1b3b86ffcdae6abede7596604c42c156 + requires_dist: + - lxml>=4.0 ; extra == 'lxml' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - zopfli>=0.1.4 ; extra == 'woff' + - unicodedata2>=17.0.0 ; python_full_version < '3.15' and extra == 'unicode' + - lz4>=1.7.4.2 ; extra == 'graphite' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - pycairo ; extra == 'interpolatable' + - matplotlib ; extra == 'plot' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - uharfbuzz>=0.45.0 ; extra == 'repacker' + - lxml>=4.0 ; extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - unicodedata2>=17.0.0 ; python_full_version < '3.15' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.45.0 ; extra == 'all' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ee/56/151b5e81d20c63834f48ad37a0cbbbe2f9b248e38f8d10387f0cf5219244/fonttools-4.65.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: fonttools + version: 4.65.0 + sha256: fb53892b570f7f1f0055e75fc4de32673e32f749c4c8a606b63d5c436650e634 + requires_dist: + - lxml>=4.0 ; extra == 'lxml' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - zopfli>=0.1.4 ; extra == 'woff' + - unicodedata2>=17.0.0 ; python_full_version < '3.15' and extra == 'unicode' + - lz4>=1.7.4.2 ; extra == 'graphite' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - pycairo ; extra == 'interpolatable' + - matplotlib ; extra == 'plot' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - uharfbuzz>=0.45.0 ; extra == 'repacker' + - lxml>=4.0 ; extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - unicodedata2>=17.0.0 ; python_full_version < '3.15' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.45.0 ; extra == 'all' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/f8/6a/36f465a1c277131f9569f6a56fe99cf135391b4860b9c4f4b23e5b1cd5df/fonttools-4.65.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: fonttools + version: 4.65.0 + sha256: e3944e0bdba42effb71959e43d91b599326b02b59c78310d5675e8a75525e7d8 + requires_dist: + - lxml>=4.0 ; extra == 'lxml' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - zopfli>=0.1.4 ; extra == 'woff' + - unicodedata2>=17.0.0 ; python_full_version < '3.15' and extra == 'unicode' + - lz4>=1.7.4.2 ; extra == 'graphite' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - pycairo ; extra == 'interpolatable' + - matplotlib ; extra == 'plot' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - uharfbuzz>=0.45.0 ; extra == 'repacker' + - lxml>=4.0 ; extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - unicodedata2>=17.0.0 ; python_full_version < '3.15' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.45.0 ; extra == 'all' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/1f/2c/b7f33fa3bd1e4afdf9bf93b760f22486350eda487ce76c47f5931f868957/fonttools-4.67.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: fonttools + version: 4.67.0 + sha256: e2b5d511ea012dce7bd6df12b279b7d7a5b01b019865717d03ae679f4b944fa5 + requires_dist: + - lxml>=4.0 ; extra == 'lxml' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - zopfli>=0.1.4 ; extra == 'woff' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'unicode' + - lz4>=1.7.4.2 ; extra == 'graphite' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - pycairo ; extra == 'interpolatable' + - matplotlib ; extra == 'plot' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - uharfbuzz>=0.45.0 ; extra == 'repacker' + - lxml>=4.0 ; extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.45.0 ; extra == 'all' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/25/5b/c418f48918e40ef8c3f0f555567fe013c0c8058a8afa8040d6baeec80683/fonttools-4.67.0-cp314-cp314-macosx_10_15_universal2.whl + name: fonttools + version: 4.67.0 + sha256: 846982e89b1861d6c9d7fcd6567aec3fa5a10ad313e7f2076045fcd339cfbd8e + requires_dist: + - lxml>=4.0 ; extra == 'lxml' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - zopfli>=0.1.4 ; extra == 'woff' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'unicode' + - lz4>=1.7.4.2 ; extra == 'graphite' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - pycairo ; extra == 'interpolatable' + - matplotlib ; extra == 'plot' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - uharfbuzz>=0.45.0 ; extra == 'repacker' + - lxml>=4.0 ; extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.45.0 ; extra == 'all' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/79/fe/fef04b2cc2930edba11095f9e9b5c2797f8594fc54316195cc39d3c3bc63/fonttools-4.67.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: fonttools + version: 4.67.0 + sha256: 916836845e4b1c1447bb61390ffb3cb5f2940fd9f5d6de4685539a81806c7764 + requires_dist: + - lxml>=4.0 ; extra == 'lxml' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'woff' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'woff' + - zopfli>=0.1.4 ; extra == 'woff' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'unicode' + - lz4>=1.7.4.2 ; extra == 'graphite' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'interpolatable' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'interpolatable' + - pycairo ; extra == 'interpolatable' + - matplotlib ; extra == 'plot' + - sympy ; extra == 'symfont' + - xattr ; sys_platform == 'darwin' and extra == 'type1' + - skia-pathops>=0.5.0 ; extra == 'pathops' + - uharfbuzz>=0.45.0 ; extra == 'repacker' + - lxml>=4.0 ; extra == 'all' + - brotli>=1.0.1 ; platform_python_implementation == 'CPython' and extra == 'all' + - brotlicffi>=0.8.0 ; platform_python_implementation != 'CPython' and extra == 'all' + - zopfli>=0.1.4 ; extra == 'all' + - unicodedata2>=18.0.0 ; python_full_version < '3.16' and extra == 'all' + - lz4>=1.7.4.2 ; extra == 'all' + - scipy ; platform_python_implementation != 'PyPy' and extra == 'all' + - munkres ; platform_python_implementation == 'PyPy' and extra == 'all' + - pycairo ; extra == 'all' + - matplotlib ; extra == 'all' + - sympy ; extra == 'all' + - xattr ; sys_platform == 'darwin' and extra == 'all' + - skia-pathops>=0.5.0 ; extra == 'all' + - uharfbuzz>=0.45.0 ; extra == 'all' + requires_python: '>=3.11' +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda + sha256: bf8e4dffe46f7d25dc06f31038cacb01672c47b9f45201f065b0f4d00ab0a83e + md5: 4afc585cd97ba8a23809406cd8a9eda8 + depends: + - libfreetype 2.14.1 ha770c72_0 + - libfreetype6 2.14.1 h73754d4_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 173114 + timestamp: 1757945422243 +- conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_2.conda + sha256: 612a8e0c1a6ecae23da54d81ef395f6ebb5c8e4468ec2611593ebce75876a4e0 + md5: 2d0ea23b23603e07ca47f23f949f67b6 + depends: + - libfreetype 2.14.3 ha770c72_2 + - libfreetype6 2.14.3 h5e6c136_2 + license: GPL-2.0-only OR FTL + purls: [] + size: 175239 + timestamp: 1786641011029 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.14.1-h8af1aa0_0.conda + sha256: 9f8de35e95ce301cecfe01bc9d539c7cc045146ffba55efe9733ff77ad1cfb21 + md5: 0c8f36ebd3678eed1685f0fc93fc2175 + depends: + - libfreetype 2.14.1 h8af1aa0_0 + - libfreetype6 2.14.1 hdae7a39_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 173174 + timestamp: 1757945489158 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.14.3-h8af1aa0_2.conda + sha256: 7af9e368efdaf59e0709ae89c2e7f0ce0e1067a857958f367318ff3c27209a64 + md5: f5d76071be867597bab18a0e45d588e1 + depends: + - libfreetype 2.14.3 h8af1aa0_2 + - libfreetype6 2.14.3 h9cc7050_2 + license: GPL-2.0-only OR FTL + purls: [] + size: 174712 + timestamp: 1786640946309 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.1-hce30654_0.conda + sha256: 14427aecd72e973a73d5f9dfd0e40b6bc3791d253de09b7bf233f6a9a190fd17 + md5: 1ec9a1ee7a2c9339774ad9bb6fe6caec + depends: + - libfreetype 2.14.1 hce30654_0 + - libfreetype6 2.14.1 h6da58f4_0 + license: GPL-2.0-only OR FTL + purls: [] + size: 173399 + timestamp: 1757947175403 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_2.conda + sha256: 90681f1d0658cb2fd49a074f644eb4774272499290487a4bebb1c4df01d6997b + md5: 2f4cc7dc2e6622591735c49dda1b92aa + depends: + - libfreetype 2.14.3 hce30654_2 + - libfreetype6 2.14.3 h2ed5691_2 + license: GPL-2.0-only OR FTL + purls: [] + size: 175388 + timestamp: 1786641016583 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.16-hb03c661_0.conda + sha256: 858283ff33d4c033f4971bf440cebff217d5552a5222ba994c49be990dacd40d + md5: f9f81ea472684d75b9dd8d0b328cf655 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 61244 + timestamp: 1757438574066 +- conda: https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.17-h7cc23a3_0.conda + sha256: 3ba79ebfbd14ca5db4affaf10ee8001064e907de5baabc2dbe5bcd9aec856c9f + md5: f9d5302bca196ee87e62f393f2626562 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: LGPL-2.1-or-later + purls: [] + size: 64102 + timestamp: 1790011855888 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fribidi-1.0.16-he30d5cf_0.conda + sha256: 1bfcd715bcb49a0b22d5d1899a22c6ff884b06f8e141eb746f3949752469a422 + md5: f3ac54914f7d3e1d68cb8d891765e5f9 + depends: + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 62909 + timestamp: 1757438620177 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/fribidi-1.0.17-h5bc82ec_0.conda + sha256: d488c0ceaeebbddae4c0381c090b4395cf008cf06e21355734af277d5831562f + md5: 1ab111523206a2b10c76634beb533d11 + depends: + - libgcc >=15 + license: LGPL-2.1-or-later + purls: [] + size: 65547 + timestamp: 1790011806963 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.16-hc919400_0.conda + sha256: d856dc6744ecfba78c5f7df3378f03a75c911aadac803fa2b41a583667b4b600 + md5: 04bdce8d93a4ed181d1d726163c2d447 + depends: + - __osx >=11.0 + license: LGPL-2.1-or-later + purls: [] + size: 59391 + timestamp: 1757438897523 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/fribidi-1.0.17-hb001647_0.conda + sha256: 803e78ec4a832a646dae842f09de0bc04acd2613deafa9b11d1970399d24d163 + md5: 29b021cee334af0050bdca46bada6e0e + depends: + - __osx >=11.0 + license: LGPL-2.1-or-later + purls: [] + size: 63577 + timestamp: 1790011838821 +- pypi: https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl + name: frozenlist + version: 1.8.0 + sha256: f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: frozenlist + version: 1.8.0 + sha256: 778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: frozenlist + version: 1.8.0 + sha256: eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: frozenlist + version: 1.8.0 + sha256: f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl + name: frozenlist + version: 1.8.0 + sha256: a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: frozenlist + version: 1.8.0 + sha256: 799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl + name: frozenlist + version: 1.8.0 + sha256: 4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: frozenlist + version: 1.8.0 + sha256: cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: frozenlist + version: 1.8.0 + sha256: fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027 + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/d5/1f/5f4a3cd9e4440e9d9bc78ad0a91a1c8d46b4d429d5239ebe6793c9fe5c41/fsspec-2026.3.0-py3-none-any.whl name: fsspec version: 2026.3.0 @@ -2696,6 +4990,115 @@ packages: - zstandard ; python_full_version < '3.14' and extra == 'test-full' - tqdm ; extra == 'tqdm' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl + name: fsspec + version: 2026.9.0 + sha256: 8dd6e646e99ea382bd85f97a45e6b526a442d79423a7dc673f1e2756d05fcb5f + requires_dist: + - adlfs ; extra == 'abfs' + - adlfs ; extra == 'adl' + - pyarrow>=1 ; extra == 'arrow' + - dask ; extra == 'dask' + - distributed ; extra == 'dask' + - pre-commit ; extra == 'dev' + - ruff>=0.5 ; extra == 'dev' + - numpydoc ; extra == 'doc' + - sphinx ; extra == 'doc' + - sphinx-design ; extra == 'doc' + - sphinx-rtd-theme ; extra == 'doc' + - yarl ; extra == 'doc' + - dropbox ; extra == 'dropbox' + - dropboxdrivefs ; extra == 'dropbox' + - requests ; extra == 'dropbox' + - adlfs ; extra == 'full' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'full' + - dask ; extra == 'full' + - distributed ; extra == 'full' + - dropbox ; extra == 'full' + - dropboxdrivefs ; extra == 'full' + - fusepy ; extra == 'full' + - gcsfs>=2026.4.0 ; extra == 'full' + - libarchive-c ; extra == 'full' + - ocifs ; extra == 'full' + - panel ; extra == 'full' + - paramiko ; extra == 'full' + - pyarrow>=1 ; extra == 'full' + - pygit2 ; extra == 'full' + - requests ; extra == 'full' + - s3fs>=2026.6.0 ; extra == 'full' + - smbprotocol ; extra == 'full' + - tqdm ; extra == 'full' + - fusepy ; extra == 'fuse' + - gcsfs>=2026.4.0 ; extra == 'gcs' + - pygit2 ; extra == 'git' + - requests ; extra == 'github' + - gcsfs>=2026.4.0 ; extra == 'gs' + - panel ; extra == 'gui' + - pyarrow>=1 ; extra == 'hdfs' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'http' + - libarchive-c ; extra == 'libarchive' + - ocifs ; extra == 'oci' + - s3fs>=2026.6.0 ; extra == 's3' + - paramiko ; extra == 'sftp' + - smbprotocol ; extra == 'smb' + - paramiko ; extra == 'ssh' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'test' + - numpy ; extra == 'test' + - pytest ; extra == 'test' + - pytest-asyncio!=0.22.0 ; extra == 'test' + - pytest-benchmark ; extra == 'test' + - pytest-cov ; extra == 'test' + - pytest-mock ; extra == 'test' + - pytest-recording ; extra == 'test' + - pytest-rerunfailures ; extra == 'test' + - requests ; extra == 'test' + - aiobotocore>=2.5.4,<3.0.0 ; extra == 'test-downstream' + - dask[dataframe,test] ; extra == 'test-downstream' + - moto[server]>4,<5 ; extra == 'test-downstream' + - pytest-timeout ; extra == 'test-downstream' + - xarray ; extra == 'test-downstream' + - zarr ; extra == 'test-downstream' + - adlfs ; extra == 'test-full' + - aiohttp!=4.0.0a0,!=4.0.0a1 ; extra == 'test-full' + - backports-zstd ; python_full_version < '3.14' and extra == 'test-full' + - cloudpickle ; extra == 'test-full' + - dask ; extra == 'test-full' + - distributed ; extra == 'test-full' + - dropbox ; extra == 'test-full' + - dropboxdrivefs ; extra == 'test-full' + - fastparquet ; extra == 'test-full' + - fusepy ; extra == 'test-full' + - gcsfs>=2026.4.0 ; extra == 'test-full' + - jinja2 ; extra == 'test-full' + - kerchunk ; extra == 'test-full' + - libarchive-c ; extra == 'test-full' + - lz4 ; extra == 'test-full' + - notebook ; extra == 'test-full' + - numpy ; extra == 'test-full' + - ocifs ; extra == 'test-full' + - pandas<3.0.0 ; extra == 'test-full' + - panel ; extra == 'test-full' + - paramiko ; extra == 'test-full' + - pyarrow>=1 ; extra == 'test-full' + - pyftpdlib ; extra == 'test-full' + - pygit2 ; extra == 'test-full' + - pytest ; extra == 'test-full' + - pytest-asyncio!=0.22.0 ; extra == 'test-full' + - pytest-benchmark ; extra == 'test-full' + - pytest-cov ; extra == 'test-full' + - pytest-mock ; extra == 'test-full' + - pytest-recording ; extra == 'test-full' + - pytest-rerunfailures ; extra == 'test-full' + - python-snappy ; extra == 'test-full' + - requests ; extra == 'test-full' + - s3fs>=2026.6.0 ; extra == 'test-full' + - smbprotocol ; extra == 'test-full' + - tqdm ; extra == 'test-full' + - urllib3 ; extra == 'test-full' + - zarr<3.2.0 ; extra == 'test-full' + - zstandard ; python_full_version < '3.14' and extra == 'test-full' + - tqdm ; extra == 'tqdm' + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.1-h2b0a6b4_0.conda sha256: b827285fe001806beeddcc30953d2bd07869aeb0efe4581d56432c92c06b0c48 md5: 2935d9c0526277bd42373cf23d49d51f @@ -2712,6 +5115,22 @@ packages: purls: [] size: 579596 timestamp: 1757867209855 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.44.8-h68053f1_2.conda + sha256: 32f3ebd1871cb6ac7eac36c78c410860f38e8d252d5857a80f6abfcb79c7e0ff + md5: d7bd6ccfc58eb6796bd6bba85878fc0e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libglib >=2.88.3,<3.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.2,<4.8.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 581961 + timestamp: 1788490424411 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gdk-pixbuf-2.44.4-h90308e0_0.conda sha256: 78a1d69c3d0da73b4d54a35001abd4e273605180d21365b4f31e9a241d9fb715 md5: 4c8c0d2f7620467869d41f29304362dc @@ -2727,6 +5146,21 @@ packages: purls: [] size: 580454 timestamp: 1761083738779 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gdk-pixbuf-2.44.8-hb26ce08_2.conda + sha256: 5455ddfeec3b69156a207654dde572ba81b711ac29f39cf9452ef8f0db8348d7 + md5: 3ae2e4bd62eb5a711f90b53f58c8258b + depends: + - libgcc >=15 + - libglib >=2.88.3,<3.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.2,<4.8.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 584259 + timestamp: 1788492620164 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.4-h7542897_0.conda sha256: 1164ba63360736439c6e50f2d390e93f04df86901e7711de41072a32d9b8bfc9 md5: 0b349c0400357e701cf2fa69371e5d39 @@ -2743,6 +5177,22 @@ packages: purls: [] size: 544149 timestamp: 1761082904334 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gdk-pixbuf-2.44.8-h5867e2c_2.conda + sha256: 96dcac22eaea2f24b9c92e8be078c3e39058829aa4db2a8103c1a4e85f8925ed + md5: 3a84786e0c929f4a05d08c07be5ec9b3 + depends: + - __osx >=11.0 + - libglib >=2.88.3,<3.0a0 + - libintl >=0.25.1,<1.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libpng >=1.6.58,<1.7.0a0 + - libtiff >=4.7.2,<4.8.0a0 + license: LGPL-2.1-or-later + license_family: LGPL + purls: [] + size: 552802 + timestamp: 1788490405006 - conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.86.0-hf516916_0.conda sha256: b77316bd5c8680bde4e5a7ab7013c8f0f10c1702cc6c3b0fd0fac3923a31fec3 md5: 1a8e49615381c381659de1bc6a3bf9ec @@ -2754,6 +5204,18 @@ packages: purls: [] size: 117284 timestamp: 1757403341964 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.90.0-h3d503d9_0.conda + sha256: a31c2244c6743a85941ac1d19a77ce2cc871afa171b0ad9aaac309af532d0677 + md5: d4adaebf8a2c9040ada6549c52a6b8d8 + depends: + - libglib ==2.90.0 h569388d_0 + - libffi + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + license: LGPL-2.1-or-later + purls: [] + size: 237225 + timestamp: 1789478025196 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glib-tools-2.86.1-hc87f4d4_1.conda sha256: 59d89ed84223775b4354c2bc0fc51c465ee1caf53607bf7eae868b0aca4b5a9e md5: eabd2c76bb4cbf80fd78bb5e7d8122d7 @@ -2764,6 +5226,17 @@ packages: purls: [] size: 126254 timestamp: 1761874152194 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/glib-tools-2.90.1-hd8364ca_0.conda + sha256: ccf37aba8832e730e6481d02bf3197e8c63e003d6378d671c6e15639c131e1b2 + md5: 3234dffae5acde6b474b201ca3483d48 + depends: + - libglib ==2.90.1 h3584fe5_0 + - libffi + - libgcc >=15 + license: LGPL-2.1-or-later + purls: [] + size: 254109 + timestamp: 1791383970778 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.86.1-hb9d6e3a_1.conda sha256: 6492472d76db47d85699c895acbe6b578ee0d4a964490388e71aec8777c0e9ec md5: 5a90e74e57c0d1e2381ce1246b0a2125 @@ -2775,6 +5248,18 @@ packages: purls: [] size: 101419 timestamp: 1761875708283 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glib-tools-2.90.0-h06d1d6c_0.conda + sha256: 4c0cd7642611222bc573e93ac761b811839b015e4d0d968ae2e880b944e8b1a9 + md5: 62aed33caa374a6807ba9b2780603b70 + depends: + - libglib ==2.90.0 hccd281b_0 + - libffi + - __osx >=11.0 + - libintl >=0.25.1,<1.0a0 + license: LGPL-2.1-or-later + purls: [] + size: 202352 + timestamp: 1789478088421 - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda sha256: 25ba37da5c39697a77fce2c9a15e48cf0a84f1464ad2aafbe53d8357a9f6cc8c md5: 2cd94587f3a401ae05e03a6caf09539d @@ -2787,6 +5272,18 @@ packages: purls: [] size: 99596 timestamp: 1755102025473 +- conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-h54a6638_1.conda + sha256: 7fa3b6a9c081fa3e545573152a788d061a0a0ba57df7251cc0f4f75225fc93e7 + md5: f9fe2984587fa8235a6af6004760cd18 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 102835 + timestamp: 1786118485753 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/graphite2-1.3.14-hfae3067_2.conda sha256: c9b1781fe329e0b77c5addd741e58600f50bef39321cae75eba72f2f381374b7 md5: 4aa540e9541cc9d6581ab23ff2043f13 @@ -2798,6 +5295,17 @@ packages: purls: [] size: 102400 timestamp: 1755102000043 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/graphite2-1.3.15-h7ac5ae9_1.conda + sha256: 5be01914b445dfbde85a4751b1d12b331740e7af68a86e4ef6313ab38a870fdd + md5: c5835ff5788e0d772d67a97bd5fe001b + depends: + - libstdcxx >=14 + - libgcc >=14 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 119414 + timestamp: 1786118514013 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.14-hec049ff_2.conda sha256: c507ae9989dbea7024aa6feaebb16cbf271faac67ac3f0342ef1ab747c20475d md5: 0fc46fee39e88bbcf5835f71a9d9a209 @@ -2809,6 +5317,17 @@ packages: purls: [] size: 81202 timestamp: 1755102333712 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/graphite2-1.3.15-h784d473_1.conda + sha256: 471f34a187fdb4f2df33e26f2e471b16c239a5b277903f643d9c3a8c9a9f44ec + md5: 0c7b78d9ffff4f5a6ca28d346f734d8f + depends: + - libcxx >=19 + - __osx >=11.0 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 86493 + timestamp: 1786118637573 - pypi: https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl name: graphviz version: '0.21' @@ -2924,6 +5443,28 @@ packages: - psutil ; extra == 'test' - setuptools ; extra == 'test' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/1d/05/dc0d54e90af2f192724936799cee990687792ff54a4638d3ae0e0a0145c0/greenlet-3.5.6-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + name: greenlet + version: 3.5.6 + sha256: b7d501d5eb5d4f67207df364752ad697465b834268744be7581c18d81d35d41d + requires_dist: + - sphinx ; extra == 'docs' + - furo ; extra == 'docs' + - objgraph ; extra == 'test' + - psutil ; extra == 'test' + - setuptools ; extra == 'test' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/94/5c/092682ae7ca1aadd44aa36b0bc35c48c9ed55f9ffebb5794980b1e2ae74b/greenlet-3.5.6-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + name: greenlet + version: 3.5.6 + sha256: eed88b64a5e5da72d6a71cdc5aaeefaa5ced9b748f8d19f89800b339961dad39 + requires_dist: + - sphinx ; extra == 'docs' + - furo ; extra == 'docs' + - objgraph ; extra == 'test' + - psutil ; extra == 'test' + - setuptools ; extra == 'test' + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.43-h0c6a113_5.conda sha256: d36263cbcbce34ec463ce92bd72efa198b55d987959eab6210cc256a0e79573b md5: 67d00e9cfe751cfe581726c5eff7c184 @@ -2965,6 +5506,49 @@ packages: purls: [] size: 5585389 timestamp: 1743405684985 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gtk3-3.24.52-ha5ea40c_0.conda + sha256: c6bb4f06331bcb0a566d84e0f0fad7af4b9035a03b13e2d5ecfaf13be57e6e10 + md5: bcaea22d85999a4f17918acfab877e61 + depends: + - __glibc >=2.17,<3.0.a0 + - at-spi2-atk >=2.38.0,<3.0a0 + - atk-1.0 >=2.38.0 + - cairo >=1.18.4,<2.0a0 + - epoxy >=1.5.10,<1.6.0a0 + - fontconfig >=2.17.1,<3.0a0 + - fonts-conda-ecosystem + - fribidi >=1.0.16,<2.0a0 + - gdk-pixbuf >=2.44.5,<3.0a0 + - glib-tools + - harfbuzz >=13.2.1 + - hicolor-icon-theme + - libcups >=2.3.3,<2.4.0a0 + - libcups >=2.3.3,<3.0a0 + - libexpat >=2.7.4,<3.0a0 + - libfreetype >=2.14.2 + - libfreetype6 >=2.14.2 + - libgcc >=14 + - libglib >=2.86.4,<3.0a0 + - liblzma >=5.8.2,<6.0a0 + - libxkbcommon >=1.13.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - pango >=1.56.4,<2.0a0 + - wayland >=1.25.0,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxcomposite >=0.4.7,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + - xorg-libxi >=1.8.2,<2.0a0 + - xorg-libxinerama >=1.1.6,<1.2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 5939083 + timestamp: 1774288645605 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gtk3-3.24.43-h4cd1324_6.conda sha256: 5b8c5255d88d97083095790765dfafda6ce99daa8dcaaa8c0b668e82c5b73187 md5: 124842a6e0b59cbd121233346bd56e33 @@ -3005,6 +5589,48 @@ packages: purls: [] size: 5660172 timestamp: 1761334356772 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/gtk3-3.24.52-h75d4e7a_0.conda + sha256: 5db1347513253a2d200b111717eb452d4b95e9380db48b6dd2c8bb7bb256d754 + md5: f6a9ed300bcf3217da7f1955f57facec + depends: + - at-spi2-atk >=2.38.0,<3.0a0 + - atk-1.0 >=2.38.0 + - cairo >=1.18.4,<2.0a0 + - epoxy >=1.5.10,<1.6.0a0 + - fontconfig >=2.17.1,<3.0a0 + - fonts-conda-ecosystem + - fribidi >=1.0.16,<2.0a0 + - gdk-pixbuf >=2.44.5,<3.0a0 + - glib-tools + - harfbuzz >=13.2.1 + - hicolor-icon-theme + - libcups >=2.3.3,<2.4.0a0 + - libcups >=2.3.3,<3.0a0 + - libexpat >=2.7.4,<3.0a0 + - libfreetype >=2.14.2 + - libfreetype6 >=2.14.2 + - libgcc >=14 + - libglib >=2.86.4,<3.0a0 + - liblzma >=5.8.2,<6.0a0 + - libxkbcommon >=1.13.1,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - pango >=1.56.4,<2.0a0 + - wayland >=1.25.0,<2.0a0 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxcomposite >=0.4.7,<1.0a0 + - xorg-libxcursor >=1.2.3,<2.0a0 + - xorg-libxdamage >=1.1.6,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + - xorg-libxi >=1.8.2,<2.0a0 + - xorg-libxinerama >=1.1.6,<1.2.0a0 + - xorg-libxrandr >=1.5.5,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 6023698 + timestamp: 1774296668544 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.43-h5febe37_6.conda sha256: bd66a3325bf3ce63ada3bf12eaafcfe036698741ee4bb595e83e5fdd3dba9f3d md5: a99f96906158ebae5e3c0904bcd45145 @@ -3014,21 +5640,47 @@ packages: - cairo >=1.18.4,<2.0a0 - epoxy >=1.5.10,<1.6.0a0 - fribidi >=1.0.16,<2.0a0 - - gdk-pixbuf >=2.44.4,<3.0a0 + - gdk-pixbuf >=2.44.4,<3.0a0 + - glib-tools + - harfbuzz >=11.5.1 + - hicolor-icon-theme + - libexpat >=2.7.1,<3.0a0 + - libglib >=2.86.0,<3.0a0 + - libintl >=0.25.1,<1.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + - pango >=1.56.4,<2.0a0 + license: LGPL-2.0-or-later + license_family: LGPL + purls: [] + size: 4768791 + timestamp: 1761328318680 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gtk3-3.24.52-hc0f3e19_0.conda + sha256: 26862a9898054b8552e55e609e5ce73c7ef1eb28bbe6fb87f0b9109d73cd09df + md5: 5557a2433b1339b8e536c264afea41ef + depends: + - __osx >=11.0 + - atk-1.0 >=2.38.0 + - cairo >=1.18.4,<2.0a0 + - epoxy >=1.5.10,<1.6.0a0 + - fribidi >=1.0.16,<2.0a0 + - gdk-pixbuf >=2.44.5,<3.0a0 - glib-tools - - harfbuzz >=11.5.1 + - harfbuzz >=13.2.1 - hicolor-icon-theme - - libexpat >=2.7.1,<3.0a0 - - libglib >=2.86.0,<3.0a0 + - libexpat >=2.7.4,<3.0a0 + - libfreetype >=2.14.2 + - libfreetype6 >=2.14.2 + - libglib >=2.86.4,<3.0a0 - libintl >=0.25.1,<1.0a0 - - liblzma >=5.8.1,<6.0a0 - - libzlib >=1.3.1,<2.0a0 + - liblzma >=5.8.2,<6.0a0 + - libzlib >=1.3.2,<2.0a0 - pango >=1.56.4,<2.0a0 license: LGPL-2.0-or-later license_family: LGPL purls: [] - size: 4768791 - timestamp: 1761328318680 + size: 9385734 + timestamp: 1774288504338 - conda: https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h977cf35_4.conda sha256: b5cd16262fefb836f69dc26d879b6508d29f8a5c5948a966c47fe99e2e19c99b md5: 4d8df0b0db060d33c9a702ada998a8fe @@ -3083,6 +5735,16 @@ packages: purls: [] size: 2048134 timestamp: 1757867460348 +- conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.6.0-ha770c72_2.conda + sha256: f2010d4e80756cc3735b6fec645c9c72293344cf9c2a780256da5dc4dc3545b1 + md5: 0709eacc38906e8a237d9ef172af6688 + depends: + - libharfbuzz-devel 14.6.0 h23af247_2 + license: MIT + license_family: MIT + purls: [] + size: 11095 + timestamp: 1791900621328 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-12.2.0-he4899c9_0.conda sha256: 5cfd74a3fbce0921af5beff93a3fe7edc5b1344d9b9668b2de1c1be932b54993 md5: 1437bf9690976948f90175a65407b65f @@ -3102,6 +5764,16 @@ packages: purls: [] size: 2156041 timestamp: 1762376447693 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-14.6.0-h8af1aa0_2.conda + sha256: 281f2d6f90bcf465c259e2276cfb7391441537a89a4816dcddd05630e415b66a + md5: ae07e8dbca22caab07e550a4f6608ccc + depends: + - libharfbuzz-devel 14.6.0 h8f7ccb3_2 + license: MIT + license_family: MIT + purls: [] + size: 11097 + timestamp: 1791900609122 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-12.1.0-haf38c7b_0.conda sha256: 8f2fac3e74608af55334ab9e77e9db9112c9078858aa938d191481d873a902d3 md5: 3fd0b257d246ddedd1f1496e5246958d @@ -3121,6 +5793,16 @@ packages: purls: [] size: 1548996 timestamp: 1759366687572 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/harfbuzz-14.6.0-hce30654_2.conda + sha256: bce1e0ca60d99d54689217499ce3e0dfba85b1040a1cc053829b7fc32e08c523 + md5: 6b3aa5e8534b5662c136abf1a4a66434 + depends: + - libharfbuzz-devel 14.6.0 hcda0f7c_2 + license: MIT + license_family: MIT + purls: [] + size: 11103 + timestamp: 1791900647143 - conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_2.tar.bz2 sha256: 336f29ceea9594f15cc8ec4c45fdc29e10796573c697ee0d57ebb7edd7e92043 md5: bbf6f174dcd3254e19a2f5d2295ce808 @@ -3129,6 +5811,14 @@ packages: purls: [] size: 13841 timestamp: 1605162808667 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hicolor-icon-theme-0.17-ha770c72_3.conda + sha256: 6d7e6e1286cb521059fe69696705100a03b006efb914ffe82a2ae97ecbae66b7 + md5: 129e404c5b001f3ef5581316971e3ea0 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 17625 + timestamp: 1771539597968 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/hicolor-icon-theme-0.17-h8af1aa0_2.tar.bz2 sha256: 479a0f95cf3e7d7db795fb7a14337cab73c2c926a5599c8512a3e8f8466f9e54 md5: 331add9f855e921695d7b569aa23d5ec @@ -3137,6 +5827,14 @@ packages: purls: [] size: 13896 timestamp: 1605162856037 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/hicolor-icon-theme-0.17-h8af1aa0_3.conda + sha256: 9f7fa161b33de5f001dc43f9d6399ba45b3fb1efb141ee2fec6bf05a48420d63 + md5: af62915a9e4154e16d97f99c64cec14e + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 17670 + timestamp: 1771540496764 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_2.tar.bz2 sha256: 286e33fb452f61133a3a61d002890235d1d1378554218ab063d6870416440281 md5: 237b05b7eb284d7eebc3c5d93f5e4bca @@ -3145,6 +5843,395 @@ packages: purls: [] size: 13800 timestamp: 1611053664863 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/hicolor-icon-theme-0.17-hce30654_3.conda + sha256: 46a4958f2f916c5938f2a6dc0709f78b175ece42f601d79a04e0276d55d25d07 + md5: cfb39109ac5fa8601eb595d66d5bf156 + license: GPL-2.0-or-later + license_family: GPL + purls: [] + size: 17616 + timestamp: 1771539622983 +- pypi: https://files.pythonhosted.org/packages/42/42/31e66ce21aa6ea030ace8874269e5a169b0c69d8a3043042e315bd64c6ad/hypothesis-6.168.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: hypothesis + version: 6.168.5 + sha256: 30208c44364b6fe1f70c74b45f3f1f8a173a749d876294a80fe88c9cf16ab6d0 + requires_dist: + - exceptiongroup>=1.0.0 ; python_full_version < '3.11' + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.21.6 ; extra == 'all' + - pandas>=1.1 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.21.6 ; extra == 'numpy' + - pandas>=1.1 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/53/1b/8257699b8456241b8348fe0071c29912aeeaf5d16ef97a45e9c1d3170ca6/hypothesis-6.168.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: hypothesis + version: 6.168.5 + sha256: 54f40be9b9c6b7b058ff56b0b18a91ff4cfa57a7c7756043eabaa094a0a162c9 + requires_dist: + - exceptiongroup>=1.0.0 ; python_full_version < '3.11' + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.21.6 ; extra == 'all' + - pandas>=1.1 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.21.6 ; extra == 'numpy' + - pandas>=1.1 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a1/60/90ccc9e18d831480920dc0f1d33a9af142e796d67dbe6a760e93d0122587/hypothesis-6.168.5-cp310-cp310-macosx_11_0_arm64.whl + name: hypothesis + version: 6.168.5 + sha256: 658563b8f2782a0577a4d8d195e31f29b18f3f3b61ba58c4dcbd8e6ac502d14d + requires_dist: + - exceptiongroup>=1.0.0 ; python_full_version < '3.11' + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.21.6 ; extra == 'all' + - pandas>=1.1 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.21.6 ; extra == 'numpy' + - pandas>=1.1 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/0a/c1/f6441724a57c5b92f71451b243b682c016cbbfc728ba792154ecd7d00c6d/hypothesis-6.170.1-cp314-cp314-macosx_11_0_arm64.whl + name: hypothesis + version: 6.170.1 + sha256: 988061b6a7e4ddf124ca5bd52ab45cf0bfe93915c09dcce40e3e2e00b9cab682 + requires_dist: + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.23.2 ; extra == 'all' + - pandas>=1.5 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.23.2 ; extra == 'numpy' + - pandas>=1.5 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/3c/2d/4e06cae907df9b6dfea73f3497ae132b865d2e957a6df242baa4b33ec728/hypothesis-6.170.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: hypothesis + version: 6.170.1 + sha256: 321522b7cc2872acebd65161bfeed4ef86a7034a64f049b072eac7e79502598f + requires_dist: + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.23.2 ; extra == 'all' + - pandas>=1.5 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.23.2 ; extra == 'numpy' + - pandas>=1.5 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/7b/b4/9ae75c3a63a87ca454383c6305f33f25538cf1eccf29491ba768b80e894d/hypothesis-6.170.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: hypothesis + version: 6.170.1 + sha256: f10b19eff9daff21e8d07552de36e9f447009994946a223bc32da21cd0dbd196 + requires_dist: + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.23.2 ; extra == 'all' + - pandas>=1.5 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.23.2 ; extra == 'numpy' + - pandas>=1.5 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/85/41/4d999c28cad9f236f5a9a155b40c917ea32e3d961915c03b2b3ab8d120f0/hypothesis-6.170.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: hypothesis + version: 6.170.1 + sha256: 7d68243fc960e2a5f202ac978ee45b2a7e92d41b9e4c1bca59a5948a1c922700 + requires_dist: + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.23.2 ; extra == 'all' + - pandas>=1.5 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.23.2 ; extra == 'numpy' + - pandas>=1.5 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/c8/ba/e389f3ac83570fe4e84ff2f68c2296621151949f2fa2f44d9c047d7acf1d/hypothesis-6.170.1-cp313-cp313-macosx_11_0_arm64.whl + name: hypothesis + version: 6.170.1 + sha256: be07bf11adcf7b37536a4c893b63d34af95e358966de1232a5711c0193b04a59 + requires_dist: + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.23.2 ; extra == 'all' + - pandas>=1.5 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.23.2 ; extra == 'numpy' + - pandas>=1.5 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/cf/40/eed6741ee50c5687c951acff0edd5f95f42722bad81eedfaedb52f8e2de2/hypothesis-6.170.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: hypothesis + version: 6.170.1 + sha256: 804f35f16c2232c800e4755d74704f1168a10d2ceed176e08361f6ccccd235f0 + requires_dist: + - sortedcontainers>=2.1.0,<3.0.0 + - black>=20.8b0 ; extra == 'all' + - click>=7.0 ; extra == 'all' + - crosshair-tool>=0.0.111 ; extra == 'all' + - django>=5.2 ; extra == 'all' + - dpcontracts>=0.4 ; extra == 'all' + - hypothesis-crosshair>=0.0.30 ; extra == 'all' + - lark>=0.10.1 ; extra == 'all' + - libcst>=0.3.16 ; extra == 'all' + - numpy>=1.23.2 ; extra == 'all' + - pandas>=1.5 ; extra == 'all' + - pytest>=4.6 ; extra == 'all' + - python-dateutil>=1.4 ; extra == 'all' + - pytz>=2014.1 ; extra == 'all' + - redis>=3.0.0 ; extra == 'all' + - rich>=9.0.0 ; extra == 'all' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'all') or (sys_platform == 'win32' and extra == 'all') + - watchdog>=4.0.0 ; extra == 'all' + - click>=7.0 ; extra == 'cli' + - black>=20.8b0 ; extra == 'cli' + - rich>=9.0.0 ; extra == 'cli' + - libcst>=0.3.16 ; extra == 'codemods' + - hypothesis-crosshair>=0.0.30 ; extra == 'crosshair' + - crosshair-tool>=0.0.111 ; extra == 'crosshair' + - python-dateutil>=1.4 ; extra == 'dateutil' + - django>=5.2 ; extra == 'django' + - dpcontracts>=0.4 ; extra == 'dpcontracts' + - black>=20.8b0 ; extra == 'ghostwriter' + - lark>=0.10.1 ; extra == 'lark' + - numpy>=1.23.2 ; extra == 'numpy' + - pandas>=1.5 ; extra == 'pandas' + - pytest>=4.6 ; extra == 'pytest' + - pytz>=2014.1 ; extra == 'pytz' + - redis>=3.0.0 ; extra == 'redis' + - watchdog>=4.0.0 ; extra == 'watchdog' + - tzdata>=2026.5 ; (sys_platform == 'emscripten' and extra == 'zoneinfo') or (sys_platform == 'win32' and extra == 'zoneinfo') + requires_python: '>=3.11' - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e md5: 8b189310083baabfb622af68fd9d3ae3 @@ -3157,6 +6244,18 @@ packages: purls: [] size: 12129203 timestamp: 1720853576813 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-py310h44b86e0_2.conda + sha256: 9f07834f0c546ab14d885ce0366285f61f44e326c0edd1fc63b8294e113ae432 + md5: 72a381cbad04f24b1c2a43ef707f45b4 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 14459115 + timestamp: 1786545741408 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda sha256: 813298f2e54ef087dbfc9cc2e56e08ded41de65cff34c639cc8ba4e27e4540c9 md5: 268203e8b983fddb6412b36f2024e75c @@ -3168,6 +6267,17 @@ packages: purls: [] size: 12282786 timestamp: 1720853454991 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/icu-78.3-py311h3512406_2.conda + sha256: 54d921defd947adb58a90e10203d3bfe6c1f209f5f1a1ad2c6a9f7617f2fb59e + md5: b35bbb1b957440ed742f35fb96eb7f1c + depends: + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + purls: [] + size: 14688525 + timestamp: 1786545779464 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 md5: 5eb22c1d7b3fc4abb50d92d621583137 @@ -3178,6 +6288,16 @@ packages: purls: [] size: 11857802 timestamp: 1720853997952 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-py310h579977c_2.conda + sha256: 6cdb5dee54c72e56ab189fb3ad33cb28533553d42590e7e831160248f4416a43 + md5: a5efc0b42bb8b42e97d0a29ae3e3c187 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 14070242 + timestamp: 1786545847761 - pypi: https://files.pythonhosted.org/packages/e5/ae/2ad30f4652712c82f1c23423d79136fbce338932ad166d70c1efb86a5998/identify-2.6.14-py2.py3-none-any.whl name: identify version: 2.6.14 @@ -3212,6 +6332,18 @@ packages: - pytest>=8.3.2 ; extra == 'all' - flake8>=7.1.1 ; extra == 'all' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/58/a2/bb081bab032533a855d44de1d56f8e8426114ff1ba5d1f07a438a0a654f8/idna-3.20-py3-none-any.whl + name: idna + version: '3.20' + sha256: ab7ae7122974553370f0bdb919e1a960b2cd1bc1ef0276416d896db81c14582c + requires_dist: + - ruff>=0.16.0 ; extra == 'all' + - mypy>=1.11.2 ; extra == 'all' + - ty>=0.0.37 ; extra == 'all' + - pytest>=8.3.2 ; extra == 'all' + - hypothesis>=6.141.1 ; extra == 'all' + - coverage>=7.10.0 ; extra == 'all' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl name: iniconfig version: 2.1.0 @@ -3222,6 +6354,64 @@ packages: version: 2.3.0 sha256: f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/56/43/4ca9e49d27a1fcf6bece6f6aec0ea46bb9112489b93d4b688fb415457bdb/iniconfig-2.3.1-py3-none-any.whl + name: iniconfig + version: 2.3.1 + sha256: 9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/c0/56/4cc7fc9e9e3f38fd324f24f8afe0ad8bb5fa41283f37f1aaf9de0612c968/ipython-8.39.0-py3-none-any.whl + name: ipython + version: 8.39.0 + sha256: bb3c51c4fa8148ab1dea07a79584d1c854e234ea44aa1283bcb37bc75054651f + requires_dist: + - colorama ; sys_platform == 'win32' + - decorator + - exceptiongroup ; python_full_version < '3.11' + - jedi>=0.16 + - matplotlib-inline + - pexpect>4.3 ; sys_platform != 'emscripten' and sys_platform != 'win32' + - prompt-toolkit>=3.0.41,<3.1.0 + - pygments>=2.4.0 + - stack-data + - traitlets>=5.13.0 + - typing-extensions>=4.6 ; python_full_version < '3.12' + - black ; extra == 'black' + - docrepr ; extra == 'doc' + - exceptiongroup ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - ipykernel ; extra == 'doc' + - ipython[test] ; extra == 'doc' + - matplotlib ; extra == 'doc' + - setuptools>=18.5 ; extra == 'doc' + - sphinx-rtd-theme ; extra == 'doc' + - sphinx>=1.3 ; extra == 'doc' + - sphinxcontrib-jquery ; extra == 'doc' + - tomli ; python_full_version < '3.11' and extra == 'doc' + - typing-extensions ; extra == 'doc' + - ipykernel ; extra == 'kernel' + - nbconvert ; extra == 'nbconvert' + - nbformat ; extra == 'nbformat' + - ipywidgets ; extra == 'notebook' + - notebook ; extra == 'notebook' + - ipyparallel ; extra == 'parallel' + - qtconsole ; extra == 'qtconsole' + - pytest ; extra == 'test' + - pytest-asyncio<0.22 ; extra == 'test' + - testpath ; extra == 'test' + - pickleshare ; extra == 'test' + - packaging ; extra == 'test' + - ipython[test] ; extra == 'test-extra' + - curio ; extra == 'test-extra' + - jupyter-ai ; extra == 'test-extra' + - matplotlib!=3.2.0 ; extra == 'test-extra' + - nbformat ; extra == 'test-extra' + - numpy>=1.23 ; extra == 'test-extra' + - pandas ; extra == 'test-extra' + - trio ; extra == 'test-extra' + - matplotlib ; extra == 'matplotlib' + - ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole] ; extra == 'all' + - ipython[test,test-extra] ; extra == 'all' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/08/2a/5628a99d04acb2d2f2e749cdf4ea571d2575e898df0528a090948018b726/ipython-9.5.0-py3-none-any.whl name: ipython version: 9.5.0 @@ -3356,6 +6546,52 @@ packages: - matplotlib>3.9 ; extra == 'matplotlib' - ipython[doc,matplotlib,test,test-extra] ; extra == 'all' requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/2d/1e/65b59cf518c106aa755e7f7da3099027738687a862ec785060702a481320/ipython-9.17.1-py3-none-any.whl + name: ipython + version: 9.17.1 + sha256: 6d1645743cfd1a07eb695d85aa2b5fa66721f8cbae9431d4049f7084bbf06509 + requires_dist: + - colorama>=0.4.4 ; sys_platform == 'win32' + - ipython-pygments-lexers>=1.0.0 + - jedi>=0.18.2 + - matplotlib-inline>=0.1.6 + - pexpect>4.6 ; sys_platform != 'emscripten' and sys_platform != 'win32' + - prompt-toolkit>=3.0.41,<3.1.0 + - psutil>=7 ; sys_platform != 'cygwin' and sys_platform != 'emscripten' + - pygments>=2.14.0 + - stack-data>=0.6.0 + - traitlets>=5.13.0 + - typing-extensions>=4.6 ; python_full_version < '3.12' + - black ; extra == 'black' + - docrepr ; extra == 'doc' + - exceptiongroup ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - ipykernel ; extra == 'doc' + - ipython[matplotlib,test] ; extra == 'doc' + - setuptools>=80.0 ; extra == 'doc' + - sphinx-toml==0.0.4 ; extra == 'doc' + - sphinx-rtd-theme>=0.1.8 ; extra == 'doc' + - sphinx>=8.0 ; extra == 'doc' + - typing-extensions ; extra == 'doc' + - pytest>=7.0.0 ; extra == 'test' + - pytest-asyncio>=1.0.0 ; extra == 'test' + - testpath>=0.2 ; extra == 'test' + - packaging>=23.0.0 ; extra == 'test' + - setuptools>=80.0 ; extra == 'test' + - ipython[test] ; extra == 'test-extra' + - curio ; extra == 'test-extra' + - jupyter-ai ; extra == 'test-extra' + - ipython[matplotlib] ; extra == 'test-extra' + - nbformat ; extra == 'test-extra' + - nbclient ; extra == 'test-extra' + - ipykernel>6.30 ; extra == 'test-extra' + - numpy>=2.0 ; extra == 'test-extra' + - pandas>2.1 ; extra == 'test-extra' + - trio>=0.22.0 ; extra == 'test-extra' + - matplotlib>3.9 ; extra == 'matplotlib' + - ipython[doc,matplotlib,test,test-extra] ; extra == 'all' + - argcomplete>=3.0 ; extra == 'all' + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl name: ipython-pygments-lexers version: 1.1.1 @@ -3403,11 +6639,64 @@ packages: - docopt ; extra == 'testing' - pytest<9.0.0 ; extra == 'testing' requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/be/c7/71eb0d51cdcbb63b857c931aa4e9218b078b5d9f8ecd383b234f28fe7b0f/jedi-0.20.1-py2.py3-none-any.whl + name: jedi + version: 0.20.1 + sha256: 0fb16d86c4a4c73c37ba518c77419975e30fcc620658a8d14fbb5720cdd34142 + requires_dist: + - parso>=0.8.7,<0.9.0 + - django ; extra == 'dev' + - attrs ; extra == 'dev' + - colorama ; extra == 'dev' + - docopt ; extra == 'dev' + - flake8==7.1.2 ; extra == 'dev' + - pytest<9.0.0 ; extra == 'dev' + - types-setuptools==80.9.0.20250529 ; extra == 'dev' + - typing-extensions ; extra == 'dev' + - zuban==0.7.0 ; extra == 'dev' + - jinja2==3.1.6 ; extra == 'docs' + - markupsafe==3.0.3 ; extra == 'docs' + - pygments==2.20.0 ; extra == 'docs' + - sphinx==9.1.0 ; extra == 'docs' + - alabaster==1.0.0 ; extra == 'docs' + - babel==2.18.0 ; extra == 'docs' + - certifi==2026.4.22 ; extra == 'docs' + - charset-normalizer==3.4.7 ; extra == 'docs' + - docutils==0.22.4 ; extra == 'docs' + - idna==3.13 ; extra == 'docs' + - imagesize==2.0.0 ; extra == 'docs' + - iniconfig==2.3.0 ; extra == 'docs' + - packaging==26.2 ; extra == 'docs' + - pluggy==1.6.0 ; extra == 'docs' + - pytest==9.0.3 ; extra == 'docs' + - requests==2.33.1 ; extra == 'docs' + - roman-numerals==4.1.0 ; extra == 'docs' + - snowballstemmer==3.0.1 ; extra == 'docs' + - sphinx-rtd-theme==3.1.0 ; extra == 'docs' + - sphinxcontrib-applehelp==2.0.0 ; extra == 'docs' + - sphinxcontrib-devhelp==2.0.0 ; extra == 'docs' + - sphinxcontrib-htmlhelp==2.1.0 ; extra == 'docs' + - sphinxcontrib-jquery==4.1 ; extra == 'docs' + - sphinxcontrib-jsmath==1.0.1 ; extra == 'docs' + - sphinxcontrib-qthelp==2.0.0 ; extra == 'docs' + - sphinxcontrib-serializinghtml==2.0.0 ; extra == 'docs' + - urllib3==2.6.3 ; extra == 'docs' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl name: jmespath version: 1.1.0 sha256: a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64 requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-h7cc23a3_1.conda + sha256: dd053c96dcb0dcfd59422aefea9d2fe937a190167f34fba7893ec1e10a7e8963 + md5: ba55d1b89fd7775e67de8291029b4059 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: LGPL-2.1-or-later + purls: [] + size: 135295 + timestamp: 1786739238128 - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 md5: b38117a3c920364aff79f870c984b4a3 @@ -3418,6 +6707,15 @@ packages: purls: [] size: 134088 timestamp: 1754905959823 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h5bc82ec_1.conda + sha256: 3999b1472f1e549a0b766428e2823abf20626aac5314b474c9b76bf6eb679def + md5: 6d9be306ecb8dfc9ff46f02cb367d5c2 + depends: + - libgcc >=15 + license: LGPL-2.1-or-later + purls: [] + size: 129546 + timestamp: 1786739205968 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.3-h86ecc28_0.conda sha256: 5ce830ca274b67de11a7075430a72020c1fb7d486161a82839be15c2b84e9988 md5: e7df0aab10b9cbb73ab2a467ebfaf8c7 @@ -3442,6 +6740,36 @@ packages: version: 1.4.9 sha256: b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098 requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/30/ae/30b94088722e7bce8b821cd5ee935a87f80023e7243125f1accf98e39bf7/kiwisolver-1.5.1-cp310-cp310-macosx_11_0_arm64.whl + name: kiwisolver + version: 1.5.1 + sha256: 0a4faea5c6db201c6a21391d2ac926ea97acf7dacdbc3c417189e1adb1a00837 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/64/96/26efc04348c0f332b6e6c471dff132bb8b18b2df494a1b65826601674e62/kiwisolver-1.5.1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + name: kiwisolver + version: 1.5.1 + sha256: c3a4e41e3096bf1f0f1b76e2ffd6d828d6547f574f702d59bdbef7acfa59db9c + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/8a/3e/053bdc3c9abdb8f2606225eda398adca25c0c91ab90add8222a69db65ee0/kiwisolver-1.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + name: kiwisolver + version: 1.5.1 + sha256: 34ec467940442c9943016fb2d4c81d1ba84351eeca2f1a78f8bc87f1ba0d414c + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a1/13/08188f0cafa3a800403e4ff62b9aad4e7a17f9c4c7e080dc8f18c64794cf/kiwisolver-1.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: kiwisolver + version: 1.5.1 + sha256: 18a0cfb124546a4c2e6087c5f3029c7f44b37c85b142e0ced71f73a7599ac208 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e1/f4/b3007a3ed5c9be73f81161140684cf7d9bdb9c4b632f5f484d2a1c713fb9/kiwisolver-1.5.1-cp314-cp314-macosx_11_0_arm64.whl + name: kiwisolver + version: 1.5.1 + sha256: c2306e8bb53601979fcb3fa09cc65e031876d9ae01eff2fcbcd7a84ef94d5bc1 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/f1/0e/9f394d693be549fa3fab62498c2778294595991047adc3dcf10aa99b91c0/kiwisolver-1.5.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl + name: kiwisolver + version: 1.5.1 + sha256: e05c2f7925f1d88778e53cb44f14e0223204a3bdd09a41664750363acfb1f2ef + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 md5: 3f43953b7d3fb3aaa1d0d0723d91e368 @@ -3457,6 +6785,22 @@ packages: purls: [] size: 1370023 timestamp: 1719463201255 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-hbc21106_2.conda + sha256: 2a5c38c85e63df84c4e69ee71439841ce570d259ae3060627bb9a49a938d66f4 + md5: 53318d715316929a574f83591308b1f8 + depends: + - __glibc >=2.17,<3.0.a0 + - keyutils >=1.6.3,<2.0a0 + - libedit >=3.1.20250104,<3.2.0a0 + - libedit >=3.1.20250104,<4.0a0 + - libgcc >=15 + - libstdcxx >=15 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1394333 + timestamp: 1786762112514 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda sha256: 0ec272afcf7ea7fbf007e07a3b4678384b7da4047348107b2ae02630a570a815 md5: 29c10432a2ca1472b53f299ffb2ffa37 @@ -3472,6 +6816,21 @@ packages: purls: [] size: 1474620 timestamp: 1719463205834 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.22.2-h095d8e5_2.conda + sha256: 601d79af94d9562ba70e2d4947034ba159aae293a3c860855335a7c76c14400e + md5: a4d0da122ba952abf76981e76d0d283c + depends: + - keyutils >=1.6.3,<2.0a0 + - libedit >=3.1.20250104,<3.2.0a0 + - libedit >=3.1.20250104,<4.0a0 + - libgcc >=15 + - libstdcxx >=15 + - openssl >=3.5.7,<4.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1538590 + timestamp: 1786762058856 - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda sha256: 1a620f27d79217c1295049ba214c2f80372062fd251b569e9873d4a953d27554 md5: 0be7c6e070c19105f966d3758448d018 @@ -3484,6 +6843,19 @@ packages: purls: [] size: 676044 timestamp: 1752032747103 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.46.1-default_hbd61a6d_103.conda + sha256: 901649b1edbe841fd4ee7e2d491a05184ed540eb01cdae506872f344babc1d28 + md5: 2362c70e7e79827c32db35547d9c179b + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.46.1 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 742109 + timestamp: 1791565468810 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-hd32f0e1_5.conda sha256: cc03f3e2d5d48f1193a2d0822971b085d583327d6e20f2a5cf7d030ffdb35f9a md5: 7c87c0b72575b30626a6dc5b49229f0c @@ -3495,6 +6867,18 @@ packages: purls: [] size: 782949 timestamp: 1762674873740 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.46.1-default_h1979696_103.conda + sha256: 516808982ad26ebdc4a9d8f71ee86ea55d0bc95382b7bca80620e0ac220971f3 + md5: ca781d8b499c39ddfe10e55a0347d6d9 + depends: + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-aarch64 2.46.1 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 905349 + timestamp: 1791565559147 - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda sha256: 412381a43d5ff9bbed82cd52a0bbca5b90623f62e41007c9c42d3870c60945ff md5: 9344155d33912347b37f0ae6c410a835 @@ -3507,6 +6891,18 @@ packages: purls: [] size: 264243 timestamp: 1745264221534 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.2.0-hdb68285_0.conda + sha256: bf9fdebf55d8bc99d83531cdffda00703f4dc5f93a1a956768c147362c72feda + md5: fb9d356b1a57d6d54768be7ebd5fce09 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 271158 + timestamp: 1785036167977 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-hfdc4d58_1.conda sha256: f01df5bbf97783fac9b89be602b4d02f94353f5221acfd80c424ec1c9a8d276c md5: 60dceb7e876f4d74a9cbd42bbbc6b9cf @@ -3518,6 +6914,17 @@ packages: purls: [] size: 227184 timestamp: 1745265544057 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.2.0-h52b7260_0.conda + sha256: 0176a71d64fcb5aec43c9e8ab4ae72faa6c6b0b1cda8f40b65e19429ce13c84d + md5: 9fcfe6be4f752b72be7abc69842ca396 + depends: + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 244850 + timestamp: 1785038308130 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-hd64df32_1.conda sha256: 12361697f8ffc9968907d1a7b5830e34c670e4a59b638117a2cdfed8f63a38f8 md5: a74332d9b60b62905e3d30709df08bf1 @@ -3529,6 +6936,31 @@ packages: purls: [] size: 188306 timestamp: 1745264362794 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.2.0-h1eee2c3_0.conda + sha256: c97aa17d16d2ac332ba9f184e82ce8f72dcb10e9a10c5f299030be2d44e191b9 + md5: e429aec4037d5cb8fd34ded9f5dadd39 + depends: + - __osx >=11.0 + - libcxx >=19 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 166477 + timestamp: 1785036480092 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda + sha256: 205c4f19550f3647832ec44e35e6d93c8c206782bdd620c1d7cf66237580ff9c + md5: 49c553b47ff679a6a1e9fc80b9c5a2d4 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 4518030 + timestamp: 1770902209173 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda sha256: cb83980c57e311783ee831832eb2c20ecb41e7dee6e86e8b70b8cef0e43eab55 md5: d4a250da4737ee127fb1fa6452a9002e @@ -3543,6 +6975,19 @@ packages: purls: [] size: 4523621 timestamp: 1749905341688 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcups-2.3.3-h4f2b762_6.conda + sha256: 41b04f995c9f63af8c4065a35931e46cbc2fdd6b9bf7e4c19f90d53cbb2bc8e5 + md5: 67828c963b17db7dc989fe5d509ef04a + depends: + - krb5 >=1.22.2,<1.23.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 4553739 + timestamp: 1770903929794 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libcups-2.3.3-h5cdc715_5.conda sha256: f3282d27be35e5d29b5b798e5136427ec798916ee6374499be7b7682c8582b72 md5: ac0333d338076ef19170938bbaf97582 @@ -3566,6 +7011,16 @@ packages: purls: [] size: 568742 timestamp: 1761852287381 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-23.1.3-h55c6f16_0.conda + sha256: 2d9d31172d7919bd276a8d1be338de567dd5afdd181e8d3d3d8c2d8695e09de2 + md5: 424521e52e65ef7f6cf957c4966e6386 + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 577501 + timestamp: 1791337239735 - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda sha256: 8420748ea1cc5f18ecc5068b4f24c7a023cc9b20971c99c824ba10641fb95ddf md5: 64f0c503da58ec25ebd359e4d990afa8 @@ -3577,6 +7032,17 @@ packages: purls: [] size: 72573 timestamp: 1747040452262 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-hd45a770_1.conda + sha256: 82e134c8a08b1eed9a2ed8ab578b89aa1730dcde3dea8dd87645ed0637878e54 + md5: 40f9b31aa9cf007789867df0decd0492 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 73710 + timestamp: 1785908694612 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-h1af38f5_0.conda sha256: 48814b73bd462da6eed2e697e30c060ae16af21e9fbed30d64feaf0aad9da392 md5: a9138815598fe6b91a1d6782ca657b0c @@ -3587,6 +7053,16 @@ packages: purls: [] size: 71117 timestamp: 1761979776756 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.25-hfa851ae_1.conda + sha256: 4cb3da1d4ce7540604219d2a016253777da66c1a710c41557bc708aae7f54a75 + md5: 8cdf7f7e4908c9b2cf50c58966f2ff64 + depends: + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 71628 + timestamp: 1785908730449 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.24-h5773f1b_0.conda sha256: 417d52b19c679e1881cce3f01cad3a2d542098fa2d6df5485aac40f01aede4d1 md5: 3baf58a5a87e7c2f4d243ce2f8f2fe5c @@ -3597,6 +7073,28 @@ packages: purls: [] size: 54790 timestamp: 1747040549847 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-he7e0567_1.conda + sha256: d896f4aa4ce4c590c2838678cb1917356fdb461d2a189991c0280c818c362172 + md5: 78650d671cb56909bb3e5c13bce310f9 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 55727 + timestamp: 1785909153744 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.129-h7cc23a3_0.conda + sha256: ea46b0ca0fa16af1f8b329b740e6cd8b4577c5378fa8717b28a885f70668633d + md5: 64cc91512b6278c315349dc13c53f680 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libpciaccess >=0.19,<0.20.0a0 + license: MIT + license_family: MIT + purls: [] + size: 313461 + timestamp: 1786684701973 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdrm-2.4.125-he30d5cf_1.conda sha256: 4e6cdb5dd37db794b88bec714b4418a0435b04d14e9f7afc8cc32f2a3ced12f2 md5: 2079727b538f6dd16f3fa579d4c3c53f @@ -3608,6 +7106,17 @@ packages: purls: [] size: 344548 timestamp: 1757212128414 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libdrm-2.4.129-h5bc82ec_0.conda + sha256: 2a0ee6c7648de5a5b7de2b861aa1aba01c860218f8b97af63ac6c81b0175b960 + md5: 3f34f401fa04c0ad38ff15b046c20596 + depends: + - libgcc >=15 + - libpciaccess >=0.19,<0.20.0a0 + license: MIT + license_family: MIT + purls: [] + size: 346564 + timestamp: 1786684730263 - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 md5: c277e0a4d549b03ac1e9d6cbbe3d017b @@ -3619,8 +7128,21 @@ packages: license: BSD-2-Clause license_family: BSD purls: [] - size: 134676 - timestamp: 1738479519902 + size: 134676 + timestamp: 1738479519902 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20260512-pl5321h373387f_0.conda + sha256: 45301f14f4eb66da57cfaf2d265f3eeee73f8b07e99614ee3afc5b26eea91126 + md5: 27dbd7e2984b8fe48e069379636ccfdd + depends: + - ncurses + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - ncurses >=6.6,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 135877 + timestamp: 1791327370514 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda sha256: c0b27546aa3a23d47919226b3a1635fccdb4f24b94e72e206a751b33f46fd8d6 md5: fb640d776fc92b682a14e001980825b1 @@ -3633,6 +7155,28 @@ packages: purls: [] size: 148125 timestamp: 1738479808948 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20260512-pl5321hc48eb74_0.conda + sha256: 58fd637d0219016bb69edf2973b089975de283e8a38149d69ef586b8fc4ad90b + md5: 3ffb447961508502fc81e063ca2071cb + depends: + - ncurses + - libgcc >=14 + - ncurses >=6.6,<7.0a0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 149680 + timestamp: 1791327362947 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_5.conda + sha256: 13d3fde9c1dcf254968cc70652222a43f25d04e69555ccf855553110e753288e + md5: 3a1b41c4591a0a1734382af3e2b8bc08 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_5 + license: LicenseRef-libglvnd + purls: [] + size: 46694 + timestamp: 1787310030923 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-1.7.0-hd24410f_2.conda sha256: 8962abf38a58c235611ce356b9899f6caeb0352a8bce631b0bcc59352fda455e md5: cf105bce884e4ef8c8ccdca9fe6695e7 @@ -3642,6 +7186,27 @@ packages: purls: [] size: 53551 timestamp: 1731330990477 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-1.7.0-hd24410f_5.conda + sha256: 8d80ccab2fd622ae38931691c27952e56a5d3007ff920efd9f3ff52e2c81a251 + md5: 911931482b914643d0a602e0d7a45813 + depends: + - libglvnd 1.7.0 hd24410f_5 + license: LicenseRef-libglvnd + purls: [] + size: 53909 + timestamp: 1787309995086 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_5.conda + sha256: fd2794bbcff7e692fb476c17886702702893d074071d429217bad7cfb072abfd + md5: 577800fc8c9d8b7d9727246bbfde704e + depends: + - __glibc >=2.17,<3.0.a0 + - libegl 1.7.0 ha4b6fd6_5 + - libgl-devel 1.7.0 ha4b6fd6_5 + - xorg-libx11 + license: LicenseRef-libglvnd + purls: [] + size: 31242 + timestamp: 1787310065866 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-devel-1.7.0-hd24410f_2.conda sha256: 9c8e9d2289316741d037f0c5003de42488780d181453543f75497dd5a4891c7c md5: cd8877e3833ba1bfac2fbaa5ae72c226 @@ -3653,6 +7218,17 @@ packages: purls: [] size: 30397 timestamp: 1731331017398 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-devel-1.7.0-hd24410f_5.conda + sha256: ae8dcab97d4905754cb70ecc345dc06df6386df0a1f1d578ec94bb987db2a4d6 + md5: 51bc749a4436ed56a8843f78940c5f49 + depends: + - libegl 1.7.0 hd24410f_5 + - libgl-devel 1.7.0 hd24410f_5 + - xorg-libx11 + license: LicenseRef-libglvnd + purls: [] + size: 31156 + timestamp: 1787310028267 - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda sha256: da2080da8f0288b95dd86765c801c6e166c4619b910b11f9a8446fb852438dc2 md5: 4211416ecba1866fab0c6470986c22d6 @@ -3666,6 +7242,19 @@ packages: purls: [] size: 74811 timestamp: 1752719572741 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.5-hd2095e1_0.conda + sha256: 6de73033a7c5f712c9c5a54124e4581e025691029835e5bceaf10a333a7e7bc0 + md5: 72cfd2b95b0540247220b42f58a148d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + constrains: + - expat 2.8.5.* + license: MIT + license_family: MIT + purls: [] + size: 77893 + timestamp: 1790346247149 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.1-hfae3067_0.conda sha256: 378cabff44ea83ce4d9f9c59f47faa8d822561d39166608b3e65d1e06c927415 md5: f75d19f3755461db2eb69401f5514f4c @@ -3678,6 +7267,18 @@ packages: purls: [] size: 74309 timestamp: 1752719762749 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.8.5-h4154aff_0.conda + sha256: d155b9ee74abac0865095a66ebb008be967937a6deea6e7666ba01ead4a155fc + md5: e5a973222327d9183c25eddfc3a80828 + depends: + - libgcc >=15 + constrains: + - expat 2.8.5.* + license: MIT + license_family: MIT + purls: [] + size: 79463 + timestamp: 1790346233159 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.1-hec049ff_0.conda sha256: 8fbb17a56f51e7113ed511c5787e0dec0d4b10ef9df921c4fd1cccca0458f648 md5: b1ca5f21335782f71a8bd69bdc093f67 @@ -3690,6 +7291,18 @@ packages: purls: [] size: 65971 timestamp: 1752719657566 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.5-h6111c0a_0.conda + sha256: 50aa761b83fc7a116ca88fe0cb4cefce5abababb10a926bf8afbafd5c298d102 + md5: 5383c550b785bdaafcc4eaf638071a7c + depends: + - __osx >=11.0 + constrains: + - expat 2.8.5.* + license: MIT + license_family: MIT + purls: [] + size: 69565 + timestamp: 1790370026495 - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda sha256: 764432d32db45466e87f10621db5b74363a9f847d2b8b1f9743746cd160f06ab md5: ede4673863426c0883c0063d853bbd85 @@ -3701,6 +7314,17 @@ packages: purls: [] size: 57433 timestamp: 1743434498161 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.7.0-h81df57d_1.conda + sha256: c8c7583ef063bc3c430f1d48298e5ad24f796a35c22ed5b14183325825a61106 + md5: 6525a0b06aa4fd390795f0740636a9dd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 68004 + timestamp: 1787753412298 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.5.2-hd65408f_0.conda sha256: 6c3332e78a975e092e54f87771611db81dcb5515a3847a3641021621de76caea md5: 0c5ad486dcfb188885e3cf8ba209b97b @@ -3711,6 +7335,16 @@ packages: purls: [] size: 55586 timestamp: 1760295405021 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.7.0-hdaad0be_1.conda + sha256: b1879d78b622c5f4817fd6d77fe7ae3ab7b501542e3c2f4ac77198c54c62c189 + md5: f9e5b3e446b526b34a7e25ecec08efd9 + depends: + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 63137 + timestamp: 1787753382033 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda sha256: 9b8acdf42df61b7bfe8bdc545c016c29e61985e79748c64ad66df47dbc2e295f md5: 411ff7cd5d1472bba0f55c0faf04453b @@ -3721,6 +7355,16 @@ packages: purls: [] size: 40251 timestamp: 1760295839166 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.7.0-h47dc5ef_1.conda + sha256: 2783389a7b9dda04c62cc54c6bbeb03dd3cef24b3fc642715d02a3fa19464a9a + md5: 216bbcc23c11e9695b3db4a8771d76eb + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 43637 + timestamp: 1787753403688 - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda sha256: 4641d37faeb97cf8a121efafd6afd040904d4bca8c46798122f417c31d5dfbec md5: f4084e4e6577797150f9b04a4560ceb0 @@ -3730,6 +7374,15 @@ packages: purls: [] size: 7664 timestamp: 1757945417134 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_2.conda + sha256: fb12ecb46c30d18d928c0e8fc346ab13b5f6fc8a9cacae4f2f4bb188782586f5 + md5: f8054e759d0ddddaf34a5c8fedc900a1 + depends: + - libfreetype6 >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 8407 + timestamp: 1786641007099 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype-2.14.1-h8af1aa0_0.conda sha256: 342c07e4be3d09d04b531c889182a11a488e7e9ba4b75f642040e4681c1e9b98 md5: 1e61fb236ccd3d6ccaf9e91cb2d7e12d @@ -3739,6 +7392,15 @@ packages: purls: [] size: 7753 timestamp: 1757945484817 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype-2.14.3-h8af1aa0_2.conda + sha256: 1e6ba895a397ee4fc1646f21000903ad13d93e9e6d9a7fb4dcef1baa837970bf + md5: 9d7e520ae06d08185ef2d500ff116d30 + depends: + - libfreetype6 >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 8409 + timestamp: 1786640943558 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.1-hce30654_0.conda sha256: 9de25a86066f078822d8dd95a83048d7dc2897d5d655c0e04a8a54fca13ef1ef md5: f35fb38e89e2776994131fbf961fa44b @@ -3748,6 +7410,15 @@ packages: purls: [] size: 7810 timestamp: 1757947168537 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_2.conda + sha256: 19ce8bd320414cb6b9889ecbf48a3ded848b0d1068b76ff6bea099bd7387d6f3 + md5: ee1fc5bba400ff0cae27fbf141a1ae0c + depends: + - libfreetype6 >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 8367 + timestamp: 1786641013393 - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda sha256: 4a7af818a3179fafb6c91111752954e29d3a2a950259c14a2fc7ba40a8b03652 md5: 8e7251989bca326a28f4a5ffbd74557a @@ -3762,6 +7433,20 @@ packages: purls: [] size: 386739 timestamp: 1757945416744 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h5e6c136_2.conda + sha256: ec607dd5445dd17ff6bf8b7fe6832e5504c226dd91d3aaea7ba83569808b0ce4 + md5: b72a266a9317036fd0464cb98b027cd0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - freetype >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 387671 + timestamp: 1786641006460 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype6-2.14.1-hdae7a39_0.conda sha256: cedc83d9733363aca353872c3bfed2e188aa7caf57b57842ba0c6d2765652b7c md5: 9c2f56b6e011c6d8010ff43b796aab2f @@ -3775,6 +7460,19 @@ packages: purls: [] size: 423210 timestamp: 1757945484108 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype6-2.14.3-h9cc7050_2.conda + sha256: d29eac57042f2362d4e45c63a4a78d64a30e5284b1da29b99e0c1526671dd4c3 + md5: f7ee9de31f98281a3e451e496fbee3b5 + depends: + - libgcc >=15 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - freetype >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 445035 + timestamp: 1786640942903 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.1-h6da58f4_0.conda sha256: cc4aec4c490123c0f248c1acd1aeab592afb6a44b1536734e20937cda748f7cd md5: 6d4ede03e2a8e20eb51f7f681d2a2550 @@ -3788,6 +7486,19 @@ packages: purls: [] size: 346703 timestamp: 1757947166116 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-h2ed5691_2.conda + sha256: d9b203d6484ad491b5b5f33d49a9d7a91189d776a9adae53d2b63af18ec11e6a + md5: 881cfb44ea02cc9849f612725a959660 + depends: + - __osx >=11.0 + - libpng >=1.6.58,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - freetype >=2.14.3 + license: GPL-2.0-only OR FTL + purls: [] + size: 340923 + timestamp: 1786641012794 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_5.conda sha256: 0caed73aac3966bfbf5710e06c728a24c6c138605121a3dacb2e03440e8baa6a md5: 264fbfba7fb20acf3b29cde153e345ce @@ -3802,6 +7513,20 @@ packages: purls: [] size: 824191 timestamp: 1757042543820 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-16.2.0-ha9f2e26_7.conda + sha256: e031634c3a928f9594eba868bc46ef5230d6571a364a0c63215b06c4563bfcea + md5: 735629b400b70eeab21a2ba8f5b1c2ce + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==16.2.0=*_7 + - libgomp 16.2.0 he0feb66_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 1058444 + timestamp: 1790303109576 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.2.0-he277a41_7.conda sha256: 616f5960930ad45b48c57f49c3adddefd9423674b331887ef0e69437798c214b md5: afa05d91f8d57dd30985827a09c21464 @@ -3815,6 +7540,19 @@ packages: purls: [] size: 510719 timestamp: 1759967448307 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-16.2.0-h205dda4_7.conda + sha256: eae7a3449d5a3b5351c5427137c076cc3fc826645c7bfc42dc10772b783826f1 + md5: 489d5c9dc62265b3f6a57f9a106f1caf + depends: + - _openmp_mutex >=4.5 + constrains: + - libgcc-ng ==16.2.0=*_7 + - libgomp 16.2.0 h8acb6b2_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 627793 + timestamp: 1790302345214 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_5.conda sha256: f54bb9c3be12b24be327f4c1afccc2969712e0b091cdfbd1d763fb3e61cda03f md5: 069afdf8ea72504e48d23ae1171d951c @@ -3825,6 +7563,16 @@ packages: purls: [] size: 29187 timestamp: 1757042549554 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-16.2.0-h69a702a_7.conda + sha256: 03b54d7008de080a17cc2174b0e4acd6d06e1c981b2eb19846f46d8f59966034 + md5: 2005515ac9340f816fb2ac7e83afe553 + depends: + - libgcc 16.2.0 ha9f2e26_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 28616 + timestamp: 1790303114248 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.2.0-he9431aa_7.conda sha256: 7d98979b2b5698330007b0146b8b4b95b3790378de12129ce13c9fc88c1ef45a md5: a5ce1f0a32f02c75c11580c5b2f9258a @@ -3835,6 +7583,38 @@ packages: purls: [] size: 29261 timestamp: 1759967452303 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-16.2.0-he9431aa_7.conda + sha256: d7502682cfce6f51aabd40bba63cd6b46db3d3e4b67abdb00f54ae72332523db + md5: 2fb8fda15618277477f6dfd0e3c0d4a9 + depends: + - libgcc 16.2.0 h205dda4_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 28711 + timestamp: 1790302349238 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h5fbf134_12.conda + sha256: 245be793e831170504f36213134f4c24eedaf39e634679809fd5391ad214480b + md5: 88c1c66987cd52a712eea89c27104be6 + depends: + - __glibc >=2.17,<3.0.a0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - icu >=78.1,<79.0a0 + - libexpat >=2.7.3,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libpng >=1.6.53,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + license: GD + license_family: BSD + purls: [] + size: 177306 + timestamp: 1766331805898 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h6f5c62b_11.conda sha256: 19e5be91445db119152217e8e8eec4fd0499d854acc7d8062044fb55a70971cd md5: 68fc66282364981589ef36868b1a7c78 @@ -3856,6 +7636,27 @@ packages: purls: [] size: 177082 timestamp: 1737548051015 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgd-2.3.3-h88aa843_12.conda + sha256: 8b7dfd29a8ce42d28c47a06dcaa7c21f6dad751b72d2352668d680b6ec951630 + md5: b4c9083a19a45047173f380624c7e5f3 + depends: + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - icu >=78.1,<79.0a0 + - libexpat >=2.7.3,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libgcc >=14 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libpng >=1.6.53,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + license: GD + license_family: BSD + purls: [] + size: 195554 + timestamp: 1766331786625 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgd-2.3.3-hc8d7b1d_11.conda sha256: 7e199bb390f985b34aee38cdb1f0d166abc09ed44bd703a1b91a3c6cd9912d45 md5: d256b0311b7a207a2c6b68d2b399f707 @@ -3876,6 +7677,28 @@ packages: purls: [] size: 191033 timestamp: 1737548098172 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-h05bcc79_12.conda + sha256: 269edce527e204a80d3d05673301e0207efcd0dbeebc036a118ceb52690d6341 + md5: fa4a92cfaae9570d89700a292a9ca714 + depends: + - __osx >=11.0 + - fontconfig >=2.15.0,<3.0a0 + - fonts-conda-ecosystem + - icu >=78.1,<79.0a0 + - libexpat >=2.7.3,<3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libiconv >=1.18,<2.0a0 + - libjpeg-turbo >=3.1.2,<4.0a0 + - libpng >=1.6.53,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + license: GD + license_family: BSD + purls: [] + size: 159247 + timestamp: 1766331953491 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgd-2.3.3-hb2c3a21_11.conda sha256: be038eb8dfe296509aee2df21184c72cb76285b0340448525664bc396aa6146d md5: 4581aa3cfcd1a90967ed02d4a9f3db4b @@ -3897,6 +7720,17 @@ packages: purls: [] size: 156868 timestamp: 1737548290283 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_5.conda + sha256: 7b2e7e31e8a4f5a01c45ecadcd8aa68c8f733b035240bc7ba9881835ef4bf7b4 + md5: 81141db127a106eb5a91df69a29c9918 + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_5 + - libglx 1.7.0 ha4b6fd6_5 + license: LicenseRef-libglvnd + purls: [] + size: 131988 + timestamp: 1787310049847 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-1.7.0-hd24410f_2.conda sha256: 3e954380f16255d1c8ae5da3bd3044d3576a0e1ac2e3c3ff2fe8f2f1ad2e467a md5: 0d00176464ebb25af83d40736a2cd3bb @@ -3907,6 +7741,27 @@ packages: purls: [] size: 145442 timestamp: 1731331005019 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-1.7.0-hd24410f_5.conda + sha256: f1814f83ebb510cd51147fa1578b7c77eaefcca3b1ce5e32c48b62c114e39de6 + md5: 643cbfb061e49e6608752d4892c98b13 + depends: + - libglvnd 1.7.0 hd24410f_5 + - libglx 1.7.0 hd24410f_5 + license: LicenseRef-libglvnd + purls: [] + size: 146493 + timestamp: 1787310012613 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_5.conda + sha256: 3bebdbeb3ce451287794dddd5cc4d7f4970aac200b2fb797f0d17ac727a71cb7 + md5: f5f7fc038c611a25b22758c0a40d25c9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgl 1.7.0 ha4b6fd6_5 + - libglx-devel 1.7.0 ha4b6fd6_5 + license: LicenseRef-libglvnd + purls: [] + size: 116212 + timestamp: 1787310061570 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-devel-1.7.0-hd24410f_2.conda sha256: ec5c3125b38295bad8acc80f793b8ee217ccb194338d73858be278db50ea82f1 md5: 5d8323dff6a93596fb6f985cf6e8521a @@ -3917,6 +7772,16 @@ packages: purls: [] size: 113925 timestamp: 1731331014056 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-devel-1.7.0-hd24410f_5.conda + sha256: 898b087c28da9cc52e1d1d31d941f12f5cb0810d5db50d7c2f14ff568473f8c7 + md5: 68857c172c3f6dba0afd52bc187a850e + depends: + - libgl 1.7.0 hd24410f_5 + - libglx-devel 1.7.0 hd24410f_5 + license: LicenseRef-libglvnd + purls: [] + size: 116134 + timestamp: 1787310023915 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.0-h1fed272_0.conda sha256: 33336bd55981be938f4823db74291e1323454491623de0be61ecbe6cf3a4619c md5: b8e4c93f4ab70c3b6f6499299627dbdc @@ -3933,6 +7798,22 @@ packages: purls: [] size: 3978602 timestamp: 1757403291664 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.90.0-h569388d_0.conda + sha256: 9e688e046550415c030c3d08ed9a3fd13a51d7bc56295283b858b6c3070bb9f8 + md5: c85e9539551f1fd21104e518d940c244 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libiconv >=1.18,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - glib >2.66 + license: LGPL-2.1-or-later + purls: [] + size: 4819130 + timestamp: 1789478025196 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.86.1-he84ff74_1.conda sha256: 5212c30d9e14a9480c7d25bf93ccca4db23d3794430c9be90e13124d9a8b1687 md5: f0fc1b2fa2e68b1309852e5c3c8e011d @@ -3948,6 +7829,21 @@ packages: purls: [] size: 4040523 timestamp: 1761874121589 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.90.1-h3584fe5_0.conda + sha256: d47ad408d845e32fc6692961f2e1c8e4ce72c86a0769cba340a8b02af638b7e0 + md5: c6c4abdcc2d525b5989c88f55f01ef5d + depends: + - libgcc >=15 + - libffi >=3.7.0,<3.8.0a0 + - libiconv >=1.18,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - glib >2.66 + license: LGPL-2.1-or-later + purls: [] + size: 4927714 + timestamp: 1791383970778 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.86.1-he69a767_1.conda sha256: 253ac4eca90006b19571f8c4766e8ebdad0f01f44de1bfa0472d3df9be9c8ac8 md5: acff031bb5b97602d2b7ef913a8ea076 @@ -3964,6 +7860,31 @@ packages: purls: [] size: 3677659 timestamp: 1761875607047 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.90.0-hccd281b_0.conda + sha256: 4ec64c476881b31f211842952f192208a3af0925a596da227f4d97fb9a63e0ac + md5: 8f63a5f35547d0c6d819830ebe0d3245 + depends: + - __osx >=11.0 + - libintl >=0.25.1,<1.0a0 + - libzlib >=1.3.2,<2.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libiconv >=1.18,<2.0a0 + - pcre2 >=10.47,<10.48.0a0 + constrains: + - glib >2.66 + license: LGPL-2.1-or-later + purls: [] + size: 4489939 + timestamp: 1789478088421 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_5.conda + sha256: 6eb77601a44b78c4631d886d54ef54f321c2bdc69fdefc4065a1e0491f030c76 + md5: cfcf11edcfd4acf0094771a9c3b8587f + depends: + - __glibc >=2.17,<3.0.a0 + license: LicenseRef-libglvnd + purls: [] + size: 133827 + timestamp: 1787310026653 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglvnd-1.7.0-hd24410f_2.conda sha256: 57ec3898a923d4bcc064669e90e8abfc4d1d945a13639470ba5f3748bd3090da md5: 9e115653741810778c9a915a2f8439e7 @@ -3971,6 +7892,24 @@ packages: purls: [] size: 152135 timestamp: 1731330986070 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglvnd-1.7.0-hd24410f_5.conda + sha256: 3d9447104ba1611cffbef1ce209a07f516db2eba0ed76d5af84fc344549b1fa1 + md5: f1b72cbc4b7096fc43e7e81cf6829763 + license: LicenseRef-libglvnd + purls: [] + size: 147361 + timestamp: 1787309990244 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_5.conda + sha256: d1f0d51aea6bcc5d915969cbed32e72a8ed6a95931b87357ef8d0866573688c4 + md5: 614e10aae180f87b84f0d1ca8ceb7d9e + depends: + - __glibc >=2.17,<3.0.a0 + - libglvnd 1.7.0 ha4b6fd6_5 + - xorg-libx11 >=1.8.13,<2.0a0 + license: LicenseRef-libglvnd + purls: [] + size: 79834 + timestamp: 1787310042550 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-1.7.0-hd24410f_2.conda sha256: 6591af640cb05a399fab47646025f8b1e1a06a0d4bbb4d2e320d6629b47a1c61 md5: 1d4269e233636148696a67e2d30dad2a @@ -3981,6 +7920,28 @@ packages: purls: [] size: 77736 timestamp: 1731330998960 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-1.7.0-hd24410f_5.conda + sha256: 4440c7ae251efa83bb7bc0d3fd25ba901640cc712b1284c75d7b8333e1c4b62e + md5: 67b3984e1892dc37d604e841cfd755d7 + depends: + - libglvnd 1.7.0 hd24410f_5 + - xorg-libx11 >=1.8.13,<2.0a0 + license: LicenseRef-libglvnd + purls: [] + size: 75476 + timestamp: 1787310005435 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_5.conda + sha256: aff3841e3404d78e1887fef988ca4842930c577399d566fa0980808756b1df51 + md5: fb00b4fb800f2d431303a5c1625ef9d0 + depends: + - __glibc >=2.17,<3.0.a0 + - libglx 1.7.0 ha4b6fd6_5 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-xorgproto + license: LicenseRef-libglvnd + purls: [] + size: 27698 + timestamp: 1787310053582 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-devel-1.7.0-hd24410f_2.conda sha256: 4bc28ecc38f30ca1ac66a8fb6c5703f4d888381ec46d3938b7c3383210061ec5 md5: 1f9ddbb175a63401662d1c6222cef6ff @@ -3992,6 +7953,17 @@ packages: purls: [] size: 26362 timestamp: 1731331008489 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-devel-1.7.0-hd24410f_5.conda + sha256: 5dcc18e1d8985fca5357df1da7f484bb9816ad4dfbf37f52e41e50c7dd2ac04e + md5: 7081f55f8ce4a6dd9971c58716b36a79 + depends: + - libglx 1.7.0 hd24410f_5 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-xorgproto + license: LicenseRef-libglvnd + purls: [] + size: 27417 + timestamp: 1787310016132 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_5.conda sha256: 125051d51a8c04694d0830f6343af78b556dd88cc249dfec5a97703ebfb1832d md5: dcd5ff1940cd38f6df777cac86819d60 @@ -4002,6 +7974,16 @@ packages: purls: [] size: 447215 timestamp: 1757042483384 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-16.2.0-he0feb66_7.conda + sha256: 92a0bb350c7be6a4eedcb4e111c60aefd176018e6b1c009a6e025dbe9bbcd758 + md5: d904c7ddd95e026254b4b80808ee9e12 + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 640566 + timestamp: 1790303046034 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.2.0-he277a41_7.conda sha256: 0a024f1e4796f5d90fb8e8555691dad1b3bdfc6ac3c2cd14d876e30f805fcac7 md5: 34cef4753287c36441f907d5fdd78d42 @@ -4010,6 +7992,146 @@ packages: purls: [] size: 450308 timestamp: 1759967379407 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-16.2.0-h8acb6b2_7.conda + sha256: 4406fd67c5beeb99b9372f9bf07aa3e40ca141e337eceebac3ff64aa72799494 + md5: 259ded79ac497cfbcf5434540e8b81b6 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 617342 + timestamp: 1790302273072 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-14.6.0-h23af247_2.conda + sha256: 3dea71a07100fd523ff173538577fd2e06a42beb5cb25e4a18ac9484773753ba + md5: 6a93147cab011f36d1659745c3f579e2 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.6,<2.0a0 + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libglib >=2.90.0,<3.0a0 + - libpng >=1.6.59,<1.7.0a0 + - libstdcxx >=15 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1463302 + timestamp: 1791900600568 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libharfbuzz-14.6.0-h8f7ccb3_2.conda + sha256: f7a3b4f030e84646c7ce6847363fee53a061086261b5131be660297e40704cf5 + md5: df2b84f08cd5afdacc5fb46e81839302 + depends: + - cairo >=1.18.6,<2.0a0 + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libglib >=2.90.1,<3.0a0 + - libpng >=1.6.59,<1.7.0a0 + - libstdcxx >=15 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1520848 + timestamp: 1791900588008 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-14.6.0-hcda0f7c_2.conda + sha256: 85e3052093357d1c22d086b0e2dbe629b35498b15a34affa3c92a27dc7de548a + md5: bb1ba75fa653dbf2f341943fede777c4 + depends: + - __osx >=11.0 + - cairo >=1.18.6,<2.0a0 + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libcxx >=21 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.90.0,<3.0a0 + - libpng >=1.6.59,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1052989 + timestamp: 1791900623707 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libharfbuzz-devel-14.6.0-h23af247_2.conda + sha256: 4470b63366a83f0cecd5c62d8caf0744674cf541ab33bf1d1165af2bfb9a26b7 + md5: 3cf459e930e6ea2e57bbf04be7fcb7c6 + depends: + - __glibc >=2.17,<3.0.a0 + - cairo >=1.18.6,<2.0a0 + - freetype + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libglib >=2.90.0,<3.0a0 + - libharfbuzz 14.6.0 h23af247_2 + - libpng >=1.6.59,<1.7.0a0 + - libstdcxx >=15 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 2250385 + timestamp: 1791900615728 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libharfbuzz-devel-14.6.0-h8f7ccb3_2.conda + sha256: 6c7ecf2802ea3bebace1ae5ecd8d625c185b09ea8efdfe081aa34358a3cb97b4 + md5: 6bdc851e2ba9de7555d955403c3218db + depends: + - cairo >=1.18.6,<2.0a0 + - freetype + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libgcc >=15 + - libglib >=2.90.1,<3.0a0 + - libharfbuzz 14.6.0 h8f7ccb3_2 + - libpng >=1.6.59,<1.7.0a0 + - libstdcxx >=15 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 2279787 + timestamp: 1791900602964 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libharfbuzz-devel-14.6.0-hcda0f7c_2.conda + sha256: ce8f8b52ec0d1f71d2da5610f5ef12781d1840768e40201dfb1e5d25d81f6c7a + md5: d651e898c4798a4a6539b3cd7ea6f89d + depends: + - __osx >=11.0 + - cairo >=1.18.6,<2.0a0 + - freetype + - graphite2 >=1.3.15,<2.0a0 + - icu >=78.3,<79.0a0 + - libcxx >=21 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libglib >=2.90.0,<3.0a0 + - libharfbuzz 14.6.0 hcda0f7c_2 + - libpng >=1.6.59,<1.7.0a0 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 1536213 + timestamp: 1791900642002 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h0cb94f2_3.conda + sha256: f943117edb9cd4d9c61cc972eee5a34291dc55ea7a6e9e38da104995841cbcb6 + md5: f92233bf33e24a25668bb2119e2c51f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: LGPL-2.1-only + purls: [] + size: 789471 + timestamp: 1787033836207 - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f md5: 915f5995e94f60e9a4826e0b0920ee88 @@ -4020,6 +8142,15 @@ packages: purls: [] size: 790176 timestamp: 1754908768807 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.18-h1ea5142_3.conda + sha256: d22c666eb887afd2119aac2110e23a0e817e9391bda99293421f7c46dab1564c + md5: 951b54a36388613a99820b33b997f690 + depends: + - libgcc >=15 + license: LGPL-2.1-only + purls: [] + size: 785924 + timestamp: 1787033793216 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.18-h90929bb_2.conda sha256: 1473451cd282b48d24515795a595801c9b65b567fe399d7e12d50b2d6cdb04d9 md5: 5a86bf847b9b926f3a4f203339748d78 @@ -4038,6 +8169,15 @@ packages: purls: [] size: 750379 timestamp: 1754909073836 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-he4c29f2_3.conda + sha256: 689a14968267f2f97c07112fca5636e7d756036b9aee969911267c20bd3eea1f + md5: 17f4744c0873e9f77ac9b5cd0a27c187 + depends: + - __osx >=11.0 + license: LGPL-2.1-only + purls: [] + size: 750816 + timestamp: 1787033961086 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-h493aca8_0.conda sha256: 99d2cebcd8f84961b86784451b010f5f0a795ed1c08f1e7c76fbb3c22abf021a md5: 5103f6a6b210a3912faf8d7db516918c @@ -4048,6 +8188,16 @@ packages: purls: [] size: 90957 timestamp: 1751558394144 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.25.1-hae62183_2.conda + sha256: b43c6645e668f86d04d5c06d6dc17efe921bbd9244be4b77946d56fb950a3fcc + md5: a3a4193101bc5427e6cf8be81d5eeddf + depends: + - __osx >=11.0 + - libiconv >=1.18,<2.0a0 + license: LGPL-2.1-or-later + purls: [] + size: 90024 + timestamp: 1790118675563 - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda sha256: 98b399287e27768bf79d48faba8a99a2289748c65cd342ca21033fab1860d4a4 md5: 9fa334557db9f63da6c9285fd2a48638 @@ -4060,6 +8210,18 @@ packages: purls: [] size: 628947 timestamp: 1745268527144 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.2.0-hb03c661_1.conda + sha256: bba8538e6538ed58a8479b332337b96986561f975d06cfa2039a016c2d246ee4 + md5: 898d1c9793eaa52efc4727bd84d2e39a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 650434 + timestamp: 1785896381946 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.1.2-he30d5cf_0.conda sha256: 84064c7c53a64291a585d7215fe95ec42df74203a5bf7615d33d49a3b0f08bb6 md5: 5109d7f837a3dfdf5c60f60e311b041f @@ -4069,8 +8231,19 @@ packages: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib purls: [] - size: 691818 - timestamp: 1762094728337 + size: 691818 + timestamp: 1762094728337 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.2.0-he30d5cf_1.conda + sha256: c5626ea672be2e59784f9be20dc8ceb7961b8ab0053651ded2dffec64e4b8245 + md5: 8730e25aa7eab80ca86dfb9a6bedf1ba + depends: + - libgcc >=14 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 716519 + timestamp: 1785896318334 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.0-h5505292_0.conda sha256: 78df2574fa6aa5b6f5fc367c03192f8ddf8e27dc23641468d54e031ff560b9d4 md5: 01caa4fbcaf0e6b08b3aef1151e91745 @@ -4082,6 +8255,17 @@ packages: purls: [] size: 553624 timestamp: 1745268405713 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.2.0-h84a0fba_1.conda + sha256: 05006418f9392c9b723e8428808db106de0350a497832f95f921b85ff1072310 + md5: b2f8c8e5a7651a1d7c05404c3a159517 + depends: + - __osx >=11.0 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + purls: [] + size: 558459 + timestamp: 1785896382474 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 md5: 1a580f7796c7bf6393fddb8bbbde58dc @@ -4094,6 +8278,18 @@ packages: purls: [] size: 112894 timestamp: 1749230047870 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_1.conda + sha256: 9787df8c22a59c9a70d3e5a10db9ad663485e75e9ccc3f09bd092cb7b95e0dab + md5: 1390b7c5ac0b1d8e447bc5efa6d3c8c2 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - xz 5.8.3.* + license: 0BSD + purls: [] + size: 112995 + timestamp: 1786348617826 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda sha256: 498ea4b29155df69d7f20990a7028d75d91dbea24d04b2eb8a3d6ef328806849 md5: 7d362346a479256857ab338588190da0 @@ -4105,6 +8301,17 @@ packages: purls: [] size: 125103 timestamp: 1749232230009 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.3-he30d5cf_1.conda + sha256: f760669fd1cea27f689f411c0d5488e26ce50e382c9b63e4ad348f959850124a + md5: e0e6411a60d00186eec7c73f4118ab67 + depends: + - libgcc >=14 + constrains: + - xz 5.8.3.* + license: 0BSD + purls: [] + size: 125578 + timestamp: 1786348561649 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda sha256: 0cb92a9e026e7bd4842f410a5c5c665c89b2eb97794ffddba519a626b8ce7285 md5: d6df911d4564d77c4374b02552cb17d1 @@ -4116,6 +8323,28 @@ packages: purls: [] size: 92286 timestamp: 1749230283517 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_1.conda + sha256: 23d0630046a3e8b164d8f80f2b74ed2605af2e7050ab9913018056402fae4311 + md5: 8ab10323068b107661a4b9a4af84f3b5 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.3.* + license: 0BSD + purls: [] + size: 91720 + timestamp: 1786348695846 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_2.conda + sha256: 46820b4a835e175940ae20bec00fdddaff804cda27a1408ab1b95e77a2196437 + md5: fcfed1dc5053eb1901b66e7b1fc32588 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 92759 + timestamp: 1786650399772 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda sha256: 3aa92d4074d4063f2a162cd8ecb45dccac93e543e565c01a787e16a43501f7ee md5: c7e925f37e3b40d893459e625f6a53f1 @@ -4137,6 +8366,16 @@ packages: purls: [] size: 114064 timestamp: 1748393729243 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libmpdec-4.0.0-he30d5cf_2.conda + sha256: 5d6621a2229777824386164b40c67ff804fe98dd4165354cf73ee73e282a2adf + md5: eaaaec4776cd8a7b987c4b1782220141 + depends: + - libgcc >=14 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 113885 + timestamp: 1786650485380 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda sha256: 0a1875fc1642324ebd6c4ac864604f3f18f57fbcf558a8264f6ced028a3c75b2 md5: 85ccccb47823dd9f7a99d2c7f530342f @@ -4147,6 +8386,161 @@ packages: purls: [] size: 71829 timestamp: 1748393749336 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_2.conda + sha256: 04cc136c5a956a73aa14e0a160b5822b0b29714e67b299fa1b1fd16dbcba5366 + md5: ff33a4dbd93abc8a798cc4e0e7c8136d + depends: + - __osx >=11.0 + license: BSD-2-Clause + license_family: BSD + purls: [] + size: 73289 + timestamp: 1786651074391 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda + sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 + md5: d864d34357c3b65a4b731f78c0801dc4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-only + license_family: GPL + purls: [] + size: 33731 + timestamp: 1750274110928 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda + sha256: c0dc4d84198e3eef1f37321299e48e2754ca83fd12e6284754e3cb231357c3a5 + md5: d5d58b2dc3e57073fe22303f5fed4db7 + depends: + - libgcc >=13 + license: LGPL-2.1-only + license_family: GPL + purls: [] + size: 34831 + timestamp: 1750274211 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpango-1.58.2-hce4e988_3.conda + sha256: 251dfa10f843dfb14ccc8c33d7ec0f18ac75f51be119100f65468a046b1b33ce + md5: d1ad1c07f3ae04176c0b218d7fe3be9b + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - cairo >=1.18.6,<2.0a0 + - fribidi >=1.0.17,<2.0a0 + - libglib >=2.90.0,<3.0a0 + - libharfbuzz >=14.6.0 + license: LGPL-2.1-or-later + purls: [] + size: 343535 + timestamp: 1792050659391 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpango-1.58.2-h1a0baab_3.conda + sha256: 309b595bfa26d3a3239e3a8ac2b4334924c62131107af0e1797d41d186273119 + md5: 2f30c19be0e6e32d49d7bd4086a3f77e + depends: + - libgcc >=15 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libharfbuzz >=14.6.0 + - libglib >=2.90.1,<3.0a0 + - fribidi >=1.0.17,<2.0a0 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - cairo >=1.18.6,<2.0a0 + license: LGPL-2.1-or-later + purls: [] + size: 353157 + timestamp: 1792050651506 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpango-1.58.2-hc2ac4f2_3.conda + sha256: 90118019ce64d713888abbe41a15be9e5885bdc8d5d4f84480e615299d2b5a01 + md5: 1261a045589c8d98219cbd258b71ea95 + depends: + - __osx >=11.0 + - fribidi >=1.0.17,<2.0a0 + - libglib >=2.90.0,<3.0a0 + - libharfbuzz >=14.6.0 + - cairo >=1.18.6,<2.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + license: LGPL-2.1-or-later + purls: [] + size: 308102 + timestamp: 1792050658787 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpango-devel-1.58.2-h317d9d0_3.conda + sha256: 75df9cf6e8c8b94648beaa8c7bf0d3c1efd7c3e0ebfb26529e415185a065c2a3 + md5: 8f2e0739636fe033c7324130121ab11c + depends: + - libpango ==1.58.2 hce4e988_3 + - freetype + - libharfbuzz-devel + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - cairo >=1.18.6,<2.0a0 + - fribidi >=1.0.17,<2.0a0 + - libglib >=2.90.0,<3.0a0 + - libharfbuzz >=14.6.0 + license: LGPL-2.1-or-later + purls: [] + size: 211407 + timestamp: 1792050659391 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpango-devel-1.58.2-h12dfbc4_3.conda + sha256: b77649aba8d7bf292b76774008da0b369a01f07fa9ca9ac3bc2fa38ee8b91743 + md5: 20c37a8c0a0f4f1ff46e29765cfba464 + depends: + - libpango ==1.58.2 h1a0baab_3 + - freetype + - libharfbuzz-devel + - libgcc >=15 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - libharfbuzz >=14.6.0 + - libglib >=2.90.1,<3.0a0 + - fribidi >=1.0.17,<2.0a0 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - cairo >=1.18.6,<2.0a0 + license: LGPL-2.1-or-later + purls: [] + size: 212537 + timestamp: 1792050651506 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpango-devel-1.58.2-haa2d88b_3.conda + sha256: 2434b8c792ce63ba089da94a21acb25ec45430a053d5cbe537d18f18771b27a7 + md5: 4bac42499647d2567ef309b6193d63f6 + depends: + - libpango ==1.58.2 hc2ac4f2_3 + - freetype + - libharfbuzz-devel + - __osx >=11.0 + - fribidi >=1.0.17,<2.0a0 + - libglib >=2.90.0,<3.0a0 + - libharfbuzz >=14.6.0 + - cairo >=1.18.6,<2.0a0 + - libfreetype >=2.14.3 + - libfreetype6 >=2.14.3 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + license: LGPL-2.1-or-later + purls: [] + size: 213942 + timestamp: 1792050658787 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_1.conda + sha256: addc80c69d362a9e6c40305c493139a8e9ee504b2f45a6687dbdaa9da3c6183c + md5: 35fa2b34bbced424e6976d30f5fde576 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 30070 + timestamp: 1785971678815 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpciaccess-0.18-h86ecc28_0.conda sha256: 7641dfdfe9bda7069ae94379e9924892f0b6604c1a016a3f76b230433bb280f2 md5: 5044e160c5306968d956c2a0a2a440d6 @@ -4157,6 +8551,16 @@ packages: purls: [] size: 29512 timestamp: 1749901899881 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpciaccess-0.19-he30d5cf_1.conda + sha256: 2161dacebeb075187f20ba35529297e7d4f6d456a89c4474062782436dbe3735 + md5: b1cb4a94819281ecb0391438e0be505c + depends: + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 31035 + timestamp: 1785971703914 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h421ea60_1.conda sha256: e75a2723000ce3a4b9fd9b9b9ce77553556c93e475a4657db6ed01abc02ea347 md5: 7af8e91b0deb5f8e25d1a595dea79614 @@ -4168,6 +8572,17 @@ packages: purls: [] size: 317390 timestamp: 1753879899951 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.59-h922cc85_0.conda + sha256: c7381be8ef713501f6cd2f5ad473e401f2edeaec3c46788296b8dbf0de165e15 + md5: 8c9caf742a485ac688ffa88bbadbf0be + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 316872 + timestamp: 1790805159503 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.50-h1abf092_1.conda sha256: e1effd7335ec101bb124f41a5f79fabb5e7b858eafe0f2db4401fb90c51505a7 md5: ed42935ac048d73109163d653d9445a0 @@ -4178,6 +8593,16 @@ packages: purls: [] size: 339168 timestamp: 1753879915462 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.59-hf9b7768_0.conda + sha256: 11a8f519c148e9ce968b78ee718965a1166af135321e1803941ee6f92bd5862c + md5: 233277975364a1c4c79ca15daaa0f49f + depends: + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 333207 + timestamp: 1790805159124 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.50-h280e0eb_1.conda sha256: a2e0240fb0c79668047b528976872307ea80cb330baf8bf6624ac2c6443449df md5: 4d0f5ce02033286551a32208a5519884 @@ -4188,6 +8613,47 @@ packages: purls: [] size: 287056 timestamp: 1753879907258 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.59-hf5e6511_0.conda + sha256: 4de4dc4cb7432c328c1eaf4f7cc9c6b48c073eab1e9c6723d4533e6d57e7d595 + md5: 4f99571d5354c39f2621b8b6e3a9afa3 + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: zlib-acknowledgement + purls: [] + size: 290299 + timestamp: 1790805167385 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpython-3.14.8-hd51c5da_102_cp314.conda + build_number: 102 + sha256: 8af871319cee737bf75913c7b6b2c65a0cc09376a8992dd80b3251d82066fc63 + md5: 5bc9fb686950e9ef2543adc230476238 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: Python-2.0 + purls: [] + size: 10537117 + timestamp: 1791827266877 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libpython-3.14.8-h150030a_102_cp314.conda + build_number: 102 + sha256: 3031a35546b70ecdb12cc160021b081254efd076293fe3efc9508b477a211c45 + md5: 19a7f516a9b4b5a214118c8e4b1e9e02 + depends: + - libgcc >=15 + license: Python-2.0 + purls: [] + size: 9708936 + timestamp: 1791827056200 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpython-3.14.8-h281bc5c_102_cp314.conda + build_number: 102 + sha256: 6237bed72df34642662b7da36046fedfaea77831ee24e926846ce56e0568286d + md5: d8b70ed3d772724a19cb4a1a578d8fa5 + depends: + - __osx >=11.0 + license: Python-2.0 + purls: [] + size: 1890587 + timestamp: 1791826523560 - conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.58.4-he92a37e_3.conda sha256: a45ef03e6e700cc6ac6c375e27904531cf8ade27eb3857e080537ff283fb0507 md5: d27665b20bc4d074b86e628b3ba5ab8b @@ -4208,6 +8674,26 @@ packages: purls: [] size: 6543651 timestamp: 1743368725313 +- conda: https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.62.4-ha427ee3_0.conda + sha256: b8036adad17ceddcef416790131492fd236d34c64a0b2754934903087fa84279 + md5: f9bda2728debbde4c641bd1af103c623 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libglib >=2.90.0,<3.0a0 + - pango >=1.58.2,<2.0a0 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - libxml2-16 >=2.15.4 + - cairo >=1.18.6,<2.0a0 + - libharfbuzz >=14.5.0 + - gdk-pixbuf >=2.44.8,<3.0a0 + constrains: + - __glibc >=2.17 + license: LGPL-2.1-or-later + purls: [] + size: 4692221 + timestamp: 1790276295756 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/librsvg-2.60.0-h8171147_0.conda sha256: b6cb38e95a447a04e624b6070981899e18c03f71915476fe024dadf384f48f15 md5: 7e4a8318e73ba685615f90bff926bfe4 @@ -4224,6 +8710,25 @@ packages: purls: [] size: 2995492 timestamp: 1759335330016 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/librsvg-2.62.4-ha90e91f_0.conda + sha256: 4692f158b2a2e79f299904e0d3ce91a0b05a58c243748bef78d9d08bf4c38348 + md5: c2b673a618c2a0134e0ca570398c7c6d + depends: + - libgcc >=15 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - libglib >=2.90.0,<3.0a0 + - libharfbuzz >=14.5.0 + - cairo >=1.18.6,<2.0a0 + - libxml2-16 >=2.15.4 + - gdk-pixbuf >=2.44.8,<3.0a0 + - pango >=1.58.2,<2.0a0 + constrains: + - __glibc >=2.17 + license: LGPL-2.1-or-later + purls: [] + size: 4653958 + timestamp: 1790276638452 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.60.0-h5c55ec3_0.conda sha256: ca5a2de5d3f68e8d6443ea1bf193c1596a278e6f86018017c0ccd4928eaf8971 md5: 05ad1d6b6fb3b384f7a07128025725cb @@ -4240,6 +8745,25 @@ packages: purls: [] size: 2344343 timestamp: 1759328503184 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/librsvg-2.62.4-hc8ac265_0.conda + sha256: 3bd54de0136907782249a0d5797744ecac5bbd77026f5f2712dd62390c5c9f77 + md5: 878c79beee628e3b683ddc9094aaad1e + depends: + - __osx >=11.0 + - pango >=1.58.2,<2.0a0 + - libglib >=2.90.0,<3.0a0 + - fontconfig >=2.18.3,<3.0a0 + - fonts-conda-ecosystem + - libxml2-16 >=2.15.4 + - gdk-pixbuf >=2.44.8,<3.0a0 + - libharfbuzz >=14.5.0 + - cairo >=1.18.6,<2.0a0 + constrains: + - __osx >=11.0 + license: LGPL-2.1-or-later + purls: [] + size: 3888310 + timestamp: 1790276358897 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda sha256: 6d9c32fc369af5a84875725f7ddfbfc2ace795c28f246dc70055a79f9b2003da md5: 0b367fad34931cb79e0d6b7e5c06bb1c @@ -4251,6 +8775,18 @@ packages: purls: [] size: 932581 timestamp: 1753948484112 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.54.1-h13e7031_0.conda + sha256: 07b3c98282022f05781122d073f84e944bfb4299946e8457ece3ce6c384ff78e + md5: 8f15f0ec9b69344df5c3bb5cce1751a1 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + license: blessing + purls: [] + size: 983668 + timestamp: 1792175587601 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.51.0-h022381a_0.conda sha256: f66a40b6e07a6f8ce6ccbd38d079b7394217d8f8ae0a05efa644aa0a40140671 md5: 8920ce2226463a3815e2183c8b5008b8 @@ -4261,6 +8797,17 @@ packages: purls: [] size: 938476 timestamp: 1762299829629 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.54.1-h399dd60_0.conda + sha256: 6ac4bef81dc57000d16f6e230f4157e2d054189800346b59f3613c07b249dceb + md5: d1e0812292cb0f17e574dfe63eb69971 + depends: + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + license: blessing + purls: [] + size: 982114 + timestamp: 1792175598077 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.50.4-h4237e3c_0.conda sha256: 802ebe62e6bc59fc26b26276b793e0542cfff2d03c086440aeaf72fb8bbcec44 md5: 1dcb0468f5146e38fae99aef9656034b @@ -4272,6 +8819,17 @@ packages: purls: [] size: 902645 timestamp: 1753948599139 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.54.1-hca69786_0.conda + sha256: 998cd122027b2523f2f25807b1d476b82a168489d16b1430fd848a9a6b606a80 + md5: b3fc67af56dfdf7c9ee7d8fdae21f654 + depends: + - __osx >=11.0 + - icu >=78.3,<79.0a0 + - libzlib >=1.3.2,<2.0a0 + license: blessing + purls: [] + size: 953427 + timestamp: 1792175622950 - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_5.conda sha256: 0f5f61cab229b6043541c13538d75ce11bd96fb2db76f94ecf81997b1fde6408 md5: 4e02a49aaa9d5190cb630fa43528fbe6 @@ -4283,6 +8841,19 @@ packages: purls: [] size: 3896432 timestamp: 1757042571458 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-16.2.0-h934c35e_7.conda + sha256: fa7018298629fc429971fffd6ecba3d27a7b02e4e890d6a93ea01ffebe8cb745 + md5: 9b6707e0caca917fa6ea9ee473f37a5d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 16.2.0 ha9f2e26_7 + constrains: + - libstdcxx-ng ==16.2.0=*_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 6615436 + timestamp: 1790303132935 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.2.0-h3f4de04_7.conda sha256: 4c6d1a2ae58044112233a57103bbf06000bd4c2aad44a0fd3b464b05fa8df514 md5: 6a2f0ee17851251a85fbebafbe707d2d @@ -4295,6 +8866,18 @@ packages: purls: [] size: 3831785 timestamp: 1759967470295 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-16.2.0-hef695bb_7.conda + sha256: b241a3acfa9cdb1359112864a1241225533c92642de3000d52ae65ef5d3a9786 + md5: 6c823668e78160957d1b061ceec15c2c + depends: + - libgcc 16.2.0 h205dda4_7 + constrains: + - libstdcxx-ng ==16.2.0=*_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 6236630 + timestamp: 1790302364054 - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_5.conda sha256: 7b8cabbf0ab4fe3581ca28fe8ca319f964078578a51dd2ca3f703c1d21ba23ff md5: 8bba50c7f4679f08c861b597ad2bda6b @@ -4305,6 +8888,16 @@ packages: purls: [] size: 29233 timestamp: 1757042603319 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-16.2.0-hdf11a46_7.conda + sha256: 337f481db36cc357884f05f404ccd1b3a7e0fc1ee48ec5c884e4281d213bc863 + md5: 25a23bde5e26d548374f3e02b4669670 + depends: + - libstdcxx 16.2.0 h934c35e_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 28665 + timestamp: 1790303164510 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.2.0-hf1166c9_7.conda sha256: 26fc1bdb39042f27302b363785fea6f6b9607f9c2f5eb949c6ae0bdbb8599574 md5: 9e5deec886ad32f3c6791b3b75c78681 @@ -4315,6 +8908,16 @@ packages: purls: [] size: 29341 timestamp: 1759967498023 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-16.2.0-hdbbeba8_7.conda + sha256: cfe644e4cf78302e605f7dc1cfa23ee4edabf5b72f84c92436f5f51c3003542c + md5: c2972e53fa11afd6ebc6a8b9a25c104d + depends: + - libstdcxx 16.2.0 hef695bb_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + purls: [] + size: 28760 + timestamp: 1790302398534 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-h8261f1e_6.conda sha256: c62694cd117548d810d2803da6d9063f78b1ffbf7367432c5388ce89474e9ebe md5: b6093922931b535a7ba566b6f384fbe6 @@ -4333,6 +8936,24 @@ packages: purls: [] size: 433078 timestamp: 1755011934951 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.2-hcc2c06a_1.conda + sha256: 10e125da82ca93e09191e66e5a94d566b2cf8d4024e2a0db3a9114297447e0d9 + md5: 0907d876f460ebc941ae590338b6102f + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.2.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=15 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libstdcxx >=15 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 459753 + timestamp: 1787755584172 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.1-hdb009f0_1.conda sha256: 7ff79470db39e803e21b8185bc8f19c460666d5557b1378d1b1e857d929c6b39 md5: 8c6fd84f9c87ac00636007c6131e457d @@ -4350,6 +8971,23 @@ packages: purls: [] size: 488407 timestamp: 1762022048105 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.2-h45f9f85_1.conda + sha256: fa6daf2301dee912def3e74344fae792a8fc2bbbf7880dedee0e93ee8fb98fc5 + md5: c6f35f8f9695623b9055341bc280a642 + depends: + - lerc >=4.2.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=15 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libstdcxx >=15 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 529726 + timestamp: 1787755625893 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h7dc4979_0.conda sha256: 6bc1b601f0d3ee853acd23884a007ac0a0290f3609dabb05a47fc5a0295e2b53 md5: 2bb9e04e2da869125e2dc334d665f00d @@ -4367,6 +9005,23 @@ packages: purls: [] size: 373640 timestamp: 1758278641520 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.2-hf67920b_1.conda + sha256: 847d22a86ae28f66d3888702698254d25bb4eac3a0f64c1fabfb1842ac00be4c + md5: b6b191267455bf202af79f973690ed5d + depends: + - __osx >=11.0 + - lerc >=4.2.0,<5.0a0 + - libcxx >=21 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.2.0,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + purls: [] + size: 380645 + timestamp: 1787756067271 - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.1-he9a06e4_0.conda sha256: 776e28735cee84b97e4d05dd5d67b95221a3e2c09b8b13e3d6dbe6494337d527 md5: af930c65e9a79a3423d6d36e265cef65 @@ -4378,6 +9033,17 @@ packages: purls: [] size: 37087 timestamp: 1757334557450 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.4-hcfc3c73_0.conda + sha256: f34b8d1de9a2bf62476d8608057d9b8a918d97444236dc53a47128f2fae79041 + md5: 54a948337e66706380ff24d6b5fde63f + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 34442 + timestamp: 1790121449161 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.41.2-h3e4203c_0.conda sha256: 7aed28ac04e0298bf8f7ad44a23d6f8ee000aa0445807344b16fceedc67cce0f md5: 3a68e44fdf2a2811672520fdd62996bd @@ -4388,6 +9054,16 @@ packages: purls: [] size: 39172 timestamp: 1758626850999 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.42.4-hd6fdeab_0.conda + sha256: 4d94770cc7c47eb4a0d7b92f16d7a450506d1a82406d63c987d6208ab2fb8a16 + md5: 9d63e86dc027166aaaeb0c99a0a90fd7 + depends: + - libgcc >=15 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 36296 + timestamp: 1790121454455 - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b md5: aea31d2e5b1091feca96fcfe945c3cf9 @@ -4401,6 +9077,19 @@ packages: purls: [] size: 429011 timestamp: 1752159441324 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_1.conda + sha256: 8415001414f488c85b72b9d8cc2071dfb3981a47bc3c8eb56ef91a57d12eae7f + md5: 9332b53d0ea93c5d39e33be03a0c611a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 428430 + timestamp: 1785954557217 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.6.0-ha2e29f5_0.conda sha256: b03700a1f741554e8e5712f9b06dd67e76f5301292958cd3cb1ac8c6fdd9ed25 md5: 24e92d0942c799db387f5c9d7b81f1af @@ -4413,6 +9102,18 @@ packages: purls: [] size: 359496 timestamp: 1752160685488 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.6.0-ha2e29f5_1.conda + sha256: 4c64e6843d64876d054a28ecbab70abdf145da3c2cbdaa4a43db26cdf0fd760b + md5: 548943c39851543734ce0d20eeb469b0 + depends: + - libgcc >=14 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 357551 + timestamp: 1785956962809 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda sha256: a4de3f371bb7ada325e1f27a4ef7bcc81b2b6a330e46fac9c2f78ac0755ea3dd md5: e5e7d467f80da752be17796b87fe6385 @@ -4425,6 +9126,18 @@ packages: purls: [] size: 294974 timestamp: 1752159906788 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h202fb40_1.conda + sha256: 0ff54650d470c7e54cbeffdd53a8c063e055a7bcf784388c0385ce5c4741b0f4 + md5: 168a13e329259710b28277abc1395b8e + depends: + - __osx >=11.0 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 294522 + timestamp: 1785955350410 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa md5: 92ed62436b625154323d40d5f2f11dd7 @@ -4439,6 +9152,20 @@ packages: purls: [] size: 395888 timestamp: 1727278577118 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-hb83e432_2.conda + sha256: 7b49e6fd2a584b7f072943e6adf4149677af5121246975278804782d37752837 + md5: 61f0ffba9161cbb3a77722869b21e4bb + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - pthread-stubs + - xorg-libxau >=1.0.12,<2.0a0 + - xorg-libxdmcp >=1.1.5,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 395120 + timestamp: 1787885788278 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda sha256: 461cab3d5650ac6db73a367de5c8eca50363966e862dcf60181d693236b1ae7b md5: cd14ee5cca2464a425b1dbfc24d90db2 @@ -4452,6 +9179,38 @@ packages: purls: [] size: 397493 timestamp: 1727280745441 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h3f04742_2.conda + sha256: bb1d9aaa885de73c504bd887e1841ad8e9d5b8432bd370b02eaf33067a001dc3 + md5: fa9fad28d04af5225221613038388d95 + depends: + - libgcc >=15 + - pthread-stubs + - xorg-libxau >=1.0.12,<2.0a0 + - xorg-libxdmcp >=1.1.5,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 401562 + timestamp: 1787885741304 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.38-h280c20c_0.conda + sha256: f7e9292dd219a6435bbb1223da9586c3e70d66d169c5a92f08db3f2127df04e9 + md5: f7a7ff5a6ab331e037abd34f379a631d + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: LGPL-2.1-or-later + purls: [] + size: 101957 + timestamp: 1785887123445 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.38-h80f16a2_0.conda + sha256: d3514900e2121972e435f7803763c1843dad6709e48aa02a2b47c4d481f83be7 + md5: b1a5cb7ff2d8f5911ba1fb6897176098 + depends: + - libgcc >=14 + license: LGPL-2.1-or-later + purls: [] + size: 133882 + timestamp: 1785887114864 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.11.0-he8b52b9_0.conda sha256: 23f47e86cc1386e7f815fa9662ccedae151471862e971ea511c5c886aa723a54 md5: 74e91c36d0eef3557915c68b6c2bef96 @@ -4468,6 +9227,22 @@ packages: purls: [] size: 791328 timestamp: 1754703902365 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-h51789e4_1.conda + sha256: d44878d396713ccf3b355df617f61c40a1442fffcf134392bc6ce0e6c2219369 + md5: f888787e0eab7a8076a67d197e155ffc + depends: + - xkeyboard-config + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libxcb >=1.17.0,<2.0a0 + - xorg-libxau >=1.0.12,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + license: MIT AND MIT-open-group AND HPND AND HPND-sell-variant AND ISC + purls: [] + size: 942571 + timestamp: 1787178780572 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxkbcommon-1.13.0-h3c6a4c8_0.conda sha256: c197e58ba06fa9ac73fcbdc20f9a78ba0164f61879d127bb2f7d0d4be346216a md5: a7c78be36bf59b4ba44ad2f2f8b92b37 @@ -4477,13 +9252,28 @@ packages: - libxcb >=1.17.0,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - - xkeyboard-config - - xorg-libxau >=1.0.12,<2.0a0 - license: MIT/X11 Derivative - license_family: MIT + - xkeyboard-config + - xorg-libxau >=1.0.12,<2.0a0 + license: MIT/X11 Derivative + license_family: MIT + purls: [] + size: 862682 + timestamp: 1762341934465 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxkbcommon-1.13.2-he51e330_1.conda + sha256: b196478988b576d324b3343fdcf4972a42647ea0a5c1b02dc77b926a64b8ceb8 + md5: a9abdfd661d3bf523244a5488ca0f6a5 + depends: + - xkeyboard-config + - libstdcxx >=14 + - libgcc >=14 + - xorg-libxau >=1.0.12,<2.0a0 + - libxcb >=1.17.0,<2.0a0 + - libxml2 + - libxml2-16 >=2.14.6 + license: MIT AND MIT-open-group AND HPND AND HPND-sell-variant AND ISC purls: [] - size: 862682 - timestamp: 1762341934465 + size: 984153 + timestamp: 1787178819842 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h04c0eec_1.conda sha256: 03deb1ec6edfafc5aaeecadfc445ee436fecffcda11fcd97fde9b6632acb583f md5: 10bcbd05e1c1c9d652fccb42b776a9fa @@ -4499,6 +9289,22 @@ packages: purls: [] size: 698448 timestamp: 1754315344761 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.4-h7df9aa5_1.conda + sha256: 8c32be9e8720bc413d2b4267517459d7d71fad4a5f398a2aa8319b6d7b8a00e6 + md5: 51e5162f95bd6840f6b2b3169125fff5 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2-16 2.15.4 hf3af7cc_1 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 46570 + timestamp: 1791465635923 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.15.1-h788dabe_0.conda sha256: db0a568e0853ee38b7a4db1cb4ee76e57fe7c32ccb1d5b75f6618a1041d3c6e4 md5: a0e7779b7625b88e37df9bd73f0638dc @@ -4514,6 +9320,38 @@ packages: purls: [] size: 47192 timestamp: 1761015739999 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.15.4-h92b9305_1.conda + sha256: f714cfffb6009f1bd028a06d237e6f4550ff4ffb3f4209a9bfaeb2b36f7c6a56 + md5: d4dbfadb069b3667818403fa8fb4770e + depends: + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libxml2-16 2.15.4 h2e5089f_1 + - libzlib >=1.3.2,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 48661 + timestamp: 1791465587205 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.4-hf3af7cc_1.conda + sha256: d1906463070c21f3215cd5de93164f31c44d5bac6ae9b5d8f5adf42b1e304039 + md5: 046d9eada488e1a1657c80b55c23da52 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - libxml2 2.15.4 + license: MIT + license_family: MIT + purls: [] + size: 569062 + timestamp: 1791465631497 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-16-2.15.1-h8591a01_0.conda sha256: 7a13450bce2eeba8f8fb691868b79bf0891377b707493a527bd930d64d9b98af md5: e7177c6fbbf815da7b215b4cc3e70208 @@ -4530,6 +9368,22 @@ packages: purls: [] size: 597078 timestamp: 1761015734476 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-16-2.15.4-h2e5089f_1.conda + sha256: 96bce6a0c8eae2e1d22e09473b9e54bb13a736b4997ddd7a111eeaef16734708 + md5: 50740a3de17dba0b97199ec90dc925d0 + depends: + - icu >=78.3,<79.0a0 + - libgcc >=15 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - libxml2 2.15.4 + license: MIT + license_family: MIT + purls: [] + size: 628135 + timestamp: 1791465583733 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.1-h0ff4647_0.conda sha256: ebe2dd9da94280ad43da936efa7127d329b559f510670772debc87602b49b06d md5: 438c97d1e9648dd7342f86049dd44638 @@ -4546,6 +9400,22 @@ packages: purls: [] size: 464952 timestamp: 1761016087733 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.4-heb56d2d_1.conda + sha256: 1acd4dd49c6f54440dc172ab469d0e2c7c3c02de5b1f21158a233eb585ec3c38 + md5: ef82ce9c5e6a0bb8bc9cb0e6935f18ca + depends: + - __osx >=11.0 + - icu >=78.3,<79.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.3,<6.0a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - libxml2 2.15.4 + license: MIT + license_family: MIT + purls: [] + size: 469239 + timestamp: 1791465646245 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 md5: edb0dca6bc32e4f4789199455a1dbeb8 @@ -4559,6 +9429,18 @@ packages: purls: [] size: 60963 timestamp: 1727963148474 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_3.conda + sha256: eb8a0db0aa570124f7d2a93d7c7f596e3390df5e047818d873baad32985fc736 + md5: 0de0122d9570a8ab637c6b73db268389 + depends: + - __glibc >=2.17,<3.0.a0 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + purls: [] + size: 63713 + timestamp: 1785362952714 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda sha256: 5a2c1eeef69342e88a98d1d95bff1603727ab1ff4ee0e421522acd8813439b84 md5: 08aad7cbe9f5a6b460d0976076b6ae64 @@ -4571,6 +9453,16 @@ packages: purls: [] size: 66657 timestamp: 1727963199518 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.2-hdc9db2a_3.conda + sha256: 76efa6cc9d7e6f5ee3bbca0939f64054af2bdfb3c3632531f8e633cf6c2ea41e + md5: bd534c2fbe56d8c2ea3b2d8f5e12bca8 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + purls: [] + size: 70108 + timestamp: 1785276540870 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b md5: 369964e85dc26bfe78f41399b366c435 @@ -4583,6 +9475,18 @@ packages: purls: [] size: 46438 timestamp: 1727963202283 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_3.conda + sha256: a18fa5d5bac452401459f966cf0d872224e8080c4ff93c77e168d43ab42ef9d7 + md5: f39288f0ea63ae962e1a2e4f355a0d75 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.2 *_3 + license: Zlib + license_family: Other + purls: [] + size: 47822 + timestamp: 1785277049190 - pypi: https://files.pythonhosted.org/packages/e5/b8/9eea6630198cb303d131d95d285a024b3b8645b1763a2916fddb44ca8760/matplotlib-3.10.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl name: matplotlib version: 3.10.6 @@ -4640,6 +9544,108 @@ packages: - setuptools-scm>=7 ; extra == 'dev' - setuptools>=64 ; extra == 'dev' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/12/49/b78e214a527ea732033b7f4d37f7afb504d74ba9d134bd47938230dfb8b1/matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + name: matplotlib + version: 3.10.9 + sha256: ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294 + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.22.0 + - kiwisolver>=1.3.1 + - numpy>=1.23 + - packaging>=20.0 + - pillow>=8 + - pyparsing>=3 + - python-dateutil>=2.7 + - meson-python>=0.13.1,<0.17.0 ; extra == 'dev' + - pybind11>=2.13.2,!=2.13.3 ; extra == 'dev' + - setuptools-scm>=7,<10 ; extra == 'dev' + - setuptools>=64 ; extra == 'dev' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/bb/2f/127081eb83162053ebb9678ceac64220b93a663e0167432566e9c7c82aab/matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl + name: matplotlib + version: 3.10.9 + sha256: 1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.22.0 + - kiwisolver>=1.3.1 + - numpy>=1.23 + - packaging>=20.0 + - pillow>=8 + - pyparsing>=3 + - python-dateutil>=2.7 + - meson-python>=0.13.1,<0.17.0 ; extra == 'dev' + - pybind11>=2.13.2,!=2.13.3 ; extra == 'dev' + - setuptools-scm>=7,<10 ; extra == 'dev' + - setuptools>=64 ; extra == 'dev' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/fc/b7/d8bcec2626c35f96972bff656299fef4578113ea6193c8fdad324710410c/matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: matplotlib + version: 3.10.9 + sha256: 1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37 + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.22.0 + - kiwisolver>=1.3.1 + - numpy>=1.23 + - packaging>=20.0 + - pillow>=8 + - pyparsing>=3 + - python-dateutil>=2.7 + - meson-python>=0.13.1,<0.17.0 ; extra == 'dev' + - pybind11>=2.13.2,!=2.13.3 ; extra == 'dev' + - setuptools-scm>=7,<10 ; extra == 'dev' + - setuptools>=64 ; extra == 'dev' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/29/5e/52f56f93d5b20815ee0ae352f7a38eff25580edb180b4fa7df87cfeba68f/matplotlib-3.11.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: matplotlib + version: 3.11.2 + sha256: 8c8255de28f986d935a64c9ca71c0ec2d2f41d355691f5ea684725dc91413f71 + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.28.2 + - kiwisolver>=1.3.1 + - numpy>=1.25 + - packaging>=20.0 + - pillow>=9 + - pyparsing>=3 + - python-dateutil>=2.7 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/4c/a0/bba05f0a25bfeb0475c2f2a87958b31103aa6b7a5386017afb1a96212825/matplotlib-3.11.2-cp314-cp314-macosx_11_0_arm64.whl + name: matplotlib + version: 3.11.2 + sha256: 1a3040b209f3968b4e84161df7b174f07a9fad33b0f2d7e48ea3bbd3075e2863 + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.28.2 + - kiwisolver>=1.3.1 + - numpy>=1.25 + - packaging>=20.0 + - pillow>=9 + - pyparsing>=3 + - python-dateutil>=2.7 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/d4/42/81d5cba4bb39b41b0998efc880e129f2d7cdb94256ca49678fdcd9c21be5/matplotlib-3.11.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl + name: matplotlib + version: 3.11.2 + sha256: bf3fe71fbfb8ec0e310e0bc8537c3405a01f38f25f9394ed2135e6202fed542b + requires_dist: + - contourpy>=1.0.1 + - cycler>=0.10 + - fonttools>=4.28.2 + - kiwisolver>=1.3.1 + - numpy>=1.25 + - packaging>=20.0 + - pillow>=9 + - pyparsing>=3 + - python-dateutil>=2.7 + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl name: matplotlib-inline version: 0.1.7 @@ -4659,6 +9665,19 @@ packages: - notebook ; extra == 'test' - pytest ; extra == 'test' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl + name: matplotlib-inline + version: 0.2.2 + sha256: 3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6 + requires_dist: + - traitlets + - flake8 ; extra == 'test' + - nbdime ; extra == 'test' + - nbval ; extra == 'test' + - notebook ; extra == 'test' + - pytest ; extra == 'test' + - matplotlib ; extra == 'test' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/89/a3/00260f8df72b51afa1f182dd609533c77fa2407918c4c2813d87b4a56725/minio-7.2.16-py3-none-any.whl name: minio version: 7.2.16 @@ -4681,6 +9700,17 @@ packages: - typing-extensions - urllib3 requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/3e/9a/b697530a882588a84db616580f2ba5d1d515c815e11c30d219145afeec87/minio-7.2.20-py3-none-any.whl + name: minio + version: 7.2.20 + sha256: eb33dd2fb80e04c3726a76b13241c6be3c4c46f8d81e1d58e757786f6501897e + requires_dist: + - argon2-cffi + - certifi + - pycryptodome + - typing-extensions + - urllib3 + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl name: multidict version: 6.7.1 @@ -4702,6 +9732,48 @@ packages: requires_dist: - typing-extensions>=4.1.0 ; python_full_version < '3.11' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/33/51/0de5fc980df34d1ffed9ef68f60ce86c2d4baa3d422598a53f8730c56757/multidict-6.9.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: multidict + version: 6.9.1 + sha256: 4736d350371337825cac1793c9f7c40701c32a03912548c2a7608e51679cbb96 + requires_dist: + - typing-extensions>=4.1.0 ; python_full_version < '3.11' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a0/97/555aab000e03ecba85c0a1837fd5674d8bf10949490df7800da3a40660ce/multidict-6.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: multidict + version: 6.9.1 + sha256: 989261c5f1735a165f2e4e87cf6d5f17ab734fa18f9ad0383d5adfcdaefce701 + requires_dist: + - typing-extensions>=4.1.0 ; python_full_version < '3.11' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/bd/53/f65ed68b96eea2b4f6fa4f9031bdfccd8f3097c429e3d371c545d1c40ca4/multidict-6.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: multidict + version: 6.9.1 + sha256: d3b6e6840421c83ccb60398e333b44f910b0907bb409597685ab2eedd1e22eab + requires_dist: + - typing-extensions>=4.1.0 ; python_full_version < '3.11' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e0/89/b845535014a9ad50537ca50204aaf6351c5368822a3acd9da854fb2a5a34/multidict-6.9.1-cp310-cp310-macosx_11_0_arm64.whl + name: multidict + version: 6.9.1 + sha256: 02f6d0c4b70f783305e73f9944d8efe6be1022550f0974ba0ae9d8893c0350fa + requires_dist: + - typing-extensions>=4.1.0 ; python_full_version < '3.11' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/f8/e5/8d54118bc228e64e1087f1729647b93bbea225eda4a3f914663a6f410bca/multidict-6.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: multidict + version: 6.9.1 + sha256: b69651732c64afb691e50cdc3387cae305e0eeff8804fe3e3ce203876494932a + requires_dist: + - typing-extensions>=4.1.0 ; python_full_version < '3.11' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/f9/b4/8e6d950f02ca6ecc00ecf671f2ddb5dab7017671a8d197326f75621f5786/multidict-6.9.1-cp314-cp314-macosx_11_0_arm64.whl + name: multidict + version: 6.9.1 + sha256: 854fd2f1bc6e8a56b89910b5cd7261a8b40f13ebb31572da985ce59c7da0886d + requires_dist: + - typing-extensions>=4.1.0 ; python_full_version < '3.11' + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 md5: 47e340acb35de30501a76c7c799c41d7 @@ -4712,6 +9784,16 @@ packages: purls: [] size: 891641 timestamp: 1738195959188 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_1.conda + sha256: 5d46557214ed184381dafe835b7c94a474a1c3b307a08a250b1ea4779b44ffb3 + md5: ee6c0cd80a60961a1f48aa3e0b91f986 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: X11 AND BSD-3-Clause + purls: [] + size: 911196 + timestamp: 1786355078102 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda sha256: 91cfb655a68b0353b2833521dc919188db3d8a7f4c64bea2c6a7557b24747468 md5: 182afabe009dc78d8b73100255ee6868 @@ -4721,6 +9803,15 @@ packages: purls: [] size: 926034 timestamp: 1738196018799 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.6-h2b6f883_1.conda + sha256: d69a04914139627f0a6bfd19412d6c7f1e37edc896af84a6011e07e8bc1e69fa + md5: c3b4349171ca987ff33a1de61f7b3f96 + depends: + - libgcc >=14 + license: X11 AND BSD-3-Clause + purls: [] + size: 955422 + timestamp: 1786355019431 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda sha256: 2827ada40e8d9ca69a153a45f7fd14f32b2ead7045d3bbb5d10964898fe65733 md5: 068d497125e4bf8a66bf707254fff5ae @@ -4730,6 +9821,50 @@ packages: purls: [] size: 797030 timestamp: 1738196177597 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-he64c551_1.conda + sha256: 7024a48c8c0d0114ed4ab53c76bf9275d50e91ba7cea367a9aead638d3c29c68 + md5: 3dfa0d0316dc246cd44937a557de4501 + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + purls: [] + size: 804298 + timestamp: 1786355189145 +- pypi: https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl + name: networkx + version: 3.4.2 + sha256: df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f + requires_dist: + - numpy>=1.24 ; extra == 'default' + - scipy>=1.10,!=1.11.0,!=1.11.1 ; extra == 'default' + - matplotlib>=3.7 ; extra == 'default' + - pandas>=2.0 ; extra == 'default' + - changelist==0.5 ; extra == 'developer' + - pre-commit>=3.2 ; extra == 'developer' + - mypy>=1.1 ; extra == 'developer' + - rtoml ; extra == 'developer' + - sphinx>=7.3 ; extra == 'doc' + - pydata-sphinx-theme>=0.15 ; extra == 'doc' + - sphinx-gallery>=0.16 ; extra == 'doc' + - numpydoc>=1.8.0 ; extra == 'doc' + - pillow>=9.4 ; extra == 'doc' + - texext>=0.6.7 ; extra == 'doc' + - myst-nb>=1.1 ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - osmnx>=1.9 ; extra == 'example' + - momepy>=0.7.2 ; extra == 'example' + - contextily>=1.6 ; extra == 'example' + - seaborn>=0.13 ; extra == 'example' + - cairocffi>=1.7 ; extra == 'example' + - igraph>=0.11 ; extra == 'example' + - scikit-learn>=1.5 ; extra == 'example' + - lxml>=4.6 ; extra == 'extra' + - pygraphviz>=1.14 ; extra == 'extra' + - pydot>=3.0.1 ; extra == 'extra' + - sympy>=1.10 ; extra == 'extra' + - pytest>=7.2 ; extra == 'test' + - pytest-cov>=4.0 ; extra == 'test' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl name: networkx version: '3.5' @@ -4766,11 +9901,71 @@ packages: - pytest-mpl ; extra == 'test-extras' - pytest-randomly ; extra == 'test-extras' requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/7e/cd/fe58041e9011f307c490e3e17dd48cc516448f7c698a3f2d9d9d65d7e6a8/networkx-3.7-py3-none-any.whl + name: networkx + version: '3.7' + sha256: e3fd2c13a7814cee3746340d8d7f8598a67f16a58bf47fb7f8793fab6efca1b0 + requires_dist: + - asv>=0.6.5 ; extra == 'benchmarking' + - py-rattler>=0.21,<0.22 ; extra == 'benchmarking' + - virtualenv ; extra == 'benchmarking' + - numpy>=2.2 ; extra == 'default' + - scipy>=1.15 ; extra == 'default' + - matplotlib>=3.10 ; extra == 'default' + - pandas>=2.3 ; extra == 'default' + - pre-commit>=4.1 ; extra == 'developer' + - mypy>=1.15 ; extra == 'developer' + - sphinx>=8.0 ; extra == 'doc' + - pydata-sphinx-theme>=0.16 ; extra == 'doc' + - sphinx-gallery>=0.18 ; extra == 'doc' + - numpydoc>=1.8.0 ; extra == 'doc' + - pillow>=10 ; extra == 'doc' + - texext>=0.6.7 ; extra == 'doc' + - myst-nb>=1.1 ; extra == 'doc' + - intersphinx-registry ; extra == 'doc' + - osmnx>=2.0.0 ; extra == 'example' + - momepy>=0.7.2 ; extra == 'example' + - contextily>=1.6 ; extra == 'example' + - seaborn>=0.13 ; extra == 'example' + - cairocffi>=1.7 ; extra == 'example' + - igraph>=0.11 ; extra == 'example' + - scikit-learn>=1.6 ; extra == 'example' + - iplotx>=0.9.0 ; extra == 'example' + - hiveplotlib>=0.28 ; extra == 'example' + - lxml>=4.6 ; extra == 'extra' + - pygraphviz>=2.0 ; extra == 'extra' + - pydot>=3.0.1 ; extra == 'extra' + - sympy>=1.10 ; extra == 'extra' + - build>=0.10 ; extra == 'release' + - twine>=4.0 ; extra == 'release' + - wheel>=0.40 ; extra == 'release' + - changelist==0.5 ; extra == 'release' + - pytest>=7.2 ; extra == 'test' + - pytest-cov>=4.0 ; extra == 'test' + - pytest-xdist>=3.0 ; extra == 'test' + - pytest-mpl ; extra == 'test-extras' + - pytest-randomly ; extra == 'test-extras' + requires_python: '>=3.12,!=3.14.1' - pypi: https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl name: nodeenv version: 1.9.1 sha256: ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9 requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*' +- pypi: https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl + name: numpy + version: 2.2.6 + sha256: 8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: numpy + version: 2.2.6 + sha256: fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: numpy + version: 2.2.6 + sha256: efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl name: numpy version: 2.3.3 @@ -4786,6 +9981,21 @@ packages: version: 2.3.4 sha256: 4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7 requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/16/d5/928078d2b28f26829b138b4a6c3980045022fb409f570657a224ae60ef4e/numpy-2.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: numpy + version: 2.5.4 + sha256: d549420b8858885cea8838a727842249218b9c1da24dd517e25c9c7a948310a3 + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/63/d6/34b0a2b0741386a63025a65a2c09caaaaaad6d0ca95b66cd65c30dd7fcb5/numpy-2.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + name: numpy + version: 2.5.4 + sha256: 4054173604cd8658796053f1f3bc0befb68ec1c0762c57fdad61e199256a8617 + requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/f3/49/fee7587c33ee35f7977f9051d7f2023d4e7246d62710c80f20c2361ea232/numpy-2.5.4-cp314-cp314-macosx_11_0_arm64.whl + name: numpy + version: 2.5.4 + sha256: ffa6ce09a1c6a08e9667dd9c97aa0b14184e8d18f2a14b78b2a2328c9147f076 + requires_python: '>=3.12' - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.2-h26f9b46_0.conda sha256: c9f54d4e8212f313be7b02eb962d0cb13a8dae015683a403d3accd4add3e520e md5: ffffb341206dd0dab0c36053c048d621 @@ -4798,6 +10008,18 @@ packages: purls: [] size: 3128847 timestamp: 1754465526100 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.5-h781a0a9_0.conda + sha256: 9c1533f1cf784f0e250919c0c9762d0b0e6bc727a79290ffb7cea1ceca1d038c + md5: 5a51a2ea653f120240b1a9bf76e0aae9 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=15 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3195299 + timestamp: 1791098052301 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.4-h8e36d6e_0.conda sha256: a24b318733c98903e2689adc7ef73448e27cbb10806852032c023f0ea4446fc5 md5: 9303e8887afe539f78517951ce25cd13 @@ -4809,6 +10031,17 @@ packages: purls: [] size: 3644584 timestamp: 1759326000128 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.6.5-he6ad1d5_0.conda + sha256: 238136f1df197ecd89bafd728a34d0642176f3170423d13d8276746d675a9485 + md5: 92aec5ec417d2ce9ae109e6c5ad377ff + depends: + - ca-certificates + - libgcc >=15 + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3716078 + timestamp: 1791097887829 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.4-h5503f6c_0.conda sha256: f0512629f9589392c2fb9733d11e753d0eab8fc7602f96e4d7f3bd95c783eb07 md5: 71118318f37f717eefe55841adb172fd @@ -4818,8 +10051,19 @@ packages: license: Apache-2.0 license_family: Apache purls: [] - size: 3067808 - timestamp: 1759324763146 + size: 3067808 + timestamp: 1759324763146 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.5-h55eecbc_0.conda + sha256: 44fbc526456571a9fb8fa18157f6f269d4d4bd40eef20183a93b9c9296f94497 + md5: 45d9179cdf6e53d88aec034246f0dad6 + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + purls: [] + size: 3111573 + timestamp: 1791097959641 - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl name: orderly-set version: 5.5.0 @@ -4841,10 +10085,288 @@ packages: version: '25.0' sha256: 29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl + name: packaging + version: '26.3' + sha256: d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/8f/52/0634adaace9be2d8cac9ef78f05c47f3a675882e068438b9d7ec7ef0c13f/pandas-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: pandas - version: 2.3.2 - sha256: 4ac8c320bded4718b298281339c1a50fb00a6ba78cb2a63521c39bec95b0209b + version: 2.3.2 + sha256: 4ac8c320bded4718b298281339c1a50fb00a6ba78cb2a63521c39bec95b0209b + requires_dist: + - numpy>=1.22.4 ; python_full_version < '3.11' + - numpy>=1.23.2 ; python_full_version == '3.11.*' + - numpy>=1.26.0 ; python_full_version >= '3.12' + - python-dateutil>=2.8.2 + - pytz>=2020.1 + - tzdata>=2022.7 + - hypothesis>=6.46.1 ; extra == 'test' + - pytest>=7.3.2 ; extra == 'test' + - pytest-xdist>=2.2.0 ; extra == 'test' + - pyarrow>=10.0.1 ; extra == 'pyarrow' + - bottleneck>=1.3.6 ; extra == 'performance' + - numba>=0.56.4 ; extra == 'performance' + - numexpr>=2.8.4 ; extra == 'performance' + - scipy>=1.10.0 ; extra == 'computation' + - xarray>=2022.12.0 ; extra == 'computation' + - fsspec>=2022.11.0 ; extra == 'fss' + - s3fs>=2022.11.0 ; extra == 'aws' + - gcsfs>=2022.11.0 ; extra == 'gcp' + - pandas-gbq>=0.19.0 ; extra == 'gcp' + - odfpy>=1.4.1 ; extra == 'excel' + - openpyxl>=3.1.0 ; extra == 'excel' + - python-calamine>=0.1.7 ; extra == 'excel' + - pyxlsb>=1.0.10 ; extra == 'excel' + - xlrd>=2.0.1 ; extra == 'excel' + - xlsxwriter>=3.0.5 ; extra == 'excel' + - pyarrow>=10.0.1 ; extra == 'parquet' + - pyarrow>=10.0.1 ; extra == 'feather' + - tables>=3.8.0 ; extra == 'hdf5' + - pyreadstat>=1.2.0 ; extra == 'spss' + - sqlalchemy>=2.0.0 ; extra == 'postgresql' + - psycopg2>=2.9.6 ; extra == 'postgresql' + - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql' + - sqlalchemy>=2.0.0 ; extra == 'mysql' + - pymysql>=1.0.2 ; extra == 'mysql' + - sqlalchemy>=2.0.0 ; extra == 'sql-other' + - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other' + - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other' + - beautifulsoup4>=4.11.2 ; extra == 'html' + - html5lib>=1.1 ; extra == 'html' + - lxml>=4.9.2 ; extra == 'html' + - lxml>=4.9.2 ; extra == 'xml' + - matplotlib>=3.6.3 ; extra == 'plot' + - jinja2>=3.1.2 ; extra == 'output-formatting' + - tabulate>=0.9.0 ; extra == 'output-formatting' + - pyqt5>=5.15.9 ; extra == 'clipboard' + - qtpy>=2.3.0 ; extra == 'clipboard' + - zstandard>=0.19.0 ; extra == 'compression' + - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard' + - adbc-driver-postgresql>=0.8.0 ; extra == 'all' + - adbc-driver-sqlite>=0.8.0 ; extra == 'all' + - beautifulsoup4>=4.11.2 ; extra == 'all' + - bottleneck>=1.3.6 ; extra == 'all' + - dataframe-api-compat>=0.1.7 ; extra == 'all' + - fastparquet>=2022.12.0 ; extra == 'all' + - fsspec>=2022.11.0 ; extra == 'all' + - gcsfs>=2022.11.0 ; extra == 'all' + - html5lib>=1.1 ; extra == 'all' + - hypothesis>=6.46.1 ; extra == 'all' + - jinja2>=3.1.2 ; extra == 'all' + - lxml>=4.9.2 ; extra == 'all' + - matplotlib>=3.6.3 ; extra == 'all' + - numba>=0.56.4 ; extra == 'all' + - numexpr>=2.8.4 ; extra == 'all' + - odfpy>=1.4.1 ; extra == 'all' + - openpyxl>=3.1.0 ; extra == 'all' + - pandas-gbq>=0.19.0 ; extra == 'all' + - psycopg2>=2.9.6 ; extra == 'all' + - pyarrow>=10.0.1 ; extra == 'all' + - pymysql>=1.0.2 ; extra == 'all' + - pyqt5>=5.15.9 ; extra == 'all' + - pyreadstat>=1.2.0 ; extra == 'all' + - pytest>=7.3.2 ; extra == 'all' + - pytest-xdist>=2.2.0 ; extra == 'all' + - python-calamine>=0.1.7 ; extra == 'all' + - pyxlsb>=1.0.10 ; extra == 'all' + - qtpy>=2.3.0 ; extra == 'all' + - scipy>=1.10.0 ; extra == 'all' + - s3fs>=2022.11.0 ; extra == 'all' + - sqlalchemy>=2.0.0 ; extra == 'all' + - tables>=3.8.0 ; extra == 'all' + - tabulate>=0.9.0 ; extra == 'all' + - xarray>=2022.12.0 ; extra == 'all' + - xlrd>=2.0.1 ; extra == 'all' + - xlsxwriter>=3.0.5 ; extra == 'all' + - zstandard>=0.19.0 ; extra == 'all' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl + name: pandas + version: 2.3.3 + sha256: e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a + requires_dist: + - numpy>=1.22.4 ; python_full_version < '3.11' + - numpy>=1.23.2 ; python_full_version == '3.11.*' + - numpy>=1.26.0 ; python_full_version >= '3.12' + - python-dateutil>=2.8.2 + - pytz>=2020.1 + - tzdata>=2022.7 + - hypothesis>=6.46.1 ; extra == 'test' + - pytest>=7.3.2 ; extra == 'test' + - pytest-xdist>=2.2.0 ; extra == 'test' + - pyarrow>=10.0.1 ; extra == 'pyarrow' + - bottleneck>=1.3.6 ; extra == 'performance' + - numba>=0.56.4 ; extra == 'performance' + - numexpr>=2.8.4 ; extra == 'performance' + - scipy>=1.10.0 ; extra == 'computation' + - xarray>=2022.12.0 ; extra == 'computation' + - fsspec>=2022.11.0 ; extra == 'fss' + - s3fs>=2022.11.0 ; extra == 'aws' + - gcsfs>=2022.11.0 ; extra == 'gcp' + - pandas-gbq>=0.19.0 ; extra == 'gcp' + - odfpy>=1.4.1 ; extra == 'excel' + - openpyxl>=3.1.0 ; extra == 'excel' + - python-calamine>=0.1.7 ; extra == 'excel' + - pyxlsb>=1.0.10 ; extra == 'excel' + - xlrd>=2.0.1 ; extra == 'excel' + - xlsxwriter>=3.0.5 ; extra == 'excel' + - pyarrow>=10.0.1 ; extra == 'parquet' + - pyarrow>=10.0.1 ; extra == 'feather' + - tables>=3.8.0 ; extra == 'hdf5' + - pyreadstat>=1.2.0 ; extra == 'spss' + - sqlalchemy>=2.0.0 ; extra == 'postgresql' + - psycopg2>=2.9.6 ; extra == 'postgresql' + - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql' + - sqlalchemy>=2.0.0 ; extra == 'mysql' + - pymysql>=1.0.2 ; extra == 'mysql' + - sqlalchemy>=2.0.0 ; extra == 'sql-other' + - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other' + - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other' + - beautifulsoup4>=4.11.2 ; extra == 'html' + - html5lib>=1.1 ; extra == 'html' + - lxml>=4.9.2 ; extra == 'html' + - lxml>=4.9.2 ; extra == 'xml' + - matplotlib>=3.6.3 ; extra == 'plot' + - jinja2>=3.1.2 ; extra == 'output-formatting' + - tabulate>=0.9.0 ; extra == 'output-formatting' + - pyqt5>=5.15.9 ; extra == 'clipboard' + - qtpy>=2.3.0 ; extra == 'clipboard' + - zstandard>=0.19.0 ; extra == 'compression' + - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard' + - adbc-driver-postgresql>=0.8.0 ; extra == 'all' + - adbc-driver-sqlite>=0.8.0 ; extra == 'all' + - beautifulsoup4>=4.11.2 ; extra == 'all' + - bottleneck>=1.3.6 ; extra == 'all' + - dataframe-api-compat>=0.1.7 ; extra == 'all' + - fastparquet>=2022.12.0 ; extra == 'all' + - fsspec>=2022.11.0 ; extra == 'all' + - gcsfs>=2022.11.0 ; extra == 'all' + - html5lib>=1.1 ; extra == 'all' + - hypothesis>=6.46.1 ; extra == 'all' + - jinja2>=3.1.2 ; extra == 'all' + - lxml>=4.9.2 ; extra == 'all' + - matplotlib>=3.6.3 ; extra == 'all' + - numba>=0.56.4 ; extra == 'all' + - numexpr>=2.8.4 ; extra == 'all' + - odfpy>=1.4.1 ; extra == 'all' + - openpyxl>=3.1.0 ; extra == 'all' + - pandas-gbq>=0.19.0 ; extra == 'all' + - psycopg2>=2.9.6 ; extra == 'all' + - pyarrow>=10.0.1 ; extra == 'all' + - pymysql>=1.0.2 ; extra == 'all' + - pyqt5>=5.15.9 ; extra == 'all' + - pyreadstat>=1.2.0 ; extra == 'all' + - pytest>=7.3.2 ; extra == 'all' + - pytest-xdist>=2.2.0 ; extra == 'all' + - python-calamine>=0.1.7 ; extra == 'all' + - pyxlsb>=1.0.10 ; extra == 'all' + - qtpy>=2.3.0 ; extra == 'all' + - scipy>=1.10.0 ; extra == 'all' + - s3fs>=2022.11.0 ; extra == 'all' + - sqlalchemy>=2.0.0 ; extra == 'all' + - tables>=3.8.0 ; extra == 'all' + - tabulate>=0.9.0 ; extra == 'all' + - xarray>=2022.12.0 ; extra == 'all' + - xlrd>=2.0.1 ; extra == 'all' + - xlsxwriter>=3.0.5 ; extra == 'all' + - zstandard>=0.19.0 ; extra == 'all' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + name: pandas + version: 2.3.3 + sha256: e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d + requires_dist: + - numpy>=1.22.4 ; python_full_version < '3.11' + - numpy>=1.23.2 ; python_full_version == '3.11.*' + - numpy>=1.26.0 ; python_full_version >= '3.12' + - python-dateutil>=2.8.2 + - pytz>=2020.1 + - tzdata>=2022.7 + - hypothesis>=6.46.1 ; extra == 'test' + - pytest>=7.3.2 ; extra == 'test' + - pytest-xdist>=2.2.0 ; extra == 'test' + - pyarrow>=10.0.1 ; extra == 'pyarrow' + - bottleneck>=1.3.6 ; extra == 'performance' + - numba>=0.56.4 ; extra == 'performance' + - numexpr>=2.8.4 ; extra == 'performance' + - scipy>=1.10.0 ; extra == 'computation' + - xarray>=2022.12.0 ; extra == 'computation' + - fsspec>=2022.11.0 ; extra == 'fss' + - s3fs>=2022.11.0 ; extra == 'aws' + - gcsfs>=2022.11.0 ; extra == 'gcp' + - pandas-gbq>=0.19.0 ; extra == 'gcp' + - odfpy>=1.4.1 ; extra == 'excel' + - openpyxl>=3.1.0 ; extra == 'excel' + - python-calamine>=0.1.7 ; extra == 'excel' + - pyxlsb>=1.0.10 ; extra == 'excel' + - xlrd>=2.0.1 ; extra == 'excel' + - xlsxwriter>=3.0.5 ; extra == 'excel' + - pyarrow>=10.0.1 ; extra == 'parquet' + - pyarrow>=10.0.1 ; extra == 'feather' + - tables>=3.8.0 ; extra == 'hdf5' + - pyreadstat>=1.2.0 ; extra == 'spss' + - sqlalchemy>=2.0.0 ; extra == 'postgresql' + - psycopg2>=2.9.6 ; extra == 'postgresql' + - adbc-driver-postgresql>=0.8.0 ; extra == 'postgresql' + - sqlalchemy>=2.0.0 ; extra == 'mysql' + - pymysql>=1.0.2 ; extra == 'mysql' + - sqlalchemy>=2.0.0 ; extra == 'sql-other' + - adbc-driver-postgresql>=0.8.0 ; extra == 'sql-other' + - adbc-driver-sqlite>=0.8.0 ; extra == 'sql-other' + - beautifulsoup4>=4.11.2 ; extra == 'html' + - html5lib>=1.1 ; extra == 'html' + - lxml>=4.9.2 ; extra == 'html' + - lxml>=4.9.2 ; extra == 'xml' + - matplotlib>=3.6.3 ; extra == 'plot' + - jinja2>=3.1.2 ; extra == 'output-formatting' + - tabulate>=0.9.0 ; extra == 'output-formatting' + - pyqt5>=5.15.9 ; extra == 'clipboard' + - qtpy>=2.3.0 ; extra == 'clipboard' + - zstandard>=0.19.0 ; extra == 'compression' + - dataframe-api-compat>=0.1.7 ; extra == 'consortium-standard' + - adbc-driver-postgresql>=0.8.0 ; extra == 'all' + - adbc-driver-sqlite>=0.8.0 ; extra == 'all' + - beautifulsoup4>=4.11.2 ; extra == 'all' + - bottleneck>=1.3.6 ; extra == 'all' + - dataframe-api-compat>=0.1.7 ; extra == 'all' + - fastparquet>=2022.12.0 ; extra == 'all' + - fsspec>=2022.11.0 ; extra == 'all' + - gcsfs>=2022.11.0 ; extra == 'all' + - html5lib>=1.1 ; extra == 'all' + - hypothesis>=6.46.1 ; extra == 'all' + - jinja2>=3.1.2 ; extra == 'all' + - lxml>=4.9.2 ; extra == 'all' + - matplotlib>=3.6.3 ; extra == 'all' + - numba>=0.56.4 ; extra == 'all' + - numexpr>=2.8.4 ; extra == 'all' + - odfpy>=1.4.1 ; extra == 'all' + - openpyxl>=3.1.0 ; extra == 'all' + - pandas-gbq>=0.19.0 ; extra == 'all' + - psycopg2>=2.9.6 ; extra == 'all' + - pyarrow>=10.0.1 ; extra == 'all' + - pymysql>=1.0.2 ; extra == 'all' + - pyqt5>=5.15.9 ; extra == 'all' + - pyreadstat>=1.2.0 ; extra == 'all' + - pytest>=7.3.2 ; extra == 'all' + - pytest-xdist>=2.2.0 ; extra == 'all' + - python-calamine>=0.1.7 ; extra == 'all' + - pyxlsb>=1.0.10 ; extra == 'all' + - qtpy>=2.3.0 ; extra == 'all' + - scipy>=1.10.0 ; extra == 'all' + - s3fs>=2022.11.0 ; extra == 'all' + - sqlalchemy>=2.0.0 ; extra == 'all' + - tables>=3.8.0 ; extra == 'all' + - tabulate>=0.9.0 ; extra == 'all' + - xarray>=2022.12.0 ; extra == 'all' + - xlrd>=2.0.1 ; extra == 'all' + - xlsxwriter>=3.0.5 ; extra == 'all' + - zstandard>=0.19.0 ; extra == 'all' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + name: pandas + version: 2.3.3 + sha256: 5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1 requires_dist: - numpy>=1.22.4 ; python_full_version < '3.11' - numpy>=1.23.2 ; python_full_version == '3.11.*' @@ -4932,10 +10454,10 @@ packages: - xlsxwriter>=3.0.5 ; extra == 'all' - zstandard>=0.19.0 ; extra == 'all' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl +- pypi: https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl name: pandas version: 2.3.3 - sha256: e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d + sha256: bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8 requires_dist: - numpy>=1.22.4 ; python_full_version < '3.11' - numpy>=1.23.2 ; python_full_version == '3.11.*' @@ -5023,10 +10545,10 @@ packages: - xlsxwriter>=3.0.5 ; extra == 'all' - zstandard>=0.19.0 ; extra == 'all' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl name: pandas version: 2.3.3 - sha256: bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8 + sha256: dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838 requires_dist: - numpy>=1.22.4 ; python_full_version < '3.11' - numpy>=1.23.2 ; python_full_version == '3.11.*' @@ -5114,6 +10636,276 @@ packages: - xlsxwriter>=3.0.5 ; extra == 'all' - zstandard>=0.19.0 ; extra == 'all' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/35/61/47ae13476995cc8a40cd609e93e7cf11f273d8692925c2903cb6d38aa0d1/pandas-3.0.6-cp314-cp314-macosx_11_0_arm64.whl + name: pandas + version: 3.0.6 + sha256: ff51a4459ed036e93d1eb1bb5e6e7b28685d3cb6b7c12b91c05b31024e234729 + requires_dist: + - numpy>=1.26.0 ; python_full_version < '3.14' + - numpy>=2.3.3 ; python_full_version >= '3.14' + - python-dateutil>=2.8.2 + - tzdata ; sys_platform == 'win32' + - tzdata ; sys_platform == 'emscripten' + - hypothesis>=6.116.0 ; extra == 'test' + - pytest>=8.3.4,<9.1 ; extra == 'test' + - pytest-xdist>=3.6.1 ; extra == 'test' + - pyarrow>=13.0.0 ; extra == 'pyarrow' + - bottleneck>=1.4.2 ; extra == 'performance' + - numba>=0.60.0 ; extra == 'performance' + - numexpr>=2.10.2 ; extra == 'performance' + - scipy>=1.14.1 ; extra == 'computation' + - xarray>=2024.10.0 ; extra == 'computation' + - fsspec>=2024.10.0 ; extra == 'fss' + - s3fs>=2024.10.0 ; extra == 'aws' + - gcsfs>=2024.10.0 ; extra == 'gcp' + - odfpy>=1.4.1 ; extra == 'excel' + - openpyxl>=3.1.5 ; extra == 'excel' + - python-calamine>=0.3.0 ; extra == 'excel' + - pyxlsb>=1.0.10 ; extra == 'excel' + - xlrd>=2.0.1 ; extra == 'excel' + - xlsxwriter>=3.2.0 ; extra == 'excel' + - pyarrow>=13.0.0 ; extra == 'parquet' + - pyarrow>=13.0.0 ; extra == 'feather' + - pyiceberg>=0.8.1 ; extra == 'iceberg' + - tables>=3.10.1 ; extra == 'hdf5' + - pyreadstat>=1.2.8 ; extra == 'spss' + - sqlalchemy>=2.0.36 ; extra == 'postgresql' + - psycopg2>=2.9.10 ; extra == 'postgresql' + - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql' + - sqlalchemy>=2.0.36 ; extra == 'mysql' + - pymysql>=1.1.1 ; extra == 'mysql' + - sqlalchemy>=2.0.36 ; extra == 'sql-other' + - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other' + - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other' + - beautifulsoup4>=4.12.3 ; extra == 'html' + - html5lib>=1.1 ; extra == 'html' + - lxml>=5.3.0 ; extra == 'html' + - lxml>=5.3.0 ; extra == 'xml' + - matplotlib>=3.9.3 ; extra == 'plot' + - jinja2>=3.1.5 ; extra == 'output-formatting' + - tabulate>=0.9.0 ; extra == 'output-formatting' + - pyqt5>=5.15.9 ; extra == 'clipboard' + - qtpy>=2.4.2 ; extra == 'clipboard' + - zstandard>=0.23.0 ; extra == 'compression' + - pytz>=2020.1 ; extra == 'timezone' + - adbc-driver-postgresql>=1.2.0 ; extra == 'all' + - adbc-driver-sqlite>=1.2.0 ; extra == 'all' + - beautifulsoup4>=4.12.3 ; extra == 'all' + - bottleneck>=1.4.2 ; extra == 'all' + - fastparquet>=2024.11.0 ; extra == 'all' + - fsspec>=2024.10.0 ; extra == 'all' + - gcsfs>=2024.10.0 ; extra == 'all' + - html5lib>=1.1 ; extra == 'all' + - hypothesis>=6.116.0 ; extra == 'all' + - jinja2>=3.1.5 ; extra == 'all' + - lxml>=5.3.0 ; extra == 'all' + - matplotlib>=3.9.3 ; extra == 'all' + - numba>=0.60.0 ; extra == 'all' + - numexpr>=2.10.2 ; extra == 'all' + - odfpy>=1.4.1 ; extra == 'all' + - openpyxl>=3.1.5 ; extra == 'all' + - psycopg2>=2.9.10 ; extra == 'all' + - pyarrow>=13.0.0 ; extra == 'all' + - pyiceberg>=0.8.1 ; extra == 'all' + - pymysql>=1.1.1 ; extra == 'all' + - pyqt5>=5.15.9 ; extra == 'all' + - pyreadstat>=1.2.8 ; extra == 'all' + - pytest>=8.3.4 ; extra == 'all' + - pytest-xdist>=3.6.1 ; extra == 'all' + - python-calamine>=0.3.0 ; extra == 'all' + - pytz>=2020.1 ; extra == 'all' + - pyxlsb>=1.0.10 ; extra == 'all' + - qtpy>=2.4.2 ; extra == 'all' + - scipy>=1.14.1 ; extra == 'all' + - s3fs>=2024.10.0 ; extra == 'all' + - sqlalchemy>=2.0.36 ; extra == 'all' + - tables>=3.10.1 ; extra == 'all' + - tabulate>=0.9.0 ; extra == 'all' + - xarray>=2024.10.0 ; extra == 'all' + - xlrd>=2.0.1 ; extra == 'all' + - xlsxwriter>=3.2.0 ; extra == 'all' + - zstandard>=0.23.0 ; extra == 'all' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/bc/f2/cc5f2adb8d6e86a85d9fb5128f8cf205a61189336f70d1f7faf0d1b53ec9/pandas-3.0.6-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl + name: pandas + version: 3.0.6 + sha256: 654aae059295dbba6ecd2328ca12712a2cf1676214c8699f1c29213f7ccf9c34 + requires_dist: + - numpy>=1.26.0 ; python_full_version < '3.14' + - numpy>=2.3.3 ; python_full_version >= '3.14' + - python-dateutil>=2.8.2 + - tzdata ; sys_platform == 'win32' + - tzdata ; sys_platform == 'emscripten' + - hypothesis>=6.116.0 ; extra == 'test' + - pytest>=8.3.4,<9.1 ; extra == 'test' + - pytest-xdist>=3.6.1 ; extra == 'test' + - pyarrow>=13.0.0 ; extra == 'pyarrow' + - bottleneck>=1.4.2 ; extra == 'performance' + - numba>=0.60.0 ; extra == 'performance' + - numexpr>=2.10.2 ; extra == 'performance' + - scipy>=1.14.1 ; extra == 'computation' + - xarray>=2024.10.0 ; extra == 'computation' + - fsspec>=2024.10.0 ; extra == 'fss' + - s3fs>=2024.10.0 ; extra == 'aws' + - gcsfs>=2024.10.0 ; extra == 'gcp' + - odfpy>=1.4.1 ; extra == 'excel' + - openpyxl>=3.1.5 ; extra == 'excel' + - python-calamine>=0.3.0 ; extra == 'excel' + - pyxlsb>=1.0.10 ; extra == 'excel' + - xlrd>=2.0.1 ; extra == 'excel' + - xlsxwriter>=3.2.0 ; extra == 'excel' + - pyarrow>=13.0.0 ; extra == 'parquet' + - pyarrow>=13.0.0 ; extra == 'feather' + - pyiceberg>=0.8.1 ; extra == 'iceberg' + - tables>=3.10.1 ; extra == 'hdf5' + - pyreadstat>=1.2.8 ; extra == 'spss' + - sqlalchemy>=2.0.36 ; extra == 'postgresql' + - psycopg2>=2.9.10 ; extra == 'postgresql' + - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql' + - sqlalchemy>=2.0.36 ; extra == 'mysql' + - pymysql>=1.1.1 ; extra == 'mysql' + - sqlalchemy>=2.0.36 ; extra == 'sql-other' + - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other' + - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other' + - beautifulsoup4>=4.12.3 ; extra == 'html' + - html5lib>=1.1 ; extra == 'html' + - lxml>=5.3.0 ; extra == 'html' + - lxml>=5.3.0 ; extra == 'xml' + - matplotlib>=3.9.3 ; extra == 'plot' + - jinja2>=3.1.5 ; extra == 'output-formatting' + - tabulate>=0.9.0 ; extra == 'output-formatting' + - pyqt5>=5.15.9 ; extra == 'clipboard' + - qtpy>=2.4.2 ; extra == 'clipboard' + - zstandard>=0.23.0 ; extra == 'compression' + - pytz>=2020.1 ; extra == 'timezone' + - adbc-driver-postgresql>=1.2.0 ; extra == 'all' + - adbc-driver-sqlite>=1.2.0 ; extra == 'all' + - beautifulsoup4>=4.12.3 ; extra == 'all' + - bottleneck>=1.4.2 ; extra == 'all' + - fastparquet>=2024.11.0 ; extra == 'all' + - fsspec>=2024.10.0 ; extra == 'all' + - gcsfs>=2024.10.0 ; extra == 'all' + - html5lib>=1.1 ; extra == 'all' + - hypothesis>=6.116.0 ; extra == 'all' + - jinja2>=3.1.5 ; extra == 'all' + - lxml>=5.3.0 ; extra == 'all' + - matplotlib>=3.9.3 ; extra == 'all' + - numba>=0.60.0 ; extra == 'all' + - numexpr>=2.10.2 ; extra == 'all' + - odfpy>=1.4.1 ; extra == 'all' + - openpyxl>=3.1.5 ; extra == 'all' + - psycopg2>=2.9.10 ; extra == 'all' + - pyarrow>=13.0.0 ; extra == 'all' + - pyiceberg>=0.8.1 ; extra == 'all' + - pymysql>=1.1.1 ; extra == 'all' + - pyqt5>=5.15.9 ; extra == 'all' + - pyreadstat>=1.2.8 ; extra == 'all' + - pytest>=8.3.4 ; extra == 'all' + - pytest-xdist>=3.6.1 ; extra == 'all' + - python-calamine>=0.3.0 ; extra == 'all' + - pytz>=2020.1 ; extra == 'all' + - pyxlsb>=1.0.10 ; extra == 'all' + - qtpy>=2.4.2 ; extra == 'all' + - scipy>=1.14.1 ; extra == 'all' + - s3fs>=2024.10.0 ; extra == 'all' + - sqlalchemy>=2.0.36 ; extra == 'all' + - tables>=3.10.1 ; extra == 'all' + - tabulate>=0.9.0 ; extra == 'all' + - xarray>=2024.10.0 ; extra == 'all' + - xlrd>=2.0.1 ; extra == 'all' + - xlsxwriter>=3.2.0 ; extra == 'all' + - zstandard>=0.23.0 ; extra == 'all' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/ca/ba/ffdcb19be4ff6bfe7d969e7cef2c567c633df5a3a1cc1053394ad053bca8/pandas-3.0.6-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl + name: pandas + version: 3.0.6 + sha256: 62f51d7f651c8054c5e82a69265c98082e795d1442df7ca6edc3a545d61214b1 + requires_dist: + - numpy>=1.26.0 ; python_full_version < '3.14' + - numpy>=2.3.3 ; python_full_version >= '3.14' + - python-dateutil>=2.8.2 + - tzdata ; sys_platform == 'win32' + - tzdata ; sys_platform == 'emscripten' + - hypothesis>=6.116.0 ; extra == 'test' + - pytest>=8.3.4,<9.1 ; extra == 'test' + - pytest-xdist>=3.6.1 ; extra == 'test' + - pyarrow>=13.0.0 ; extra == 'pyarrow' + - bottleneck>=1.4.2 ; extra == 'performance' + - numba>=0.60.0 ; extra == 'performance' + - numexpr>=2.10.2 ; extra == 'performance' + - scipy>=1.14.1 ; extra == 'computation' + - xarray>=2024.10.0 ; extra == 'computation' + - fsspec>=2024.10.0 ; extra == 'fss' + - s3fs>=2024.10.0 ; extra == 'aws' + - gcsfs>=2024.10.0 ; extra == 'gcp' + - odfpy>=1.4.1 ; extra == 'excel' + - openpyxl>=3.1.5 ; extra == 'excel' + - python-calamine>=0.3.0 ; extra == 'excel' + - pyxlsb>=1.0.10 ; extra == 'excel' + - xlrd>=2.0.1 ; extra == 'excel' + - xlsxwriter>=3.2.0 ; extra == 'excel' + - pyarrow>=13.0.0 ; extra == 'parquet' + - pyarrow>=13.0.0 ; extra == 'feather' + - pyiceberg>=0.8.1 ; extra == 'iceberg' + - tables>=3.10.1 ; extra == 'hdf5' + - pyreadstat>=1.2.8 ; extra == 'spss' + - sqlalchemy>=2.0.36 ; extra == 'postgresql' + - psycopg2>=2.9.10 ; extra == 'postgresql' + - adbc-driver-postgresql>=1.2.0 ; extra == 'postgresql' + - sqlalchemy>=2.0.36 ; extra == 'mysql' + - pymysql>=1.1.1 ; extra == 'mysql' + - sqlalchemy>=2.0.36 ; extra == 'sql-other' + - adbc-driver-postgresql>=1.2.0 ; extra == 'sql-other' + - adbc-driver-sqlite>=1.2.0 ; extra == 'sql-other' + - beautifulsoup4>=4.12.3 ; extra == 'html' + - html5lib>=1.1 ; extra == 'html' + - lxml>=5.3.0 ; extra == 'html' + - lxml>=5.3.0 ; extra == 'xml' + - matplotlib>=3.9.3 ; extra == 'plot' + - jinja2>=3.1.5 ; extra == 'output-formatting' + - tabulate>=0.9.0 ; extra == 'output-formatting' + - pyqt5>=5.15.9 ; extra == 'clipboard' + - qtpy>=2.4.2 ; extra == 'clipboard' + - zstandard>=0.23.0 ; extra == 'compression' + - pytz>=2020.1 ; extra == 'timezone' + - adbc-driver-postgresql>=1.2.0 ; extra == 'all' + - adbc-driver-sqlite>=1.2.0 ; extra == 'all' + - beautifulsoup4>=4.12.3 ; extra == 'all' + - bottleneck>=1.4.2 ; extra == 'all' + - fastparquet>=2024.11.0 ; extra == 'all' + - fsspec>=2024.10.0 ; extra == 'all' + - gcsfs>=2024.10.0 ; extra == 'all' + - html5lib>=1.1 ; extra == 'all' + - hypothesis>=6.116.0 ; extra == 'all' + - jinja2>=3.1.5 ; extra == 'all' + - lxml>=5.3.0 ; extra == 'all' + - matplotlib>=3.9.3 ; extra == 'all' + - numba>=0.60.0 ; extra == 'all' + - numexpr>=2.10.2 ; extra == 'all' + - odfpy>=1.4.1 ; extra == 'all' + - openpyxl>=3.1.5 ; extra == 'all' + - psycopg2>=2.9.10 ; extra == 'all' + - pyarrow>=13.0.0 ; extra == 'all' + - pyiceberg>=0.8.1 ; extra == 'all' + - pymysql>=1.1.1 ; extra == 'all' + - pyqt5>=5.15.9 ; extra == 'all' + - pyreadstat>=1.2.8 ; extra == 'all' + - pytest>=8.3.4 ; extra == 'all' + - pytest-xdist>=3.6.1 ; extra == 'all' + - python-calamine>=0.3.0 ; extra == 'all' + - pytz>=2020.1 ; extra == 'all' + - pyxlsb>=1.0.10 ; extra == 'all' + - qtpy>=2.4.2 ; extra == 'all' + - scipy>=1.14.1 ; extra == 'all' + - s3fs>=2024.10.0 ; extra == 'all' + - sqlalchemy>=2.0.36 ; extra == 'all' + - tables>=3.10.1 ; extra == 'all' + - tabulate>=0.9.0 ; extra == 'all' + - xarray>=2024.10.0 ; extra == 'all' + - xlrd>=2.0.1 ; extra == 'all' + - xlsxwriter>=3.2.0 ; extra == 'all' + - zstandard>=0.23.0 ; extra == 'all' + requires_python: '>=3.11' - conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.56.4-hadf4263_0.conda sha256: 3613774ad27e48503a3a6a9d72017087ea70f1426f6e5541dbdb59a3b626eaaf md5: 79f71230c069a287efe3a8614069ddf1 @@ -5135,6 +10927,15 @@ packages: purls: [] size: 455420 timestamp: 1751292466873 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pango-1.58.2-h2f58602_3.conda + sha256: 255c7311441dddee7858279b799156387a6719d3a114dce861cbf379f6fb9d6e + md5: 4a1bbf999cdcdcf3ca1bfd819259f795 + depends: + - libpango-devel ==1.58.2 h317d9d0_3 + license: LGPL-2.1-or-later + purls: [] + size: 15014 + timestamp: 1792050659391 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pango-1.56.4-he55ef5b_0.conda sha256: dd36cd5b6bc1c2988291a6db9fa4eb8acade9b487f6f1da4eaa65a1eebb0a12d md5: a22cc88bf6059c9bcc158c94c9aab5b8 @@ -5155,6 +10956,15 @@ packages: purls: [] size: 468811 timestamp: 1751293869070 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pango-1.58.2-ha8baf52_3.conda + sha256: 2c85bd0810c46656d518ec8359a1eca95505ea3c2048bbfdac17e2b9876ded16 + md5: aab3e43f750309004a4e24cf711d4166 + depends: + - libpango-devel ==1.58.2 h12dfbc4_3 + license: LGPL-2.1-or-later + purls: [] + size: 15014 + timestamp: 1792050651506 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.56.4-h875632e_0.conda sha256: 705484ad60adee86cab1aad3d2d8def03a699ece438c864e8ac995f6f66401a6 md5: 7d57f8b4b7acfc75c777bc231f0d31be @@ -5175,6 +10985,15 @@ packages: purls: [] size: 426931 timestamp: 1751292636271 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pango-1.58.2-hc6efcc1_3.conda + sha256: 4206a8d179dbc2ae070d7c5ed148b73ccf5ec8a962f428e2645527bad9c016de + md5: 17bb0626b5ac18be2c28e65b6d949e3a + depends: + - libpango-devel ==1.58.2 haa2d88b_3 + license: LGPL-2.1-or-later + purls: [] + size: 15035 + timestamp: 1792050658787 - pypi: https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl name: parso version: 0.8.5 @@ -5186,6 +11005,17 @@ packages: - mypy==0.971 ; extra == 'qa' - types-setuptools==67.2.0.1 ; extra == 'qa' requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl + name: parso + version: 0.8.7 + sha256: a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c + requires_dist: + - flake8==5.0.4 ; extra == 'qa' + - types-setuptools==67.2.0.1 ; extra == 'qa' + - zuban==0.5.1 ; extra == 'qa' + - docopt ; extra == 'testing' + - pytest ; extra == 'testing' + requires_python: '>=3.6' - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.46-h1321c63_0.conda sha256: 5c7380c8fd3ad5fc0f8039069a45586aa452cf165264bc5a437ad80397b32934 md5: 7fa07cb0fb1b625a089ccc01218ee5b1 @@ -5199,6 +11029,19 @@ packages: purls: [] size: 1209177 timestamp: 1756742976157 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-h8b3dc9c_1.conda + sha256: 9ebb10a4eb3be51ae67d85c679f5a7a50cb040ed25c783e6331a225ea09991d4 + md5: 06f6113cf1ff4a54b65f87ead132a5f1 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1218833 + timestamp: 1787294571916 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pcre2-10.46-h15761aa_0.conda sha256: 75800e60e0e44d957c691a964085f56c9ac37dcd75e6c6904809d7b68f39e4ea md5: 5128cb5188b630a58387799ea1366e37 @@ -5211,6 +11054,18 @@ packages: purls: [] size: 1161914 timestamp: 1756742893031 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pcre2-10.47-he574923_1.conda + sha256: 16e5bee11686028d60a495004f99a10c613ada44cc27e330bee18a1b67fb184c + md5: c98763518d0a4c9895e515190afb7dec + depends: + - bzip2 >=1.0.8,<2.0a0 + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 1199286 + timestamp: 1787294520352 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.46-h7125dd6_0.conda sha256: 5bf2eeaa57aab6e8e95bea6bd6bb2a739f52eb10572d8ed259d25864d3528240 md5: 0e6e82c3cc3835f4692022e9b9cd5df8 @@ -5223,16 +11078,215 @@ packages: purls: [] size: 835080 timestamp: 1756743041908 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.47-he63d830_1.conda + sha256: f8c415329b542e1fca1ff2917b80e687c18302dfa0353530668b90cb225b494f + md5: 5ab90033816cbe4097e8a297e5179f67 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 851704 + timestamp: 1787294559157 - pypi: https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl name: pexpect version: 4.9.0 sha256: 7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 requires_dist: - - ptyprocess>=0.5 -- pypi: https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - ptyprocess>=0.5 +- pypi: https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: pillow + version: 11.3.0 + sha256: 13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8 + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=8.2 ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - pyarrow ; extra == 'test-arrow' + - check-manifest ; extra == 'tests' + - coverage>=7.4.2 ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pyroma ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - trove-classifiers>=2024.10.12 ; extra == 'tests' + - typing-extensions ; python_full_version < '3.10' and extra == 'typing' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + name: pillow + version: 12.0.0 + sha256: 0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=8.2 ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - arro3-compute ; extra == 'test-arrow' + - arro3-core ; extra == 'test-arrow' + - nanoarrow ; extra == 'test-arrow' + - pyarrow ; extra == 'test-arrow' + - check-manifest ; extra == 'tests' + - coverage>=7.4.2 ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pyroma>=5 ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - trove-classifiers>=2024.10.12 ; extra == 'tests' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl + name: pillow + version: 12.0.0 + sha256: 5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=8.2 ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - arro3-compute ; extra == 'test-arrow' + - arro3-core ; extra == 'test-arrow' + - nanoarrow ; extra == 'test-arrow' + - pyarrow ; extra == 'test-arrow' + - check-manifest ; extra == 'tests' + - coverage>=7.4.2 ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pyroma>=5 ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - trove-classifiers>=2024.10.12 ; extra == 'tests' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/07/50/b5d688cc9c52d4482f3d5bcab6ce20bc2a74a85d2343841c907444a3be2c/pillow-12.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + name: pillow + version: 12.3.0 + sha256: 5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=8.2 ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - arro3-compute ; extra == 'test-arrow' + - arro3-core ; extra == 'test-arrow' + - nanoarrow ; extra == 'test-arrow' + - pyarrow ; extra == 'test-arrow' + - coverage>=7.4.2 ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - setuptools ; extra == 'tests' + - trove-classifiers>=2024.10.12 ; extra == 'tests' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/4e/89/36f4cd76cf4baf05c50ababb976249153f18c959171c7f6ba09a6f217260/pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + name: pillow + version: 12.3.0 + sha256: f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=8.2 ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - arro3-compute ; extra == 'test-arrow' + - arro3-core ; extra == 'test-arrow' + - nanoarrow ; extra == 'test-arrow' + - pyarrow ; extra == 'test-arrow' + - coverage>=7.4.2 ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - setuptools ; extra == 'tests' + - trove-classifiers>=2024.10.12 ; extra == 'tests' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/6b/ba/3762f376a2948e3036488d773a146e0ae6ecc2ca03ac20e2615bd0b2ba02/pillow-12.3.0-cp310-cp310-macosx_11_0_arm64.whl + name: pillow + version: 12.3.0 + sha256: bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7 + requires_dist: + - furo ; extra == 'docs' + - olefile ; extra == 'docs' + - sphinx>=8.2 ; extra == 'docs' + - sphinx-autobuild ; extra == 'docs' + - sphinx-copybutton ; extra == 'docs' + - sphinx-inline-tabs ; extra == 'docs' + - sphinxext-opengraph ; extra == 'docs' + - olefile ; extra == 'fpx' + - olefile ; extra == 'mic' + - arro3-compute ; extra == 'test-arrow' + - arro3-core ; extra == 'test-arrow' + - nanoarrow ; extra == 'test-arrow' + - pyarrow ; extra == 'test-arrow' + - coverage>=7.4.2 ; extra == 'tests' + - defusedxml ; extra == 'tests' + - markdown2 ; extra == 'tests' + - olefile ; extra == 'tests' + - packaging ; extra == 'tests' + - pytest ; extra == 'tests' + - pytest-cov ; extra == 'tests' + - pytest-timeout ; extra == 'tests' + - pytest-xdist ; extra == 'tests' + - setuptools ; extra == 'tests' + - trove-classifiers>=2024.10.12 ; extra == 'tests' + - defusedxml ; extra == 'xmp' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/1d/f2/1371707ce438ba9a43f9d24089828494ec6f9396e28cd7903aa704dd6306/pillow-13.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl name: pillow - version: 11.3.0 - sha256: 13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8 + version: 13.0.0 + sha256: e91c2b1cbeae768c98752e67e717e283d68dae45717b41f2d91eaee648e37b7d requires_dist: - furo ; extra == 'docs' - olefile ; extra == 'docs' @@ -5243,26 +11297,28 @@ packages: - sphinxext-opengraph ; extra == 'docs' - olefile ; extra == 'fpx' - olefile ; extra == 'mic' + - arro3-compute ; extra == 'test-arrow' + - arro3-core ; extra == 'test-arrow' + - nanoarrow ; extra == 'test-arrow' - pyarrow ; extra == 'test-arrow' - - check-manifest ; extra == 'tests' - coverage>=7.4.2 ; extra == 'tests' - defusedxml ; extra == 'tests' - markdown2 ; extra == 'tests' - olefile ; extra == 'tests' - packaging ; extra == 'tests' - - pyroma ; extra == 'tests' + - psutil ; (sys_platform == 'darwin' and extra == 'tests') or (sys_platform == 'linux' and extra == 'tests') - pytest ; extra == 'tests' - pytest-cov ; extra == 'tests' - pytest-timeout ; extra == 'tests' - pytest-xdist ; extra == 'tests' + - setuptools ; extra == 'tests' - trove-classifiers>=2024.10.12 ; extra == 'tests' - - typing-extensions ; python_full_version < '3.10' and extra == 'typing' - defusedxml ; extra == 'xmp' - requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/83/16/b603274617b3e6728da3a0133d4fba1ea8d7fbe0c4ea5fef9cbffa398b8b/pillow-13.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl name: pillow - version: 12.0.0 - sha256: 0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e + version: 13.0.0 + sha256: 5933fcea5a5ba64c686f55c886a115cbfd92f2900460784a1e79909fdbafd7b6 requires_dist: - furo ; extra == 'docs' - olefile ; extra == 'docs' @@ -5277,24 +11333,24 @@ packages: - arro3-core ; extra == 'test-arrow' - nanoarrow ; extra == 'test-arrow' - pyarrow ; extra == 'test-arrow' - - check-manifest ; extra == 'tests' - coverage>=7.4.2 ; extra == 'tests' - defusedxml ; extra == 'tests' - markdown2 ; extra == 'tests' - olefile ; extra == 'tests' - packaging ; extra == 'tests' - - pyroma>=5 ; extra == 'tests' + - psutil ; (sys_platform == 'darwin' and extra == 'tests') or (sys_platform == 'linux' and extra == 'tests') - pytest ; extra == 'tests' - pytest-cov ; extra == 'tests' - pytest-timeout ; extra == 'tests' - pytest-xdist ; extra == 'tests' + - setuptools ; extra == 'tests' - trove-classifiers>=2024.10.12 ; extra == 'tests' - defusedxml ; extra == 'xmp' - requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/b5/f4/0bf6f1ac9ba7ee2ffe9914aeb410a1035a0c1a3c42f122388d7fc950bc4b/pillow-13.0.0-cp314-cp314-macosx_11_0_arm64.whl name: pillow - version: 12.0.0 - sha256: 5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b + version: 13.0.0 + sha256: d9b0a2bee3f3e03e9f29057aedb807dbf1e1d96ee1b9d0f422d9db6c8583271d requires_dist: - furo ; extra == 'docs' - olefile ; extra == 'docs' @@ -5309,20 +11365,20 @@ packages: - arro3-core ; extra == 'test-arrow' - nanoarrow ; extra == 'test-arrow' - pyarrow ; extra == 'test-arrow' - - check-manifest ; extra == 'tests' - coverage>=7.4.2 ; extra == 'tests' - defusedxml ; extra == 'tests' - markdown2 ; extra == 'tests' - olefile ; extra == 'tests' - packaging ; extra == 'tests' - - pyroma>=5 ; extra == 'tests' + - psutil ; (sys_platform == 'darwin' and extra == 'tests') or (sys_platform == 'linux' and extra == 'tests') - pytest ; extra == 'tests' - pytest-cov ; extra == 'tests' - pytest-timeout ; extra == 'tests' - pytest-xdist ; extra == 'tests' + - setuptools ; extra == 'tests' - trove-classifiers>=2024.10.12 ; extra == 'tests' - defusedxml ; extra == 'xmp' - requires_python: '>=3.10' + requires_python: '>=3.11' - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda sha256: 43d37bc9ca3b257c5dd7bf76a8426addbdec381f6786ff441dc90b1a49143b6a md5: c01af13bdc553d1a8fbfff6e8db075f0 @@ -5336,6 +11392,18 @@ packages: purls: [] size: 450960 timestamp: 1754665235234 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_3.conda + sha256: 829d8288764282de5a9f7b9169acb75cc7dc0b6c3fe2535cfe87dea3436bbc5d + md5: 0ee5bb30034b081a1386c1e2c98ab0a7 + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 376704 + timestamp: 1786106621354 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pixman-0.46.4-h7ac5ae9_1.conda sha256: e6b0846a998f2263629cfeac7bca73565c35af13251969f45d385db537a514e4 md5: 1587081d537bd4ae77d1c0635d465ba5 @@ -5348,6 +11416,28 @@ packages: purls: [] size: 357913 timestamp: 1754665583353 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pixman-0.46.4-h7ac5ae9_3.conda + sha256: ed718d35c744ceef33063b5686f27b4b8d30d4065485e24efafb248658ee83b5 + md5: c5f34596fc05afb9ccc2782c42bcfcc5 + depends: + - libstdcxx >=14 + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 304657 + timestamp: 1786106625126 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h784d473_3.conda + sha256: 4779cd57231ce2e96fac643fcbdd4a6f51a57986264545445574e9c4acf526d3 + md5: 9a99c0b60efe41c194d01c182d000733 + depends: + - __osx >=11.0 + - libcxx >=19 + license: MIT + license_family: MIT + purls: [] + size: 198717 + timestamp: 1786106922508 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.46.4-h81086ad_1.conda sha256: 29c9b08a9b8b7810f9d4f159aecfd205fce051633169040005c0b7efad4bc718 md5: 17c3d745db6ea72ae2fce17e7338547f @@ -5402,6 +11492,11 @@ packages: - pytest-benchmark ; extra == 'testing' - coverage ; extra == 'testing' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl + name: pluggy + version: 1.7.0 + sha256: 7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/b4/db/08f4ca10c5018813e7e0b59e4472302328b3d2ab1512f5a2157a814540e0/polars-1.39.3-py3-none-any.whl name: polars version: 1.39.3 @@ -5439,6 +11534,43 @@ packages: - cudf-polars-cu12 ; extra == 'gpu' - polars[async,cloudpickle,database,deltalake,excel,fsspec,graph,iceberg,numpy,pandas,plot,pyarrow,pydantic,style,timezone] ; extra == 'all' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ac/09/cc33bbd5463749c116b62c204d88bed6c02a6cb901eac7adab0d38651b07/polars-2.0.0-py3-none-any.whl + name: polars + version: 2.0.0 + sha256: 35d62f3541b7a6d4c360a2e2f07fccc0c2bcbd33b0ea51c83a25417a47a3f3ad + requires_dist: + - polars-runtime-32==2.0.0 + - polars-runtime-64==2.0.0 ; extra == 'rt64' + - polars-runtime-compat==2.0.0 ; extra == 'rtcompat' + - polars-cloud>=0.11.0 ; extra == 'polars-cloud' + - numpy>=1.16.0 ; extra == 'numpy' + - pandas ; extra == 'pandas' + - polars[pyarrow] ; extra == 'pandas' + - pyarrow>=7.0.0 ; extra == 'pyarrow' + - pydantic ; extra == 'pydantic' + - fastexcel>=0.9 ; extra == 'calamine' + - openpyxl>=3.0.0 ; extra == 'openpyxl' + - xlsx2csv>=0.8.0 ; extra == 'xlsx2csv' + - xlsxwriter ; extra == 'xlsxwriter' + - polars[calamine,openpyxl,xlsx2csv,xlsxwriter] ; extra == 'excel' + - adbc-driver-manager[dbapi] ; extra == 'adbc' + - adbc-driver-sqlite[dbapi] ; extra == 'adbc' + - connectorx>=0.3.2 ; extra == 'connectorx' + - sqlalchemy ; extra == 'sqlalchemy' + - polars[pandas] ; extra == 'sqlalchemy' + - polars[adbc,connectorx,sqlalchemy] ; extra == 'database' + - fsspec ; extra == 'fsspec' + - deltalake>=1.0.0,!=1.5.* ; extra == 'deltalake' + - pyiceberg>=0.12.0 ; extra == 'iceberg' + - gevent ; extra == 'async' + - cloudpickle ; extra == 'cloudpickle' + - matplotlib ; extra == 'graph' + - altair>=5.4.0 ; extra == 'plot' + - great-tables>=0.8.0 ; extra == 'style' + - tzdata ; sys_platform == 'win32' and extra == 'timezone' + - cudf-polars-cu12 ; extra == 'gpu' + - polars[async,cloudpickle,database,deltalake,excel,fsspec,graph,iceberg,numpy,pandas,plot,pyarrow,pydantic,style,timezone] ; extra == 'all' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/3d/3e/e65236d9d0d9babfa0ecba593413c06530fca60a8feb8f66243aa5dba92e/polars_runtime_32-1.39.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl name: polars-runtime-32 version: 1.39.3 @@ -5454,6 +11586,21 @@ packages: version: 1.39.3 sha256: 8bc9e13dc1d2e828331f2fe8ccbc9757554dc4933a8d3e85e906b988178f95ed requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/0d/0d/85e3ed90417996fc09770be91b39979074fe2978fc15b431bf8a9459760d/polars_runtime_32-2.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: polars-runtime-32 + version: 2.0.0 + sha256: 8b85bb42e6009acc9629afcc70a83473fd468694d6a30ffb0ab376c8dd1a0a17 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/5f/fd/2237bf53ffaff47cdf1edc6c10587a7a6444d4951150eeb08d84f3493ff8/polars_runtime_32-2.0.0-cp310-abi3-macosx_11_0_arm64.whl + name: polars-runtime-32 + version: 2.0.0 + sha256: 7012d8a0201bd95638545ce8f256c0efe2c5cab0f806eb043021dddde5a9498b + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/83/88/e9fecfd49159da92f54ff2445883577a0f1bc195da53ecc9535c458d55dd/polars_runtime_32-2.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: polars-runtime-32 + version: 2.0.0 + sha256: 0d6ac584ea2b38913784db943879412380d92e28ab9cb88e20a77ba71ba3f911 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl name: pre-commit version: 4.3.0 @@ -5483,6 +11630,13 @@ packages: requires_dist: - wcwidth requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl + name: prompt-toolkit + version: 3.0.53 + sha256: 01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2 + requires_dist: + - wcwidth>=0.1.4 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl name: propcache version: 0.4.1 @@ -5498,6 +11652,168 @@ packages: version: 0.4.1 sha256: d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/96/d3/e97cd6f5de2176bd90ed4076c7a9b5e09d0f0b9687d00a576507988bb62c/propcache-0.5.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: propcache + version: 0.5.4 + sha256: cc07876cfb079b6f6f36d21ce75784ad6c2c6b563eeac0ed26c2fa2669b85df9 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/bf/d1/7fd3ffb5ca0e669a8fbf55d9fbb17c50ff5f44becfefcab27a9b9b67908f/propcache-0.5.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: propcache + version: 0.5.4 + sha256: c9281e922c072158c91974d4589f1dbe0fee6d467f284c28e463f9f5a4d933f4 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e2/22/b533b493d7025456f44518b33e53e000021a20fe7c27b88cf3d341df7186/propcache-0.5.4-cp314-cp314-macosx_11_0_arm64.whl + name: propcache + version: 0.5.4 + sha256: 1d759d05634f1b038fb625a66662a8c85e5a8fec912da381b5149ddac107482b + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/f0/51/5adee15e12a7e314cece4543fbf295b0fe1b504dc78b32dff4c8eb1e29b0/propcache-0.5.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: propcache + version: 0.5.4 + sha256: f574e460d1c8a08384a016fdb09ccf3543433263ed6b2f97104f979e64ea57c2 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/f1/74/70ac8430e28f21e442c7bcb964eb46c4363f6881ade4aa0e978bfd8d503a/propcache-0.5.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: propcache + version: 0.5.4 + sha256: 251c63dd46a0659bb875cb254dc4c1e79ee91a847c737cd62373295afc2235dc + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ff/9a/6be90814d8762952594a9e380161802541bff690f3a2e011dcc28ec193ce/propcache-0.5.4-cp310-cp310-macosx_11_0_arm64.whl + name: propcache + version: 0.5.4 + sha256: d83b12902eb8bce151259c86c03ba746600b2d994543de46e370cecf96c452f2 + requires_python: '>=3.10' +- pypi: 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 + name: psutil + version: 7.2.2 + sha256: b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e + requires_dist: + - psleak ; extra == 'dev' + - pytest ; extra == 'dev' + - pytest-instafail ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - setuptools ; extra == 'dev' + - abi3audit ; extra == 'dev' + - black ; extra == 'dev' + - check-manifest ; extra == 'dev' + - coverage ; extra == 'dev' + - packaging ; extra == 'dev' + - pylint ; extra == 'dev' + - pyperf ; extra == 'dev' + - pypinfo ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - requests ; extra == 'dev' + - rstcheck ; extra == 'dev' + - ruff ; extra == 'dev' + - sphinx ; extra == 'dev' + - sphinx-rtd-theme ; extra == 'dev' + - toml-sort ; extra == 'dev' + - twine ; extra == 'dev' + - validate-pyproject[all] ; extra == 'dev' + - virtualenv ; extra == 'dev' + - vulture ; extra == 'dev' + - wheel ; extra == 'dev' + - colorama ; os_name == 'nt' and extra == 'dev' + - pyreadline3 ; os_name == 'nt' and extra == 'dev' + - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - psleak ; extra == 'test' + - pytest ; extra == 'test' + - pytest-instafail ; extra == 'test' + - pytest-xdist ; extra == 'test' + - setuptools ; extra == 'test' + - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl + name: psutil + version: 7.2.2 + sha256: 1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979 + requires_dist: + - psleak ; extra == 'dev' + - pytest ; extra == 'dev' + - pytest-instafail ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - setuptools ; extra == 'dev' + - abi3audit ; extra == 'dev' + - black ; extra == 'dev' + - check-manifest ; extra == 'dev' + - coverage ; extra == 'dev' + - packaging ; extra == 'dev' + - pylint ; extra == 'dev' + - pyperf ; extra == 'dev' + - pypinfo ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - requests ; extra == 'dev' + - rstcheck ; extra == 'dev' + - ruff ; extra == 'dev' + - sphinx ; extra == 'dev' + - sphinx-rtd-theme ; extra == 'dev' + - toml-sort ; extra == 'dev' + - twine ; extra == 'dev' + - validate-pyproject[all] ; extra == 'dev' + - virtualenv ; extra == 'dev' + - vulture ; extra == 'dev' + - wheel ; extra == 'dev' + - colorama ; os_name == 'nt' and extra == 'dev' + - pyreadline3 ; os_name == 'nt' and extra == 'dev' + - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - psleak ; extra == 'test' + - pytest ; extra == 'test' + - pytest-instafail ; extra == 'test' + - pytest-xdist ; extra == 'test' + - setuptools ; extra == 'test' + - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + requires_python: '>=3.6' +- pypi: 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 + name: psutil + version: 7.2.2 + sha256: 076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9 + requires_dist: + - psleak ; extra == 'dev' + - pytest ; extra == 'dev' + - pytest-instafail ; extra == 'dev' + - pytest-xdist ; extra == 'dev' + - setuptools ; extra == 'dev' + - abi3audit ; extra == 'dev' + - black ; extra == 'dev' + - check-manifest ; extra == 'dev' + - coverage ; extra == 'dev' + - packaging ; extra == 'dev' + - pylint ; extra == 'dev' + - pyperf ; extra == 'dev' + - pypinfo ; extra == 'dev' + - pytest-cov ; extra == 'dev' + - requests ; extra == 'dev' + - rstcheck ; extra == 'dev' + - ruff ; extra == 'dev' + - sphinx ; extra == 'dev' + - sphinx-rtd-theme ; extra == 'dev' + - toml-sort ; extra == 'dev' + - twine ; extra == 'dev' + - validate-pyproject[all] ; extra == 'dev' + - virtualenv ; extra == 'dev' + - vulture ; extra == 'dev' + - wheel ; extra == 'dev' + - colorama ; os_name == 'nt' and extra == 'dev' + - pyreadline3 ; os_name == 'nt' and extra == 'dev' + - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'dev' + - psleak ; extra == 'test' + - pytest ; extra == 'test' + - pytest-instafail ; extra == 'test' + - pytest-xdist ; extra == 'test' + - setuptools ; extra == 'test' + - pywin32 ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + - wheel ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + - wmi ; implementation_name != 'pypy' and os_name == 'nt' and extra == 'test' + requires_python: '>=3.6' - pypi: https://files.pythonhosted.org/packages/11/32/b2ffe8f3853c181e88f0a157c5fb4e383102238d73c52ac6d93a5c8bffe6/psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl name: psycopg2-binary version: 2.9.11 @@ -5513,6 +11829,47 @@ packages: version: 2.9.11 sha256: 366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/0f/f4/e3a789de34c9ac25d20b25c2be583da16394a2ba0926da1c863653831f41/psycopg2_binary-2.9.13-cp314-cp314-macosx_11_0_arm64.whl + name: psycopg2-binary + version: 2.9.13 + sha256: 4c0214c7da18a28d108aa7108c8a3cca8035c7911ec97ef9ec0827569c9a2720 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/27/ef/e6f9280c2327e52f6c139ab549e57a21202b724384d4697dce5794c59f64/psycopg2_binary-2.9.13-cp310-cp310-macosx_11_0_arm64.whl + name: psycopg2-binary + version: 2.9.13 + sha256: 086659ab083119f7ee87a779e31b94211cf162b708fc9a6bec771f75c73ac3e6 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/3d/2a/1a472059b198942d99651656e2bc610575584478bfe68d297ecabbd4887f/psycopg2_binary-2.9.13-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + name: psycopg2-binary + version: 2.9.13 + sha256: c00ebe9a2f31151aade0db233dc1446513a95e92c39ce055ee097af0ae86be1c + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/91/ad/7f52f92cc65c23778daff7eec4ee2099236694a0a4723a5f180d0708b607/psycopg2_binary-2.9.13-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: psycopg2-binary + version: 2.9.13 + sha256: 190c18b97d9ef72f2e88c451b6588af90d6bd7bf54cb94b963280dc86a2c7076 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ee/29/b41c62219b232c9631db1a8cfb99a93abb9afb8a00291579b830f756ce89/psycopg2_binary-2.9.13-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl + name: psycopg2-binary + version: 2.9.13 + sha256: f124954a32640dfb5c000d33028f48053930d7ff226bc74cde5fb316f9c6fcb6 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/fd/96/ce330aa1959250e6fd254678e6489b3b39eb956669122ad4b45dde3b02c3/psycopg2_binary-2.9.13-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: psycopg2-binary + version: 2.9.13 + sha256: d2fc9342aad969b9a28490a4c3eaba94b35beb2d26e9a39b31d1430378aa71b2 + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h7cc23a3_1004.conda + sha256: 4a44fd00ea73b79ca2c89b0727b9ccf61c506ead71e67a9abfa4c590042b5a4a + md5: bb66b610707b811e3c65181a6431e7a8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 9630 + timestamp: 1788381877753 - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 md5: b3c17d95b5a10c6e64a21fa17573e70e @@ -5524,6 +11881,16 @@ packages: purls: [] size: 8252 timestamp: 1726802366959 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h5bc82ec_1004.conda + sha256: c66ea2c8f64b815b24344340a8a9d62152359dbf48a6151b1f282fdf35204d83 + md5: ac6610f5c63c18f5a3df229aa5f7ba37 + depends: + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 9592 + timestamp: 1788383332468 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda sha256: 977dfb0cb3935d748521dd80262fe7169ab82920afd38ed14b7fee2ea5ec01ba md5: bb5a90c93e3bac3d5690acf76b4a6386 @@ -5544,6 +11911,12 @@ packages: sha256: 1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0 requires_dist: - pytest ; extra == 'tests' +- pypi: https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl + name: pure-eval + version: 0.2.4 + sha256: 96cae060a313cfaad51bb761278bfb0e62dc0248d9315a81173752dc546cd37a + requires_dist: + - pytest ; extra == 'tests' - pypi: https://files.pythonhosted.org/packages/47/10/2cbe4c6f0fb83d2de37249567373d64327a5e4d8db72f486db42875b08f6/pyarrow-23.0.1-cp313-cp313-macosx_12_0_arm64.whl name: pyarrow version: 23.0.1 @@ -5559,11 +11932,46 @@ packages: version: 23.0.1 sha256: 71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677 requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/0a/3e/5cd70becb51e1d044c54ba5e627424a6e87df5b98008cbd22cc6abd409ca/pyarrow-25.0.1-cp310-cp310-macosx_12_0_arm64.whl + name: pyarrow + version: 25.0.1 + sha256: 0b1edbb2f385a6a65e9711b62ba86ac54a7816a3f8d17bb3e8a5929d65fb2485 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/42/34/e138b451fd3970a6eda4599f68ae3b2b32b661bc958de3239d54a0bf6575/pyarrow-25.0.1-cp310-cp310-manylinux_2_28_aarch64.whl + name: pyarrow + version: 25.0.1 + sha256: bddd0c4f7630c2a3ddf6347c1bdaa79d97bcf6bd445f9e60c816b7d77c85a5ae + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/57/5c/f8fc0eb2de03464a557d5a4d0c15e972d73362414696618833b771f7eddd/pyarrow-25.0.1-cp310-cp310-manylinux_2_28_x86_64.whl + name: pyarrow + version: 25.0.1 + sha256: a4d6d5e9a3d1879a97c08ded0c797579b7965eafd0f0c26c30b45ccc06db939b + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/81/bc/c90fcbbcf893631e23dab1b0fb3fa29a508a8614326571b03c0894eda00b/pyarrow-26.0.0-cp314-cp314-manylinux_2_28_aarch64.whl + name: pyarrow + version: 26.0.0 + sha256: eaf9e7cc7ab59f6c760232bbde18f64d559bbc50544841303bfb32be53533297 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/8c/32/01858422a37f083911c2bb4d15cc32c5eeaa9d9b2bf5ddedee995a7146a6/pyarrow-26.0.0-cp314-cp314-macosx_12_0_arm64.whl + name: pyarrow + version: 26.0.0 + sha256: 5780d487ff6c6ed7b42298609680d87fe0036e529a9dc2e1105364bce9697f50 + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/ec/c1/0c1ff38ab7df1b2cf54cf0ad9f19a516c4e416c6c9b4c966cc2c9d587f77/pyarrow-26.0.0-cp314-cp314-manylinux_2_28_x86_64.whl + name: pyarrow + version: 26.0.0 + sha256: ab6914db225d7f399652ae1f08588dfbc9efe617612715701e3d9d5cfa5ca19f + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl name: pycparser version: '2.23' sha256: e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934 requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/90/11/0e6f11117525ff0eec40ebac3d313376f102df93ca44ad9e893ee85e4f89/pycparser-3.11-py3-none-any.whl + name: pycparser + version: '3.11' + sha256: 51d5a8ba2be0bbe440b99d2112604c95bbbc3c2748a64260186c541e1729cd80 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl name: pycryptodome version: 3.23.0 @@ -5579,6 +11987,30 @@ packages: version: 3.23.0 sha256: 187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27 requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*' +- pypi: https://files.pythonhosted.org/packages/22/5c/a8a903c48ab726e8a72d4ab750e0a7fe0ace0dcd6fb426ce5b004ff618c6/pycryptodome-4.0.1-cp39-abi3-macosx_10_9_universal2.whl + name: pycryptodome + version: 4.0.1 + sha256: 1e9ec908360f26087724b03cd582b24063eecdf21c43c48a01828f3555ac54df + requires_dist: + - pytest ; extra == 'test' + - pycryptodome-test-vectors ; extra == 'test' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/3b/7e/e72be1be5879001d922ce66d54e940fdf1bf6ce19bd6521e130ad26fe58d/pycryptodome-4.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + name: pycryptodome + version: 4.0.1 + sha256: 33b1a327ad08afc3c74bf0b7f36885149dade11c81758eabed0bf6b8e7bf4da4 + requires_dist: + - pytest ; extra == 'test' + - pycryptodome-test-vectors ; extra == 'test' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/ca/f6/1d91d5cbe2f3acd0ba6f596d54466ab8703485d4edf84ca28d9507db8d43/pycryptodome-4.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: pycryptodome + version: 4.0.1 + sha256: 71d7e3b83bf0ffc2c00dee1f7a7283914d0e406a55220f979b82a89baf2a19b0 + requires_dist: + - pytest ; extra == 'test' + - pycryptodome-test-vectors ; extra == 'test' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl name: pydantic version: 2.12.5 @@ -5591,27 +12023,81 @@ packages: - email-validator>=2.0.0 ; extra == 'email' - tzdata ; python_full_version >= '3.9' and sys_platform == 'win32' and extra == 'timezone' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl + name: pydantic + version: 2.14.1 + sha256: 9195d967ec791692a04438115466764fb8b9a27b31f14a760437694f40d6b454 + requires_dist: + - annotated-types>=0.6.0 + - pydantic-core==2.50.1 + - typing-extensions>=4.16.0 + - typing-inspection>=0.4.4 + - email-validator>=2.0.0 ; extra == 'email' + - tzdata ; sys_platform == 'win32' and extra == 'timezone' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl name: pydantic-core - version: 2.41.5 - sha256: 0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 + version: 2.41.5 + sha256: 0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0 + requires_dist: + - typing-extensions>=4.14.1 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl + name: pydantic-core + version: 2.41.5 + sha256: 112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 + requires_dist: + - typing-extensions>=4.14.1 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: pydantic-core + version: 2.41.5 + sha256: 406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 + requires_dist: + - typing-extensions>=4.14.1 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/1b/1a/21a58791021397d9ca35295e3f4291c16e319155c5904334b89eb07e97e1/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: pydantic-core + version: 2.50.1 + sha256: f77ac30b19221cd9bd3fcfa3d4614eff93140d0572ab730cded17b64adca05f3 + requires_dist: + - typing-extensions>=4.16.0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/45/35/38f6d6564fae57d9b12e5676dfa947e0e7d5c46dbeaa7eb3352261d6d299/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: pydantic-core + version: 2.50.1 + sha256: 42b54c2c90ad348b5e3a85e03e715d572c1fde357ef104cdfe3b03b697a404ea + requires_dist: + - typing-extensions>=4.16.0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/94/40/15c06410c9b7b8da5805d27b64e09bd3f900e986728106db2689dbc51513/pydantic_core-2.50.1-cp314-cp314-macosx_11_0_arm64.whl + name: pydantic-core + version: 2.50.1 + sha256: 4df197990c15b5a37c5a277d131d9f2c67de6133f2e5dafd80d9bba4b99f46f9 + requires_dist: + - typing-extensions>=4.16.0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a1/4e/5eb629f6efc2a27e421d789dd4bfacbb5f6d09c80c28b13d1e87d73163d5/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + name: pydantic-core + version: 2.50.1 + sha256: 0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c requires_dist: - - typing-extensions>=4.14.1 - requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl + - typing-extensions>=4.16.0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/bf/be/f75637e222085d29a3076edbc5cfd0dd9b77d145e310ff7dc1150f07558a/pydantic_core-2.50.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl name: pydantic-core - version: 2.41.5 - sha256: 112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34 + version: 2.50.1 + sha256: 3aa9de446b793de2beb6fa2d9d0961803126c4e2a99c2f25ab59b9fd6ea125c0 requires_dist: - - typing-extensions>=4.14.1 - requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - typing-extensions>=4.16.0 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/c1/17/6963f4ee55b7df7658f5c036c0f90baf8cd3d413f5d8b551f227e8467afc/pydantic_core-2.50.1-cp310-cp310-macosx_11_0_arm64.whl name: pydantic-core - version: 2.41.5 - sha256: 406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 + version: 2.50.1 + sha256: 1fa4c8bc12c1354c5550c0c35c1852c8c1901e89e06561724e03f8d0342e1f87 requires_dist: - - typing-extensions>=4.14.1 - requires_python: '>=3.9' + - typing-extensions>=4.16.0 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl name: pydantic-settings version: 2.12.0 @@ -5628,6 +12114,37 @@ packages: - tomli>=2.0.1 ; extra == 'toml' - pyyaml>=6.0.1 ; extra == 'yaml' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl + name: pydantic-settings + version: 2.15.0 + sha256: 0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42 + requires_dist: + - pydantic>=2.7.0 + - python-dotenv>=0.21.0 + - typing-inspection>=0.4.0 + - boto3>=1.35.0 ; extra == 'aws-secrets-manager' + - azure-identity>=1.16.0 ; extra == 'azure-key-vault' + - azure-keyvault-secrets>=4.8.0 ; extra == 'azure-key-vault' + - google-cloud-secret-manager>=2.23.1 ; extra == 'gcp-secret-manager' + - tomli>=2.0.1 ; extra == 'toml' + - pyyaml>=6.0.1 ; extra == 'yaml' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/53/f4/b987bf8c51e5b19a95fa66d1ee596074141e085d9c2ddf97920803c7029b/pydantic_settings-2.16.0-py3-none-any.whl + name: pydantic-settings + version: 2.16.0 + sha256: 7e73acf7f61936a15e5a3b6eedaea29f133357faf7272f2607ba479b049dd7f2 + requires_dist: + - pydantic>=2.7.0 + - python-dotenv>=1.2.2 + - typing-extensions>=4.0.0 + - typing-inspection>=0.4.0 + - boto3>=1.35.0 ; extra == 'aws-secrets-manager' + - boto3>=1.35.0 ; extra == 'aws-systems-manager' + - azure-identity>=1.16.0 ; extra == 'azure-key-vault' + - azure-keyvault-secrets>=4.8.0 ; extra == 'azure-key-vault' + - google-cloud-secret-manager>=2.23.1 ; extra == 'gcp-secret-manager' + - pyyaml>=6.0.1 ; extra == 'yaml' + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/7e/32/a7125fb28c4261a627f999d5fb4afff25b523800faed2c30979949d6facd/pydot-4.0.1-py3-none-any.whl name: pydot version: 4.0.1 @@ -5654,6 +12171,13 @@ packages: requires_dist: - colorama>=0.4.6 ; extra == 'windows-terminal' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl + name: pygments + version: 2.21.0 + sha256: 2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 + requires_dist: + - colorama>=0.4.6 ; extra == 'windows-terminal' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/7c/4c/ad33b92b9864cbde84f259d5df035a6447f91891f5be77788e2a3892bce3/pymysql-1.1.2-py3-none-any.whl name: pymysql version: 1.1.2 @@ -5662,6 +12186,14 @@ packages: - cryptography ; extra == 'rsa' - pynacl>=1.4.0 ; extra == 'ed25519' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/a9/4b/0a906d8184f011ff8dbd4722743783867589b33269d2c5fff238d636fdcb/pymysql-1.2.3-py3-none-any.whl + name: pymysql + version: 1.2.3 + sha256: 14f1c68e2ed859243ae5ca41ffbe677027fc46bc136a9f0be8a4e928e5e7415a + requires_dist: + - cryptography>=46.0.7 ; extra == 'rsa' + - pynacl>=1.6.2 ; extra == 'ed25519' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/53/b8/fbab973592e23ae313042d450fc26fa24282ebffba21ba373786e1ce63b4/pyparsing-3.2.4-py3-none-any.whl name: pyparsing version: 3.2.4 @@ -5678,6 +12210,14 @@ packages: - railroad-diagrams ; extra == 'diagrams' - jinja2 ; extra == 'diagrams' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl + name: pyparsing + version: 3.3.3 + sha256: ece8c00a69cf01b45d0b1dedabb469c90d8caf996d4fda40f147627a122849a4 + requires_dist: + - railroad-diagrams ; extra == 'diagrams' + - jinja2 ; extra == 'diagrams' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl name: pytest version: 8.4.2 @@ -5718,6 +12258,26 @@ packages: - setuptools ; extra == 'dev' - xmlschema ; extra == 'dev' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl + name: pytest + version: 9.1.1 + sha256: 37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c + requires_dist: + - colorama>=0.4 ; sys_platform == 'win32' + - exceptiongroup>=1 ; python_full_version < '3.11' + - iniconfig>=1.0.1 + - packaging>=22 + - pluggy>=1.5,<2 + - pygments>=2.7.2 + - tomli>=1 ; python_full_version < '3.11' + - argcomplete ; extra == 'dev' + - attrs>=19.2 ; extra == 'dev' + - hypothesis>=3.56 ; extra == 'dev' + - mock ; extra == 'dev' + - requests ; extra == 'dev' + - setuptools ; extra == 'dev' + - xmlschema ; extra == 'dev' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl name: pytest-cov version: 7.0.0 @@ -5730,6 +12290,45 @@ packages: - pytest-xdist ; extra == 'testing' - virtualenv ; extra == 'testing' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl + name: pytest-cov + version: 7.1.0 + sha256: a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 + requires_dist: + - coverage[toml]>=7.10.6 + - pluggy>=1.2 + - pytest>=7 + - process-tests ; extra == 'testing' + - pytest-xdist ; extra == 'testing' + - virtualenv ; extra == 'testing' + requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.22-h267e890_0_cpython.conda + sha256: 819fa140a71ab005c448bf164eb182f91610b50f0bffca5b0b94cb6773c5e2f4 + md5: 4dd10eb73930c39aa7bc48b979fa24e0 + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=14 + - liblzma >=5.8.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libuuid >=2.42.4,<3.0a0 + - libxcrypt >=4.4.38 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.10.* *_cp310 + license: Python-2.0 + purls: [] + size: 25334749 + timestamp: 1791328449078 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.7-h2b335a9_100_cp313.conda build_number: 100 sha256: 16cc30a5854f31ca6c3688337d34e37a79cdc518a06375fe3482ea8e2d6b34c8 @@ -5757,6 +12356,61 @@ packages: size: 33583088 timestamp: 1756911465277 python_site_packages_path: lib/python3.13/site-packages +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.8-hcd007b5_102_cp314.conda + build_number: 102 + sha256: 32a39d6b85fd55d9949ea48d63887bef0dc61dca46d9844d4b409eb993460617 + md5: bcfadea0ff38555e26872aab0e974dbd + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libpython 3.14.8 hd51c5da_102_cp314 + - libsqlite >=3.54.0,<4.0a0 + - libuuid >=2.42.4,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - python_abi 3.14.* *_cp314 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Python-2.0 + purls: [] + size: 26647968 + timestamp: 1791827304033 + python_site_packages_path: lib/python3.14/site-packages +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.10.22-h4f76b5d_0_cpython.conda + sha256: fc09bcc08d4f3d2a42eac87ef6e1007fa2f9b416c23b3cc5b110a8d069ccfc29 + md5: 69d1fa51283884bef951e5dbf6d186a0 + depends: + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-aarch64 >=2.36.1 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.4,<4.0a0 + - libgcc >=14 + - liblzma >=5.8.3,<6.0a0 + - libnsl >=2.0.1,<2.1.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libuuid >=2.42.4,<3.0a0 + - libxcrypt >=4.4.38 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.10.* *_cp310 + license: Python-2.0 + purls: [] + size: 13258124 + timestamp: 1791327279268 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.13.9-h4c0d347_101_cp313.conda build_number: 101 sha256: 95f11d8f8e8007ead0927ff15401a9a48a28df92b284f41a08824955c009e974 @@ -5783,6 +12437,56 @@ packages: size: 33737136 timestamp: 1761175607146 python_site_packages_path: lib/python3.13/site-packages +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.14.8-hbec3b18_102_cp314.conda + build_number: 102 + sha256: 83a74d9d10049de392f1cd79d468f9d0eba820df2179a5cccbf0335d98a97845 + md5: 179286fdbdf9b3c4728f31c1ae804f45 + depends: + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-aarch64 >=2.36.1 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libpython 3.14.8 h150030a_102_cp314 + - libsqlite >=3.54.0,<4.0a0 + - libuuid >=2.42.4,<3.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - python_abi 3.14.* *_cp314 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Python-2.0 + purls: [] + size: 25396826 + timestamp: 1791827089103 + python_site_packages_path: lib/python3.14/site-packages +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.22-hac0b6dc_0_cpython.conda + sha256: 66d5322d2a5b9e78bd65f301bd0a9da54452bedbbaead46a2b7da808cf485c80 + md5: b6dd4544c50c6fc3793cdec56467488a + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.4,<4.0a0 + - liblzma >=5.8.3,<6.0a0 + - libsqlite >=3.53.4,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + constrains: + - python_abi 3.10.* *_cp310 + license: Python-2.0 + purls: [] + size: 12877536 + timestamp: 1791327519372 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.9-hfc2f54d_101_cp313.conda build_number: 101 sha256: 516229f780b98783a5ef4112a5a4b5e5647d4f0177c4621e98aa60bb9bc32f98 @@ -5807,6 +12511,32 @@ packages: size: 11915380 timestamp: 1761176793936 python_site_packages_path: lib/python3.13/site-packages +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.8-h0ae1c2c_102_cp314.conda + build_number: 102 + sha256: b8e5729761450f22fd591af7e565af02112a4dc0fa21717519c4f06c59ee6781 + md5: a3bbba11fd9d39c475627842b34eda46 + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.8.5,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - liblzma >=5.8.3,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libpython 3.14.8 h281bc5c_102_cp314 + - libsqlite >=3.54.0,<4.0a0 + - libzlib >=1.3.2,<2.0a0 + - ncurses >=6.6,<7.0a0 + - openssl >=3.5.9,<4.0a0 + - python_abi 3.14.* *_cp314 + - readline >=8.3,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Python-2.0 + purls: [] + size: 12458293 + timestamp: 1791826556052 + python_site_packages_path: lib/python3.14/site-packages - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl name: python-dateutil version: 2.9.0.post0 @@ -5821,6 +12551,13 @@ packages: requires_dist: - click>=5.0 ; extra == 'cli' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/60/d1/38f3a3405989a89ac18390803e70c6ad7c7760da4f9b83cbeca0c44a0c72/python_dotenv-1.2.4-py3-none-any.whl + name: python-dotenv + version: 1.2.4 + sha256: 42269a8a5b3fd54ffa6f3d84b18abed50064717576b4ecf03dc4a55d8aa04fdc + requires_dist: + - click>=5.0 ; extra == 'cli' + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda build_number: 8 sha256: 210bffe7b121e651419cb196a2a63687b087497595c9be9d20ebe97dd06060a7 @@ -5832,10 +12569,25 @@ packages: purls: [] size: 7002 timestamp: 1752805902938 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-9_cp314.conda + build_number: 9 + sha256: e7f8e965bbfb98e08feb2365cacdad8bb218fa5b5a0a2752f747287f425f213c + md5: 350d89b50d7de805abf2e63e29dee451 + constrains: + - python 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 6791 + timestamp: 1788302824440 - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl name: pytz version: '2025.2' sha256: 5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00 +- pypi: https://files.pythonhosted.org/packages/4f/ef/c66110d46fb800dda0bf33164182dfadabe26a90e4476844d502a23dca8e/pytz-2026.5-py2.py3-none-any.whl + name: pytz + version: '2026.5' + sha256: e658af3757f9e26a9d25dd2aff38335acd92bc9104f890a894b2c1ba28311b03 - pypi: https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: pyyaml version: 6.0.2 @@ -5862,6 +12614,18 @@ packages: purls: [] size: 282480 timestamp: 1740379431762 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-hd6e31c0_1.conda + sha256: 01b3fe073a66e321970d09e04b388708f8cbdca5cdfbfcb7c9eeb470ad10383d + md5: 69c01c781e7c8190bbdaf79e3848c5ca + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - ncurses >=6.6,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 348899 + timestamp: 1787033801506 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda sha256: 54bed3a3041befaa9f5acde4a37b1a02f44705b7796689574bcf9d7beaad2959 md5: c0f08fc2737967edde1a272d4bf41ed9 @@ -5873,6 +12637,17 @@ packages: purls: [] size: 291806 timestamp: 1740380591358 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.3-ha7194a6_1.conda + sha256: 80167dcf73a96b6d0c1bb2298d7b8b9bc21b27cc5bf56979f9c548b49a4d1722 + md5: 5b399a7afa10098f2a6b02263181426e + depends: + - libgcc >=15 + - ncurses >=6.6,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 364344 + timestamp: 1787033761576 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda sha256: 7db04684d3904f6151eff8673270922d31da1eea7fa73254d01c437f49702e34 md5: 63ef3f6e6d6d5c589e64f11263dc5676 @@ -5883,6 +12658,17 @@ packages: purls: [] size: 252359 timestamp: 1740379663071 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h8b90a29_1.conda + sha256: d6782b430e6dce4fe8c7ac118cd988d5a193eb4a7f60741edfaede58016b6110 + md5: 9347fd56a002e6b58946831d48fe62f6 + depends: + - __osx >=11.0 + - ncurses >=6.6,<7.0a0 + license: GPL-3.0-only + license_family: GPL + purls: [] + size: 314395 + timestamp: 1787033878899 - pypi: https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl name: requests version: 2.32.5 @@ -5895,6 +12681,18 @@ packages: - pysocks>=1.5.6,!=1.5.7 ; extra == 'socks' - chardet>=3.0.2,<6 ; extra == 'use-chardet-on-py3' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl + name: requests + version: 2.34.2 + sha256: 2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 + requires_dist: + - charset-normalizer>=2,<4 + - idna>=2.5,<4 + - urllib3>=1.26,<3 + - certifi>=2023.5.7 + - pysocks>=1.5.6,!=1.5.7 ; extra == 'socks' + - chardet>=3.0.2,<8 ; extra == 'use-chardet-on-py3' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/7d/f8/2be49047f929d6965401855461e697ab185e1a6a683d914c5c19c7962d9e/ruff-0.14.9-py3-none-macosx_11_0_arm64.whl name: ruff version: 0.14.9 @@ -5910,24 +12708,113 @@ packages: version: 0.14.9 sha256: 72034534e5b11e8a593f517b2f2f2b273eb68a30978c6a2d40473ad0aaa4cb4a requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl - name: s3fs - version: 2026.3.0 - sha256: 2fa40a64c03003cfa5ae0e352788d97aa78ae8f9e25ea98b28ce9d21ba10c1b8 - requires_dist: - - aiobotocore>=2.19.0,<4.0.0 - - fsspec==2026.3.0 - - aiohttp>=3.9.0,!=4.0.0a0,!=4.0.0a1 - requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl - name: six - version: 1.17.0 - sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 - requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' -- pypi: https://files.pythonhosted.org/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl +- pypi: https://files.pythonhosted.org/packages/6a/52/5ccdc01f7a8a61357d15a66b5d8a6580aa8529cb33f32e6cbb71c52622c5/s3fs-2026.3.0-py3-none-any.whl + name: s3fs + version: 2026.3.0 + sha256: 2fa40a64c03003cfa5ae0e352788d97aa78ae8f9e25ea98b28ce9d21ba10c1b8 + requires_dist: + - aiobotocore>=2.19.0,<4.0.0 + - fsspec==2026.3.0 + - aiohttp>=3.9.0,!=4.0.0a0,!=4.0.0a1 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/bc/9c/15fb18bd708949dc953fe120be8d3c7a1b1b140c2592a9c81ce7961e9ecf/s3fs-2026.9.0-py3-none-any.whl + name: s3fs + version: 2026.9.0 + sha256: eb185fd98e20a1c9375087d9b2a6ef506f0cd01ae0c0c94efc79f88403fb81fe + requires_dist: + - aiobotocore>=2.19.0,<4.0.0 + - fsspec>=2026.9.0,<2026.9.1 + - aiohttp>=3.9.0,!=4.0.0a0,!=4.0.0a1 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl + name: six + version: 1.17.0 + sha256: 4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*' +- pypi: https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl + name: sortedcontainers + version: 2.4.0 + sha256: a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0 +- pypi: https://files.pythonhosted.org/packages/6d/ff/f4e04a4bd5a24304f38cb0d4aa2ad4c0fb34999f8b884c656535e1b2b74c/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: sqlalchemy + version: 2.0.48 + sha256: 2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f + requires_dist: + - importlib-metadata ; python_full_version < '3.8' + - greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + - typing-extensions>=4.6.0 + - greenlet>=1 ; extra == 'asyncio' + - mypy>=0.910 ; extra == 'mypy' + - pyodbc ; extra == 'mssql' + - pymssql ; extra == 'mssql-pymssql' + - pyodbc ; extra == 'mssql-pyodbc' + - mysqlclient>=1.4.0 ; extra == 'mysql' + - mysql-connector-python ; extra == 'mysql-connector' + - mariadb>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10 ; extra == 'mariadb-connector' + - cx-oracle>=8 ; extra == 'oracle' + - oracledb>=1.0.1 ; extra == 'oracle-oracledb' + - psycopg2>=2.7 ; extra == 'postgresql' + - pg8000>=1.29.1 ; extra == 'postgresql-pg8000' + - greenlet>=1 ; extra == 'postgresql-asyncpg' + - asyncpg ; extra == 'postgresql-asyncpg' + - psycopg2-binary ; extra == 'postgresql-psycopg2binary' + - psycopg2cffi ; extra == 'postgresql-psycopg2cffi' + - psycopg>=3.0.7 ; extra == 'postgresql-psycopg' + - psycopg[binary]>=3.0.7 ; extra == 'postgresql-psycopgbinary' + - pymysql ; extra == 'pymysql' + - greenlet>=1 ; extra == 'aiomysql' + - aiomysql>=0.2.0 ; extra == 'aiomysql' + - greenlet>=1 ; extra == 'aioodbc' + - aioodbc ; extra == 'aioodbc' + - greenlet>=1 ; extra == 'asyncmy' + - asyncmy>=0.2.3,!=0.2.4,!=0.2.6 ; extra == 'asyncmy' + - greenlet>=1 ; extra == 'aiosqlite' + - aiosqlite ; extra == 'aiosqlite' + - typing-extensions!=3.10.0.1 ; extra == 'aiosqlite' + - sqlcipher3-binary ; extra == 'sqlcipher' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl + name: sqlalchemy + version: 2.0.48 + sha256: e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 + requires_dist: + - importlib-metadata ; python_full_version < '3.8' + - greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + - typing-extensions>=4.6.0 + - greenlet>=1 ; extra == 'asyncio' + - mypy>=0.910 ; extra == 'mypy' + - pyodbc ; extra == 'mssql' + - pymssql ; extra == 'mssql-pymssql' + - pyodbc ; extra == 'mssql-pyodbc' + - mysqlclient>=1.4.0 ; extra == 'mysql' + - mysql-connector-python ; extra == 'mysql-connector' + - mariadb>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10 ; extra == 'mariadb-connector' + - cx-oracle>=8 ; extra == 'oracle' + - oracledb>=1.0.1 ; extra == 'oracle-oracledb' + - psycopg2>=2.7 ; extra == 'postgresql' + - pg8000>=1.29.1 ; extra == 'postgresql-pg8000' + - greenlet>=1 ; extra == 'postgresql-asyncpg' + - asyncpg ; extra == 'postgresql-asyncpg' + - psycopg2-binary ; extra == 'postgresql-psycopg2binary' + - psycopg2cffi ; extra == 'postgresql-psycopg2cffi' + - psycopg>=3.0.7 ; extra == 'postgresql-psycopg' + - psycopg[binary]>=3.0.7 ; extra == 'postgresql-psycopgbinary' + - pymysql ; extra == 'pymysql' + - greenlet>=1 ; extra == 'aiomysql' + - aiomysql>=0.2.0 ; extra == 'aiomysql' + - greenlet>=1 ; extra == 'aioodbc' + - aioodbc ; extra == 'aioodbc' + - greenlet>=1 ; extra == 'asyncmy' + - asyncmy>=0.2.3,!=0.2.4,!=0.2.6 ; extra == 'asyncmy' + - greenlet>=1 ; extra == 'aiosqlite' + - aiosqlite ; extra == 'aiosqlite' + - typing-extensions!=3.10.0.1 ; extra == 'aiosqlite' + - sqlcipher3-binary ; extra == 'sqlcipher' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: sqlalchemy version: 2.0.48 - sha256: 2645b7d8a738763b664a12a1542c89c940daa55196e8d73e55b169cc5c99f65f + sha256: b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed requires_dist: - importlib-metadata ; python_full_version < '3.8' - greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' @@ -5962,10 +12849,10 @@ packages: - typing-extensions!=3.10.0.1 ; extra == 'aiosqlite' - sqlcipher3-binary ; extra == 'sqlcipher' requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/d1/c6/569dc8bf3cd375abc5907e82235923e986799f301cd79a903f784b996fca/sqlalchemy-2.0.48-cp313-cp313-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/3a/df/221b08eb2c1f421624b92a6cb24fd606268bcf299b96bd73e3ad83951aa0/sqlalchemy-2.0.55-cp310-cp310-macosx_11_0_arm64.whl name: sqlalchemy - version: 2.0.48 - sha256: e3070c03701037aa418b55d36532ecb8f8446ed0135acb71c678dbdf12f5b6e4 + version: 2.0.55 + sha256: 87a95fd30cf979a2563c7e709c640feb64fafa6449add14eb7c465a950968c80 requires_dist: - importlib-metadata ; python_full_version < '3.8' - greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' @@ -5994,16 +12881,16 @@ packages: - greenlet>=1 ; extra == 'aioodbc' - aioodbc ; extra == 'aioodbc' - greenlet>=1 ; extra == 'asyncmy' - - asyncmy>=0.2.3,!=0.2.4,!=0.2.6 ; extra == 'asyncmy' + - asyncmy>=0.2.12 ; extra == 'asyncmy' - greenlet>=1 ; extra == 'aiosqlite' - aiosqlite ; extra == 'aiosqlite' - typing-extensions!=3.10.0.1 ; extra == 'aiosqlite' - sqlcipher3-binary ; extra == 'sqlcipher' requires_python: '>=3.7' -- pypi: https://files.pythonhosted.org/packages/fe/88/cb59509e4668d8001818d7355d9995be90c321313078c912420603a7cb95/sqlalchemy-2.0.48-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/4e/1f/c4661f84df99587a216fd2f11c00a19776c4d67428dc084db6dd745a4c23/sqlalchemy-2.0.55-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: sqlalchemy - version: 2.0.48 - sha256: b19151e76620a412c2ac1c6f977ab1b9fa7ad43140178345136456d5265b32ed + version: 2.0.55 + sha256: 0772f7bb7cdade7041cd8bfa1bb63ea61eaa42710d395aa1163f36bd5baca222 requires_dist: - importlib-metadata ; python_full_version < '3.8' - greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' @@ -6032,12 +12919,167 @@ packages: - greenlet>=1 ; extra == 'aioodbc' - aioodbc ; extra == 'aioodbc' - greenlet>=1 ; extra == 'asyncmy' - - asyncmy>=0.2.3,!=0.2.4,!=0.2.6 ; extra == 'asyncmy' + - asyncmy>=0.2.12 ; extra == 'asyncmy' + - greenlet>=1 ; extra == 'aiosqlite' + - aiosqlite ; extra == 'aiosqlite' + - typing-extensions!=3.10.0.1 ; extra == 'aiosqlite' + - sqlcipher3-binary ; extra == 'sqlcipher' + requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/d7/9f/65b06d212a305ac2a1ad6aebf45087c41e36b9416d07c981adb7438d095c/sqlalchemy-2.0.55-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: sqlalchemy + version: 2.0.55 + sha256: 3528e49819bef71ab6b05d95b1fa33aef8b15b4341f4d5d7954d1e2bc7e0fbe0 + requires_dist: + - importlib-metadata ; python_full_version < '3.8' + - greenlet>=1 ; platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' + - typing-extensions>=4.6.0 + - greenlet>=1 ; extra == 'asyncio' + - mypy>=0.910 ; extra == 'mypy' + - pyodbc ; extra == 'mssql' + - pymssql ; extra == 'mssql-pymssql' + - pyodbc ; extra == 'mssql-pyodbc' + - mysqlclient>=1.4.0 ; extra == 'mysql' + - mysql-connector-python ; extra == 'mysql-connector' + - mariadb>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10 ; extra == 'mariadb-connector' + - cx-oracle>=8 ; extra == 'oracle' + - oracledb>=1.0.1 ; extra == 'oracle-oracledb' + - psycopg2>=2.7 ; extra == 'postgresql' + - pg8000>=1.29.1 ; extra == 'postgresql-pg8000' + - greenlet>=1 ; extra == 'postgresql-asyncpg' + - asyncpg ; extra == 'postgresql-asyncpg' + - psycopg2-binary ; extra == 'postgresql-psycopg2binary' + - psycopg2cffi ; extra == 'postgresql-psycopg2cffi' + - psycopg>=3.0.7 ; extra == 'postgresql-psycopg' + - psycopg[binary]>=3.0.7 ; extra == 'postgresql-psycopgbinary' + - pymysql ; extra == 'pymysql' + - greenlet>=1 ; extra == 'aiomysql' + - aiomysql>=0.2.0 ; extra == 'aiomysql' + - greenlet>=1 ; extra == 'aioodbc' + - aioodbc ; extra == 'aioodbc' + - greenlet>=1 ; extra == 'asyncmy' + - asyncmy>=0.2.12 ; extra == 'asyncmy' - greenlet>=1 ; extra == 'aiosqlite' - aiosqlite ; extra == 'aiosqlite' - typing-extensions!=3.10.0.1 ; extra == 'aiosqlite' - sqlcipher3-binary ; extra == 'sqlcipher' requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/01/b0/59b26375cd618e861a9295d855b866ca85be654d20ed47e494e33c3bf26e/sqlalchemy-2.1.5-cp314-cp314-macosx_11_0_arm64.whl + name: sqlalchemy + version: 2.1.5 + sha256: 5541a110ac4bb6377c785aea1b8f0b4d6658ed0ca5061ed1115d0725062b026b + requires_dist: + - typing-extensions>=4.6.0 + - greenlet>=1 ; extra == 'asyncio' + - mypy>=2.4 ; extra == 'mypy' + - types-greenlet>=2 ; extra == 'mypy' + - pyodbc ; extra == 'mssql' + - pymssql ; extra == 'mssql-pymssql' + - pyodbc ; extra == 'mssql-pyodbc' + - mssql-python>=1.9.0 ; extra == 'mssql-python' + - mysqlclient>=1.4.0 ; extra == 'mysql' + - mysql-connector-python ; extra == 'mysql-connector' + - mariadb>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10 ; extra == 'mariadb-connector' + - oracledb>=2.0.1 ; extra == 'oracle' + - cx-oracle>=8 ; extra == 'oracle-cxoracle' + - oracledb>=2.0.1 ; extra == 'oracle-oracledb' + - psycopg>=3.0.7,!=3.1.15 ; extra == 'postgresql' + - pg8000>=1.29.3 ; extra == 'postgresql-pg8000' + - sqlalchemy[asyncio] ; extra == 'postgresql-asyncpg' + - asyncpg ; extra == 'postgresql-asyncpg' + - psycopg2-binary ; extra == 'postgresql-psycopg2binary' + - psycopg2cffi ; extra == 'postgresql-psycopg2cffi' + - psycopg>=3.0.7,!=3.1.15 ; extra == 'postgresql-psycopg' + - psycopg[binary]>=3.0.7,!=3.1.15 ; extra == 'postgresql-psycopgbinary' + - pymysql ; extra == 'pymysql' + - cymysql ; extra == 'cymysql' + - sqlalchemy[asyncio] ; extra == 'aiomysql' + - aiomysql ; extra == 'aiomysql' + - sqlalchemy[asyncio] ; extra == 'aioodbc' + - aioodbc ; extra == 'aioodbc' + - sqlalchemy[asyncio] ; extra == 'asyncmy' + - asyncmy>=0.2.12 ; extra == 'asyncmy' + - sqlalchemy[asyncio] ; extra == 'aiosqlite' + - aiosqlite ; extra == 'aiosqlite' + - sqlcipher3-binary ; extra == 'sqlcipher' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/42/9e/fa478482503a69dfb3b0cb82f3f80fe3816026530dd34b5359e2acd9f30d/sqlalchemy-2.1.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: sqlalchemy + version: 2.1.5 + sha256: cc44dcc80e89a006af415edffc7409b1f2976a4597065449c166987f95801ab4 + requires_dist: + - typing-extensions>=4.6.0 + - greenlet>=1 ; extra == 'asyncio' + - mypy>=2.4 ; extra == 'mypy' + - types-greenlet>=2 ; extra == 'mypy' + - pyodbc ; extra == 'mssql' + - pymssql ; extra == 'mssql-pymssql' + - pyodbc ; extra == 'mssql-pyodbc' + - mssql-python>=1.9.0 ; extra == 'mssql-python' + - mysqlclient>=1.4.0 ; extra == 'mysql' + - mysql-connector-python ; extra == 'mysql-connector' + - mariadb>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10 ; extra == 'mariadb-connector' + - oracledb>=2.0.1 ; extra == 'oracle' + - cx-oracle>=8 ; extra == 'oracle-cxoracle' + - oracledb>=2.0.1 ; extra == 'oracle-oracledb' + - psycopg>=3.0.7,!=3.1.15 ; extra == 'postgresql' + - pg8000>=1.29.3 ; extra == 'postgresql-pg8000' + - sqlalchemy[asyncio] ; extra == 'postgresql-asyncpg' + - asyncpg ; extra == 'postgresql-asyncpg' + - psycopg2-binary ; extra == 'postgresql-psycopg2binary' + - psycopg2cffi ; extra == 'postgresql-psycopg2cffi' + - psycopg>=3.0.7,!=3.1.15 ; extra == 'postgresql-psycopg' + - psycopg[binary]>=3.0.7,!=3.1.15 ; extra == 'postgresql-psycopgbinary' + - pymysql ; extra == 'pymysql' + - cymysql ; extra == 'cymysql' + - sqlalchemy[asyncio] ; extra == 'aiomysql' + - aiomysql ; extra == 'aiomysql' + - sqlalchemy[asyncio] ; extra == 'aioodbc' + - aioodbc ; extra == 'aioodbc' + - sqlalchemy[asyncio] ; extra == 'asyncmy' + - asyncmy>=0.2.12 ; extra == 'asyncmy' + - sqlalchemy[asyncio] ; extra == 'aiosqlite' + - aiosqlite ; extra == 'aiosqlite' + - sqlcipher3-binary ; extra == 'sqlcipher' + requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/9e/a1/01931ba38770f169ce70e4034b0dcf2c1a776fe7d8dde93a34b6355510b1/sqlalchemy-2.1.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: sqlalchemy + version: 2.1.5 + sha256: f029b2f7114623c03f762596f313515a2358004c44eb18924a15940f2e27d30f + requires_dist: + - typing-extensions>=4.6.0 + - greenlet>=1 ; extra == 'asyncio' + - mypy>=2.4 ; extra == 'mypy' + - types-greenlet>=2 ; extra == 'mypy' + - pyodbc ; extra == 'mssql' + - pymssql ; extra == 'mssql-pymssql' + - pyodbc ; extra == 'mssql-pyodbc' + - mssql-python>=1.9.0 ; extra == 'mssql-python' + - mysqlclient>=1.4.0 ; extra == 'mysql' + - mysql-connector-python ; extra == 'mysql-connector' + - mariadb>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10 ; extra == 'mariadb-connector' + - oracledb>=2.0.1 ; extra == 'oracle' + - cx-oracle>=8 ; extra == 'oracle-cxoracle' + - oracledb>=2.0.1 ; extra == 'oracle-oracledb' + - psycopg>=3.0.7,!=3.1.15 ; extra == 'postgresql' + - pg8000>=1.29.3 ; extra == 'postgresql-pg8000' + - sqlalchemy[asyncio] ; extra == 'postgresql-asyncpg' + - asyncpg ; extra == 'postgresql-asyncpg' + - psycopg2-binary ; extra == 'postgresql-psycopg2binary' + - psycopg2cffi ; extra == 'postgresql-psycopg2cffi' + - psycopg>=3.0.7,!=3.1.15 ; extra == 'postgresql-psycopg' + - psycopg[binary]>=3.0.7,!=3.1.15 ; extra == 'postgresql-psycopgbinary' + - pymysql ; extra == 'pymysql' + - cymysql ; extra == 'cymysql' + - sqlalchemy[asyncio] ; extra == 'aiomysql' + - aiomysql ; extra == 'aiomysql' + - sqlalchemy[asyncio] ; extra == 'aioodbc' + - aioodbc ; extra == 'aioodbc' + - sqlalchemy[asyncio] ; extra == 'asyncmy' + - asyncmy>=0.2.12 ; extra == 'asyncmy' + - sqlalchemy[asyncio] ; extra == 'aiosqlite' + - aiosqlite ; extra == 'aiosqlite' + - sqlcipher3-binary ; extra == 'sqlcipher' + requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl name: stack-data version: 0.6.3 @@ -6106,6 +13148,78 @@ packages: - trino ; extra == 'trino' - weaviate-client>=4 ; extra == 'weaviate' requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/00/7e/424aac8b355597835deb333e757a0e94b5ccf38ad00f07fe6ed1f4e17c88/testcontainers-4.15.0-py3-none-any.whl + name: testcontainers + version: 4.15.0 + sha256: 8796c14e76604031ad39cf0ed3b8e9806283a1fbf5270965c2b1c594caa31b74 + requires_dist: + - docker + - python-dotenv + - typing-extensions + - urllib3 + - wrapt + - python-arango>=8 ; extra == 'arangodb' + - boto3>=1 ; extra == 'aws' + - httpx ; extra == 'aws' + - azure-storage-blob>=12 ; extra == 'azurite' + - chromadb-client>=1 ; extra == 'chroma' + - clickhouse-driver ; extra == 'clickhouse' + - azure-cosmos>=4 ; extra == 'cosmosdb' + - httpx ; extra == 'cratedb' + - sqlalchemy-cratedb ; extra == 'cratedb' + - ibm-db-sa ; platform_machine != 'aarch64' and platform_machine != 'arm64' and extra == 'db2' + - sqlalchemy ; extra == 'db2' + - httpx ; extra == 'generic' + - redis>=7 ; extra == 'generic' + - sqlalchemy ; extra == 'generic' + - google-cloud-datastore>=2 ; extra == 'google' + - google-cloud-pubsub>=2 ; extra == 'google' + - influxdb-client>=1 ; extra == 'influxdb' + - influxdb>=5 ; extra == 'influxdb' + - kubernetes ; extra == 'k3s' + - pyyaml>=6.0.3 ; extra == 'k3s' + - python-keycloak>=6 ; python_full_version < '4' and extra == 'keycloak' + - boto3>=1 ; extra == 'localstack' + - cryptography ; extra == 'mailpit' + - minio>=7 ; extra == 'minio' + - pymongo>=4 ; extra == 'mongodb' + - pymssql>=2 ; extra == 'mssql' + - sqlalchemy ; extra == 'mssql' + - pymysql[rsa]>=1 ; extra == 'mysql' + - sqlalchemy ; extra == 'mysql' + - nats-py>=2 ; extra == 'nats' + - neo4j>=6 ; extra == 'neo4j' + - openfga-sdk ; extra == 'openfga' + - opensearch-py>=3 ; python_full_version < '4' and extra == 'opensearch' + - oracledb>=3 ; extra == 'oracle' + - sqlalchemy ; extra == 'oracle' + - oracledb>=3 ; extra == 'oracle-free' + - sqlalchemy ; extra == 'oracle-free' + - qdrant-client>=1 ; extra == 'qdrant' + - pika>=1 ; extra == 'rabbitmq' + - redis>=7 ; extra == 'redis' + - bcrypt>=5 ; extra == 'registry' + - cassandra-driver>=3 ; python_full_version < '3.14' and extra == 'scylla' + - selenium>=4 ; extra == 'selenium' + - cryptography ; extra == 'sftp' + - httpx ; extra == 'test-module-import' + - trino ; extra == 'trino' + - weaviate-client>=4 ; extra == 'weaviate' + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h1df4ec4_4.conda + build_number: 104 + sha256: a1a241d172c1ccab067ba245206dd048bc3c2d1b84504b53c9468e99adfc16a1 + md5: 676a2f9b1c4fcf57b70aa5c65f6c60d0 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + constrains: + - xorg-libx11 >=1.8.13,<2.0a0 + license: TCL + purls: [] + size: 3566806 + timestamp: 1787272857910 - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda sha256: a84ff687119e6d8752346d1d408d5cf360dee0badd487a472aa8ddedfdc219e1 md5: a0116df4f4ed05c303811a837d5b39d8 @@ -6129,6 +13243,19 @@ packages: purls: [] size: 3342845 timestamp: 1748393219221 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_hf03c496_4.conda + build_number: 104 + sha256: a1aa0d0f0b3d539644ff992e6dd0facf950c79df58fc37590ce5be84ff5e3f22 + md5: ebaded4b4b74c2cc43a754ded4eed76f + depends: + - libgcc >=15 + - libzlib >=1.3.2,<2.0a0 + constrains: + - xorg-libx11 >=1.8.13,<2.0a0 + license: TCL + purls: [] + size: 3664220 + timestamp: 1787272859143 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda sha256: cb86c522576fa95c6db4c878849af0bccfd3264daf0cc40dd18e7f4a7bfced0e md5: 7362396c170252e7b7b0c8fb37fe9c78 @@ -6140,6 +13267,21 @@ packages: purls: [] size: 3125538 timestamp: 1748388189063 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-hbeba79b_4.conda + sha256: 857d89087ae7f2c328cf256728affcb7343031a148b4af847334d248a9cf564c + md5: 90a01bf394d1c594e0eb9258f967d828 + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: TCL + purls: [] + size: 3342183 + timestamp: 1787272852357 +- pypi: https://files.pythonhosted.org/packages/60/3f/3e3f8fd0919249b0200c80fbc4f9a1e70be19f9883da71dfb7f8b9ab8aca/tomli-2.5.0-py3-none-any.whl + name: tomli + version: 2.5.0 + sha256: 32a7b79ac57a2e83670ce329ccf675798bc5a2094783a63676866b70503f2e2b + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl name: tqdm version: 4.67.1 @@ -6156,6 +13298,20 @@ packages: - requests ; extra == 'telegram' - ipywidgets>=6 ; extra == 'notebook' requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl + name: tqdm + version: 4.70.1 + sha256: c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73 + requires_dist: + - colorama ; sys_platform == 'win32' + - requests ; extra == 'discord' + - envwrap ; extra == 'discord' + - slack-sdk ; extra == 'slack' + - envwrap ; extra == 'slack' + - requests ; extra == 'telegram' + - envwrap ; extra == 'telegram' + - ipywidgets>=6 ; extra == 'notebook' + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl name: traitlets version: 5.14.3 @@ -6171,11 +13327,32 @@ packages: - pytest-mypy-testing ; extra == 'test' - pytest>=7.0,<8.2 ; extra == 'test' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl + name: traitlets + version: 5.16.1 + sha256: f775618166caa0396c8e337099240f2bd3e5e917d203b2e6fbe21a58d3cb1f6b + requires_dist: + - myst-parser ; extra == 'docs' + - pydata-sphinx-theme ; extra == 'docs' + - sphinx ; extra == 'docs' + - argcomplete>=3.0.3 ; python_full_version < '3.12' and extra == 'test' + - argcomplete>=3.5.2 ; python_full_version >= '3.12' and extra == 'test' + - mypy>=2.0 ; implementation_name != 'pypy' and extra == 'test' + - pre-commit ; extra == 'test' + - pytest-mock ; extra == 'test' + - pytest-mypy-testing ; implementation_name != 'pypy' and extra == 'test' + - pytest>=7.0,<10.0 ; extra == 'test' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl name: typing-extensions version: 4.15.0 sha256: f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl + name: typing-extensions + version: 4.16.0 + sha256: 481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl name: typing-inspection version: 0.4.2 @@ -6183,11 +13360,23 @@ packages: requires_dist: - typing-extensions>=4.12.0 requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/67/81/4add07e5172b7ac40d8ed5ff580409a7801a4fe26d529bdd915401dabfbe/typing_inspection-0.4.4-py3-none-any.whl + name: typing-inspection + version: 0.4.4 + sha256: 65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147 + requires_dist: + - typing-extensions>=4.15.0 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl name: tzdata version: '2025.2' sha256: 1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8 requires_python: '>=2' +- pypi: https://files.pythonhosted.org/packages/94/21/1e5995a1c920cce14e4bffae20c665ec10e7ed03ab25e006cd741092b718/tzdata-2026.5-py2.py3-none-any.whl + name: tzdata + version: '2026.5' + sha256: b683bd1b6659ddcd810ff02ad09ba821d4bf1065072805063eb35c49617905ac + requires_python: '>=2' - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda sha256: 5aaa366385d716557e365f0a4e9c3fca43ba196872abbbe3d56bb610d131e192 md5: 4222072737ccff51314b5ece9c7d6f5a @@ -6195,6 +13384,13 @@ packages: purls: [] size: 122968 timestamp: 1742727099393 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2026c-h151e31d_0.conda + sha256: b928c30ddcb0e3f544c6eade8352737e6e610e263276b90232db6a578ef899d8 + md5: fcb489df604d100968b737f2cb6076c6 + license: LicenseRef-Public-Domain + purls: [] + size: 118849 + timestamp: 1784250406640 - pypi: https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl name: urllib3 version: 2.5.0 @@ -6206,6 +13402,17 @@ packages: - pysocks>=1.5.6,!=1.5.7,<2.0 ; extra == 'socks' - zstandard>=0.18.0 ; extra == 'zstd' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl + name: urllib3 + version: 2.8.0 + sha256: 0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 + requires_dist: + - brotli>=1.2.0 ; platform_python_implementation == 'CPython' and extra == 'brotli' + - brotlicffi>=1.2.0.0 ; platform_python_implementation != 'CPython' and extra == 'brotli' + - h2>=4,<5 ; extra == 'h2' + - pysocks>=1.5.6,!=1.5.7,<2.0 ; extra == 'socks' + - backports-zstd>=1.0.0 ; python_full_version < '3.14' and extra == 'zstd' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/76/06/04c8e804f813cf972e3262f3f8584c232de64f0cde9f703b46cf53a45090/virtualenv-20.34.0-py3-none-any.whl name: virtualenv version: 20.34.0 @@ -6280,6 +13487,20 @@ packages: purls: [] size: 330474 timestamp: 1751817998141 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.26.0-hc1c935e_2.conda + sha256: df65b987979e6d7f88e756494da5a230d4f2d08d17437d04eecd35559c3a04e4 + md5: 88a47e22b53e50865b1cabe2eb648352 + depends: + - __glibc >=2.17,<3.0.a0 + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - libstdcxx >=15 + license: MIT + license_family: MIT + purls: [] + size: 340058 + timestamp: 1787793849093 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wayland-1.24.0-h4f8a99f_1.conda sha256: d94af8f287db764327ac7b48f6c0cd5c40da6ea2606afd34ac30671b7c85d8ee md5: f6966cb1f000c230359ae98c29e37d87 @@ -6293,37 +13514,113 @@ packages: purls: [] size: 331480 timestamp: 1761174368396 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/wayland-1.26.0-h637a836_2.conda + sha256: 0692327145e39b3484382c2b069003b08398e961575bb6d35d3246f1cafa5d58 + md5: cee65dc8a90933553988b50f5a03079a + depends: + - libexpat >=2.8.1,<3.0a0 + - libffi >=3.7.0,<3.8.0a0 + - libgcc >=15 + - libstdcxx >=15 + license: MIT + license_family: MIT + purls: [] + size: 339803 + timestamp: 1787793907254 - pypi: https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl name: wcwidth version: 0.2.13 sha256: 3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859 requires_dist: - - backports-functools-lru-cache>=1.2.1 ; python_full_version < '3.2' -- pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl - name: wcwidth - version: 0.2.14 - sha256: a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1 - requires_python: '>=3.6' -- pypi: https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - backports-functools-lru-cache>=1.2.1 ; python_full_version < '3.2' +- pypi: https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl + name: wcwidth + version: 0.2.14 + sha256: a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1 + requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/a0/07/cb6940e81134b7ed25fa312ee9ab536a63db0793b149f88a90e603ceace9/wcwidth-0.9.2-cp310-abi3-macosx_11_0_arm64.whl + name: wcwidth + version: 0.9.2 + sha256: ae0800c5339423cc53d33a266ad264b42ba8aaa16d4464f6e6b1bee607f50b17 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/bc/f0/b8ef7758003d66b60f093695831a86dcc726aac01ee6446ffcbda27b61e3/wcwidth-0.9.2-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl + name: wcwidth + version: 0.9.2 + sha256: 674b518af28d38ee645ff97b74f5760abee5fad4bac74413bfc4b881ef2ce724 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/db/6c/f940133c71427c208575910e981942bd78c98b1f7cd0d1425ca4b7457c04/wcwidth-0.9.2-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + name: wcwidth + version: 0.9.2 + sha256: 751bef0ab404b6a1dc028b56b4b85d46486be1c55833f80da533e42dc691f389 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: wrapt + version: 2.1.2 + sha256: bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb + requires_dist: + - pytest ; extra == 'dev' + - setuptools ; extra == 'dev' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: wrapt + version: 2.1.2 + sha256: 16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca + requires_dist: + - pytest ; extra == 'dev' + - setuptools ; extra == 'dev' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl + name: wrapt + version: 2.1.2 + sha256: 4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e + requires_dist: + - pytest ; extra == 'dev' + - setuptools ; extra == 'dev' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/81/56/01ebc86b88056f5782b9d50f962fb398c6b82aa11efc98f50c64896d94e3/wrapt-2.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: wrapt + version: 2.5.1 + sha256: 380f72610181883f66b41442cfc7c0f7552b42169efb2113def26e6380013d37 + requires_dist: + - pytest ; extra == 'dev' + - setuptools ; extra == 'dev' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/84/d0/9c0409db3062fc7ef8347c578a251b0fe3471741871c3bc849ea07c98e98/wrapt-2.5.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: wrapt + version: 2.5.1 + sha256: 016602dd8827d190280a707c5e67f9a80038f54bac1782cc8ff68a2a16c618bc + requires_dist: + - pytest ; extra == 'dev' + - setuptools ; extra == 'dev' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/93/39/cca8afb80dbb9fce6103e59db507a9415291c4dbe97ea055875ff62901fb/wrapt-2.5.1-cp314-cp314-macosx_11_0_arm64.whl name: wrapt - version: 2.1.2 - sha256: bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb + version: 2.5.1 + sha256: 9f0750cbc2e29e4f3c9529d3587d4e7ed8f60638ceafb80b87a95833b0c5acd9 requires_dist: - pytest ; extra == 'dev' - setuptools ; extra == 'dev' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl +- pypi: https://files.pythonhosted.org/packages/a4/da/a295034b0ba4b4a66ac8c173312214845c406f681d7fbf636b57c6ff8f7f/wrapt-2.5.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl name: wrapt - version: 2.1.2 - sha256: 16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca + version: 2.5.1 + sha256: 8bdf4696fb5bb141a7f96710ac6d9a6aa9a57a14c54075f9c7d3946869d457df requires_dist: - pytest ; extra == 'dev' - setuptools ; extra == 'dev' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/aa/a0/e784d7a9fd277a2ee395490ec4df96608b7fe218bb1ef7dced2a1caea490/wrapt-2.5.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl name: wrapt - version: 2.1.2 - sha256: 4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e + version: 2.5.1 + sha256: 3cf273b7e8d2038abb7f0a8c6550aff4f617b9d486a9965c8e8acc96a3a04de9 + requires_dist: + - pytest ; extra == 'dev' + - setuptools ; extra == 'dev' + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/fe/13/38001295886db51429dd29882c9097fa56ff642f811439ca13998127d8d4/wrapt-2.5.1-cp310-cp310-macosx_11_0_arm64.whl + name: wrapt + version: 2.5.1 + sha256: 9bc472825027b276d4bf678d2ac64149db0b122f80ae6f59c423e6d31f0c4bb7 requires_dist: - pytest ; extra == 'dev' - setuptools ; extra == 'dev' @@ -6340,6 +13637,18 @@ packages: purls: [] size: 392406 timestamp: 1749375847832 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.48-h280c20c_0.conda + sha256: 3b04afd5d1a65d2d27ac2d49a63b01ab8bcd875776779ec63e337370ed38afdc + md5: b233b41be0bf210989d57160ed39b394 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 441670 + timestamp: 1782027360439 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xkeyboard-config-2.46-he30d5cf_0.conda sha256: c440a757d210e84c7f315ac3b034266980a8b4c986600649d296b9198b5b4f5e md5: 9524f30d9dea7dd5d6ead43a8823b6c2 @@ -6351,6 +13660,28 @@ packages: purls: [] size: 396706 timestamp: 1759543850920 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xkeyboard-config-2.48-h80f16a2_0.conda + sha256: 96078068df25ddccc60958be740e6fa99efb1e0fa2dae2f84e775201bf84d70c + md5: 3dbc6d9e1f8a8768e7ef9f57585a43ca + depends: + - libgcc >=14 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 442725 + timestamp: 1782027381059 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-h280c20c_0.conda + sha256: 49b532d1df875c6749d9078b56a76f3f5db49a5abe0ca620b593ed474ef0ebf1 + md5: 85c9442aec283b4e464fa9ecc484a2f3 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 62517 + timestamp: 1786474410404 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b md5: fb901ff28063514abb6046c9ec2c4a45 @@ -6362,6 +13693,16 @@ packages: purls: [] size: 58628 timestamp: 1734227592886 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libice-1.1.2-h80f16a2_0.conda + sha256: e56bb636aefe3503a53520a0b7f3737f3a26fe0accf0befffc24d1c0ddd27008 + md5: 0566e37830f85911b141524635939941 + depends: + - libgcc >=14 + license: MIT + license_family: MIT + purls: [] + size: 63847 + timestamp: 1786474771090 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libice-1.1.2-h86ecc28_0.conda sha256: a2ba1864403c7eb4194dacbfe2777acf3d596feae43aada8d1b478617ce45031 md5: c8d8ec3e00cd0fd8a231789b91a7c5b7 @@ -6372,6 +13713,19 @@ packages: purls: [] size: 60433 timestamp: 1734229908988 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-h0d788c3_1.conda + sha256: ef907caee0665cf3b0f775602cc50e388e3bade8ce22ecade0873ff26604b2fd + md5: aa7459ed9ad086ba11dda843d764b33d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libice >=1.1.2,<2.0a0 + - libuuid >=2.42.2,<3.0a0 + license: MIT + license_family: MIT + purls: [] + size: 30739 + timestamp: 1786545374265 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda sha256: 277841c43a39f738927145930ff963c5ce4c4dacf66637a3d95d802a64173250 md5: 1c74ff8c35dcadf952a16f752ca5aa49 @@ -6397,6 +13751,18 @@ packages: purls: [] size: 28701 timestamp: 1741897678254 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libsm-1.2.6-hf23e593_1.conda + sha256: 4978f89a764b54dc8be7a115b2d57f570f793243f3227653232742ce0946101b + md5: f5f4a1233da463827499fb4e5e1f0172 + depends: + - libgcc >=14 + - xorg-libice >=1.1.2,<2.0a0 + - libuuid >=2.42.2,<3.0a0 + license: MIT + license_family: MIT + purls: [] + size: 31476 + timestamp: 1786546266048 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda sha256: 51909270b1a6c5474ed3978628b341b4d4472cd22610e5f22b506855a5e20f67 md5: db038ce880f100acc74dba10302b5630 @@ -6409,6 +13775,18 @@ packages: purls: [] size: 835896 timestamp: 1741901112627 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_1.conda + sha256: 68053eebfa9f0d91666786c8fb5839d989aa9b869add92cb8815228bb2d7302c + md5: 8c282bbe4808a3cc80a5c98e9aec1cfc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 839578 + timestamp: 1787087012372 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libx11-1.8.12-hca56bd8_0.conda sha256: 452977d8ad96f04ec668ba74f46e70a53e00f99c0e0307956aeca75894c8131d md5: 3df132f0048b9639bc091ef22937c111 @@ -6420,6 +13798,28 @@ packages: purls: [] size: 864850 timestamp: 1741901264068 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libx11-1.8.13-h63a1b12_1.conda + sha256: 9ff59d55cbf85561833be3604a8c0a5497b0f13cdf122e8bb490e9cf3138d116 + md5: 8e873e9efc395329b914ee44e305b624 + depends: + - libgcc >=14 + - libxcb >=1.17.0,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 875768 + timestamp: 1787087025456 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-h7cc23a3_2.conda + sha256: 3ec065b94554dc48a4ca582a960a5484bc166b26e83ce0954653e08d17e8bd53 + md5: da33efee1a93603d92e06d4a2b68def6 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 18793 + timestamp: 1788960512381 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda sha256: ed10c9283974d311855ae08a16dfd7e56241fac632aec3b92e3cfe73cff31038 md5: f6ebe2cb3f82ba6c057dde5d9debe4f7 @@ -6431,6 +13831,16 @@ packages: purls: [] size: 14780 timestamp: 1734229004433 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h5bc82ec_2.conda + sha256: 6b22ffc0137003b41d44db995a3536737e2a2c854c8ed55c8a4eb6377d502abf + md5: 2f53d3d55e1d8855a89218d7afe19d07 + depends: + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 19649 + timestamp: 1788962171600 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda sha256: 7829a0019b99ba462aece7592d2d7f42e12d12ccd3b9614e529de6ddba453685 md5: d5397424399a66d33c80b1f2345a36a6 @@ -6454,6 +13864,19 @@ packages: purls: [] size: 13603 timestamp: 1727884600744 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda + sha256: 048c103000af9541c919deef03ae7c5e9c570ffb4024b42ecb58dbde402e373a + md5: f2ba4192d38b6cef2bb2c25029071d90 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14415 + timestamp: 1770044404696 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxcomposite-0.4.6-h86ecc28_2.conda sha256: 0cb82160412adb6d83f03cf50e807a8e944682d556b2215992a6fbe9ced18bc0 md5: 86051eee0766c3542be24844a9c3cf36 @@ -6466,6 +13889,18 @@ packages: purls: [] size: 13982 timestamp: 1727884626338 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxcomposite-0.4.7-he30d5cf_0.conda + sha256: 554f986ffa781d3393079926931465e61291d1eb8a56a03ad4a8e54b1ae233f4 + md5: 9c639c1abdbfe6759c5beb2c1db4bc13 + depends: + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14915 + timestamp: 1770044415607 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda sha256: 832f538ade441b1eee863c8c91af9e69b356cd3e9e1350fff4fe36cc573fc91a md5: 2ccd714aa2242315acaf0a67faea780b @@ -6520,6 +13955,17 @@ packages: purls: [] size: 13794 timestamp: 1727891406431 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-h7cc23a3_2.conda + sha256: 8a095df9bc1d2e50f2504ce8858a047ae29ea634aad6cc0b43298c63361da6d9 + md5: eabfc45786244de252090cf8d55128a8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 22606 + timestamp: 1789676943335 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda sha256: 6b250f3e59db07c2514057944a3ea2044d6a8cdde8a47b6497c254520fade1ee md5: 8035c64cb77ed555e3f150b7b3972480 @@ -6541,6 +13987,16 @@ packages: purls: [] size: 20615 timestamp: 1727796660574 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h5bc82ec_2.conda + sha256: db2b37227a888d23269855409a9b2179e493c59f2ec9ee2071bf3fd198767ffd + md5: bfd0b70add0a8cff1ee035532b0deaa5 + depends: + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 23110 + timestamp: 1789678433513 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda sha256: da5dc921c017c05f38a38bd75245017463104457b63a1ce633ed41f214159c14 md5: febbab7d15033c913d53c7a2c102309d @@ -6553,6 +14009,18 @@ packages: purls: [] size: 50060 timestamp: 1727752228921 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-h7cc23a3_1.conda + sha256: aa9bbe8b278aacc194e280ff5037f9f9a1f2c5b33ed97de8e7f01cfbe90dda43 + md5: e5b6b28536b81b3f4cb20db4668a4642 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 53124 + timestamp: 1787100841900 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxext-1.3.6-h57736b2_0.conda sha256: 8e216b024f52e367463b4173f237af97cf7053c77d9ce3e958bc62473a053f71 md5: bd1e86dd8aa3afd78a4bfdb4ef918165 @@ -6564,6 +14032,17 @@ packages: purls: [] size: 50746 timestamp: 1727754268156 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxext-1.3.7-h5bc82ec_1.conda + sha256: e2cc4ba2b5291710f9ae3e88d917d778ce7911262450e673ba8a597a5d43490a + md5: 32734d534d08a853f22bdef1d43223b7 + depends: + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 53801 + timestamp: 1787103097310 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda sha256: 2fef37e660985794617716eb915865ce157004a4d567ed35ec16514960ae9271 md5: 4bdb303603e9821baf5fe5fdff1dc8f8 @@ -6576,6 +14055,29 @@ packages: purls: [] size: 19575 timestamp: 1727794961233 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-h7cc23a3_1.conda + sha256: aec84f554fc897bf4085c7bc8b8f0740e4c224e13bca3cc51c93e7699d56f83a + md5: 09132e874fe0e1f5e4492b0b6b904b6e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 21440 + timestamp: 1787248059682 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxfixes-6.0.2-h5bc82ec_1.conda + sha256: fc432dbef14598e2fd283760c2a14536472c2441986264308b4cc1877a263e00 + md5: 3364fc126aef75debcc1d291e0e463c8 + depends: + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 22039 + timestamp: 1787250187374 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxfixes-6.0.2-he30d5cf_0.conda sha256: 8cb9c88e25c57e47419e98f04f9ef3154ad96b9f858c88c570c7b91216a64d0e md5: e8b4056544341daf1d415eaeae7a040c @@ -6601,6 +14103,20 @@ packages: purls: [] size: 47179 timestamp: 1727799254088 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-h7cc23a3_1.conda + sha256: a523d71344a2f640efe6fc42b88fc261d9cd389d4f9838a5d42dcdb120c2b0c8 + md5: a1412b2b1184dacda45f8476fef2cc25 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 49165 + timestamp: 1787257060460 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxi-1.8.2-h57736b2_0.conda sha256: 7b587407ecb9ccd2bbaf0fb94c5dbdde4d015346df063e9502dc0ce2b682fb5e md5: eeee3bdb31c6acde2b81ad1b8c287087 @@ -6614,6 +14130,19 @@ packages: purls: [] size: 48197 timestamp: 1727801059062 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxi-1.8.3-h5bc82ec_1.conda + sha256: 3759f62da8f9e093dc94e8fb87a30b99b4a2414e0c197810126e5c2c25a6d63a + md5: 3008fa4872fd265945da6a35f684addb + depends: + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxfixes >=6.0.2,<7.0a0 + license: MIT + license_family: MIT + purls: [] + size: 50969 + timestamp: 1787259526749 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.5-h5888daf_1.conda sha256: 1b9141c027f9d84a9ee5eb642a0c19457c788182a5a73c5a9083860ac5c20a8c md5: 5e2eb9bf77394fc2e5918beefec9f9ab @@ -6628,6 +14157,20 @@ packages: purls: [] size: 13891 timestamp: 1727908521531 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxinerama-1.1.6-hecca717_0.conda + sha256: 3a9da41aac6dca9d3ff1b53ee18b9d314de88add76bafad9ca2287a494abcd86 + md5: 93f5d4b5c17c8540479ad65f206fea51 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 14818 + timestamp: 1769432261050 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxinerama-1.1.5-h5ad3122_1.conda sha256: 5f84f820397db504e187754665d48d385e0a2a49f07ffc2372c7f42fa36dd972 md5: a7b99f104e14b99ca773d2fe2d195585 @@ -6641,6 +14184,19 @@ packages: purls: [] size: 14388 timestamp: 1727908606602 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxinerama-1.1.6-hfae3067_0.conda + sha256: 2039990aa8454f19e8f890ff1e8582f12fa475af7e836b184adc17b88a22c9b8 + md5: a488ab283de297dc0de69796f7467a71 + depends: + - libgcc >=14 + - libstdcxx >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 15322 + timestamp: 1769432283298 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda sha256: ac0f037e0791a620a69980914a77cb6bb40308e26db11698029d6708f5aa8e0d md5: 2de7f99d6581a4a7adbff607b5c278ca @@ -6655,6 +14211,20 @@ packages: purls: [] size: 29599 timestamp: 1727794874300 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-h7cc23a3_1.conda + sha256: 05a7f25d7f7f5cd32b27a019233ece97167fd8ade255bc13a0e49e53387f4c30 + md5: 798a8c9d171859a022e1cb89e7d0eb10 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 31106 + timestamp: 1787246614086 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrandr-1.5.4-h86ecc28_0.conda sha256: b2588a2b101d1b0a4e852532c8b9c92c59ef584fc762dd700567bdbf8cd00650 md5: dd3e74283a082381aa3860312e3c721e @@ -6668,6 +14238,31 @@ packages: purls: [] size: 30197 timestamp: 1727794957221 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrandr-1.5.5-h5bc82ec_1.conda + sha256: f2461efa138a03d25c4cff337e9db52c762ae93ab426f578234a2ea1cac50eb5 + md5: 2d0c7631c23e56dbb66f4fe711c6ab63 + depends: + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxrender >=0.9.12,<0.10.0a0 + license: MIT + license_family: MIT + purls: [] + size: 31842 + timestamp: 1787246653258 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb03c661_1.conda + sha256: 6901f91d398811e4ec89d7e20a69abac02a7bfebfaf073338b7ea3d1a99685b7 + md5: e470d224a7a5be1b1d021bded7abb536 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 34645 + timestamp: 1787100191192 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda sha256: 044c7b3153c224c6cedd4484dd91b389d2d7fd9c776ad0f4a34f099b3389f4a1 md5: 96d57aba173e878a2089d5638016dc5e @@ -6691,6 +14286,17 @@ packages: purls: [] size: 33649 timestamp: 1734229123157 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrender-0.9.12-he30d5cf_1.conda + sha256: 3cbac6f69e4a8634ba6b60cf283cba92606c8c74caa1369b799325f96b2f0cbf + md5: 1bcdaa1fc263291e6f42ff061666f3ae + depends: + - libgcc >=14 + - xorg-libx11 >=1.8.13,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 35814 + timestamp: 1787100239228 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda sha256: 752fdaac5d58ed863bbf685bb6f98092fe1a488ea8ebb7ed7b606ccfce08637a md5: 7bbe9a0cc0df0ac5f5a8ad6d6a11af2f @@ -6705,6 +14311,20 @@ packages: purls: [] size: 32808 timestamp: 1727964811275 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.6-h7cc23a3_0.conda + sha256: 9d8e7a2db860da3f362284650b447a96688b834bdb3122707b04bae512a1a178 + md5: 05e756ed9e8b2358591d47f6b8939db4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxi >=1.8.3,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 34918 + timestamp: 1792204941937 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxtst-1.2.5-h57736b2_3.conda sha256: 6eaffce5a34fc0a16a21ddeaefb597e792a263b1b0c387c1ce46b0a967d558e1 md5: c05698071b5c8e0da82a282085845860 @@ -6718,6 +14338,32 @@ packages: purls: [] size: 33786 timestamp: 1727964907993 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxtst-1.2.6-h5bc82ec_0.conda + sha256: fb783690e69a901f57cd564ad5de9b8f490c6dcf36620205ef9435a0da162b33 + md5: c3a96b724f4452281ea027cfee29a548 + depends: + - libgcc >=15 + - xorg-libx11 >=1.8.13,<2.0a0 + - xorg-libxext >=1.3.7,<2.0a0 + - xorg-libxi >=1.8.3,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 35500 + timestamp: 1792204927820 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda + sha256: 64db17baaf36fa03ed8fae105e2e671a7383e22df4077486646f7dbf12842c9f + md5: 665d152b9c6e78da404086088077c844 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 18701 + timestamp: 1769434732453 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxxf86vm-1.1.6-h86ecc28_0.conda sha256: 012f0d1fd9fb1d949e0dccc0b28d9dd5a8895a1f3e2a7edc1fa2e1b33fc0f233 md5: d745faa2d7c15092652e40a22bb261ed @@ -6730,6 +14376,29 @@ packages: purls: [] size: 18185 timestamp: 1734214652726 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxxf86vm-1.1.7-he30d5cf_0.conda + sha256: 6f6f2b32754e09c2334e067ba5d2d38715f2432cd9fb41d6f66de0591f8f4f94 + md5: b15ca02584678f38df6e114c32f93959 + depends: + - libgcc >=14 + - xorg-libx11 >=1.8.12,<2.0a0 + - xorg-libxext >=1.3.6,<2.0a0 + license: MIT + license_family: MIT + purls: [] + size: 19148 + timestamp: 1769434729220 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hebe6cf0_3.conda + sha256: 8e9c7ca4fcfe9f0c300e4aa853b79c25411fc4e470decfe8f909b74f60dfb6dd + md5: c3d0a6f03ba3aa312777b41b20a71ec0 + depends: + - libgcc >=15 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 596340 + timestamp: 1788874111132 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-xorgproto-2024.1-h86ecc28_1.conda sha256: 3dbbf4cdb5ad82d3479ab2aa68ae67de486a6d57d67f0402d8e55869f6f13aec md5: 91cef7867bf2b47f614597b59705ff56 @@ -6740,6 +14409,16 @@ packages: purls: [] size: 566948 timestamp: 1726847598167 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-xorgproto-2025.1-h29ee22c_3.conda + sha256: c5b0c6e21dc57ca8b33d8f42328e963ecada4153f7ca2a31d55b50fe8c8a1d21 + md5: b4fac720de771a0520d897a1c60974ca + depends: + - libgcc >=15 + license: MIT + license_family: MIT + purls: [] + size: 596281 + timestamp: 1788874480627 - pypi: https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl name: yarl version: 1.23.0 @@ -6767,6 +14446,71 @@ packages: - multidict>=4.0 - propcache>=0.2.1 requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/1d/fc/21d74789198ad68a23a209ab0d73fe5a52d39ebbd9945041e72827acdd77/yarl-1.25.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: yarl + version: 1.25.1 + sha256: 8ee202350cf57abf0e9502a41601841019c25d3db7ff52d980aaf31446254059 + requires_dist: + - idna>=2.0 + - multidict>=4.0 + - propcache>=0.2.1 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/28/b9/7818ac6dec7fbcd16be2d19495807c01e2c38834e57d41c63356a75e786b/yarl-1.25.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: yarl + version: 1.25.1 + sha256: e5637ca8d0bd7fb72648a6c7934af4baaccb697657f7438c9d264fc2abb8b0b1 + requires_dist: + - idna>=2.0 + - multidict>=4.0 + - propcache>=0.2.1 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/6e/bb/dda344765ffd3430afe1a1c66c866a57fae67786537d4f14607df6505ac1/yarl-1.25.1-cp314-cp314-macosx_11_0_arm64.whl + name: yarl + version: 1.25.1 + sha256: 7a5c3115595995779ee21f2567035793911c3802a43c74f3fbb0314929ec67ac + requires_dist: + - idna>=2.0 + - multidict>=4.0 + - propcache>=0.2.1 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/a2/be/415b00c0fe5a0615b062a456b26623d7ec91c2bee20faea1a14045aa0469/yarl-1.25.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: yarl + version: 1.25.1 + sha256: 30eec96e8a91bd588ce897c9543f6d5d8d34b28fbcba28a4dedf20ebeae9fe57 + requires_dist: + - idna>=2.0 + - multidict>=4.0 + - propcache>=0.2.1 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ae/de/4204e6646e278b1cd4a9cf73ebe8b7cfcf16693f1249b324eb273d67602a/yarl-1.25.1-cp310-cp310-macosx_11_0_arm64.whl + name: yarl + version: 1.25.1 + sha256: 9e23c82b63cd7652fc24d33ed6cc17099d607aa3b4fc4ddc75e95062f3d82df4 + requires_dist: + - idna>=2.0 + - multidict>=4.0 + - propcache>=0.2.1 + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e5/5f/ed1538bcd06009fe990d6d283dd7667f639e62a81e35c6d8c6ef6c08fb3c/yarl-1.25.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + name: yarl + version: 1.25.1 + sha256: 77e5099b99b37f3cf79c246998ca9f7313a78054cd1809ec46bc1afad47e1c4c + requires_dist: + - idna>=2.0 + - multidict>=4.0 + - propcache>=0.2.1 + requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_7.conda + sha256: 47d682b9f6d6ec9eb1a6e6c3e75ea6273e899e78fb7fc59f81d39745009fbc60 + md5: aa459086047c0e5e27023ab19f8cb86a + depends: + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 601301 + timestamp: 1786599621503 - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda sha256: a4166e3d8ff4e35932510aaff7aa90772f84b4d07e9f6f83c614cba7ceefe0eb md5: 6432cb5d4ac0046c3ac0a8a0f95842f9 @@ -6780,6 +14524,16 @@ packages: purls: [] size: 567578 timestamp: 1742433379869 +- conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-h9d15635_7.conda + sha256: 427fd14bcb3b8659796fecc682716617409350fb5a98e5b7b47558a10d1a2fc7 + md5: d942e34ac3920ba83f8b2d0169570187 + depends: + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 615477 + timestamp: 1786599613561 - conda: https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-hbcf94c1_2.conda sha256: 0812e7b45f087cfdd288690ada718ce5e13e8263312e03b643dd7aa50d08b51b md5: 5be90c5a3e4b43c53e38f50a85e11527 @@ -6803,3 +14557,14 @@ packages: purls: [] size: 399979 timestamp: 1742433432699 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hf451053_7.conda + sha256: da867f5092eb0cb746d353694f0098031fd9817a4ce7d5743121209ae0f406ca + md5: 4ec2684c73812cc2c3d78379384a39cc + depends: + - __osx >=11.0 + - libzlib >=1.3.2,<2.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 433687 + timestamp: 1786599629846 diff --git a/pyproject.toml b/pyproject.toml index 050bde152..8e663f1bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,7 @@ datajoint = "datajoint.cli:cli" test = [ "pytest", "pytest-cov", + "hypothesis", "requests", "faker", "matplotlib", @@ -108,6 +109,7 @@ viz = ["matplotlib", "ipython"] test = [ "pytest", "pytest-cov", + "hypothesis", "requests", "faker", "matplotlib", @@ -125,6 +127,7 @@ dev = [ # including test "pytest", "pytest-cov", + "hypothesis", "polars>=0.20.0", "pyarrow>=14.0.0", ] diff --git a/tests/conftest.py b/tests/conftest.py index bfbbc293d..985c8c0f2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,11 +19,14 @@ import logging import os from os import remove +from pathlib import Path from typing import Dict, List import certifi import pytest import urllib3 +from hypothesis import settings +from hypothesis.database import DirectoryBasedExampleDatabase import datajoint as dj from datajoint.errors import DataJointError @@ -35,6 +38,26 @@ logger = logging.getLogger(__name__) +# ============================================================================= +# Hypothesis Profiles +# ============================================================================= +# Select a profile with HYPOTHESIS_PROFILE=dev|ci|thorough. Without it, the +# profile is "ci" when the CI environment variable is set (GitHub Actions sets +# it) and "dev" otherwise. The example counts are for the model-level +# properties (tests/unit/test_expand_spec.py); property tests that run against +# a database set their own, smaller max_examples. +# dev fast local runs +# ci 500 examples, the same examples on every run +# thorough 2000 random examples; failing examples are saved under +# .hypothesis/ (gitignored) and replayed first on the next run. +# Run it before each Diagram.expand merge and before a release tag. +_HYPOTHESIS_DB = DirectoryBasedExampleDatabase(str(Path(__file__).resolve().parent.parent / ".hypothesis" / "examples")) +settings.register_profile("dev", max_examples=50, deadline=None, database=_HYPOTHESIS_DB) +settings.register_profile("ci", max_examples=500, derandomize=True, deadline=None, database=None, print_blob=True) +settings.register_profile("thorough", max_examples=2000, deadline=None, database=_HYPOTHESIS_DB) +settings.load_profile(os.environ.get("HYPOTHESIS_PROFILE") or ("ci" if os.environ.get("CI") else "dev")) + + # ============================================================================= # Pytest Hooks # ============================================================================= From 26b7b43492d86648ff02c299a9f7bb755a93be61 Mon Sep 17 00:00:00 2001 From: Dimitri Yatsenko Date: Sat, 17 Oct 2026 16:54:36 -0500 Subject: [PATCH 2/4] test(diagram): reference model and properties for expand Add an executable reference model of Diagram.expand (L0 of the test program) that needs no database: - tests/unit/expand_model.py: TableSpec, FK and Schema; databases as {table: {pk_tuple: row}}; ref_e under MATCH SIMPLE (A2); the owning reference (A1), with parts that violate it traversed as plain tables; the row relations child, parent, inst, down and up; D, D1, U and both as breadth-first closures; the checkers ri_ok, group_whole, group_closed, lift_closed and q_acyclic; apply_delete; the self.upstream of a row not inserted yet; and the Dependencies graph built the way Dependencies.load builds it. - tests/unit/test_expand_spec.py: Hypothesis generators for schemas and consistent databases, and properties for T1-T8. Minimality (T4, T5) is checked by brute force on databases of at most eight rows. T8 calls datajoint.dependencies.topo_sort with MySQL and PostgreSQL quoting. Two examples show that T8 needs A1, and parametrized checks confirm that the generators reach each shape the properties rely on. - tests/unit/test_expand_shapes_model.py: one test per shape (S1-S7, G1-G6, K1, K2, K5, K6) with the expected rows written by hand. --- tests/unit/expand_model.py | 606 ++++++++++++++++++++ tests/unit/test_expand_shapes_model.py | 538 ++++++++++++++++++ tests/unit/test_expand_spec.py | 749 +++++++++++++++++++++++++ 3 files changed, 1893 insertions(+) create mode 100644 tests/unit/expand_model.py create mode 100644 tests/unit/test_expand_shapes_model.py create mode 100644 tests/unit/test_expand_spec.py diff --git a/tests/unit/expand_model.py b/tests/unit/expand_model.py new file mode 100644 index 000000000..7ad47f157 --- /dev/null +++ b/tests/unit/expand_model.py @@ -0,0 +1,606 @@ +""" +Executable reference model of ``Diagram.expand``. + +This module is an executable copy of the traversal specification (the +normative section of ``diagram.md``). It holds rows in plain Python +dictionaries, needs no database, and defines the row sets that the engine +must produce: + +- ``D(S)``: the least superset of the seed rows ``S`` closed under ``down``. + This is ``expand(seed, "down")`` and ``cascade(seed, part_integrity="cascade")``. +- ``D1(S)``: the closure under ``child`` alone, which ``part_integrity="ignore"`` + deletes and ``"enforce"`` deletes when it is lift-closed. +- ``U(S)``: the least superset closed under ``up``. This is ``expand(seed, "up")`` + and ``trace(seed)``. +- ``both(S) = U(D(S))``, which is ``expand(seed, "both")``. + +Each operation is a plain breadth-first search over row identifiers +``(table, pk_tuple)``. ``test_expand_spec.py`` checks the model against the +theorems of the specification, and later test layers check the engine against +the model. + +Model +----- +- A schema is a list of tables in declaration order and a list of foreign keys. + An FK maps child attributes onto the parent's primary key (``attr_map``). + Parallel FKs between the same two tables are separate edges. +- A2: an FK with any NULL column references nothing, as in SQL's MATCH SIMPLE. + FKs can share columns, so partial NULLs occur. +- A1, the owning reference: a part's heading contains its master's primary key, + unrenamed and non-null, inherited through ``-> master`` directly or through a + sibling part. A part that violates A1 is traversed as a plain table. +- A database is ``{table: {pk_tuple: row_dict}}``, where ``pk_tuple`` lists the + primary-key values in the order of ``TableSpec.pk``. +""" + +from __future__ import annotations + +from collections import defaultdict, deque +from dataclasses import dataclass, field +from typing import Callable, Iterable, Mapping + +import networkx as nx + +from datajoint.dependencies import Dependencies + +#: A row identifier: ``(table name, primary-key tuple)``. +Row = tuple +#: A database: ``{table: {pk_tuple: row_dict}}``. +DB = dict + + +# ============================================================================= +# Schema +# ============================================================================= + + +@dataclass(frozen=True) +class TableSpec: + """ + One table of a model schema. + + Parameters + ---------- + name : str + Table name. A part's name is ``__``, as in DataJoint, so + that ``datajoint.dependencies.extract_master`` recognizes it. + pk : tuple[str, ...] + Primary-key attributes, in order. + secondary : tuple[str, ...], optional + Secondary attributes. + master : str, optional + Name of the master table if this table is a part. + """ + + name: str + pk: tuple[str, ...] + secondary: tuple[str, ...] = () + master: str | None = None + + @property + def heading(self) -> tuple[str, ...]: + """All attributes: the primary key, then the secondary attributes.""" + return self.pk + self.secondary + + +@dataclass(frozen=True) +class FK: + """ + One foreign key, an edge ``parent -> child`` of the dependency graph. + + Parameters + ---------- + child : str + The referencing table. + parent : str + The referenced table. + attr_map : dict[str, str] + Maps each child attribute of the FK to the parent primary-key attribute + it references. An unrenamed FK maps every attribute to itself. + nullable : bool, optional + Whether the FK was declared ``-> [nullable]``. The attributes it + introduces may then hold NULL. + """ + + child: str + parent: str + attr_map: Mapping[str, str] = field(hash=False) + nullable: bool = False + + @property + def renamed(self) -> bool: + """True if any child attribute has a different name in the parent.""" + return any(c != p for c, p in self.attr_map.items()) + + +class Schema: + """ + Tables in declaration order and the foreign keys between them. + + Parameters + ---------- + tables : iterable of TableSpec + Tables in declaration order. + fks : iterable of FK + Foreign keys. Each child's FKs are listed in declaration order. + + Notes + ----- + Two facts are derived from the declarations: + + - **Nullable attributes.** A secondary attribute is nullable when the first + FK that includes it is nullable, as in ``declare.py``, where an FK adds + only the attributes not declared yet. Primary-key attributes and + attributes outside every FK are never NULL. + - **Groups.** Each master forms a group with its parts that satisfy A1. + ``group[t]`` names the master of table ``t`` for the master itself and + for those parts. A part that violates A1 is in no group. + """ + + def __init__(self, tables: Iterable[TableSpec], fks: Iterable[FK]) -> None: + self.tables = tuple(tables) + self.fks = tuple(fks) + self._by_name = {t.name: t for t in self.tables} + if len(self._by_name) != len(self.tables): + raise ValueError("table names must be unique") + self._validate() + self.nullable = {t.name: self._nullable_attributes(t) for t in self.tables} + self.a1_parts = {} + self.group = {} + for t in self.tables: + parts = [p for p in self.tables if p.master == t.name] + if parts: + self.a1_parts[t.name] = self._find_a1_parts(t, parts) + self.group[t.name] = t.name + self.group.update({p: t.name for p in self.a1_parts[t.name]}) + + def __repr__(self) -> str: + lines = ["Schema("] + for t in self.tables: + master = f", master={t.master!r}" if t.master else "" + lines.append(f" TableSpec({t.name!r}, pk={t.pk!r}, secondary={t.secondary!r}{master}),") + for fk in self.fks: + null = ", nullable=True" if fk.nullable else "" + lines.append(f" FK({fk.child!r}, {fk.parent!r}, {dict(fk.attr_map)!r}{null}),") + return "\n".join(lines) + "\n)" + + def table(self, name: str) -> TableSpec: + """Return the table named ``name``.""" + return self._by_name[name] + + def fks_of(self, child: str) -> list[FK]: + """Return the FKs declared in ``child``, in declaration order.""" + return [fk for fk in self.fks if fk.child == child] + + def is_primary(self, fk: FK) -> bool: + """True if every attribute of the FK is in the child's primary key (as in ``Dependencies``).""" + return set(fk.attr_map) <= set(self.table(fk.child).pk) + + def master_key(self, table: str) -> tuple[str, ...] | None: + """Primary key of the group that ``table`` belongs to, or None for a table in no group.""" + master = self.group.get(table) + return None if master is None else self.table(master).pk + + def _validate(self) -> None: + declared = set() + for t in self.tables: + if not t.pk: + raise ValueError(f"{t.name} has no primary key") + if len(set(t.heading)) != len(t.heading): + raise ValueError(f"{t.name} repeats an attribute") + if t.master is not None: + if t.master not in declared or self.table(t.master).master is not None: + raise ValueError(f"{t.name}: master {t.master} must be a table declared earlier, not a part") + if not t.name.startswith(t.master + "__"): + raise ValueError(f"part {t.name} must be named {t.master}__") + declared.add(t.name) + for fk in self.fks: + child, parent = self.table(fk.child), self.table(fk.parent) + if not set(fk.attr_map) <= set(child.heading): + raise ValueError(f"{fk}: attributes missing from {child.name}") + if sorted(fk.attr_map.values()) != sorted(parent.pk): + raise ValueError(f"{fk}: must map onto the whole primary key of {parent.name}") + + def _nullable_attributes(self, table: TableSpec) -> frozenset[str]: + seen, nullable = set(), set() + for fk in self.fks_of(table.name): + for attr in fk.attr_map: + if attr not in seen: + seen.add(attr) + if fk.nullable and attr not in table.pk: + nullable.add(attr) + return frozenset(nullable) + + def _find_a1_parts(self, master: TableSpec, parts: list[TableSpec]) -> tuple[str, ...]: + # A part satisfies A1 if it has an FK to the master, or to a sibling that + # satisfies A1, that carries every master-key attribute unrenamed and + # non-null. Repeat until no new part qualifies, so that the result does + # not depend on the order in which the parts are listed. + owners = {master.name} + changed = True + while changed: + changed = False + for part in parts: + if part.name not in owners and any(self._is_owning(fk, master.pk, owners) for fk in self.fks_of(part.name)): + owners.add(part.name) + changed = True + return tuple(p.name for p in parts if p.name in owners) + + def _is_owning(self, fk: FK, master_key: tuple[str, ...], owners: set[str]) -> bool: + return ( + fk.parent in owners + and fk.parent != fk.child + and all(fk.attr_map.get(a) == a and a not in self.nullable[fk.child] for a in master_key) + ) + + +# ============================================================================= +# Databases and references +# ============================================================================= + + +def make_db(schema: Schema, data: Mapping[str, Iterable[tuple]]) -> DB: + """ + Build a database from rows written as tuples in heading order. + + Parameters + ---------- + schema : Schema + The schema. + data : dict[str, iterable of tuple] + For each table, its rows as tuples of the primary-key values followed by + the secondary values. Tables left out are empty. + + Returns + ------- + DB + ``{table: {pk_tuple: row_dict}}``. + """ + db = {t.name: {} for t in schema.tables} + for name, rows in data.items(): + table = schema.table(name) + for values in rows: + if len(values) != len(table.heading): + raise ValueError(f"{name}: expected {table.heading}, got {values}") + row = dict(zip(table.heading, values)) + key = tuple(row[a] for a in table.pk) + if key in db[name]: + raise ValueError(f"{name}: duplicate primary key {key}") + db[name][key] = row + return db + + +def all_rows(db: DB) -> frozenset[Row]: + """Return the identifiers of every row in ``db``.""" + return frozenset((t, k) for t, rows in db.items() for k in rows) + + +def fk_value(schema: Schema, fk: FK, row: Mapping) -> tuple | None: + """ + Return the parent primary key that ``row`` names through ``fk``. + + Returns None if any FK attribute is NULL (A2, MATCH SIMPLE). + """ + by_parent_attr = {p: c for c, p in fk.attr_map.items()} + value = tuple(row[by_parent_attr[a]] for a in schema.table(fk.parent).pk) + return None if any(v is None for v in value) else value + + +def ref_e(schema: Schema, db: DB, fk: FK, row: Mapping) -> Row | None: + """ + Return the parent row that ``row`` references through ``fk``. + + Returns None if the FK value has a NULL (A2) or names no existing row. + """ + value = fk_value(schema, fk, row) + if value is None or value not in db.get(fk.parent, {}): + return None + return (fk.parent, value) + + +def master_of(schema: Schema, table: str, row: Mapping) -> Row | None: + """ + ``master(r)``: identifier of the master row of a row in a group. + + The master row is named by the row's master-key attributes, which A1 + guarantees are present, unrenamed and non-null. Returns None for a row in no + group. The identifier is returned whether or not the master row exists. + """ + key_attrs = schema.master_key(table) + if key_attrs is None: + return None + return (schema.group[table], tuple(row[a] for a in key_attrs)) + + +def apply_delete(db: DB, rows: Iterable[Row]) -> DB: + """Return a copy of ``db`` without ``rows``.""" + gone = set(rows) + return {t: {k: r for k, r in table.items() if (t, k) not in gone} for t, table in db.items()} + + +def subdatabase(db: DB, rows: Iterable[Row]) -> DB: + """Return the database that holds only ``rows`` (an export of them).""" + keep = set(rows) + return {t: {k: r for k, r in table.items() if (t, k) in keep} for t, table in db.items()} + + +# ============================================================================= +# Integrity checkers +# ============================================================================= + + +def ri_ok(schema: Schema, db: DB) -> bool: + """ + P1, referential integrity: every non-NULL FK value names an existing parent row. + + An FK value with any NULL attribute names nothing (A2) and is not checked. + """ + for fk in schema.fks: + parent_rows = db.get(fk.parent, {}) + for row in db.get(fk.child, {}).values(): + value = fk_value(schema, fk, row) + if value is not None and value not in parent_rows: + return False + return True + + +def group_whole(schema: Schema, db: DB) -> bool: + """ + The static half of P2: no part row exists without its master row. + + This is a property of one database state. P2 for a delete is stronger: the + delete must remove each master-part instance whole or not at all, which + ``RowGraph.group_closed`` checks on the deleted rows. Removing one part row + of an instance leaves a database that still passes this check, yet breaks + P2. Under A1 the check also follows from ``ri_ok``, since each part row + references its master directly or through a sibling. + """ + for table, master in schema.group.items(): + if table == master: + continue + for row in db.get(table, {}).values(): + m = master_of(schema, table, row) + if m[1] not in db.get(master, {}): + return False + return True + + +def quotient(schema: Schema) -> nx.DiGraph: + """ + The group quotient Q: the FK graph with each master-part group contracted to one node. + + FKs between two tables of the same group are left out. A part that violates + A1 is in no group and stays its own node. A self-referencing FK is a + self-loop, which counts as a cycle. + """ + node = {t.name: schema.group.get(t.name, t.name) for t in schema.tables} + q = nx.DiGraph() + q.add_nodes_from(set(node.values())) + for fk in schema.fks: + u, v = node[fk.parent], node[fk.child] + if u != v or fk.parent == fk.child: + q.add_edge(u, v) + return q + + +def q_acyclic(schema: Schema) -> bool: + """True if the group quotient Q is acyclic.""" + return nx.is_directed_acyclic_graph(quotient(schema)) + + +# ============================================================================= +# Row relations and the operations +# ============================================================================= + + +class RowGraph: + """ + The row relations of one database and the closures over them. + + Parameters + ---------- + schema : Schema + The schema. + db : DB + The database. + + Notes + ----- + - ``child(r) = {c : ref_e(c) = r}`` and ``parent(r) = {ref_e(r)}``, over + every FK edge e. + - ``inst(r)`` is the master-part instance of a row in a group: the master + row and every part row that carries the same master key. It is empty for + a row in no group. + - ``down = child ∪ inst`` and ``up = parent ∪ inst``, so ``up`` is the + converse of ``down``. + """ + + def __init__(self, schema: Schema, db: DB) -> None: + self.schema = schema + self.db = db + self.rows = all_rows(db) + self._parents = {r: set() for r in self.rows} + self._children = {r: set() for r in self.rows} + for fk in schema.fks: + for key, row in db.get(fk.child, {}).items(): + ref = ref_e(schema, db, fk, row) + if ref is not None: + self._parents[(fk.child, key)].add(ref) + self._children[ref].add((fk.child, key)) + # Instances are keyed by the master row's identifier, which exists in a + # consistent database; the key works even when it does not. + instances = defaultdict(set) + self._master = {} + for table, rows in db.items(): + for key, row in rows.items(): + m = master_of(schema, table, row) + if m is not None: + self._master[(table, key)] = m + instances[m].add((table, key)) + self._inst = {r: frozenset(instances[m]) for r, m in self._master.items()} + + def _check(self, rows: Iterable[Row]) -> set[Row]: + rows = set(rows) + unknown = rows - self.rows + if unknown: + raise ValueError(f"rows not in the database: {sorted(unknown)}") + return rows + + # --- relations ----------------------------------------------------------- + + def child(self, r: Row) -> frozenset[Row]: + """Rows that reference ``r`` through some FK.""" + return frozenset(self._children[r]) + + def parent(self, r: Row) -> frozenset[Row]: + """Rows that ``r`` references through some FK.""" + return frozenset(self._parents[r]) + + def inst(self, r: Row) -> frozenset[Row]: + """The master-part instance of ``r``; empty for a row in no group.""" + return self._inst.get(r, frozenset()) + + def master(self, r: Row) -> Row | None: + """``master(r)`` for a row in a group, else None.""" + return self._master.get(r) + + def down(self, r: Row) -> frozenset[Row]: + """``down = child ∪ inst``.""" + return self.child(r) | self.inst(r) + + def up(self, r: Row) -> frozenset[Row]: + """``up = parent ∪ inst``.""" + return self.parent(r) | self.inst(r) + + # --- operations ---------------------------------------------------------- + + def closure(self, seed: Iterable[Row], step: Callable[[Row], Iterable[Row]]) -> frozenset[Row]: + """The least superset of ``seed`` closed under ``step``, by breadth-first search.""" + seen = self._check(seed) + queue = deque(seen) + while queue: + for s in step(queue.popleft()): + if s not in seen: + seen.add(s) + queue.append(s) + return frozenset(seen) + + def D(self, seed: Iterable[Row]) -> frozenset[Row]: + """``expand(seed, "down")``: the closure under ``down``.""" + return self.closure(seed, self.down) + + def D1(self, seed: Iterable[Row]) -> frozenset[Row]: + """The closure under ``child`` alone, with no group rule.""" + return self.closure(seed, self.child) + + def U(self, seed: Iterable[Row]) -> frozenset[Row]: + """``expand(seed, "up")``: the closure under ``up``.""" + return self.closure(seed, self.up) + + def both(self, seed: Iterable[Row]) -> frozenset[Row]: + """``expand(seed, "both") = U(D(seed))``.""" + return self.U(self.D(seed)) + + # --- group predicates ---------------------------------------------------- + + def lift_closed(self, rows: Iterable[Row]) -> bool: + """True if every part row in ``rows`` that belongs to a group has its master row in ``rows``.""" + rows = set(rows) + return all(self._master[r] in rows for r in rows if r in self._master and self._master[r] != r) + + def group_closed(self, rows: Iterable[Row]) -> bool: + """ + True if ``rows`` takes every master-part instance it touches whole. + + This is P2 for an operation: a delete or an export of ``rows`` keeps + each instance whole if and only if this holds. + """ + rows = set(rows) + return all(self.inst(r) <= rows for r in rows) + + +# ============================================================================= +# self.upstream for a row not inserted yet +# ============================================================================= + + +def relabel(schema: Schema, fk: FK, key: Mapping) -> tuple | None: + """ + ``relabel_e(key)``: the parent primary key named by a child's primary-key values. + + ``fk`` must be a primary FK of the child, so ``key`` holds every one of its + attributes. Returns None if a value is NULL. + """ + return fk_value(schema, fk, key) + + +def virtual_upstream(schema: Schema, db: DB, table: str, key: Mapping) -> frozenset[Row]: + """ + ``self.upstream`` for a row of ``table`` with primary key ``key``, inserted or not. + + ``make()`` runs before its row is inserted, and ``U(∅) = ∅``, so the upstream + of the row is built from its primary FKs instead: the union, over each FK e + whose attributes all lie in the primary key, of + ``U(rows of P_e matching relabel_e(key))``. Secondary FKs are unknown before + the insert and are left out. + + Parameters + ---------- + schema : Schema + The schema. + db : DB + The database. + table : str + The table that ``make()`` fills. + key : dict + The primary-key values of the row. + + Returns + ------- + frozenset + The upstream rows. The row itself is not included. + """ + graph = RowGraph(schema, db) + upstream = frozenset() + for fk in schema.fks_of(table): + if schema.is_primary(fk): + value = relabel(schema, fk, key) + matching = {(fk.parent, value)} if value is not None and value in db.get(fk.parent, {}) else set() + upstream |= graph.U(matching) + return upstream + + +# ============================================================================= +# The dependency graph, built the way Dependencies.load builds it +# ============================================================================= + + +def full_table_name(name: str, quote: str = "`", database: str = "model") -> str: + """Full table name as ``Dependencies`` stores it, e.g. ```model`.`t0__p1```.""" + return f"{quote}{database}{quote}.{quote}{name}{quote}" + + +def dependency_graph(schema: Schema, quote: str = "`", database: str = "model") -> Dependencies: + """ + Build the ``Dependencies`` graph that ``Dependencies.load`` would build for ``schema``. + + Nodes are full table names with a ``primary_key`` attribute. Each FK is one + edge ``parent -> child`` keyed by the tuple of its child attributes, with the + edge attributes ``primary``, ``attr_map``, ``aliased`` and ``multi``. + ``quote`` is the backtick for MySQL or the double quote for PostgreSQL. + """ + graph = Dependencies() # no connection: nodes and edges are added by hand + for t in schema.tables: + graph.add_node(full_table_name(t.name, quote, database), primary_key=set(t.pk)) + for fk in schema.fks: + child_pk = set(schema.table(fk.child).pk) + graph.add_edge( + full_table_name(fk.parent, quote, database), + full_table_name(fk.child, quote, database), + key=tuple(fk.attr_map), + primary=set(fk.attr_map) <= child_pk, + attr_map=dict(fk.attr_map), + aliased=fk.renamed, + multi=set(fk.attr_map) != child_pk, + ) + return graph diff --git a/tests/unit/test_expand_shapes_model.py b/tests/unit/test_expand_shapes_model.py new file mode 100644 index 000000000..9824c2d06 --- /dev/null +++ b/tests/unit/test_expand_shapes_model.py @@ -0,0 +1,538 @@ +""" +Shape catalog of ``Diagram.expand``, checked against the reference model. + +One small test per shape, with the expected rows written out by hand. The shape +IDs match the examples in the specification (``diagram.md``) one to one: + +- S1-S7, foreign-key shapes: identity, secondary, nullable with partial NULLs, + renamed, parallel, diamond with a shared key, diamond reached through a + secondary FK. +- G1-G6, master-part groups: the part -> master lift, part-of-part with two + chains, a second renamed FK to the master, a sibling FK that renames the master + key, the merge table, a part that violates A1. +- K1, K2, K5, K6, seeds: restricted by a descendant, a self-referencing table, + empty, unrestricted. + +K3, K4, X1 and X2 need a database and are tested in the integration suite. +Rows are written ``r(table, *primary_key_values)``. +""" + +import networkx as nx +import pytest + +from .expand_model import ( + FK, + RowGraph, + Schema, + TableSpec, + apply_delete, + dependency_graph, + group_whole, + make_db, + q_acyclic, + ri_ok, +) + + +def r(table, *key): + """A row identifier written by hand.""" + return (table, key) + + +def graph(schema, data): + db = make_db(schema, data) + assert ri_ok(schema, db) and group_whole(schema, db), "the fixture itself must be consistent" + return RowGraph(schema, db) + + +# ============================================================================= +# S: foreign-key shapes +# ============================================================================= + + +def test_S1_identity_fk_in_primary_key(): + """An unrenamed FK inside the child's primary key: session -> subject.""" + schema = Schema( + [TableSpec("subject", ("subject_id",)), TableSpec("session", ("subject_id", "session_id"))], + [FK("session", "subject", {"subject_id": "subject_id"})], + ) + g = graph(schema, {"subject": [(1,), (2,)], "session": [(1, 1), (1, 2), (2, 1)]}) + assert g.D({r("subject", 1)}) == {r("subject", 1), r("session", 1, 1), r("session", 1, 2)} + assert g.U({r("session", 1, 2)}) == {r("session", 1, 2), r("subject", 1)} + + +def test_S2_secondary_fk(): + """An FK below the --- line: session references rig.""" + schema = Schema( + [TableSpec("rig", ("rig_id",)), TableSpec("session", ("session_id",), ("rig_id",))], + [FK("session", "rig", {"rig_id": "rig_id"})], + ) + g = graph(schema, {"rig": [(1,), (2,)], "session": [(1, 1), (2, 1), (3, 2)]}) + assert g.D({r("rig", 1)}) == {r("rig", 1), r("session", 1), r("session", 2)} + assert g.U({r("session", 3)}) == {r("session", 3), r("rig", 2)} + + +def test_S3_nullable_fk_sharing_a_primary_key_column(): + """ + A nullable FK (lab_id, person_id) whose lab_id is also in the primary key. + + Assignment (1, 2) has lab_id = 1 and person_id = NULL: by A2 it references no + person, although its lab_id matches persons (1, 1) and (1, 2). It is still + reached from lab 1 through its own FK to lab. + """ + schema = Schema( + [ + TableSpec("lab", ("lab_id",)), + TableSpec("person", ("lab_id", "person_id")), + TableSpec("assignment", ("lab_id", "task_id"), ("person_id",)), + ], + [ + FK("person", "lab", {"lab_id": "lab_id"}), + FK("assignment", "lab", {"lab_id": "lab_id"}), + FK("assignment", "person", {"lab_id": "lab_id", "person_id": "person_id"}, nullable=True), + ], + ) + assert schema.nullable["assignment"] == {"person_id"} + g = graph( + schema, + { + "lab": [(1,), (2,)], + "person": [(1, 1), (1, 2), (2, 1)], + "assignment": [(1, 1, 1), (1, 2, None), (2, 1, 1)], + }, + ) + assert g.D({r("person", 1, 1)}) == {r("person", 1, 1), r("assignment", 1, 1)} + assert g.D({r("lab", 1)}) == { + r("lab", 1), + r("person", 1, 1), + r("person", 1, 2), + r("assignment", 1, 1), + r("assignment", 1, 2), + } + assert g.U({r("assignment", 1, 2)}) == {r("assignment", 1, 2), r("lab", 1)} + assert g.U({r("assignment", 2, 1)}) == {r("assignment", 2, 1), r("person", 2, 1), r("lab", 2)} + + +def test_S4_renamed_fk(): + """litter.mother_id references animal.animal_id. Litter 1 has mother 2, so animal 1 does not reach it.""" + schema = Schema( + [TableSpec("animal", ("animal_id",)), TableSpec("litter", ("litter_id",), ("mother_id",))], + [FK("litter", "animal", {"mother_id": "animal_id"})], + ) + g = graph(schema, {"animal": [(1,), (2,)], "litter": [(1, 2), (2, 1), (3, 1)]}) + assert g.D({r("animal", 1)}) == {r("animal", 1), r("litter", 2), r("litter", 3)} + assert g.U({r("litter", 1)}) == {r("litter", 1), r("animal", 2)} + + +def test_S5_parallel_fks_plain_and_renamed(): + """ + synapse references neuron twice: neuron_id (plain) and pre_id (renamed). + + The two FKs are separate edges and combine with OR: neuron 2 reaches + synapse (1, 2) through pre_id and synapse (2, 3) through neuron_id. + """ + schema = Schema( + [TableSpec("neuron", ("neuron_id",)), TableSpec("synapse", ("neuron_id", "pre_id"))], + [FK("synapse", "neuron", {"neuron_id": "neuron_id"}), FK("synapse", "neuron", {"pre_id": "neuron_id"})], + ) + g = graph(schema, {"neuron": [(1,), (2,), (3,)], "synapse": [(1, 2), (2, 3), (3, 3)]}) + assert g.D({r("neuron", 2)}) == {r("neuron", 2), r("synapse", 1, 2), r("synapse", 2, 3)} + assert g.D({r("neuron", 1)}) == {r("neuron", 1), r("synapse", 1, 2)} + assert g.U({r("synapse", 1, 2)}) == {r("synapse", 1, 2), r("neuron", 1), r("neuron", 2)} + assert g.U({r("synapse", 3, 3)}) == {r("synapse", 3, 3), r("neuron", 3)} + + +def test_S6_diamond_with_a_shared_key(): + """recording references session and implant, which share subject_id.""" + schema = Schema( + [ + TableSpec("subject", ("subject_id",)), + TableSpec("session", ("subject_id", "session_id")), + TableSpec("implant", ("subject_id", "implant_id")), + TableSpec("recording", ("subject_id", "session_id", "implant_id")), + ], + [ + FK("session", "subject", {"subject_id": "subject_id"}), + FK("implant", "subject", {"subject_id": "subject_id"}), + FK("recording", "session", {"subject_id": "subject_id", "session_id": "session_id"}), + FK("recording", "implant", {"subject_id": "subject_id", "implant_id": "implant_id"}), + ], + ) + g = graph( + schema, + { + "subject": [(1,), (2,)], + "session": [(1, 1), (2, 1)], + "implant": [(1, 1), (1, 2), (2, 1)], + "recording": [(1, 1, 1), (1, 1, 2), (2, 1, 1)], + }, + ) + assert g.D({r("implant", 1, 2)}) == {r("implant", 1, 2), r("recording", 1, 1, 2)} + assert g.D({r("subject", 2)}) == { + r("subject", 2), + r("session", 2, 1), + r("implant", 2, 1), + r("recording", 2, 1, 1), + } + assert g.U({r("recording", 1, 1, 2)}) == { + r("recording", 1, 1, 2), + r("session", 1, 1), + r("implant", 1, 2), + r("subject", 1), + } + # both adds the other parent of the descendant (session (1, 1)) and its ancestors. + assert g.both({r("implant", 1, 2)}) == { + r("implant", 1, 2), + r("recording", 1, 1, 2), + r("session", 1, 1), + r("subject", 1), + } + + +def test_S7_diamond_reached_through_a_secondary_fk(): + """ + D references B in its primary key and C as a secondary FK; C references B. + + D (2, 1) has b_id = 2 but references C 10, which references B 1, so deleting + B 1 must delete it (the Phase 0 diamond: copying ``b_id = 1`` down to D + missed it). D (1, 2) is reached through its own b_id. + """ + schema = Schema( + [ + TableSpec("b", ("b_id",)), + TableSpec("c", ("c_id",), ("b_id",)), + TableSpec("d", ("b_id", "d_id"), ("c_id",)), + ], + [FK("c", "b", {"b_id": "b_id"}), FK("d", "b", {"b_id": "b_id"}), FK("d", "c", {"c_id": "c_id"})], + ) + g = graph( + schema, + {"b": [(1,), (2,)], "c": [(10, 1), (11, 2)], "d": [(2, 1, 10), (1, 2, 11), (2, 3, 11)]}, + ) + assert g.D({r("b", 1)}) == {r("b", 1), r("c", 10), r("d", 2, 1), r("d", 1, 2)} + assert g.U({r("d", 2, 1)}) == {r("d", 2, 1), r("b", 2), r("c", 10), r("b", 1)} + + +# ============================================================================= +# G: master-part groups +# ============================================================================= + + +def g1_schema(): + """session is a master; its part trial references a stimulus.""" + return Schema( + [ + TableSpec("stimulus", ("stim_id",)), + TableSpec("session", ("session_id",)), + TableSpec("session__trial", ("session_id", "trial_id"), ("stim_id",), master="session"), + ], + [ + FK("session__trial", "session", {"session_id": "session_id"}), + FK("session__trial", "stimulus", {"stim_id": "stim_id"}), + ], + ) + + +G1_DATA = { + "stimulus": [(1,), (2,)], + "session": [(1,), (2,), (3,)], # session 3 has no trials + "session__trial": [(1, 1, 1), (1, 2, 2), (2, 1, 2)], +} + + +def test_G1_part_to_master_lift(): + """ + A part row lifts its master, and the master brings its other parts. + + Stimulus 1 reaches trial (1, 1), which lifts session 1 and with it trial + (1, 2). Sessions 2 and 3 are untouched; session 3 has no part rows at all. + """ + g = graph(g1_schema(), G1_DATA) + assert g.D({r("stimulus", 1)}) == { + r("stimulus", 1), + r("session__trial", 1, 1), + r("session", 1), + r("session__trial", 1, 2), + } + # Without the group rule the set is not lift-closed, so "enforce" raises. + d1 = g.D1({r("stimulus", 1)}) + assert d1 == {r("stimulus", 1), r("session__trial", 1, 1)} + assert not g.lift_closed(d1) + assert g.D({r("session", 3)}) == {r("session", 3)} + # trace includes the master's other parts and their parents (#1481). + assert g.U({r("session__trial", 1, 1)}) == { + r("session__trial", 1, 1), + r("session", 1), + r("session__trial", 1, 2), + r("stimulus", 1), + r("stimulus", 2), + } + assert g.U({r("session__trial", 2, 1)}) == {r("session__trial", 2, 1), r("session", 2), r("stimulus", 2)} + + +def test_G2_part_of_part_with_two_chains(): + """ + Part P references its master directly and through sibling Q (the #1525 shape). + + Ext 1 reaches Q (1, 10) and P (1, 100); both lift master 1. Master 2 is untouched. + """ + schema = Schema( + [ + TableSpec("ext", ("ext_id",)), + TableSpec("master", ("master_id",)), + TableSpec("master__q", ("master_id", "q_id"), ("ext_id",), master="master"), + TableSpec("master__p", ("master_id", "p_id"), ("q_id", "ext_id"), master="master"), + ], + [ + FK("master__q", "master", {"master_id": "master_id"}), + FK("master__q", "ext", {"ext_id": "ext_id"}), + FK("master__p", "master", {"master_id": "master_id"}), + FK("master__p", "master__q", {"master_id": "master_id", "q_id": "q_id"}), + FK("master__p", "ext", {"ext_id": "ext_id"}), + ], + ) + g = graph( + schema, + { + "ext": [(1,), (2,)], + "master": [(1,), (2,)], + "master__q": [(1, 10, 1), (2, 20, 2)], + "master__p": [(1, 100, 10, 1), (2, 200, 20, 2)], + }, + ) + assert g.D({r("ext", 1)}) == {r("ext", 1), r("master__q", 1, 10), r("master__p", 1, 100), r("master", 1)} + assert g.D({r("master__q", 1, 10)}) == {r("master__q", 1, 10), r("master__p", 1, 100), r("master", 1)} + assert g.U({r("master__p", 1, 100)}) == {r("master__p", 1, 100), r("master", 1), r("master__q", 1, 10), r("ext", 1)} + + +def test_G3_part_with_second_renamed_fk_to_its_master(): + """ + The lift follows the owning key, never the renamed second FK. + + cage__move references its master twice: cage_id (owning) and dest_cage + (renamed). Move (1, 1) belongs to cage 1 and points to cage 2. Seeded alone, + it lifts cage 1, not cage 2. Seeding cage 2 reaches move (1, 1) as a child + through dest_cage, which then lifts cage 1. + """ + schema = Schema( + [ + TableSpec("cage", ("cage_id",)), + TableSpec("cage__move", ("cage_id", "move_id"), ("dest_cage",), master="cage"), + ], + [FK("cage__move", "cage", {"cage_id": "cage_id"}), FK("cage__move", "cage", {"dest_cage": "cage_id"})], + ) + g = graph(schema, {"cage": [(1,), (2,), (3,)], "cage__move": [(1, 1, 2), (2, 1, 3), (3, 1, 3)]}) + assert g.D({r("cage__move", 1, 1)}) == {r("cage__move", 1, 1), r("cage", 1)} + assert g.D({r("cage", 2)}) == {r("cage", 2), r("cage__move", 2, 1), r("cage__move", 1, 1), r("cage", 1)} + assert g.U({r("cage__move", 1, 1)}) == { + r("cage__move", 1, 1), + r("cage", 1), + r("cage", 2), + r("cage__move", 2, 1), + r("cage", 3), + r("cage__move", 3, 1), + } + + +def test_G4_sibling_fk_renaming_the_master_key(): + """ + A sibling FK that renames the master key links different instances of the group. + + A transfer recorded in cohort 2 refers to an animal of cohort 1 (from_cohort), + and one in cohort 3 to an animal of cohort 2: a chain of three instances. + This is the case where the engine iterates the group step. + """ + schema = Schema( + [ + TableSpec("cohort", ("cohort_id",)), + TableSpec("cohort__animal", ("cohort_id", "animal_id"), master="cohort"), + TableSpec("cohort__transfer", ("cohort_id", "transfer_id"), ("from_cohort", "animal_id"), master="cohort"), + ], + [ + FK("cohort__animal", "cohort", {"cohort_id": "cohort_id"}), + FK("cohort__transfer", "cohort", {"cohort_id": "cohort_id"}), + FK("cohort__transfer", "cohort__animal", {"from_cohort": "cohort_id", "animal_id": "animal_id"}), + ], + ) + g = graph( + schema, + { + "cohort": [(1,), (2,), (3,)], + "cohort__animal": [(1, 1), (2, 1), (3, 1)], + "cohort__transfer": [(2, 1, 1, 1), (3, 1, 2, 1)], + }, + ) + everything = { + r("cohort", 1), + r("cohort", 2), + r("cohort", 3), + r("cohort__animal", 1, 1), + r("cohort__animal", 2, 1), + r("cohort__animal", 3, 1), + r("cohort__transfer", 2, 1), + r("cohort__transfer", 3, 1), + } + assert g.D({r("cohort", 1)}) == everything + assert g.D({r("cohort", 2)}) == { + r("cohort", 2), + r("cohort__animal", 2, 1), + r("cohort__transfer", 2, 1), + r("cohort", 3), + r("cohort__animal", 3, 1), + r("cohort__transfer", 3, 1), + } + assert g.D({r("cohort", 3)}) == {r("cohort", 3), r("cohort__animal", 3, 1), r("cohort__transfer", 3, 1)} + d1 = g.D1({r("cohort", 1)}) + assert d1 == {r("cohort", 1), r("cohort__animal", 1, 1), r("cohort__transfer", 2, 1)} + assert not g.lift_closed(d1) + assert g.U({r("cohort", 1)}) == {r("cohort", 1), r("cohort__animal", 1, 1)} + assert g.U({r("cohort__transfer", 3, 1)}) == everything + + +def test_G5_merge_table(): + """ + Each part of a merge table references a different upstream table. + + The upstream of an analysis resolves to the one source that fed its merge entry. + """ + schema = Schema( + [ + TableSpec("src_a", ("a_id",)), + TableSpec("src_b", ("b_id",)), + TableSpec("merge", ("merge_id",)), + TableSpec("merge__a", ("merge_id",), ("a_id",), master="merge"), + TableSpec("merge__b", ("merge_id",), ("b_id",), master="merge"), + TableSpec("analysis", ("merge_id",)), + ], + [ + FK("merge__a", "merge", {"merge_id": "merge_id"}), + FK("merge__a", "src_a", {"a_id": "a_id"}), + FK("merge__b", "merge", {"merge_id": "merge_id"}), + FK("merge__b", "src_b", {"b_id": "b_id"}), + FK("analysis", "merge", {"merge_id": "merge_id"}), + ], + ) + g = graph( + schema, + { + "src_a": [(1,), (2,)], + "src_b": [(1,)], + "merge": [(1,), (2,), (3,)], + "merge__a": [(1, 1), (2, 2)], + "merge__b": [(3, 1)], + "analysis": [(1,), (3,)], + }, + ) + assert g.D({r("src_a", 1)}) == {r("src_a", 1), r("merge__a", 1), r("merge", 1), r("analysis", 1)} + assert g.U({r("analysis", 3)}) == {r("analysis", 3), r("merge", 3), r("merge__b", 3), r("src_b", 1)} + assert g.U({r("analysis", 1)}) == {r("analysis", 1), r("merge", 1), r("merge__a", 1), r("src_a", 1)} + + +def test_G6_part_violating_a1(): + """ + m__note refers to its master through a renamed FK, so it violates A1. + + It is traversed as a plain table: no lift from it, and it is not part of the + master's instance. + """ + schema = Schema( + [ + TableSpec("m", ("m_id",)), + TableSpec("m__item", ("m_id", "item_id"), master="m"), + TableSpec("m__note", ("note_id",), ("about_m",), master="m"), + ], + [FK("m__item", "m", {"m_id": "m_id"}), FK("m__note", "m", {"about_m": "m_id"})], + ) + assert schema.a1_parts == {"m": ("m__item",)} + g = graph(schema, {"m": [(1,), (2,)], "m__item": [(1, 1), (2, 1)], "m__note": [(1, 1), (2, 2)]}) + assert g.D({r("m__note", 1)}) == {r("m__note", 1)} + assert g.D({r("m", 1)}) == {r("m", 1), r("m__item", 1, 1), r("m__note", 1)} + assert g.U({r("m__note", 1)}) == {r("m__note", 1), r("m", 1), r("m__item", 1, 1)} + assert g.U({r("m__item", 1, 1)}) == {r("m", 1), r("m__item", 1, 1)} + + +# ============================================================================= +# K: seeds +# ============================================================================= + + +def test_K1_seed_restricted_by_a_descendant(): + """ + ``subject & (session & "quality = 'bad'")``: the seed rows are named by a descendant (#1496). + + The seed itself must be deleted along with its sessions; the engine used to + leave it, because its restriction read sessions already deleted. + """ + schema = Schema( + [TableSpec("subject", ("subject_id",)), TableSpec("session", ("subject_id", "session_id"), ("quality",))], + [FK("session", "subject", {"subject_id": "subject_id"})], + ) + db = make_db( + schema, + {"subject": [(1,), (2,)], "session": [(1, 1, "good"), (1, 2, "bad"), (2, 1, "good")]}, + ) + g = RowGraph(schema, db) + seed = {r("subject", 1)} # the subjects with a bad session + deleted = g.D(seed) + assert deleted == {r("subject", 1), r("session", 1, 1), r("session", 1, 2)} + remaining = apply_delete(db, deleted) + assert remaining == {"subject": {(2,): {"subject_id": 2}}, "session": {(2, 1): db["session"][(2, 1)]}} + + +def test_K2_self_referencing_table(): + """ + employee.manager_id references employee.emp_id (nullable): rows form chains. + + The model follows the chain in both directions. DataJoint's dependency graph + must be acyclic, so this table cannot be declared: Q has a self-loop and + ``topo_sort`` raises. + """ + schema = Schema( + [TableSpec("employee", ("emp_id",), ("manager_id",))], + [FK("employee", "employee", {"manager_id": "emp_id"}, nullable=True)], + ) + g = graph(schema, {"employee": [(1, None), (2, 1), (3, 2), (4, 1)]}) + assert g.D({r("employee", 1)}) == {r("employee", 1), r("employee", 2), r("employee", 3), r("employee", 4)} + assert g.D({r("employee", 2)}) == {r("employee", 2), r("employee", 3)} + assert g.U({r("employee", 3)}) == {r("employee", 3), r("employee", 2), r("employee", 1)} + assert g.U({r("employee", 4)}) == {r("employee", 4), r("employee", 1)} + assert not q_acyclic(schema) + with pytest.raises(nx.NetworkXUnfeasible): + dependency_graph(schema).topo_sort() + + +def test_K5_empty_seed(): + """An empty seed (``False``, ``[]``, a condition that matches nothing) expands to nothing.""" + g = graph(g1_schema(), G1_DATA) + assert g.D(set()) == set() + assert g.D1(set()) == set() + assert g.U(set()) == set() + assert g.both(set()) == set() + + +def test_K6_unrestricted_seed(): + """ + Every stimulus row as the seed. + + Both stimuli reach all trials, which lift sessions 1 and 2. Session 3 has no + trials and is untouched. + """ + g = graph(g1_schema(), G1_DATA) + assert g.D({r("stimulus", 1), r("stimulus", 2)}) == { + r("stimulus", 1), + r("stimulus", 2), + r("session__trial", 1, 1), + r("session__trial", 1, 2), + r("session__trial", 2, 1), + r("session", 1), + r("session", 2), + } + assert g.U({r("session__trial", 1, 1), r("session__trial", 1, 2), r("session__trial", 2, 1)}) == { + r("session__trial", 1, 1), + r("session__trial", 1, 2), + r("session__trial", 2, 1), + r("session", 1), + r("session", 2), + r("stimulus", 1), + r("stimulus", 2), + } diff --git a/tests/unit/test_expand_spec.py b/tests/unit/test_expand_spec.py new file mode 100644 index 000000000..1bd391822 --- /dev/null +++ b/tests/unit/test_expand_spec.py @@ -0,0 +1,749 @@ +""" +Property tests of the ``Diagram.expand`` reference model against the specification. + +The specification states eight results, T1-T8, about the operations ``D``, +``D1``, ``U`` and ``both`` (see ``expand_model.py``). Each one is tested here on +random schemas and databases drawn by Hypothesis, with no database server: + +- T1 closure: D and U are extensive, monotone and idempotent. +- T2 additivity: ``X(A ∪ B) = X(A) ∪ X(B)`` for D, U and both. +- T3 duality: ``y ∈ D({x})`` if and only if ``x ∈ U({y})``. +- T4 delete safety: removing D(S) leaves a consistent database consistent, and + D(S) is the least such superset of S. +- T5 read boundary: U(S) is consistent on its own and is the least such superset. +- T6 both: U(D(S)) is up-closed, group-closed and contains D(S); ``both`` is + not idempotent. +- T7 enforce: D1(S) is lift-closed if and only if D1(S) = D(S). +- T8 ordering: under A1, ``datajoint.dependencies.topo_sort`` succeeds if and + only if the group quotient Q is acyclic, and its order is valid. + +The tests of ``self.upstream`` for a row not inserted yet come last. + +Drawn schemas may contain group cycles (Q cyclic), which E1b refuses at +declaration. T1-T7 and the ``self.upstream`` properties are stated for acyclic Q +and discard the other schemas with ``assume``; T8 tests both cases. Select the +number of examples with ``HYPOTHESIS_PROFILE`` (see ``tests/conftest.py``). +""" + +from __future__ import annotations + +import itertools +from dataclasses import dataclass, field + +import networkx as nx +import pytest +from hypothesis import assume, find, given, settings +from hypothesis import strategies as st + +from datajoint.dependencies import extract_master +from datajoint.errors import DataJointError + +from .expand_model import ( + DB, + FK, + RowGraph, + Schema, + TableSpec, + all_rows, + apply_delete, + dependency_graph, + fk_value, + full_table_name, + group_whole, + make_db, + q_acyclic, + ri_ok, + subdatabase, + virtual_upstream, +) + +#: Attribute values are drawn from this many small integers, so that keys collide. +DOMAIN = 3 +#: Tier prefixes of DataJoint table names: manual, lookup, imported, computed. +TIER_PREFIXES = ("", "#", "_", "__") + + +# ============================================================================= +# Generators +# ============================================================================= + + +class _TableBuilder: + """Collects the attributes and FKs of one table while it is drawn.""" + + def __init__(self, name: str, master: str | None = None) -> None: + self.name = name + self.master = master + self.pk: list[str] = [] + self.secondary: list[str] = [] + self.fks: list[FK] = [] + + def has(self, attr: str) -> bool: + return attr in self.pk or attr in self.secondary + + def add_fk(self, parent: TableSpec, primary: bool, renames: dict, nullable: bool = False) -> None: + # An FK adds only the attributes not declared yet, as in declare.py, so + # unrenamed attributes shared with an earlier FK are reused. + attr_map = {} + for attr in parent.pk: + name = renames.get(attr, attr) + attr_map[name] = attr + if not self.has(name): + (self.pk if primary else self.secondary).append(name) + if any(f.parent == parent.name and dict(f.attr_map) == attr_map for f in self.fks): + return # an identical FK adds nothing + self.fks.append(FK(self.name, parent.name, attr_map, nullable=nullable and not primary)) + + def spec(self) -> TableSpec: + return TableSpec(self.name, tuple(self.pk), tuple(self.secondary), self.master) + + +def _draw_renames(draw, parent: TableSpec, fresh: itertools.count, keep: tuple = ()) -> dict: + """Rename none or some of the parent's key attributes, never those in ``keep``.""" + candidates = [a for a in parent.pk if a not in keep] + if not candidates or not draw(st.booleans()): + return {} + renamed = draw(st.sets(st.sampled_from(candidates), min_size=1)) + return {a: f"{a}_r{next(fresh)}" for a in sorted(renamed)} + + +def _descendants(fks: list[FK], names: set[str]) -> set[str]: + """Tables that reference any of ``names``, directly or through other tables.""" + found, frontier = set(), set(names) + while frontier: + frontier = {fk.child for fk in fks if fk.parent in frontier} - found - names + found |= frontier + return found + + +def _draw_fks(draw, tables, fks, fresh, n, solid, group=None) -> list: + """ + Draw ``n`` FKs from the table being built to tables declared earlier. + + A uniform choice of parent rarely makes the shapes that matter, so the + parent is drawn from one of several pools: every table; tables whose key + shares some but not all of its attributes with this table's non-null + attributes ``solid`` (diamonds and, for a nullable FK, partial NULLs); and, + for a part, its own group (renamed references to the master or a sibling), + the parts of other groups, and the tables downstream of its group (group + cycles). + """ + drawn = [] + solid, known = set(solid), set(solid) + for _ in range(n): + kind = draw(st.sampled_from(["primary", "secondary", "nullable"])) + sharing = [t for t in tables if set(t.pk) & solid and not set(t.pk) <= known] + pools = [tables, sharing] + if kind == "nullable": + pools.append(sharing) # twice as likely, to make partial NULLs + if group is not None: + master = group[0] + pools.append([t for t in tables if t.name in group]) + pools.append([t for t in tables if t.master and t.master != master]) + downstream = _descendants(fks, set(group)) + pools.append([t for t in tables if t.name in downstream]) + parent = draw(st.sampled_from(draw(st.sampled_from([p for p in pools if p])))) + renames = _draw_renames(draw, parent, fresh) + attrs = {renames.get(a, a) for a in parent.pk} + if kind != "nullable": + solid |= attrs - known + known |= attrs + drawn.append((parent, kind == "primary", renames, kind == "nullable")) + return drawn + + +def _finish(draw, builder: _TableBuilder, tables: list[TableSpec], fks: list, own_pk: str, force_own: bool) -> TableSpec: + # Primary FKs first, then the table's own key attribute, then secondary FKs, + # as a definition lists them above and below the --- line. + for parent, primary, renames, nullable in fks: + if primary: + builder.add_fk(parent, True, renames) + # Without its own attribute, a table's key is its parents' (one-to-one, as in S1). + if force_own or not builder.pk or draw(st.sampled_from([True, True, False])): + builder.pk.append(own_pk) + for parent, primary, renames, nullable in fks: + if not primary: + builder.add_fk(parent, False, renames, nullable) + # Sometimes add a namesake: an attribute that shares a name with another + # table's key attribute but belongs to no FK. + namesakes = sorted({a for t in tables for a in t.pk if not builder.has(a)}) + if namesakes and draw(st.booleans()): + builder.secondary.append(draw(st.sampled_from(namesakes))) + return builder.spec() + + +@st.composite +def schemas(draw, max_tables: int = 7, a1_only: bool = False) -> Schema: + """ + Draw a schema: tables in declaration order, so the FK graph is a DAG. + + - Each step declares a plain table, a master, or a part of a master declared + earlier, so parts can be added late, after other tables. + - A master has 0-3 parts. A part's owning reference is direct (``-> master`` + in the primary key), through a sibling part, secondary, or (unless + ``a1_only``) it violates A1: it is missing, renamed or nullable. + - Other FKs are primary or secondary, nullable, renamed and parallel, to any + table declared earlier. Diamonds that share attributes, part -> part across + groups, renamed references within a group and group cycles all occur. + - Some tables get a namesake attribute that belongs to no FK. + """ + fresh = itertools.count() + tables: list[TableSpec] = [] + fks: list[FK] = [] + parts_of: dict[str, list[str]] = {} + a1_parts: dict[str, list[str]] = {} # parts whose owning reference satisfies A1 by construction + for i in range(draw(st.integers(min(2, max_tables), max_tables))): + by_name = {t.name: t for t in tables} + open_masters = [m for m, parts in parts_of.items() if len(parts) < 3] + kind = draw(st.sampled_from(["plain", "master"] + (["part", "part"] if open_masters else []))) + if kind != "part": + builder = _TableBuilder(f"{draw(st.sampled_from(TIER_PREFIXES))}t{i}") + extra = _draw_fks(draw, tables, fks, fresh, draw(st.integers(0, 3)) if tables else 0, set()) + spec = _finish(draw, builder, tables, extra, own_pk=f"k{i}", force_own=False) + if kind == "master": + parts_of[spec.name], a1_parts[spec.name] = [], [] + else: + master = draw(st.sampled_from(open_masters)) + mspec = by_name[master] + builder = _TableBuilder(f"{master}__p{len(parts_of[master])}", master=master) + owners = [s for s in a1_parts[master] if set(mspec.pk) <= set(by_name[s].pk)] + modes = ["direct", "secondary"] + (["sibling"] if owners else []) + ([] if a1_only else ["violate"]) + mode = draw(st.sampled_from(modes)) + how = None + owning = [] # the owning reference, when it is a secondary FK + if mode == "direct": + builder.add_fk(mspec, True, {}) + elif mode == "sibling": + sibling = by_name[draw(st.sampled_from(owners))] + builder.add_fk(sibling, True, _draw_renames(draw, sibling, fresh, keep=mspec.pk)) + elif mode == "secondary": + owning = [(mspec, False, {}, False)] + else: + how = draw(st.sampled_from(["missing", "renamed", "nullable"])) + if how == "renamed": + builder.add_fk(mspec, True, {a: f"{a}_r{next(fresh)}" for a in mspec.pk}) + elif how == "nullable": + owning = [(mspec, False, {}, True)] + solid = set(builder.pk) | {a for o in owning if not o[3] for a in o[0].pk} + group = [master] + parts_of[master] + extra = _draw_fks(draw, tables, fks, fresh, draw(st.integers(0, 3)), solid, group) + # With a secondary owning reference the master key is not in the + # primary key, so the part needs a key attribute of its own. + spec = _finish(draw, builder, tables, owning + extra, own_pk=f"k{i}", force_own=bool(owning)) + parts_of[master].append(spec.name) + if mode != "violate": + a1_parts[master].append(spec.name) + tables.append(spec) + fks.extend(builder.fks) + schema = Schema(tables, fks) + if a1_only: + assert all(t.name in schema.group for t in schema.tables if t.master), schema + return schema + + +def _draw_row(draw, schema: Schema, db: DB, table: TableSpec) -> dict | None: + """Draw one row whose FK values name existing parent rows or are NULL; None if no row fits.""" + row = {} + nullable = schema.nullable[table.name] + for fk in schema.fks_of(table.name): + pairs = list(fk.attr_map.items()) + if any(row.get(c, 0) is None for c, _ in pairs): + continue # an earlier FK set a shared attribute to NULL, so this FK references nothing + matches = [p for p in db[fk.parent].values() if all(c not in row or row[c] == p[a] for c, a in pairs)] + can_be_null = any(c in nullable and c not in row for c, _ in pairs) + options = len(matches) + can_be_null + if not options: + return None + choice = draw(st.integers(0, options - 1)) + if choice < len(matches): + row.update({c: matches[choice][a] for c, a in pairs}) + else: + # NULL in the attributes this FK introduced; shared key attributes keep values (partial NULL). + row.update({c: None for c, _ in pairs if c in nullable and c not in row}) + for attr in table.heading: + if attr not in row: + row[attr] = draw(st.integers(0, DOMAIN - 1)) + return row + + +@st.composite +def databases(draw, schema: Schema, max_rows: int = 4) -> DB: + """Draw a consistent database: tables filled in declaration order over small integer domains.""" + db = {} + for table in schema.tables: + rows = {} + for _ in range(draw(st.integers(1, max_rows))): + row = _draw_row(draw, schema, db, table) + if row is not None: + rows.setdefault(tuple(row[a] for a in table.pk), row) + db[table.name] = rows + return db + + +@dataclass +class Case: + """A drawn schema and database, with the row graph built once.""" + + schema: Schema + db: DB + graph: RowGraph = field(repr=False) + + def __repr__(self) -> str: + rows = {t: list(r.values()) for t, r in self.db.items() if r} + return f"Case(\nschema={self.schema!r},\ndb={rows!r})" + + +@st.composite +def cases(draw, max_tables: int = 7, max_rows: int = 4, acyclic: bool = True, max_total: int | None = None) -> Case: + """Draw a schema and a database. With ``acyclic``, schemas whose Q has a cycle are discarded.""" + schema = draw(schemas(max_tables)) + if acyclic: + assume(q_acyclic(schema)) + db = draw(databases(schema, max_rows)) + if max_total is not None: + assume(len(all_rows(db)) <= max_total) + return Case(schema, db, RowGraph(schema, db)) + + +def seeds(rows) -> st.SearchStrategy: + """Draw a set of seed rows from ``rows``.""" + rows = sorted(rows) + return st.frozensets(st.sampled_from(rows)) if rows else st.just(frozenset()) + + +#: Tiny databases for the brute-force minimality checks (at most 2^8 supersets). +tiny_cases = cases(max_tables=4, max_rows=2, max_total=8) + + +def supersets(rows, seed): + """Every subset of ``rows`` that contains ``seed``.""" + rest = sorted(set(rows) - set(seed)) + for k in range(len(rest) + 1): + for extra in itertools.combinations(rest, k): + yield frozenset(seed) | frozenset(extra) + + +# ============================================================================= +# The generators and the model +# ============================================================================= + + +@given(case=cases(acyclic=False)) +def test_generated_databases_are_consistent(case): + """Every drawn database satisfies P1 and the static half of P2.""" + assert ri_ok(case.schema, case.db) + assert group_whole(case.schema, case.db) + + +@given(case=cases(acyclic=False)) +def test_up_is_the_converse_of_down(case): + """``s ∈ down(r)`` if and only if ``r ∈ up(s)``.""" + g = case.graph + for r in g.rows: + for s in g.down(r): + assert r in g.up(s) + for s in g.up(r): + assert r in g.down(s) + + +def _has_partial_null(case: Case) -> bool: + for fk in case.schema.fks: + for row in case.db[fk.child].values(): + values = [row[c] for c in fk.attr_map] + if None in values and any(v is not None for v in values): + return True + return False + + +def _crosses_instances(case: Case) -> bool: + # A part row references a row of its own group in another master instance, + # through an FK that renames the master key. + g = case.graph + for r in g.rows: + for p in g.parent(r): + mr, mp = g.master(r), g.master(p) + if mr and mp and mr[0] == mp[0] and mr != mp: + return True + return False + + +def _group_parts(s: Schema): + return [t for t in s.tables if t.master and t.name in s.group] + + +def _is_owning_direct(s: Schema, fk: FK) -> bool: + return fk.parent == s.table(fk.child).master and all(fk.attr_map.get(a) == a for a in s.table(fk.parent).pk) + + +def _renames_master_key(s: Schema, fk: FK) -> bool: + master = s.group.get(fk.child) + return ( + master is not None + and fk.child != master + and s.group.get(fk.parent) == master + and any(c != p for c, p in fk.attr_map.items() if p in s.table(master).pk) + ) + + +SCHEMA_FEATURES = { + "group cycle": lambda s: not q_acyclic(s), + "part violating A1": lambda s: any(t.master and t.name not in s.group for t in s.tables), + "owning reference through a sibling": lambda s: any( + not any(_is_owning_direct(s, fk) for fk in s.fks_of(t.name)) for t in _group_parts(s) + ), + "secondary owning reference": lambda s: any(not set(s.table(t.master).pk) <= set(t.pk) for t in _group_parts(s)), + "renamed FK to the own master": lambda s: any( + _renames_master_key(s, fk) and fk.parent == s.group[fk.child] for fk in s.fks + ), + "sibling FK renaming the master key": lambda s: any( + _renames_master_key(s, fk) and fk.parent != s.group[fk.child] for fk in s.fks + ), + "part -> part across groups": lambda s: any( + s.table(fk.child).master and s.table(fk.parent).master and s.table(fk.child).master != s.table(fk.parent).master + for fk in s.fks + ), + "parallel FKs": lambda s: any(a.child == b.child and a.parent == b.parent for a, b in itertools.combinations(s.fks, 2)), + "diamond sharing an attribute": lambda s: any( + a.child == b.child and a.parent != b.parent and set(a.attr_map) & set(b.attr_map) + for a, b in itertools.combinations(s.fks, 2) + ), + "nullable FK": lambda s: any(fk.nullable for fk in s.fks), + "namesake attribute": lambda s: any( + a in u.pk + for t in s.tables + for a in t.secondary + if not any(a in fk.attr_map for fk in s.fks_of(t.name)) + for u in s.tables + if u is not t + ), +} + +DATA_FEATURES = { + "partial NULL": _has_partial_null, + "reference across master instances": _crosses_instances, +} + +_FIND_SETTINGS = settings(max_examples=5000, database=None, derandomize=True, deadline=None) + + +@pytest.mark.parametrize("feature", sorted(SCHEMA_FEATURES)) +def test_generator_draws_schema_feature(feature): + """The schema generator reaches every shape that the properties rely on.""" + find(schemas(), SCHEMA_FEATURES[feature], settings=_FIND_SETTINGS) + + +@pytest.mark.parametrize("feature", sorted(DATA_FEATURES)) +def test_generator_draws_data_feature(feature): + """The data generator reaches partial NULLs and references across master instances.""" + find(cases(acyclic=False), DATA_FEATURES[feature], settings=_FIND_SETTINGS) + + +# ============================================================================= +# T1-T3 +# ============================================================================= + + +@given(case=cases(), data=st.data()) +def test_t1_closure(case, data): + """T1: D and U are extensive, monotone and idempotent.""" + g = case.graph + b = data.draw(seeds(g.rows), label="B") + a = data.draw(seeds(b), label="A ⊆ B") + for op in (g.D, g.U): + assert a <= op(a) + assert op(a) <= op(b) + assert op(op(a)) == op(a) + + +@given(case=cases(), data=st.data()) +def test_t2_additivity(case, data): + """T2: X(A ∪ B) = X(A) ∪ X(B) for X in D, U and both.""" + g = case.graph + a = data.draw(seeds(g.rows), label="A") + b = data.draw(seeds(g.rows), label="B") + for op in (g.D, g.U, g.both): + assert op(a | b) == op(a) | op(b) + + +@given(case=cases()) +def test_t3_duality(case): + """T3: y ∈ D({x}) if and only if x ∈ U({y}).""" + g = case.graph + up = {y: g.U({y}) for y in g.rows} + for x in g.rows: + down = g.D({x}) + for y in g.rows: + assert (y in down) == (x in up[y]), (x, y) + + +# ============================================================================= +# T4-T5: delete safety and the read boundary +# ============================================================================= + + +@given(case=cases(), data=st.data()) +def test_t4_delete_safety(case, data): + """T4: removing D(S) leaves the database consistent and takes every master-part instance whole.""" + g = case.graph + s = data.draw(seeds(g.rows), label="S") + d = g.D(s) + after = apply_delete(case.db, d) + assert ri_ok(case.schema, after) + assert group_whole(case.schema, after) + assert g.group_closed(d) + + +@given(case=tiny_cases, data=st.data()) +def test_t4_minimality(case, data): + """T4: D(S) is the least superset of S whose removal is safe.""" + g = case.graph + s = data.draw(seeds(g.rows), label="S") + safe = [x for x in supersets(g.rows, s) if ri_ok(case.schema, apply_delete(case.db, x)) and g.group_closed(x)] + d = g.D(s) + assert d in safe + assert all(d <= x for x in safe) + + +@given(case=cases(), data=st.data()) +def test_t5_read_boundary(case, data): + """T5: U(S) is consistent on its own and holds every master-part instance it touches whole.""" + g = case.graph + s = data.draw(seeds(g.rows), label="S") + u = g.U(s) + export = subdatabase(case.db, u) + assert ri_ok(case.schema, export) + assert group_whole(case.schema, export) + assert g.group_closed(u) + + +@given(case=tiny_cases, data=st.data()) +def test_t5_minimality(case, data): + """T5: U(S) is the least superset of S that is consistent on its own.""" + g = case.graph + s = data.draw(seeds(g.rows), label="S") + consistent = [x for x in supersets(g.rows, s) if ri_ok(case.schema, subdatabase(case.db, x)) and g.group_closed(x)] + u = g.U(s) + assert u in consistent + assert all(u <= x for x in consistent) + + +# ============================================================================= +# T6-T7: both and enforce +# ============================================================================= + + +@given(case=cases(), data=st.data()) +def test_t6_both(case, data): + """T6: U(D(S)) is up-closed, group-closed and contains D(S).""" + g = case.graph + s = data.draw(seeds(g.rows), label="S") + b = g.both(s) + assert g.D(s) <= b + assert g.U(b) == b + assert g.group_closed(b) + assert b <= g.both(b) # extensive, though not idempotent (next test) + + +def test_t6_both_is_not_idempotent(): + """T6: A -> B and A -> C with S = {b} give both(S) = {a, b}, then both(both(S)) = {a, b, c}.""" + schema = Schema( + [TableSpec("a", ("a",)), TableSpec("b", ("b",), ("a",)), TableSpec("c", ("c",), ("a",))], + [FK("b", "a", {"a": "a"}), FK("c", "a", {"a": "a"})], + ) + db = make_db(schema, {"a": [(1,)], "b": [(1, 1)], "c": [(1, 1)]}) + g = RowGraph(schema, db) + a, b, c = ("a", (1,)), ("b", (1,)), ("c", (1,)) + assert g.both({b}) == {a, b} + assert g.both(g.both({b})) == {a, b, c} + + +@given(case=cases(), data=st.data()) +def test_t7_enforce(case, data): + """T7: D1(S) is lift-closed if and only if D1(S) = D(S).""" + g = case.graph + s = data.draw(seeds(g.rows), label="S") + d1, d = g.D1(s), g.D(s) + assert d1 <= d + assert g.lift_closed(d1) == (d1 == d) + + +# ============================================================================= +# T8: topo_sort and the group quotient +# ============================================================================= + + +def assert_valid_order(schema: Schema, order: list[str], quote: str) -> None: + """Every parent precedes its children, and each master is followed at once by its parts.""" + name = {t.name: full_table_name(t.name, quote) for t in schema.tables} + assert sorted(order) == sorted(name.values()) + pos = {n: i for i, n in enumerate(order)} + for fk in schema.fks: + assert pos[name[fk.parent]] < pos[name[fk.child]], fk + for master, parts in schema.a1_parts.items(): + block = order[pos[name[master]] : pos[name[master]] + 1 + len(parts)] + assert set(block) == {name[master]} | {name[p] for p in parts}, (master, order) + + +@pytest.mark.parametrize("quote", ["`", '"']) +@given(schema=schemas(a1_only=True)) +def test_t8_topo_sort(quote, schema): + """ + T8: under A1, topo_sort succeeds if and only if Q is acyclic, and its order is valid. + + The graph is a ``Dependencies`` object filled the way ``Dependencies.load`` + fills it, and sorted by ``datajoint.dependencies.topo_sort``. Both MySQL and + PostgreSQL quoting are used, since ``extract_master`` recognizes parts by name. + """ + graph = dependency_graph(schema, quote) + if q_acyclic(schema): + assert_valid_order(schema, graph.topo_sort(), quote) + else: + # E1b will replace NetworkXUnfeasible with a DataJointError that names the cycle. + with pytest.raises((nx.NetworkXUnfeasible, DataJointError)): + graph.topo_sort() + + +@given(schema=schemas()) +def test_model_part_names_follow_datajoint(schema): + """``extract_master`` finds the master of every part in the model, and of no other table.""" + for quote in ("`", '"'): + for t in schema.tables: + expected = full_table_name(t.master, quote) if t.master else None + assert extract_master(full_table_name(t.name, quote)) == expected + + +def _violating_part_schema(*, nullable_owner: bool) -> Schema: + if not nullable_owner: + # M; X references M; part P of M has no reference to M and references X. + return Schema( + [TableSpec("m", ("m",)), TableSpec("x", ("x",), ("m",)), TableSpec("m__p", ("p",), ("x",), master="m")], + [FK("x", "m", {"m": "m"}), FK("m__p", "x", {"x": "x"})], + ) + # M; part P1 references M through a nullable FK (violates A1); part P2 references M and P1. + return Schema( + [ + TableSpec("m", ("m",)), + TableSpec("m__p1", ("p1",), ("m",), master="m"), + TableSpec("m__p2", ("m", "p2"), ("p1",), master="m"), + ], + [FK("m__p1", "m", {"m": "m"}, nullable=True), FK("m__p2", "m", {"m": "m"}), FK("m__p2", "m__p1", {"p1": "p1"})], + ) + + +def test_t8_needs_a1_q_acyclic_but_topo_sort_fails(): + """ + Without A1, T8 fails in one direction: Q is acyclic but topo_sort raises. + + P violates A1, so the traversal treats it as a plain table and Q is + ``M -> X -> P``. ``topo_sort`` still recognizes P as a part by its name and + adds the edge ``X -> M``, which closes the cycle ``M -> X -> M``. + """ + schema = _violating_part_schema(nullable_owner=False) + assert schema.a1_parts == {"m": ()} + assert q_acyclic(schema) + with pytest.raises(nx.NetworkXUnfeasible): + dependency_graph(schema).topo_sort() + + +def test_t8_needs_a1_q_cyclic_but_topo_sort_succeeds(): + """ + Without A1, T8 fails in the other direction: Q has a cycle but topo_sort succeeds. + + P1 references M through a nullable FK, so it violates A1 and is its own node + in Q. Q is then ``{M, P2} -> P1 -> {M, P2}``, a cycle, so Q has no + topological order for the one-pass plan to follow: a row of P1 belongs to + one master instance by its FK and is referenced by a part row of another. + ``topo_sort`` sees both P1 and P2 as parts of M by name, adds no edge, and + succeeds. The E1b rule as planned allows this schema, since P1 references + its master and P2 references its master and a sibling part. + """ + schema = _violating_part_schema(nullable_owner=True) + assert schema.a1_parts == {"m": ("m__p2",)} + assert not q_acyclic(schema) + # topo_sort keeps the parts by name after their master, so P1 sits inside the block of M. + assert dependency_graph(schema).topo_sort() == [full_table_name(n) for n in ("m", "m__p1", "m__p2")] + # The rows show the cycle: P1 row 5 belongs to master 1 by its FK, and P2 row (2, 1) of master 2 references it. + db = make_db(schema, {"m": [(1,), (2,)], "m__p1": [(5, 1)], "m__p2": [(2, 1, 5)]}) + g = RowGraph(schema, db) + assert g.D({("m", (1,))}) == {("m", (1,)), ("m__p1", (5,)), ("m__p2", (2, 1)), ("m", (2,))} + + +# ============================================================================= +# self.upstream for a row not inserted yet +# ============================================================================= + + +def _rows_outside_parts(case: Case): + # make() fills a plain table or a master (with its parts), never a part on its own. + return sorted(r for r in case.graph.rows if case.schema.table(r[0]).master is None) + + +@given(case=cases(), data=st.data()) +def test_virtual_upstream_does_not_need_the_row(case, data): + """ + ``self.upstream`` is the same before and after the row is inserted. + + Before the insert, the row, its parts and its descendants are absent: + ``db - D({r})``. Its upstream is built from its primary FKs alone. + """ + rows = _rows_outside_parts(case) + assume(rows) + r = data.draw(st.sampled_from(rows), label="r") + table = case.schema.table(r[0]) + key = dict(zip(table.pk, r[1])) + before = apply_delete(case.db, case.graph.D({r})) + upstream = virtual_upstream(case.schema, before, table.name, key) + assert upstream == virtual_upstream(case.schema, case.db, table.name, key) + assert r not in upstream + + +@given(case=cases(), data=st.data()) +def test_virtual_upstream_matches_trace_of_the_inserted_row(case, data): + """ + Once the row exists, ``U({r})`` contains ``self.upstream``. They are equal, + apart from r itself, when r is in no group and all its FKs are primary. + """ + rows = _rows_outside_parts(case) + assume(rows) + r = data.draw(st.sampled_from(rows), label="r") + schema, g = case.schema, case.graph + table = schema.table(r[0]) + upstream = virtual_upstream(schema, case.db, table.name, dict(zip(table.pk, r[1]))) + traced = g.U({r}) + assert upstream <= traced + if table.name not in schema.group and all(schema.is_primary(fk) for fk in schema.fks_of(table.name)): + assert upstream == traced - {r} + + +def test_virtual_upstream_of_a_row_not_inserted(): + """U(∅) = ∅, so the upstream of a row not inserted yet comes from its primary FKs.""" + schema = Schema( + [ + TableSpec("subject", ("subject_id",)), + TableSpec("rig", ("rig_id",)), + TableSpec("session", ("subject_id", "session_id"), ("rig_id",)), + TableSpec("analysis", ("subject_id", "session_id")), + ], + [ + FK("session", "subject", {"subject_id": "subject_id"}), + FK("session", "rig", {"rig_id": "rig_id"}), + FK("analysis", "session", {"subject_id": "subject_id", "session_id": "session_id"}), + ], + ) + db = make_db(schema, {"subject": [(1,), (2,)], "rig": [(7,)], "session": [(1, 1, 7), (2, 1, 7)]}) + assert RowGraph(schema, db).U(set()) == set() + assert virtual_upstream(schema, db, "analysis", {"subject_id": 1, "session_id": 1}) == { + ("session", (1, 1)), + ("subject", (1,)), + ("rig", (7,)), # reached through session's secondary FK, which is known + } + # A key with no matching parent row has an empty upstream. + assert virtual_upstream(schema, db, "analysis", {"subject_id": 1, "session_id": 9}) == set() + + +def test_fk_value_follows_match_simple(): + """A2: an FK value with any NULL names nothing, even when its other attributes match a row.""" + fk = FK("child", "parent", {"a": "a", "b": "b"}, nullable=True) + schema = Schema([TableSpec("parent", ("a", "b")), TableSpec("child", ("a", "c"), ("b",))], [fk]) + assert fk_value(schema, fk, {"a": 1, "b": None, "c": 1}) is None + assert fk_value(schema, fk, {"a": 1, "b": 2, "c": 1}) == (1, 2) From 38a83569f3b6f5718ed87f36420f4f029dd5a6de Mon Sep 17 00:00:00 2001 From: Dimitri Yatsenko Date: Sat, 17 Oct 2026 17:27:16 -0500 Subject: [PATCH 3/4] test(diagram): mirror the shape catalog and check the computation rules Rewrite tests/unit/test_expand_shapes_model.py to mirror the Traversal Shape Catalog (traversal-shapes.md) one to one: each shape's schema, rows and seeds, the rows it states, and its node set, including the tables listed as none. The catalog's claims about copies, relabeling, delete modes and the rounds of an iterating group are checked too. The seed evaluations that need a database (string seeds, Top, the warning) and X1 are left to the integration suite; X2's cycle in Q is checked. Extend the reference model: - inst(r) = {r} for a row in no group, as diagram.md defines it; - table_reach, the node set of an expanded diagram; - RowGraph.delete with the three part_integrity modes; - one_pass, the computation over Q evaluated on rows, with the iteration rule of diagram.md or the exact one; - condition semantics for the R1 copy rule and for relabeling keys; - Schema(..., a1="chain"), A1 generalized to owning chains with renames (#1429), with the owning attributes composed along the chain. In test_expand_spec.py, T1-T8 now run under both readings of A1, T1 and T2 include D1, and T6 checks monotonicity and minimality. New properties check the one pass, the copy rule and relabeling. Three strict xfails record where diagram.md's rules disagree with the definitions: a sibling FK that does not carry the master key links instances without renaming it; relabeling drops a key on a secondary attribute of the parent; and a NULL key value on a nullable FK relabels to the NULL child rows. --- tests/unit/expand_model.py | 444 ++++++++++-- tests/unit/test_expand_shapes_model.py | 898 ++++++++++++++----------- tests/unit/test_expand_spec.py | 585 ++++++++++++++-- 3 files changed, 1437 insertions(+), 490 deletions(-) diff --git a/tests/unit/expand_model.py b/tests/unit/expand_model.py index 7ad47f157..54c1352c9 100644 --- a/tests/unit/expand_model.py +++ b/tests/unit/expand_model.py @@ -2,9 +2,9 @@ Executable reference model of ``Diagram.expand``. This module is an executable copy of the traversal specification (the -normative section of ``diagram.md``). It holds rows in plain Python -dictionaries, needs no database, and defines the row sets that the engine -must produce: +"Traversal specification" section of ``diagram.md``). It holds rows in plain +Python dictionaries, needs no database, and defines the row sets that the +engine must produce: - ``D(S)``: the least superset of the seed rows ``S`` closed under ``down``. This is ``expand(seed, "down")`` and ``cascade(seed, part_integrity="cascade")``. @@ -19,6 +19,12 @@ theorems of the specification, and later test layers check the engine against the model. +The module also models parts of the specification's "Computation" section, so +that its claims can be checked against the definitions: the table-level reach +(the node set of an expanded diagram), the one pass over the group quotient with +its iterating groups, and the conditions under which R1 may copy a condition or +relabel materialized keys. + Model ----- - A schema is a list of tables in declaration order and a list of foreign keys. @@ -29,6 +35,7 @@ - A1, the owning reference: a part's heading contains its master's primary key, unrenamed and non-null, inherited through ``-> master`` directly or through a sibling part. A part that violates A1 is traversed as a plain table. + ``Schema(..., a1="chain")`` selects a generalized A1, described in ``Schema``. - A database is ``{table: {pk_tuple: row_dict}}``, where ``pk_tuple`` lists the primary-key values in the order of ``TableSpec.pk``. """ @@ -123,21 +130,37 @@ class Schema: Tables in declaration order. fks : iterable of FK Foreign keys. Each child's FKs are listed in declaration order. + a1 : {"strict", "chain"}, optional + Which owning reference makes a part a member of its master's group. + + - ``"strict"`` (the specification): an FK to the master, or to a sibling + that is a member, that carries the master's key unrenamed and non-null. + - ``"chain"`` (a generalization, for #1429): also a primary-key FK, with + renames allowed, to the master or to a member whose owning attributes + lie in its primary key. The owning attributes are then composed along + the chain. When a part has several such FKs, an unrenamed one wins, + and otherwise the first in declaration order. Notes ----- - Two facts are derived from the declarations: + Three facts are derived from the declarations: - **Nullable attributes.** A secondary attribute is nullable when the first FK that includes it is nullable, as in ``declare.py``, where an FK adds only the attributes not declared yet. Primary-key attributes and attributes outside every FK are never NULL. - - **Groups.** Each master forms a group with its parts that satisfy A1. - ``group[t]`` names the master of table ``t`` for the master itself and - for those parts. A part that violates A1 is in no group. + - **Groups.** Each master that has parts forms a group with its member + parts. ``group[t]`` names the master of table ``t`` for the master itself + and for its members. A part that is not a member is in no group. + - **Owning attributes.** ``owning[t]`` maps each attribute of the master's + primary key to the attribute of member ``t`` that holds it: the identity + for the master, and for every member under ``a1="strict"``. """ - def __init__(self, tables: Iterable[TableSpec], fks: Iterable[FK]) -> None: + def __init__(self, tables: Iterable[TableSpec], fks: Iterable[FK], a1: str = "strict") -> None: + if a1 not in ("strict", "chain"): + raise ValueError(f"a1 must be 'strict' or 'chain', not {a1!r}") + self.a1 = a1 self.tables = tuple(tables) self.fks = tuple(fks) self._by_name = {t.name: t for t in self.tables} @@ -147,12 +170,14 @@ def __init__(self, tables: Iterable[TableSpec], fks: Iterable[FK]) -> None: self.nullable = {t.name: self._nullable_attributes(t) for t in self.tables} self.a1_parts = {} self.group = {} + self.owning = {} for t in self.tables: parts = [p for p in self.tables if p.master == t.name] if parts: - self.a1_parts[t.name] = self._find_a1_parts(t, parts) - self.group[t.name] = t.name - self.group.update({p: t.name for p in self.a1_parts[t.name]}) + owning = self._find_members(t, parts) + self.a1_parts[t.name] = tuple(p.name for p in parts if p.name in owning) + self.owning.update(owning) + self.group.update({name: t.name for name in owning}) def __repr__(self) -> str: lines = ["Schema("] @@ -162,7 +187,8 @@ def __repr__(self) -> str: for fk in self.fks: null = ", nullable=True" if fk.nullable else "" lines.append(f" FK({fk.child!r}, {fk.parent!r}, {dict(fk.attr_map)!r}{null}),") - return "\n".join(lines) + "\n)" + a1 = "" if self.a1 == "strict" else f" a1={self.a1!r},\n" + return "\n".join(lines) + "\n" + a1 + ")" def table(self, name: str) -> TableSpec: """Return the table named ``name``.""" @@ -176,10 +202,20 @@ def is_primary(self, fk: FK) -> bool: """True if every attribute of the FK is in the child's primary key (as in ``Dependencies``).""" return set(fk.attr_map) <= set(self.table(fk.child).pk) - def master_key(self, table: str) -> tuple[str, ...] | None: - """Primary key of the group that ``table`` belongs to, or None for a table in no group.""" + def owning_attributes(self, table: str) -> tuple[str, ...] | None: + """Attributes of ``table`` that hold its master's key, in the master's key order; None if in no group.""" master = self.group.get(table) - return None if master is None else self.table(master).pk + if master is None: + return None + return tuple(self.owning[table][a] for a in self.table(master).pk) + + def intra_group_fks(self, master: str) -> list[FK]: + """FKs between two different tables of the group of ``master``.""" + return [ + fk + for fk in self.fks + if fk.child != fk.parent and self.group.get(fk.child) == master and self.group.get(fk.parent) == master + ] def _validate(self) -> None: declared = set() @@ -211,27 +247,45 @@ def _nullable_attributes(self, table: TableSpec) -> frozenset[str]: nullable.add(attr) return frozenset(nullable) - def _find_a1_parts(self, master: TableSpec, parts: list[TableSpec]) -> tuple[str, ...]: - # A part satisfies A1 if it has an FK to the master, or to a sibling that - # satisfies A1, that carries every master-key attribute unrenamed and - # non-null. Repeat until no new part qualifies, so that the result does - # not depend on the order in which the parts are listed. - owners = {master.name} - changed = True - while changed: - changed = False - for part in parts: - if part.name not in owners and any(self._is_owning(fk, master.pk, owners) for fk in self.fks_of(part.name)): - owners.add(part.name) - changed = True - return tuple(p.name for p in parts if p.name in owners) - - def _is_owning(self, fk: FK, master_key: tuple[str, ...], owners: set[str]) -> bool: - return ( - fk.parent in owners - and fk.parent != fk.child - and all(fk.attr_map.get(a) == a and a not in self.nullable[fk.child] for a in master_key) - ) + def _find_members(self, master: TableSpec, parts: list[TableSpec]) -> dict[str, dict[str, str]]: + # The strict owning reference is found first, to a fixed point, so that + # the result does not depend on the order in which the parts are listed. + # Under a1="chain", primary-key FKs with renames are then accepted for + # the parts left over. + owning = {master.name: {a: a for a in master.pk}} + rules = [self._strict_owning] + ([self._chain_owning] if self.a1 == "chain" else []) + for rule in rules: + changed = True + while changed: + changed = False + for part in parts: + if part.name in owning: + continue + for fk in self.fks_of(part.name): + found = rule(fk, master.pk, owning) + if found is not None: + owning[part.name] = found + changed = True + break + return owning + + def _strict_owning(self, fk: FK, master_key: tuple, owning: dict) -> dict | None: + # Unrenamed and non-null: the part holds the master's key under its names. + if fk.parent == fk.child or fk.parent not in owning or any(owning[fk.parent][a] != a for a in master_key): + return None + if all(fk.attr_map.get(a) == a and a not in self.nullable[fk.child] for a in master_key): + return {a: a for a in master_key} + return None + + def _chain_owning(self, fk: FK, master_key: tuple, owning: dict) -> dict | None: + # A primary-key FK to a member whose owning attributes it carries, renamed or not. + if fk.parent == fk.child or fk.parent not in owning or not self.is_primary(fk): + return None + by_parent_attr = {p: c for c, p in fk.attr_map.items()} + parent_owning = owning[fk.parent] + if not all(parent_owning[a] in by_parent_attr for a in master_key): + return None # the parent's owning attributes are not in its primary key + return {a: by_parent_attr[parent_owning[a]] for a in master_key} # ============================================================================= @@ -302,14 +356,14 @@ def master_of(schema: Schema, table: str, row: Mapping) -> Row | None: """ ``master(r)``: identifier of the master row of a row in a group. - The master row is named by the row's master-key attributes, which A1 - guarantees are present, unrenamed and non-null. Returns None for a row in no - group. The identifier is returned whether or not the master row exists. + The master row is named by the row's owning attributes, which A1 guarantees + are present and non-null. Returns None for a row in no group. The identifier + is returned whether or not the master row exists. """ - key_attrs = schema.master_key(table) - if key_attrs is None: + attrs = schema.owning_attributes(table) + if attrs is None: return None - return (schema.group[table], tuple(row[a] for a in key_attrs)) + return (schema.group[table], tuple(row[a] for a in attrs)) def apply_delete(db: DB, rows: Iterable[Row]) -> DB: @@ -388,11 +442,45 @@ def q_acyclic(schema: Schema) -> bool: return nx.is_directed_acyclic_graph(quotient(schema)) +def table_reach(schema: Schema, tables: Iterable[str], direction: str = "down", group_step: bool = True) -> frozenset: + """ + The node set of an expanded diagram: the table-level reach of ``tables``. + + The tables reached by following FKs in the direction of travel, together + with the master and all member parts of every group that has a reached + member. ``group_step=False`` leaves the group step out (``D1``). For + ``"both"``, it is the upward reach of the downward reach. + """ + if direction == "both": + return table_reach(schema, table_reach(schema, tables, "down", group_step), "up", group_step) + if direction not in ("down", "up"): + raise ValueError(f"direction must be 'down', 'up' or 'both', not {direction!r}") + seen = set(tables) + stack = list(seen) + while stack: + t = stack.pop() + if direction == "down": + nxt = {fk.child for fk in schema.fks if fk.parent == t} + else: + nxt = {fk.parent for fk in schema.fks if fk.child == t} + if group_step and t in schema.group: + master = schema.group[t] + nxt |= {master, *schema.a1_parts[master]} + for n in nxt - seen: + seen.add(n) + stack.append(n) + return frozenset(seen) + + # ============================================================================= # Row relations and the operations # ============================================================================= +class PartIntegrityError(Exception): + """``part_integrity="enforce"`` refuses a delete that would split an instance.""" + + class RowGraph: """ The row relations of one database and the closures over them. @@ -409,8 +497,8 @@ class RowGraph: - ``child(r) = {c : ref_e(c) = r}`` and ``parent(r) = {ref_e(r)}``, over every FK edge e. - ``inst(r)`` is the master-part instance of a row in a group: the master - row and every part row that carries the same master key. It is empty for - a row in no group. + row and every member part row that carries the same master key. For a row + in no group, ``inst(r) = {r}``. - ``down = child ∪ inst`` and ``up = parent ∪ inst``, so ``up`` is the converse of ``down``. """ @@ -457,8 +545,8 @@ def parent(self, r: Row) -> frozenset[Row]: return frozenset(self._parents[r]) def inst(self, r: Row) -> frozenset[Row]: - """The master-part instance of ``r``; empty for a row in no group.""" - return self._inst.get(r, frozenset()) + """The master-part instance of ``r``; ``{r}`` for a row in no group.""" + return self._inst.get(r, frozenset({r})) def master(self, r: Row) -> Row | None: """``master(r)`` for a row in a group, else None.""" @@ -501,6 +589,27 @@ def both(self, seed: Iterable[Row]) -> frozenset[Row]: """``expand(seed, "both") = U(D(seed))``.""" return self.U(self.D(seed)) + def delete(self, seed: Iterable[Row], part_integrity: str = "enforce") -> frozenset[Row]: + """ + The rows ``delete(part_integrity=...)`` removes. + + ``"cascade"`` deletes D(S) and ``"ignore"`` deletes D1(S). ``"enforce"`` + deletes D1(S) if it is lift-closed, and otherwise raises + ``PartIntegrityError`` before deleting anything, naming a part row and + its master. + """ + if part_integrity == "cascade": + return self.D(seed) + if part_integrity not in ("ignore", "enforce"): + raise ValueError(f"part_integrity must be 'enforce', 'ignore' or 'cascade', not {part_integrity!r}") + rows = self.D1(seed) + if part_integrity == "enforce": + for r in sorted(rows): + m = self._master.get(r) + if m is not None and m != r and m not in rows: + raise PartIntegrityError(f"{r} would be deleted without its master {m}") + return rows + # --- group predicates ---------------------------------------------------- def lift_closed(self, rows: Iterable[Row]) -> bool: @@ -519,6 +628,249 @@ def group_closed(self, rows: Iterable[Row]) -> bool: return all(self.inst(r) <= rows for r in rows) +# ============================================================================= +# The one pass over the group quotient +# ============================================================================= + + +def preserves_instance(schema: Schema, fk: FK) -> bool: + """ + True if an FK inside a group always references a row of the same instance. + + It does when it maps the child's owning attribute of each master-key + attribute onto the parent's owning attribute of the same one. + """ + master = schema.group[fk.child] + child_owning, parent_owning = schema.owning[fk.child], schema.owning[fk.parent] + return all(fk.attr_map.get(child_owning[a]) == parent_owning[a] for a in schema.table(master).pk) + + +def renames_master_key(schema: Schema, fk: FK) -> bool: + """The specification's test for an iterating group: the FK renames an attribute of the master's key.""" + parent_owning = set(schema.owning[fk.parent].values()) + return any(c != p for c, p in fk.attr_map.items() if p in parent_owning) + + +ITERATION_TESTS = { + "spec": renames_master_key, + "exact": lambda schema, fk: not preserves_instance(schema, fk), +} + + +def one_pass( + schema: Schema, db: DB, seed: Iterable[Row], direction: str = "down", iterate: str | None = "exact" +) -> tuple[frozenset[Row], dict]: + """ + The engine's computation, evaluated on rows: one visit per node of Q. + + The nodes of Q are visited in topological order for ``"down"`` and in + reverse order for ``"up"``. A plain table takes its seed rows and the rows + that R1 reaches from its neighbors on the incoming side. A group takes the + master keys of its seed rows and of the rows R1 reaches in any of its + tables, then all rows of those instances. A group that iterates repeats the + group step, following the FKs inside the group, until no new master key + appears. + + Parameters + ---------- + schema, db : Schema, DB + The schema, whose Q must be acyclic, and the database. + seed : iterable of Row + The seed rows. + direction : {"down", "up"} + The direction of travel. + iterate : {"exact", "spec"} or None + Which groups iterate: those with an FK inside the group that does not + preserve the instance (``"exact"``), those with an FK inside the group + that renames the master's key (``"spec"``, the rule in ``diagram.md``), + or none. + + Returns + ------- + rows : frozenset + The rows reached. They equal D(seed) or U(seed) when the computation is exact. + rounds : dict + For each group, the master keys after each group step. + """ + q = quotient(schema) + if not nx.is_directed_acyclic_graph(q): + raise ValueError("the group quotient has a cycle") + graph = RowGraph(schema, db) + seed = graph._check(seed) + order = list(nx.topological_sort(q)) + if direction == "up": + order.reverse() + node_of = {t.name: schema.group.get(t.name, t.name) for t in schema.tables} + members = defaultdict(list) + for t in schema.tables: + members[node_of[t.name]].append(t.name) + test = ITERATION_TESTS[iterate] if iterate else None + + def rows_of(table): + return {(table, k) for k in db[table]} + + def ref(fk, r): + return ref_e(schema, db, fk, db[r[0]][r[1]]) + + def crossing(fk, reached): + # R1 across one FK: children of reached parents (down) or parents of reached children (up). + if direction == "down": + return {c for c in rows_of(fk.child) if ref(fk, c) in reached} + return {p for c in reached if c[0] == fk.child for p in [ref(fk, c)] if p is not None} + + reached, rounds = set(), {} + for node in order: + mem = members[node] + incoming = {r for r in seed if r[0] in mem} + for fk in schema.fks: + inside = fk.child if direction == "down" else fk.parent + outside = fk.parent if direction == "down" else fk.child + if inside in mem and node_of[outside] != node: + incoming |= crossing(fk, reached) + if node not in schema.a1_parts: + reached |= incoming + continue + inner = schema.intra_group_fks(node) + iterates = test is not None and any(test(schema, fk) for fk in inner) + keys = {graph.master(r) for r in incoming} + history = [set(keys)] + while True: + instance = {r for t in mem for r in rows_of(t) if graph.master(r) in keys} + if not iterates: + break + step = set().union(*(crossing(fk, instance) for fk in inner)) + grown = keys | {graph.master(r) for r in step} + if grown == keys: + break + keys = grown + history.append(set(keys)) + rounds[node] = history + reached |= instance + return frozenset(reached), rounds + + +# ============================================================================= +# Conditions: R1 copies and relabeled keys +# ============================================================================= + + +@dataclass(frozen=True) +class Relation: + """ + A query expression used as a condition: rows over ``heading``. + + Restricting a table by it keeps the rows whose values on the shared + attributes appear in it; with no shared attribute, it keeps every row if it + has any rows. + """ + + heading: tuple[str, ...] + rows: frozenset = frozenset() + + +@dataclass(frozen=True) +class Not: + """ + The complement of a condition: ``table - cond``, or ``table & dj.Not(cond)``. + + The complement of a relation follows SQL's ``NOT IN``: a NULL on either side + makes it unknown, so the row is not kept. With several shared attributes, + any NULL counts as unknown, which is exact for one shared attribute. + """ + + cond: object + + +def condition_attributes(cond) -> frozenset[str]: + """The attributes a condition names: a dict's keys, a relation's heading.""" + if isinstance(cond, dict): + return frozenset(cond) + if isinstance(cond, (list, tuple)): + return frozenset().union(*(condition_attributes(c) for c in cond)) if cond else frozenset() + if isinstance(cond, Relation): + return frozenset(cond.heading) + if isinstance(cond, Not): + return condition_attributes(cond.cond) + raise TypeError(f"unsupported condition {cond!r}") + + +def satisfies(row: Mapping, heading: tuple[str, ...], cond) -> bool: + """ + Whether ``row`` of a table with ``heading`` passes the restriction by ``cond``. + + As in DataJoint: a dict compares the keys that are attributes of the table + and ignores the others, and None matches only NULL; a list is the OR of its + elements, so the empty list matches nothing. + """ + if isinstance(cond, dict): + return all(row[k] == v for k, v in cond.items() if k in heading) + if isinstance(cond, (list, tuple)): + return any(satisfies(row, heading, c) for c in cond) + if isinstance(cond, Relation): + shared = [i for i, a in enumerate(cond.heading) if a in heading] + if not shared: + return bool(cond.rows) + value = tuple(row[cond.heading[i]] for i in shared) + return None not in value and value in {tuple(r[i] for i in shared) for r in cond.rows} + if isinstance(cond, Not): + inner = cond.cond + if isinstance(inner, Relation): + shared = [i for i, a in enumerate(inner.heading) if a in heading] + if shared and inner.rows: + value = [row[inner.heading[i]] for i in shared] + if None in value or any(r[i] is None for r in inner.rows for i in shared): + return False # x NOT IN (..., NULL) is never true + return not satisfies(row, heading, inner) + raise TypeError(f"unsupported condition {cond!r}") + + +def restrict(schema: Schema, db: DB, table: str, cond) -> frozenset[Row]: + """The rows of ``table`` that pass the restriction by ``cond``.""" + heading = schema.table(table).heading + return frozenset((table, k) for k, row in db[table].items() if satisfies(row, heading, cond)) + + +def r1_down(schema: Schema, db: DB, fk: FK, parent_rows: Iterable[Row]) -> frozenset[Row]: + """R1 downward across ``fk``: the child rows that reference one of ``parent_rows``.""" + parents = set(parent_rows) + return frozenset((fk.child, k) for k, row in db[fk.child].items() if ref_e(schema, db, fk, row) in parents) + + +def r1_up(schema: Schema, db: DB, fk: FK, child_rows: Iterable[Row]) -> frozenset[Row]: + """R1 upward across ``fk``: the parent rows that one of ``child_rows`` references.""" + refs = {ref_e(schema, db, fk, db[fk.child][k]) for t, k in child_rows if t == fk.child} + return frozenset(refs - {None}) + + +def copy_allowed(schema: Schema, fk: FK, cond) -> bool: + """ + R1's rule for copying a parent's condition to the child unchanged. + + The attributes the condition names are among the FK's attributes, the FK is + not renamed, and none of its columns is nullable. + """ + return ( + condition_attributes(cond) <= set(fk.attr_map) and not fk.renamed and not set(fk.attr_map) & schema.nullable[fk.child] + ) + + +def relabel_down(fk: FK, keys: list[dict]) -> list[dict]: + """Relabel parent keys for the child: keep the keys the FK carries, renamed through ``attr_map``.""" + by_parent_attr = {p: c for c, p in fk.attr_map.items()} + return [{by_parent_attr[a]: v for a, v in key.items() if a in by_parent_attr} for key in keys] + + +def relabel_up(fk: FK, keys: list[dict]) -> list[dict]: + """Relabel child keys for the parent: keep the keys the FK carries, renamed through ``attr_map``.""" + return [{fk.attr_map[c]: v for c, v in key.items() if c in fk.attr_map} for key in keys] + + +def relabel_down_allowed(schema: Schema, fk: FK, keys: list[dict]) -> bool: + """The Seeds rule for relabeling downward: the FK has no nullable column, or every key names all its columns.""" + parent_attrs = set(fk.attr_map.values()) + return not set(fk.attr_map) & schema.nullable[fk.child] or all(parent_attrs <= set(key) for key in keys) + + # ============================================================================= # self.upstream for a row not inserted yet # ============================================================================= diff --git a/tests/unit/test_expand_shapes_model.py b/tests/unit/test_expand_shapes_model.py index 9824c2d06..979990750 100644 --- a/tests/unit/test_expand_shapes_model.py +++ b/tests/unit/test_expand_shapes_model.py @@ -1,20 +1,29 @@ """ -Shape catalog of ``Diagram.expand``, checked against the reference model. - -One small test per shape, with the expected rows written out by hand. The shape -IDs match the examples in the specification (``diagram.md``) one to one: - -- S1-S7, foreign-key shapes: identity, secondary, nullable with partial NULLs, - renamed, parallel, diamond with a shared key, diamond reached through a - secondary FK. -- G1-G6, master-part groups: the part -> master lift, part-of-part with two - chains, a second renamed FK to the master, a sibling FK that renames the master - key, the merge table, a part that violates A1. -- K1, K2, K5, K6, seeds: restricted by a descendant, a self-referencing table, - empty, unrestricted. - -K3, K4, X1 and X2 need a database and are tested in the integration suite. -Rows are written ``r(table, *primary_key_values)``. +The Traversal Shape Catalog, checked against the reference model. + +Each test mirrors one shape of ``traversal-shapes.md`` (datajoint-docs, +``src/reference/specs/``) under the same ID: the same schema, rows and seeds, +and the rows the catalog states, copied from it by hand. The node set of each +result (every table the catalog lists, including those marked *none*) is +checked as the table-level reach. Where the catalog's "Why" makes a claim the +model can express (a copy or a relabeling that would select other rows, the +delete modes, the rounds of an iterating group), that claim is checked too. + +A seed is written as the rows its condition selects, by hand. Where the model +can evaluate the condition (a dict, a list of dicts, a query expression, a +``Not``), the test also checks that it selects those rows. + +Left to the integration suite, because they need a database: + +- K3's string seeds (with and without a subquery) and the ``AndList`` with a + string; the seed kinds the model can express are checked here; +- K4's ``dj.Top`` evaluation; the expansion of the two rows it keeps is checked + here; +- K6's warning for a dict that names no attribute; +- X1, the unloaded downstream schema; +- X2's refusal at declaration; the cycle in Q that it refuses is checked here. + +Table names are DataJoint's: ``session__trial`` is ``Session.Trial``. """ import networkx as nx @@ -22,129 +31,183 @@ from .expand_model import ( FK, + Not, + PartIntegrityError, + Relation, RowGraph, Schema, TableSpec, apply_delete, + copy_allowed, dependency_graph, group_whole, make_db, + one_pass, q_acyclic, + r1_down, + relabel_down_allowed, + relabel_up, + renames_master_key, + restrict, ri_ok, + subdatabase, + table_reach, ) -def r(table, *key): - """A row identifier written by hand.""" - return (table, key) +def rows(spec: dict) -> frozenset: + """Row identifiers from ``{table: [key, ...]}``; a bare value is a one-attribute key.""" + return frozenset((t, k if isinstance(k, tuple) else (k,)) for t, keys in spec.items() for k in keys) -def graph(schema, data): - db = make_db(schema, data) - assert ri_ok(schema, db) and group_whole(schema, db), "the fixture itself must be consistent" - return RowGraph(schema, db) +class Shape: + """One shape: its schema and rows, and the operations on them.""" + + def __init__(self, tables, fks, data): + self.schema = Schema(tables, fks) + self.db = make_db(self.schema, data) + assert ri_ok(self.schema, self.db) and group_whole(self.schema, self.db), "the shape must be consistent" + self.g = RowGraph(self.schema, self.db) + + def seed(self, table, cond, selected): + """The seed rows, written by hand, after checking that ``table & cond`` selects them.""" + seed = rows({table: selected}) + if cond is not None: + assert restrict(self.schema, self.db, table, cond) == seed + return seed + + def check(self, table, seed, direction, expected): + """``expand(seed, direction)`` returns ``expected``: its rows, and a node for every table it lists.""" + op = {"down": self.g.D, "up": self.g.U, "both": self.g.both}[direction] + assert op(seed) == rows(expected) + assert table_reach(self.schema, {table}, direction) == set(expected) + + def fk(self, child, parent, *attrs): + """The FK from ``child`` to ``parent``, chosen by its child attributes when there are several.""" + (fk,) = [ + f + for f in self.schema.fks + if f.child == child and f.parent == parent and (not attrs or set(attrs) == set(f.attr_map)) + ] + return fk # ============================================================================= -# S: foreign-key shapes +# Foreign-key shapes # ============================================================================= def test_S1_identity_fk_in_primary_key(): - """An unrenamed FK inside the child's primary key: session -> subject.""" - schema = Schema( + """traversal-shapes.md, S1. Identity foreign key in the primary key.""" + s = Shape( [TableSpec("subject", ("subject_id",)), TableSpec("session", ("subject_id", "session_id"))], [FK("session", "subject", {"subject_id": "subject_id"})], + {"subject": [(1,), (2,)], "session": [(1, 1), (1, 2), (2, 1)]}, ) - g = graph(schema, {"subject": [(1,), (2,)], "session": [(1, 1), (1, 2), (2, 1)]}) - assert g.D({r("subject", 1)}) == {r("subject", 1), r("session", 1, 1), r("session", 1, 2)} - assert g.U({r("session", 1, 2)}) == {r("session", 1, 2), r("subject", 1)} + seed = s.seed("subject", {"subject_id": 1}, [1]) + s.check("subject", seed, "down", {"subject": [1], "session": [(1, 1), (1, 2)]}) + seed = s.seed("session", {"session_id": 1}, [(1, 1), (2, 1)]) + s.check("session", seed, "up", {"session": [(1, 1), (2, 1)], "subject": [1, 2]}) + # Why: the condition subject_id = 1 is copied to Session unchanged. + fk, cond = s.fk("session", "subject"), {"subject_id": 1} + assert copy_allowed(s.schema, fk, cond) + assert restrict(s.schema, s.db, "session", cond) == r1_down(s.schema, s.db, fk, rows({"subject": [1]})) def test_S2_secondary_fk(): - """An FK below the --- line: session references rig.""" - schema = Schema( - [TableSpec("rig", ("rig_id",)), TableSpec("session", ("session_id",), ("rig_id",))], - [FK("session", "rig", {"rig_id": "rig_id"})], + """traversal-shapes.md, S2. Secondary foreign key.""" + s = Shape( + [TableSpec("#rig", ("rig_id",)), TableSpec("session", ("session_id",), ("rig_id",))], + [FK("session", "#rig", {"rig_id": "rig_id"})], + {"#rig": [(1,), (2,), (3,)], "session": [(1, 1), (2, 1), (3, 2)]}, ) - g = graph(schema, {"rig": [(1,), (2,)], "session": [(1, 1), (2, 1), (3, 2)]}) - assert g.D({r("rig", 1)}) == {r("rig", 1), r("session", 1), r("session", 2)} - assert g.U({r("session", 3)}) == {r("session", 3), r("rig", 2)} + seed = s.seed("#rig", {"rig_id": 1}, [1]) + s.check("#rig", seed, "down", {"#rig": [1], "session": [1, 2]}) + seed = s.seed("session", {"session_id": 3}, [3]) + s.check("session", seed, "up", {"session": [3], "#rig": [2]}) + # Why: downward, rig_id = 1 names only the FK's own non-null attribute, so it is copied. + fk = s.fk("session", "#rig") + assert copy_allowed(s.schema, fk, {"rig_id": 1}) + # Why: relabeling the dict upward would drop session_id and select every rig. + relabeled = relabel_up(fk, [{"session_id": 3}]) + assert relabeled == [{}] + assert restrict(s.schema, s.db, "#rig", relabeled) == rows({"#rig": [1, 2, 3]}) def test_S3_nullable_fk_sharing_a_primary_key_column(): - """ - A nullable FK (lab_id, person_id) whose lab_id is also in the primary key. - - Assignment (1, 2) has lab_id = 1 and person_id = NULL: by A2 it references no - person, although its lab_id matches persons (1, 1) and (1, 2). It is still - reached from lab 1 through its own FK to lab. - """ - schema = Schema( + """traversal-shapes.md, S3. Nullable foreign key sharing a primary-key column.""" + s = Shape( [ - TableSpec("lab", ("lab_id",)), - TableSpec("person", ("lab_id", "person_id")), - TableSpec("assignment", ("lab_id", "task_id"), ("person_id",)), + TableSpec("subject", ("subject_id",)), + TableSpec("surgery", ("subject_id", "surgery_id")), + TableSpec("session", ("subject_id", "session_id"), ("surgery_id",)), ], [ - FK("person", "lab", {"lab_id": "lab_id"}), - FK("assignment", "lab", {"lab_id": "lab_id"}), - FK("assignment", "person", {"lab_id": "lab_id", "person_id": "person_id"}, nullable=True), + FK("surgery", "subject", {"subject_id": "subject_id"}), + FK("session", "subject", {"subject_id": "subject_id"}), + FK("session", "surgery", {"subject_id": "subject_id", "surgery_id": "surgery_id"}, nullable=True), ], - ) - assert schema.nullable["assignment"] == {"person_id"} - g = graph( - schema, { - "lab": [(1,), (2,)], - "person": [(1, 1), (1, 2), (2, 1)], - "assignment": [(1, 1, 1), (1, 2, None), (2, 1, 1)], + "subject": [(1,), (2,)], + "surgery": [(1, 1), (1, 2), (2, 1)], + "session": [(1, 1, 1), (1, 2, None), (1, 3, 2), (2, 1, 1)], }, ) - assert g.D({r("person", 1, 1)}) == {r("person", 1, 1), r("assignment", 1, 1)} - assert g.D({r("lab", 1)}) == { - r("lab", 1), - r("person", 1, 1), - r("person", 1, 2), - r("assignment", 1, 1), - r("assignment", 1, 2), - } - assert g.U({r("assignment", 1, 2)}) == {r("assignment", 1, 2), r("lab", 1)} - assert g.U({r("assignment", 2, 1)}) == {r("assignment", 2, 1), r("person", 2, 1), r("lab", 2)} + assert s.schema.nullable["session"] == {"surgery_id"} # only surgery_id can be NULL + seed = s.seed("surgery", {"subject_id": 1}, [(1, 1), (1, 2)]) + s.check("surgery", seed, "down", {"surgery": [(1, 1), (1, 2)], "session": [(1, 1), (1, 3)]}) + seed = s.seed("session", {"subject_id": 1, "session_id": 2}, [(1, 2)]) + s.check("session", seed, "up", {"session": [(1, 2)], "subject": [1], "surgery": []}) + # Why: the FK has a nullable column, so subject_id = 1 is not copied; a copy + # would also delete session (1, 2) (#1590). Relabeling the keys is refused too. + fk, cond = s.fk("session", "surgery"), {"subject_id": 1} + assert not copy_allowed(s.schema, fk, cond) + assert ("session", (1, 2)) in restrict(s.schema, s.db, "session", cond) + assert not relabel_down_allowed(s.schema, fk, [cond]) def test_S4_renamed_fk(): - """litter.mother_id references animal.animal_id. Litter 1 has mother 2, so animal 1 does not reach it.""" - schema = Schema( - [TableSpec("animal", ("animal_id",)), TableSpec("litter", ("litter_id",), ("mother_id",))], - [FK("litter", "animal", {"mother_id": "animal_id"})], + """traversal-shapes.md, S4. Renamed foreign key, with an unrelated attribute named neuron_id.""" + s = Shape( + [TableSpec("neuron", ("neuron_id",)), TableSpec("input", ("source_id", "input_id"), ("neuron_id",))], + [FK("input", "neuron", {"source_id": "neuron_id"})], + {"neuron": [(1,), (2,), (3,)], "input": [(1, 1, 2), (1, 2, 3), (2, 1, 1), (3, 1, 1)]}, ) - g = graph(schema, {"animal": [(1,), (2,)], "litter": [(1, 2), (2, 1), (3, 1)]}) - assert g.D({r("animal", 1)}) == {r("animal", 1), r("litter", 2), r("litter", 3)} - assert g.U({r("litter", 1)}) == {r("litter", 1), r("animal", 2)} - - -def test_S5_parallel_fks_plain_and_renamed(): - """ - synapse references neuron twice: neuron_id (plain) and pre_id (renamed). - - The two FKs are separate edges and combine with OR: neuron 2 reaches - synapse (1, 2) through pre_id and synapse (2, 3) through neuron_id. - """ - schema = Schema( - [TableSpec("neuron", ("neuron_id",)), TableSpec("synapse", ("neuron_id", "pre_id"))], - [FK("synapse", "neuron", {"neuron_id": "neuron_id"}), FK("synapse", "neuron", {"pre_id": "neuron_id"})], + seed = s.seed("neuron", {"neuron_id": 1}, [1]) + s.check("neuron", seed, "down", {"neuron": [1], "input": [(1, 1), (1, 2)]}) + seed = s.seed("input", {"source_id": 2, "input_id": 1}, [(2, 1)]) + s.check("input", seed, "up", {"input": [(2, 1)], "neuron": [2]}) + # Why: copying neuron_id = 1 to Input would match the unrelated label. + cond = {"neuron_id": 1} + assert not copy_allowed(s.schema, s.fk("input", "neuron"), cond) + assert restrict(s.schema, s.db, "input", cond) == rows({"input": [(2, 1), (3, 1)]}) + + +def test_S5_parallel_fks(): + """traversal-shapes.md, S5. Parallel foreign keys.""" + s = Shape( + [TableSpec("neuron", ("neuron_id",)), TableSpec("synapse", ("neuron_id", "target_id"))], + [FK("synapse", "neuron", {"neuron_id": "neuron_id"}), FK("synapse", "neuron", {"target_id": "neuron_id"})], + {"neuron": [(1,), (2,), (3,)], "synapse": [(1, 2), (2, 3), (3, 1)]}, ) - g = graph(schema, {"neuron": [(1,), (2,), (3,)], "synapse": [(1, 2), (2, 3), (3, 3)]}) - assert g.D({r("neuron", 2)}) == {r("neuron", 2), r("synapse", 1, 2), r("synapse", 2, 3)} - assert g.D({r("neuron", 1)}) == {r("neuron", 1), r("synapse", 1, 2)} - assert g.U({r("synapse", 1, 2)}) == {r("synapse", 1, 2), r("neuron", 1), r("neuron", 2)} - assert g.U({r("synapse", 3, 3)}) == {r("synapse", 3, 3), r("neuron", 3)} + seed = s.seed("neuron", {"neuron_id": 1}, [1]) + s.check("neuron", seed, "down", {"neuron": [1], "synapse": [(1, 2), (3, 1)]}) + s.check("neuron", seed, "both", {"neuron": [1, 2, 3], "synapse": [(1, 2), (3, 1)]}) + up_seed = s.seed("synapse", {"neuron_id": 1, "target_id": 2}, [(1, 2)]) + s.check("synapse", up_seed, "up", {"synapse": [(1, 2)], "neuron": [1, 2]}) + # Why: combining the two FKs with AND selects only synapses from neuron 1 to itself: none. + per_fk = [r1_down(s.schema, s.db, fk, seed) for fk in s.schema.fks] + assert per_fk[0] & per_fk[1] == set() + # Why (and diagram.md, "Why both is U(D(S))"): D(S) ∪ U(S) leaves out neurons 2 and 3, + # so it carries synapse (3, 1) without neuron 3. + union = s.g.D(seed) | s.g.U(seed) + assert union == rows({"neuron": [1], "synapse": [(1, 2), (3, 1)]}) + assert not ri_ok(s.schema, subdatabase(s.db, union)) def test_S6_diamond_with_a_shared_key(): - """recording references session and implant, which share subject_id.""" - schema = Schema( + """traversal-shapes.md, S6. Diamond with a shared key.""" + s = Shape( [ TableSpec("subject", ("subject_id",)), TableSpec("session", ("subject_id", "session_id")), @@ -157,126 +220,115 @@ def test_S6_diamond_with_a_shared_key(): FK("recording", "session", {"subject_id": "subject_id", "session_id": "session_id"}), FK("recording", "implant", {"subject_id": "subject_id", "implant_id": "implant_id"}), ], - ) - g = graph( - schema, { "subject": [(1,), (2,)], - "session": [(1, 1), (2, 1)], + "session": [(1, 1), (1, 2), (2, 1)], "implant": [(1, 1), (1, 2), (2, 1)], - "recording": [(1, 1, 1), (1, 1, 2), (2, 1, 1)], + "recording": [(1, 1, 1), (1, 2, 2), (2, 1, 1)], + }, + ) + seed = s.seed("subject", {"subject_id": 1}, [1]) + s.check( + "subject", + seed, + "down", + { + "subject": [1], + "session": [(1, 1), (1, 2)], + "implant": [(1, 1), (1, 2)], + "recording": [(1, 1, 1), (1, 2, 2)], }, ) - assert g.D({r("implant", 1, 2)}) == {r("implant", 1, 2), r("recording", 1, 1, 2)} - assert g.D({r("subject", 2)}) == { - r("subject", 2), - r("session", 2, 1), - r("implant", 2, 1), - r("recording", 2, 1, 1), - } - assert g.U({r("recording", 1, 1, 2)}) == { - r("recording", 1, 1, 2), - r("session", 1, 1), - r("implant", 1, 2), - r("subject", 1), - } - # both adds the other parent of the descendant (session (1, 1)) and its ancestors. - assert g.both({r("implant", 1, 2)}) == { - r("implant", 1, 2), - r("recording", 1, 1, 2), - r("session", 1, 1), - r("subject", 1), - } + seed = s.seed("implant", {"subject_id": 1, "implant_id": 1}, [(1, 1)]) + s.check("implant", seed, "down", {"implant": [(1, 1)], "recording": [(1, 1, 1)]}) + s.check( + "implant", + seed, + "both", + {"implant": [(1, 1)], "recording": [(1, 1, 1)], "subject": [1], "session": [(1, 1)]}, + ) + seed = s.seed("recording", {"subject_id": 1, "session_id": 2, "implant_id": 2}, [(1, 2, 2)]) + s.check( + "recording", + seed, + "up", + {"recording": [(1, 2, 2)], "session": [(1, 2)], "implant": [(1, 2)], "subject": [1]}, + ) def test_S7_diamond_reached_through_a_secondary_fk(): - """ - D references B in its primary key and C as a secondary FK; C references B. - - D (2, 1) has b_id = 2 but references C 10, which references B 1, so deleting - B 1 must delete it (the Phase 0 diamond: copying ``b_id = 1`` down to D - missed it). D (1, 2) is reached through its own b_id. - """ - schema = Schema( + """traversal-shapes.md, S7. Diamond reached through a secondary foreign key.""" + s = Shape( [ - TableSpec("b", ("b_id",)), - TableSpec("c", ("c_id",), ("b_id",)), - TableSpec("d", ("b_id", "d_id"), ("c_id",)), + TableSpec("lab", ("lab_id",)), + TableSpec("rig", ("rig_id",), ("lab_id",)), + TableSpec("session", ("lab_id", "session_id"), ("rig_id",)), ], - [FK("c", "b", {"b_id": "b_id"}), FK("d", "b", {"b_id": "b_id"}), FK("d", "c", {"c_id": "c_id"})], - ) - g = graph( - schema, - {"b": [(1,), (2,)], "c": [(10, 1), (11, 2)], "d": [(2, 1, 10), (1, 2, 11), (2, 3, 11)]}, + [ + FK("rig", "lab", {"lab_id": "lab_id"}), + FK("session", "lab", {"lab_id": "lab_id"}), + FK("session", "rig", {"rig_id": "rig_id"}), + ], + {"lab": [(1,), (2,)], "rig": [(10, 1), (20, 2)], "session": [(1, 1, 20), (2, 1, 10), (2, 2, 20)]}, ) - assert g.D({r("b", 1)}) == {r("b", 1), r("c", 10), r("d", 2, 1), r("d", 1, 2)} - assert g.U({r("d", 2, 1)}) == {r("d", 2, 1), r("b", 2), r("c", 10), r("b", 1)} + seed = s.seed("lab", {"lab_id": 1}, [1]) + s.check("lab", seed, "down", {"lab": [1], "rig": [10], "session": [(1, 1), (2, 1)]}) + seed = s.seed("session", {"lab_id": 2, "session_id": 1}, [(2, 1)]) + s.check("session", seed, "up", {"session": [(2, 1)], "rig": [10], "lab": [1, 2]}) + seed = s.seed("rig", {"rig_id": 10}, [10]) + s.check("rig", seed, "both", {"rig": [10], "session": [(2, 1)], "lab": [1, 2]}) + # Why: Rig's condition lab_id = 1 names an attribute that Session's FK to Rig does not + # carry, so it is not copied. A copy would miss session (2, 1), and the delete would + # then break its FK to rig 10. + cond = {"lab_id": 1} + assert not copy_allowed(s.schema, s.fk("session", "rig"), cond) + copied = restrict(s.schema, s.db, "session", cond) + assert copied == rows({"session": [(1, 1)]}) + assert not ri_ok(s.schema, apply_delete(s.db, rows({"lab": [1], "rig": [10]}) | copied)) # ============================================================================= -# G: master-part groups +# Group shapes # ============================================================================= -def g1_schema(): - """session is a master; its part trial references a stimulus.""" - return Schema( +def test_G1_part_to_master_lift(): + """traversal-shapes.md, G1. Part-to-master lift, and the delete modes.""" + s = Shape( [ - TableSpec("stimulus", ("stim_id",)), TableSpec("session", ("session_id",)), - TableSpec("session__trial", ("session_id", "trial_id"), ("stim_id",), master="session"), + TableSpec("session__trial", ("session_id", "trial_id"), master="session"), + TableSpec("review", ("session_id", "review_id")), ], [ FK("session__trial", "session", {"session_id": "session_id"}), - FK("session__trial", "stimulus", {"stim_id": "stim_id"}), + FK("review", "session", {"session_id": "session_id"}), ], + { + "session": [(1,), (2,), (3,)], + "session__trial": [(1, 1), (1, 2), (2, 1), (3, 1)], + "review": [(1, 1), (2, 1)], + }, ) - - -G1_DATA = { - "stimulus": [(1,), (2,)], - "session": [(1,), (2,), (3,)], # session 3 has no trials - "session__trial": [(1, 1, 1), (1, 2, 2), (2, 1, 2)], -} - - -def test_G1_part_to_master_lift(): - """ - A part row lifts its master, and the master brings its other parts. - - Stimulus 1 reaches trial (1, 1), which lifts session 1 and with it trial - (1, 2). Sessions 2 and 3 are untouched; session 3 has no part rows at all. - """ - g = graph(g1_schema(), G1_DATA) - assert g.D({r("stimulus", 1)}) == { - r("stimulus", 1), - r("session__trial", 1, 1), - r("session", 1), - r("session__trial", 1, 2), - } - # Without the group rule the set is not lift-closed, so "enforce" raises. - d1 = g.D1({r("stimulus", 1)}) - assert d1 == {r("stimulus", 1), r("session__trial", 1, 1)} - assert not g.lift_closed(d1) - assert g.D({r("session", 3)}) == {r("session", 3)} - # trace includes the master's other parts and their parents (#1481). - assert g.U({r("session__trial", 1, 1)}) == { - r("session__trial", 1, 1), - r("session", 1), - r("session__trial", 1, 2), - r("stimulus", 1), - r("stimulus", 2), - } - assert g.U({r("session__trial", 2, 1)}) == {r("session__trial", 2, 1), r("session", 2), r("stimulus", 2)} + seed = s.seed("session__trial", {"trial_id": 2}, [(1, 2)]) + down = {"session__trial": [(1, 1), (1, 2)], "session": [1], "review": [(1, 1)]} + s.check("session__trial", seed, "down", down) + s.check("session__trial", seed, "up", {"session__trial": [(1, 1), (1, 2)], "session": [1]}) + # Why: the lift is Session & (Session.Trial & {"trial_id": 2}), only session 1. The + # old upward copy of trial_id = 2 named no attribute of Session and selected every session. + trials = Relation(("session_id", "trial_id"), frozenset({(1, 2)})) + assert restrict(s.schema, s.db, "session", trials) == rows({"session": [1]}) + assert restrict(s.schema, s.db, "session", {"trial_id": 2}) == rows({"session": [1, 2, 3]}) + # The delete modes. + assert s.g.delete(seed, "cascade") == rows(down) + assert s.g.delete(seed, "ignore") == rows({"session__trial": [(1, 2)]}) + with pytest.raises(PartIntegrityError, match="session__trial"): + s.g.delete(seed, "enforce") def test_G2_part_of_part_with_two_chains(): - """ - Part P references its master directly and through sibling Q (the #1525 shape). - - Ext 1 reaches Q (1, 10) and P (1, 100); both lift master 1. Master 2 is untouched. - """ - schema = Schema( + """traversal-shapes.md, G2. Part-of-part with two chains.""" + s = Shape( [ TableSpec("ext", ("ext_id",)), TableSpec("master", ("master_id",)), @@ -290,249 +342,337 @@ def test_G2_part_of_part_with_two_chains(): FK("master__p", "master__q", {"master_id": "master_id", "q_id": "q_id"}), FK("master__p", "ext", {"ext_id": "ext_id"}), ], - ) - g = graph( - schema, { "ext": [(1,), (2,)], - "master": [(1,), (2,)], - "master__q": [(1, 10, 1), (2, 20, 2)], - "master__p": [(1, 100, 10, 1), (2, 200, 20, 2)], + "master": [(1,), (2,), (3,)], + "master__q": [(1, 10, 1), (2, 20, 2), (3, 30, 2)], + "master__p": [(1, 100, 10, 2), (2, 200, 20, 1), (3, 300, 30, 2)], }, ) - assert g.D({r("ext", 1)}) == {r("ext", 1), r("master__q", 1, 10), r("master__p", 1, 100), r("master", 1)} - assert g.D({r("master__q", 1, 10)}) == {r("master__q", 1, 10), r("master__p", 1, 100), r("master", 1)} - assert g.U({r("master__p", 1, 100)}) == {r("master__p", 1, 100), r("master", 1), r("master__q", 1, 10), r("ext", 1)} - + seed = s.seed("ext", {"ext_id": 1}, [1]) + s.check( + "ext", + seed, + "down", + {"ext": [1], "master__q": [(1, 10), (2, 20)], "master__p": [(1, 100), (2, 200)], "master": [1, 2]}, + ) + seed = s.seed("master__p", {"master_id": 1, "p_id": 100}, [(1, 100)]) + s.check( + "master__p", + seed, + "up", + {"master__p": [(1, 100)], "master": [1], "master__q": [(1, 10)], "ext": [1, 2]}, + ) + # Why: the lift is one step on master_id, so the group needs no iteration. + assert not any(renames_master_key(s.schema, fk) for fk in s.schema.intra_group_fks("master")) -def test_G3_part_with_second_renamed_fk_to_its_master(): - """ - The lift follows the owning key, never the renamed second FK. - cage__move references its master twice: cage_id (owning) and dest_cage - (renamed). Move (1, 1) belongs to cage 1 and points to cage 2. Seeded alone, - it lifts cage 1, not cage 2. Seeding cage 2 reaches move (1, 1) as a child - through dest_cage, which then lifts cage 1. - """ - schema = Schema( +def test_G3_second_renamed_fk_to_its_own_master(): + """traversal-shapes.md, G3. A second, renamed foreign key to its own master.""" + s = Shape( + [ + TableSpec("session", ("session_id",)), + TableSpec("session__link", ("session_id", "link_id"), ("other_session",), master="session"), + ], [ - TableSpec("cage", ("cage_id",)), - TableSpec("cage__move", ("cage_id", "move_id"), ("dest_cage",), master="cage"), + FK("session__link", "session", {"session_id": "session_id"}), + FK("session__link", "session", {"other_session": "session_id"}), ], - [FK("cage__move", "cage", {"cage_id": "cage_id"}), FK("cage__move", "cage", {"dest_cage": "cage_id"})], + {"session": [(1,), (2,), (3,)], "session__link": [(1, 1, 2), (3, 1, 1)]}, ) - g = graph(schema, {"cage": [(1,), (2,), (3,)], "cage__move": [(1, 1, 2), (2, 1, 3), (3, 1, 3)]}) - assert g.D({r("cage__move", 1, 1)}) == {r("cage__move", 1, 1), r("cage", 1)} - assert g.D({r("cage", 2)}) == {r("cage", 2), r("cage__move", 2, 1), r("cage__move", 1, 1), r("cage", 1)} - assert g.U({r("cage__move", 1, 1)}) == { - r("cage__move", 1, 1), - r("cage", 1), - r("cage", 2), - r("cage__move", 2, 1), - r("cage", 3), - r("cage__move", 3, 1), - } - - -def test_G4_sibling_fk_renaming_the_master_key(): - """ - A sibling FK that renames the master key links different instances of the group. - - A transfer recorded in cohort 2 refers to an animal of cohort 1 (from_cohort), - and one in cohort 3 to an animal of cohort 2: a chain of three instances. - This is the case where the engine iterates the group step. - """ - schema = Schema( + seed = s.seed("session__link", {"session_id": 1}, [(1, 1)]) + s.check("session__link", seed, "down", {"session__link": [(1, 1), (3, 1)], "session": [1, 3]}) + s.check("session__link", seed, "up", {"session__link": [(1, 1)], "session": [1, 2]}) + # Why: the renamed FK links one instance to another, so this group iterates. One + # pass alone would stop at session 1; with the iteration it reaches session 3. + assert renames_master_key(s.schema, s.fk("session__link", "session", "other_session")) + assert one_pass(s.schema, s.db, seed, "down", iterate=None)[0] == rows({"session__link": [(1, 1)], "session": [1]}) + assert one_pass(s.schema, s.db, seed, "down", iterate="spec")[0] == s.g.D(seed) + # With part_integrity="enforce", deleting session 1 raises: link (3, 1) would go without session 3. + with pytest.raises(PartIntegrityError, match=r"session__link.*\(3, 1\)"): + s.g.delete(rows({"session": [1]}), "enforce") + + +def test_G4_renamed_sibling_fk_chained_across_three_instances(): + """traversal-shapes.md, G4. Renamed sibling foreign key, chained across three instances.""" + s = Shape( [ - TableSpec("cohort", ("cohort_id",)), - TableSpec("cohort__animal", ("cohort_id", "animal_id"), master="cohort"), - TableSpec("cohort__transfer", ("cohort_id", "transfer_id"), ("from_cohort", "animal_id"), master="cohort"), + TableSpec("run", ("run_id",)), + TableSpec("run__step", ("run_id", "step_id"), master="run"), + TableSpec("run__carry", ("run_id", "carry_id"), ("prev_run", "prev_step"), master="run"), ], [ - FK("cohort__animal", "cohort", {"cohort_id": "cohort_id"}), - FK("cohort__transfer", "cohort", {"cohort_id": "cohort_id"}), - FK("cohort__transfer", "cohort__animal", {"from_cohort": "cohort_id", "animal_id": "animal_id"}), + FK("run__step", "run", {"run_id": "run_id"}), + FK("run__carry", "run", {"run_id": "run_id"}), + FK("run__carry", "run__step", {"prev_run": "run_id", "prev_step": "step_id"}), ], - ) - g = graph( - schema, { - "cohort": [(1,), (2,), (3,)], - "cohort__animal": [(1, 1), (2, 1), (3, 1)], - "cohort__transfer": [(2, 1, 1, 1), (3, 1, 2, 1)], + "run": [(1,), (2,), (3,), (4,)], + "run__step": [(1, 1), (2, 1), (3, 1), (4, 1)], + "run__carry": [(2, 1, 1, 1), (3, 1, 2, 1)], }, ) - everything = { - r("cohort", 1), - r("cohort", 2), - r("cohort", 3), - r("cohort__animal", 1, 1), - r("cohort__animal", 2, 1), - r("cohort__animal", 3, 1), - r("cohort__transfer", 2, 1), - r("cohort__transfer", 3, 1), - } - assert g.D({r("cohort", 1)}) == everything - assert g.D({r("cohort", 2)}) == { - r("cohort", 2), - r("cohort__animal", 2, 1), - r("cohort__transfer", 2, 1), - r("cohort", 3), - r("cohort__animal", 3, 1), - r("cohort__transfer", 3, 1), - } - assert g.D({r("cohort", 3)}) == {r("cohort", 3), r("cohort__animal", 3, 1), r("cohort__transfer", 3, 1)} - d1 = g.D1({r("cohort", 1)}) - assert d1 == {r("cohort", 1), r("cohort__animal", 1, 1), r("cohort__transfer", 2, 1)} - assert not g.lift_closed(d1) - assert g.U({r("cohort", 1)}) == {r("cohort", 1), r("cohort__animal", 1, 1)} - assert g.U({r("cohort__transfer", 3, 1)}) == everything + run1 = s.seed("run", {"run_id": 1}, [1]) + s.check( + "run", + run1, + "down", + {"run": [1, 2, 3], "run__step": [(1, 1), (2, 1), (3, 1)], "run__carry": [(2, 1), (3, 1)]}, + ) + run2 = s.seed("run", {"run_id": 2}, [2]) + s.check("run", run2, "down", {"run": [2, 3], "run__step": [(2, 1), (3, 1)], "run__carry": [(2, 1), (3, 1)]}) + s.check("run", run2, "up", {"run": [1, 2], "run__step": [(1, 1), (2, 1)], "run__carry": [(2, 1)]}) + s.check( + "run", + run2, + "both", + {"run": [1, 2, 3], "run__step": [(1, 1), (2, 1), (3, 1)], "run__carry": [(2, 1), (3, 1)]}, + ) + # Why: one pass over the group would stop at run 1. Iterating, the master keys grow + # as {1}, {1, 2}, {1, 2, 3}, and a fourth round adds nothing. Upward the chain runs + # the other way, from run 2 back to run 1. + assert one_pass(s.schema, s.db, run1, "down", iterate=None)[0] == rows({"run": [1], "run__step": [(1, 1)]}) + found, rounds = one_pass(s.schema, s.db, run1, "down", iterate="spec") + assert found == s.g.D(run1) + assert rounds["run"] == [rows({"run": [1]}), rows({"run": [1, 2]}), rows({"run": [1, 2, 3]})] + found, rounds = one_pass(s.schema, s.db, run2, "up", iterate="spec") + assert found == s.g.U(run2) + assert rounds["run"] == [rows({"run": [2]}), rows({"run": [1, 2]})] def test_G5_merge_table(): - """ - Each part of a merge table references a different upstream table. - - The upstream of an analysis resolves to the one source that fed its merge entry. - """ - schema = Schema( + """traversal-shapes.md, G5. Merge table.""" + s = Shape( [ - TableSpec("src_a", ("a_id",)), - TableSpec("src_b", ("b_id",)), - TableSpec("merge", ("merge_id",)), - TableSpec("merge__a", ("merge_id",), ("a_id",), master="merge"), - TableSpec("merge__b", ("merge_id",), ("b_id",), master="merge"), - TableSpec("analysis", ("merge_id",)), + TableSpec("ephys_source", ("ephys_id",)), + TableSpec("imaging_source", ("imaging_id",)), + TableSpec("recording", ("recording_id",)), + TableSpec("recording__ephys", ("recording_id",), ("ephys_id",), master="recording"), + TableSpec("recording__imaging", ("recording_id",), ("imaging_id",), master="recording"), + TableSpec("analysis", ("recording_id",)), ], [ - FK("merge__a", "merge", {"merge_id": "merge_id"}), - FK("merge__a", "src_a", {"a_id": "a_id"}), - FK("merge__b", "merge", {"merge_id": "merge_id"}), - FK("merge__b", "src_b", {"b_id": "b_id"}), - FK("analysis", "merge", {"merge_id": "merge_id"}), + FK("recording__ephys", "recording", {"recording_id": "recording_id"}), + FK("recording__ephys", "ephys_source", {"ephys_id": "ephys_id"}), + FK("recording__imaging", "recording", {"recording_id": "recording_id"}), + FK("recording__imaging", "imaging_source", {"imaging_id": "imaging_id"}), + FK("analysis", "recording", {"recording_id": "recording_id"}), ], + { + "ephys_source": [(1,), (2,)], + "imaging_source": [(1,)], + "recording": [(1,), (2,), (3,)], + "recording__ephys": [(1, 1), (2, 2)], + "recording__imaging": [(3, 1)], + "analysis": [(1,), (2,), (3,)], + }, ) - g = graph( - schema, + seed = s.seed("analysis", {"recording_id": 1}, [1]) + s.check( + "analysis", + seed, + "up", { - "src_a": [(1,), (2,)], - "src_b": [(1,)], - "merge": [(1,), (2,), (3,)], - "merge__a": [(1, 1), (2, 2)], - "merge__b": [(3, 1)], - "analysis": [(1,), (3,)], + "analysis": [1], + "recording": [1], + "recording__ephys": [1], + "recording__imaging": [], + "ephys_source": [1], + "imaging_source": [], }, ) - assert g.D({r("src_a", 1)}) == {r("src_a", 1), r("merge__a", 1), r("merge", 1), r("analysis", 1)} - assert g.U({r("analysis", 3)}) == {r("analysis", 3), r("merge", 3), r("merge__b", 3), r("src_b", 1)} - assert g.U({r("analysis", 1)}) == {r("analysis", 1), r("merge", 1), r("merge__a", 1), r("src_a", 1)} + seed = s.seed("ephys_source", {"ephys_id": 2}, [2]) + s.check( + "ephys_source", + seed, + "down", + {"ephys_source": [2], "recording__ephys": [2], "recording": [2], "recording__imaging": [], "analysis": [2]}, + ) + # With part_integrity="enforce" that delete raises: the part row would go without its master. + with pytest.raises(PartIntegrityError, match="recording__ephys"): + s.g.delete(seed, "enforce") def test_G6_part_violating_a1(): - """ - m__note refers to its master through a renamed FK, so it violates A1. + """traversal-shapes.md, G6. A part violating A1, traversed as a plain table.""" + s = Shape( + [TableSpec("probe", ("probe_id",)), TableSpec("probe__channel", ("host_id", "channel_id"), master="probe")], + [FK("probe__channel", "probe", {"host_id": "probe_id"})], + {"probe": [(1,), (2,)], "probe__channel": [(1, 1), (1, 2), (2, 1)]}, + ) + assert s.schema.a1_parts == {"probe": ()} + seed = s.seed("probe__channel", {"host_id": 1, "channel_id": 1}, [(1, 1)]) + s.check("probe__channel", seed, "down", {"probe__channel": [(1, 1)]}) + s.check("probe__channel", seed, "up", {"probe__channel": [(1, 1)], "probe": [1]}) + seed = s.seed("probe", {"probe_id": 1}, [1]) + s.check("probe", seed, "down", {"probe": [1], "probe__channel": [(1, 1), (1, 2)]}) - It is traversed as a plain table: no lift from it, and it is not part of the - master's instance. - """ - schema = Schema( + +# ============================================================================= +# Seed shapes: K1 to K6 share one schema +# ============================================================================= + + +def k_shape(extra_flags=()): + return Shape( [ - TableSpec("m", ("m_id",)), - TableSpec("m__item", ("m_id", "item_id"), master="m"), - TableSpec("m__note", ("note_id",), ("about_m",), master="m"), + TableSpec("subject", ("subject_id",), ("species",)), + TableSpec("session", ("subject_id", "session_id"), ("quality",)), + TableSpec("trial", ("subject_id", "session_id", "trial_id")), + TableSpec("flag", ("flag_id",), ("subject_id",)), ], - [FK("m__item", "m", {"m_id": "m_id"}), FK("m__note", "m", {"about_m": "m_id"})], + [ + FK("session", "subject", {"subject_id": "subject_id"}), + FK("trial", "session", {"subject_id": "subject_id", "session_id": "session_id"}), + FK("flag", "subject", {"subject_id": "subject_id"}, nullable=True), + ], + { + "subject": [(1, "mouse"), (2, "rat"), (3, "mouse"), (4, "rat")], + "session": [(1, 1, 0), (1, 2, 1), (2, 1, 1), (2, 2, 0), (3, 1, 1)], + "trial": [(1, 1, 1), (1, 2, 1), (2, 1, 1), (2, 2, 1), (3, 1, 1)], + "flag": [(1, 1), (2, 3), *extra_flags], + }, ) - assert schema.a1_parts == {"m": ("m__item",)} - g = graph(schema, {"m": [(1,), (2,)], "m__item": [(1, 1), (2, 1)], "m__note": [(1, 1), (2, 2)]}) - assert g.D({r("m__note", 1)}) == {r("m__note", 1)} - assert g.D({r("m", 1)}) == {r("m", 1), r("m__item", 1, 1), r("m__note", 1)} - assert g.U({r("m__note", 1)}) == {r("m__note", 1), r("m", 1), r("m__item", 1, 1)} - assert g.U({r("m__item", 1, 1)}) == {r("m", 1), r("m__item", 1, 1)} -# ============================================================================= -# K: seeds -# ============================================================================= +SESSION = Relation(("subject_id", "session_id", "quality")) +#: ``Session & "quality = 0"``, written out: the sessions of quality 0. +QUALITY_0 = Relation(SESSION.heading, frozenset({(1, 1, 0), (2, 2, 0)})) +ALL_SESSIONS = [(1, 1), (1, 2), (2, 1), (2, 2), (3, 1)] +ALL_TRIALS = [(1, 1, 1), (1, 2, 1), (2, 1, 1), (2, 2, 1), (3, 1, 1)] def test_K1_seed_restricted_by_a_descendant(): - """ - ``subject & (session & "quality = 'bad'")``: the seed rows are named by a descendant (#1496). - - The seed itself must be deleted along with its sessions; the engine used to - leave it, because its restriction read sessions already deleted. - """ - schema = Schema( - [TableSpec("subject", ("subject_id",)), TableSpec("session", ("subject_id", "session_id"), ("quality",))], - [FK("session", "subject", {"subject_id": "subject_id"})], + """traversal-shapes.md, K1. Seed restricted by a descendant.""" + s = k_shape() + seed = s.seed("subject", QUALITY_0, [1, 2]) # Subject & (Session & "quality = 0") + s.check( + "subject", + seed, + "down", + { + "subject": [1, 2], + "session": [(1, 1), (1, 2), (2, 1), (2, 2)], + "trial": [(1, 1, 1), (1, 2, 1), (2, 1, 1), (2, 2, 1)], + "flag": [1], + }, ) - db = make_db( - schema, - {"subject": [(1,), (2,)], "session": [(1, 1, "good"), (1, 2, "bad"), (2, 1, "good")]}, + s.check("subject", seed, "up", {"subject": [1, 2]}) + # diagram.md, R1: copied to Session, the seed's condition would select only the + # sessions of quality 0; and the seed Subject - (Session & "quality = 0") would + # select every session of another quality, including sessions of subjects that are + # not deleted. + fk = s.fk("session", "subject") + assert not copy_allowed(s.schema, fk, QUALITY_0) + assert restrict(s.schema, s.db, "session", QUALITY_0) == rows({"session": [(1, 1), (2, 2)]}) + others = s.seed("subject", Not(QUALITY_0), [3, 4]) + assert s.g.D(others) == rows({"subject": [3, 4], "session": [(3, 1)], "trial": [(3, 1, 1)], "flag": [2]}) + assert restrict(s.schema, s.db, "session", Not(QUALITY_0)) == rows({"session": [(1, 2), (2, 1), (3, 1)]}) + + +def test_K2_self_referencing_seed(): + """traversal-shapes.md, K2. Self-referencing seed: its condition reads its own table.""" + s = k_shape() + # Session & (dj.U("subject_id") & (Session & "quality = 0")): the subjects with a session of quality 0. + subjects_with_q0 = Relation(("subject_id",), frozenset({(1,), (2,)})) + seed = s.seed("session", subjects_with_q0, [(1, 1), (1, 2), (2, 1), (2, 2)]) + s.check( + "session", + seed, + "down", + {"session": [(1, 1), (1, 2), (2, 1), (2, 2)], "trial": [(1, 1, 1), (1, 2, 1), (2, 1, 1), (2, 2, 1)]}, ) - g = RowGraph(schema, db) - seed = {r("subject", 1)} # the subjects with a bad session - deleted = g.D(seed) - assert deleted == {r("subject", 1), r("session", 1, 1), r("session", 1, 2)} - remaining = apply_delete(db, deleted) - assert remaining == {"subject": {(2,): {"subject_id": 2}}, "session": {(2, 1): db["session"][(2, 1)]}} + s.check("session", seed, "up", {"session": [(1, 1), (1, 2), (2, 1), (2, 2)], "subject": [1, 2]}) -def test_K2_self_referencing_table(): +def test_K3_seed_kinds_give_the_same_rows(): """ - employee.manager_id references employee.emp_id (nullable): rows form chains. + traversal-shapes.md, K3. Seed kinds give the same rows. - The model follows the chain in both directions. DataJoint's dependency graph - must be acyclic, so this table cannot be declared: Q has a self-loop and - ``topo_sort`` raises. + The kinds the model can evaluate are checked to select sessions (2, 1) and + (2, 2); the string kinds need a database. """ - schema = Schema( - [TableSpec("employee", ("emp_id",), ("manager_id",))], - [FK("employee", "employee", {"manager_id": "emp_id"}, nullable=True)], - ) - g = graph(schema, {"employee": [(1, None), (2, 1), (3, 2), (4, 1)]}) - assert g.D({r("employee", 1)}) == {r("employee", 1), r("employee", 2), r("employee", 3), r("employee", 4)} - assert g.D({r("employee", 2)}) == {r("employee", 2), r("employee", 3)} - assert g.U({r("employee", 3)}) == {r("employee", 3), r("employee", 2), r("employee", 1)} - assert g.U({r("employee", 4)}) == {r("employee", 4), r("employee", 1)} - assert not q_acyclic(schema) - with pytest.raises(nx.NetworkXUnfeasible): - dependency_graph(schema).topo_sort() + s = k_shape() + sessions_of_2 = [(2, 1), (2, 2)] + rats = Relation(("subject_id", "species"), frozenset({(2, "rat"), (4, "rat")})) + flags = Relation(("flag_id", "subject_id"), frozenset({(1, 1), (2, 3)})) + for cond in ( + {"subject_id": 2}, + [{"subject_id": 2, "session_id": 1}, {"subject_id": 2, "session_id": 2}], + rats, # Session & (Subject & {"species": "rat"}) + Not(flags), # Session & dj.Not(Flag), or Session - Flag + ): + seed = s.seed("session", cond, sessions_of_2) + s.check("session", seed, "down", {"session": [(2, 1), (2, 2)], "trial": [(2, 1, 1), (2, 2, 1)]}) + s.check("session", seed, "up", {"session": [(2, 1), (2, 2)], "subject": [2]}) + # The NOT IN NULL trap: after inserting Flag (3; NULL), Session & dj.Not(Flag) selects nothing. + s = k_shape(extra_flags=[(3, None)]) + flags = Relation(("flag_id", "subject_id"), frozenset({(1, 1), (2, 3), (3, None)})) + seed = s.seed("session", Not(flags), []) + s.check("session", seed, "down", {"session": [], "trial": []}) + s.check("session", seed, "up", {"session": [], "subject": []}) + + +def test_K4_top_seed(): + """traversal-shapes.md, K4. ``Session & "quality = 1" & dj.Top(2)`` keeps sessions (1, 2) and (2, 1).""" + s = k_shape() + seed = s.seed("session", None, [(1, 2), (2, 1)]) + s.check("session", seed, "down", {"session": [(1, 2), (2, 1)], "trial": [(1, 2, 1), (2, 1, 1)]}) + s.check("session", seed, "up", {"session": [(1, 2), (2, 1)], "subject": [1, 2]}) + + +def test_K5_empty_seeds(): + """traversal-shapes.md, K5. Empty seeds: nothing in, nothing out, and the nodes are present.""" + s = k_shape() + for cond in ([], {"subject_id": 4}, None): # None stands for Session & False + seed = s.seed("session", cond, []) + s.check("session", seed, "down", {"session": [], "trial": []}) + s.check("session", seed, "up", {"session": [], "subject": []}) -def test_K5_empty_seed(): - """An empty seed (``False``, ``[]``, a condition that matches nothing) expands to nothing.""" - g = graph(g1_schema(), G1_DATA) - assert g.D(set()) == set() - assert g.D1(set()) == set() - assert g.U(set()) == set() - assert g.both(set()) == set() +def test_K6_unrestricted_seed(): + """traversal-shapes.md, K6. Unrestricted seed; a dict naming no attribute of Session means all rows.""" + s = k_shape() + for cond in ({}, {"species": "rat"}): + seed = s.seed("session", cond, ALL_SESSIONS) + s.check("session", seed, "down", {"session": ALL_SESSIONS, "trial": ALL_TRIALS}) + s.check("session", seed, "up", {"session": ALL_SESSIONS, "subject": [1, 2, 3]}) -def test_K6_unrestricted_seed(): +# ============================================================================= +# Graph shapes +# ============================================================================= + + +def test_X2_group_cycle(): """ - Every stimulus row as the seed. + traversal-shapes.md, X2. The cycle in Q that declaring Session.Note would close. - Both stimuli reach all trials, which lift sessions 1 and 2. Session 3 has no - trials and is untouched. + The refusal itself happens at declaration and is tested with a database. """ - g = graph(g1_schema(), G1_DATA) - assert g.D({r("stimulus", 1), r("stimulus", 2)}) == { - r("stimulus", 1), - r("stimulus", 2), - r("session__trial", 1, 1), - r("session__trial", 1, 2), - r("session__trial", 2, 1), - r("session", 1), - r("session", 2), - } - assert g.U({r("session__trial", 1, 1), r("session__trial", 1, 2), r("session__trial", 2, 1)}) == { - r("session__trial", 1, 1), - r("session__trial", 1, 2), - r("session__trial", 2, 1), - r("session", 1), - r("session", 2), - r("stimulus", 1), - r("stimulus", 2), - } + tables = [ + TableSpec("session", ("session_id",)), + TableSpec("session__trial", ("session_id", "trial_id"), master="session"), + TableSpec("review", ("session_id",)), + ] + fks = [ + FK("session__trial", "session", {"session_id": "session_id"}), + FK("review", "session", {"session_id": "session_id"}), + ] + before = Schema(tables, fks) + assert q_acyclic(before) + dependency_graph(before).topo_sort() + # Session.Note: -> master, then -> Review below the line. Review's key is session_id, + # which Note already holds, so the FK adds no attribute. + after = Schema( + [*tables, TableSpec("session__note", ("session_id",), master="session")], + [ + *fks, + FK("session__note", "session", {"session_id": "session_id"}), + FK("session__note", "review", {"session_id": "session_id"}), + ], + ) + assert after.a1_parts == {"session": ("session__trial", "session__note")} + assert not q_acyclic(after) # Session -> Review -> Session.Note, which belongs to Session's group + with pytest.raises(nx.NetworkXUnfeasible): + dependency_graph(after).topo_sort() diff --git a/tests/unit/test_expand_spec.py b/tests/unit/test_expand_spec.py index 1bd391822..d4bb35088 100644 --- a/tests/unit/test_expand_spec.py +++ b/tests/unit/test_expand_spec.py @@ -17,10 +17,18 @@ - T8 ordering: under A1, ``datajoint.dependencies.topo_sort`` succeeds if and only if the group quotient Q is acyclic, and its order is valid. -The tests of ``self.upstream`` for a row not inserted yet come last. +T1-T8 run under both readings of A1: the specification's, and a generalization +to owning chains with renames (#1429; ``Schema(..., a1="chain")``). + +Then come checks of the specification's computation (``diagram.md``, +"Computation") against the definitions: the one pass over Q and the groups that +iterate, R1's rule for copying a condition, and the rule for relabeling +materialized keys. Each claim that does not hold is kept as a strict ``xfail`` +with a minimal counterexample. The tests of ``self.upstream`` for a row not +inserted yet, and of the chain reading of A1, come last. Drawn schemas may contain group cycles (Q cyclic), which E1b refuses at -declaration. T1-T7 and the ``self.upstream`` properties are stated for acyclic Q +declaration. T1-T7 and the other row-level properties are stated for acyclic Q and discard the other schemas with ``assume``; T8 tests both cases. Select the number of examples with ``HYPOTHESIS_PROFILE`` (see ``tests/conftest.py``). """ @@ -41,17 +49,30 @@ from .expand_model import ( DB, FK, + Not, + PartIntegrityError, + Relation, RowGraph, Schema, TableSpec, all_rows, apply_delete, + copy_allowed, dependency_graph, fk_value, full_table_name, group_whole, make_db, + one_pass, + preserves_instance, q_acyclic, + r1_down, + r1_up, + relabel_down, + relabel_down_allowed, + relabel_up, + renames_master_key, + restrict, ri_ok, subdatabase, virtual_upstream, @@ -173,15 +194,18 @@ def _finish(draw, builder: _TableBuilder, tables: list[TableSpec], fks: list, ow @st.composite -def schemas(draw, max_tables: int = 7, a1_only: bool = False) -> Schema: +def schemas(draw, max_tables: int = 7, a1_only: bool = False, a1: str = "strict") -> Schema: """ Draw a schema: tables in declaration order, so the FK graph is a DAG. - Each step declares a plain table, a master, or a part of a master declared earlier, so parts can be added late, after other tables. - A master has 0-3 parts. A part's owning reference is direct (``-> master`` - in the primary key), through a sibling part, secondary, or (unless - ``a1_only``) it violates A1: it is missing, renamed or nullable. + in the primary key), through a sibling part, or secondary. Otherwise it is + missing, nullable, or renamed: to the master, or to a sibling as in #1429. + A renamed owning reference in the primary key violates A1 under + ``a1="strict"`` and satisfies it under ``a1="chain"``. With ``a1_only``, + every part satisfies A1. - Other FKs are primary or secondary, nullable, renamed and parallel, to any table declared earlier. Diamonds that share attributes, part -> part across groups, renamed references within a group and group cycles all occur. @@ -191,7 +215,6 @@ def schemas(draw, max_tables: int = 7, a1_only: bool = False) -> Schema: tables: list[TableSpec] = [] fks: list[FK] = [] parts_of: dict[str, list[str]] = {} - a1_parts: dict[str, list[str]] = {} # parts whose owning reference satisfies A1 by construction for i in range(draw(st.integers(min(2, max_tables), max_tables))): by_name = {t.name: t for t in tables} open_masters = [m for m, parts in parts_of.items() if len(parts) < 3] @@ -201,29 +224,47 @@ def schemas(draw, max_tables: int = 7, a1_only: bool = False) -> Schema: extra = _draw_fks(draw, tables, fks, fresh, draw(st.integers(0, 3)) if tables else 0, set()) spec = _finish(draw, builder, tables, extra, own_pk=f"k{i}", force_own=False) if kind == "master": - parts_of[spec.name], a1_parts[spec.name] = [], [] + parts_of[spec.name] = [] else: master = draw(st.sampled_from(open_masters)) - mspec = by_name[master] + mspec, mk = by_name[master], by_name[master].pk builder = _TableBuilder(f"{master}__p{len(parts_of[master])}", master=master) - owners = [s for s in a1_parts[master] if set(mspec.pk) <= set(by_name[s].pk)] - modes = ["direct", "secondary"] + (["sibling"] if owners else []) + ([] if a1_only else ["violate"]) - mode = draw(st.sampled_from(modes)) - how = None + so_far = Schema(tables, fks, a1=a1) + members = [p for p in parts_of[master] if p in so_far.group] + # Siblings an FK can inherit the key from: unrenamed (key in the primary key under + # the master's names), or renamed (owning attributes in the primary key). + carrying = [p for p in members if so_far.owning_attributes(p) == mk and set(mk) <= set(by_name[p].pk)] + chainable = [p for p in members if set(so_far.owning_attributes(p)) <= set(by_name[p].pk)] + renamed_modes = ["renamed"] + (["renamed sibling"] if chainable else []) + owner_modes = ["direct", "secondary"] + (["sibling"] if carrying else []) + violations = ["missing", "nullable"] + if a1 == "chain": + owner_modes += renamed_modes + else: + violations += renamed_modes + mode = draw(st.sampled_from(owner_modes + ([] if a1_only else ["violate"]))) + if mode == "violate": + mode = draw(st.sampled_from(violations)) owning = [] # the owning reference, when it is a secondary FK if mode == "direct": builder.add_fk(mspec, True, {}) elif mode == "sibling": - sibling = by_name[draw(st.sampled_from(owners))] - builder.add_fk(sibling, True, _draw_renames(draw, sibling, fresh, keep=mspec.pk)) + sibling = by_name[draw(st.sampled_from(carrying))] + builder.add_fk(sibling, True, _draw_renames(draw, sibling, fresh, keep=mk)) elif mode == "secondary": owning = [(mspec, False, {}, False)] - else: - how = draw(st.sampled_from(["missing", "renamed", "nullable"])) - if how == "renamed": - builder.add_fk(mspec, True, {a: f"{a}_r{next(fresh)}" for a in mspec.pk}) - elif how == "nullable": - owning = [(mspec, False, {}, True)] + elif mode == "nullable": + owning = [(mspec, False, {}, True)] + elif mode == "renamed": + renamed = draw(st.sets(st.sampled_from(mk), min_size=1)) + builder.add_fk(mspec, True, {a: f"{a}_r{next(fresh)}" for a in sorted(renamed)}) + elif mode == "renamed sibling": + sibling = by_name[draw(st.sampled_from(chainable))] + key = so_far.owning_attributes(sibling.name) + renamed = draw(st.sets(st.sampled_from(key), min_size=1)) + renames = _draw_renames(draw, sibling, fresh, keep=key) + renames.update({a: f"{a}_r{next(fresh)}" for a in sorted(renamed)}) + builder.add_fk(sibling, True, renames) solid = set(builder.pk) | {a for o in owning if not o[3] for a in o[0].pk} group = [master] + parts_of[master] extra = _draw_fks(draw, tables, fks, fresh, draw(st.integers(0, 3)), solid, group) @@ -231,11 +272,9 @@ def schemas(draw, max_tables: int = 7, a1_only: bool = False) -> Schema: # primary key, so the part needs a key attribute of its own. spec = _finish(draw, builder, tables, owning + extra, own_pk=f"k{i}", force_own=bool(owning)) parts_of[master].append(spec.name) - if mode != "violate": - a1_parts[master].append(spec.name) tables.append(spec) fks.extend(builder.fks) - schema = Schema(tables, fks) + schema = Schema(tables, fks, a1=a1) if a1_only: assert all(t.name in schema.group for t in schema.tables if t.master), schema return schema @@ -294,9 +333,11 @@ def __repr__(self) -> str: @st.composite -def cases(draw, max_tables: int = 7, max_rows: int = 4, acyclic: bool = True, max_total: int | None = None) -> Case: +def cases( + draw, max_tables: int = 7, max_rows: int = 4, acyclic: bool = True, max_total: int | None = None, a1: str = "strict" +) -> Case: """Draw a schema and a database. With ``acyclic``, schemas whose Q has a cycle are discarded.""" - schema = draw(schemas(max_tables)) + schema = draw(schemas(max_tables, a1=a1)) if acyclic: assume(q_acyclic(schema)) db = draw(databases(schema, max_rows)) @@ -311,8 +352,13 @@ def seeds(rows) -> st.SearchStrategy: return st.frozensets(st.sampled_from(rows)) if rows else st.just(frozenset()) -#: Tiny databases for the brute-force minimality checks (at most 2^8 supersets). -tiny_cases = cases(max_tables=4, max_rows=2, max_total=8) +#: Both readings of A1: the specification's, and the generalization for #1429. +A1_MODES = ["strict", "chain"] + + +def tiny_cases(a1: str) -> st.SearchStrategy: + """Tiny databases for the brute-force minimality checks (at most 2^8 supersets).""" + return cases(max_tables=4, max_rows=2, max_total=8, a1=a1) def supersets(rows, seed): @@ -441,34 +487,38 @@ def test_generator_draws_data_feature(feature): # ============================================================================= # T1-T3 # ============================================================================= +# Each property runs under both readings of A1 (see the A1 section at the end). -@given(case=cases(), data=st.data()) -def test_t1_closure(case, data): - """T1: D and U are extensive, monotone and idempotent.""" - g = case.graph +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t1_closure(a1, data): + """T1: D, U and D1 are extensive, monotone and idempotent.""" + g = data.draw(cases(a1=a1), label="case").graph b = data.draw(seeds(g.rows), label="B") a = data.draw(seeds(b), label="A ⊆ B") - for op in (g.D, g.U): + for op in (g.D, g.U, g.D1): assert a <= op(a) assert op(a) <= op(b) assert op(op(a)) == op(a) -@given(case=cases(), data=st.data()) -def test_t2_additivity(case, data): - """T2: X(A ∪ B) = X(A) ∪ X(B) for X in D, U and both.""" - g = case.graph +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t2_additivity(a1, data): + """T2: X(A ∪ B) = X(A) ∪ X(B) for X in D, U, D1 and both.""" + g = data.draw(cases(a1=a1), label="case").graph a = data.draw(seeds(g.rows), label="A") b = data.draw(seeds(g.rows), label="B") - for op in (g.D, g.U, g.both): + for op in (g.D, g.U, g.D1, g.both): assert op(a | b) == op(a) | op(b) -@given(case=cases()) -def test_t3_duality(case): +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t3_duality(a1, data): """T3: y ∈ D({x}) if and only if x ∈ U({y}).""" - g = case.graph + g = data.draw(cases(a1=a1), label="case").graph up = {y: g.U({y}) for y in g.rows} for x in g.rows: down = g.D({x}) @@ -481,9 +531,11 @@ def test_t3_duality(case): # ============================================================================= -@given(case=cases(), data=st.data()) -def test_t4_delete_safety(case, data): +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t4_delete_safety(a1, data): """T4: removing D(S) leaves the database consistent and takes every master-part instance whole.""" + case = data.draw(cases(a1=a1), label="case") g = case.graph s = data.draw(seeds(g.rows), label="S") d = g.D(s) @@ -493,9 +545,11 @@ def test_t4_delete_safety(case, data): assert g.group_closed(d) -@given(case=tiny_cases, data=st.data()) -def test_t4_minimality(case, data): +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t4_minimality(a1, data): """T4: D(S) is the least superset of S whose removal is safe.""" + case = data.draw(tiny_cases(a1), label="case") g = case.graph s = data.draw(seeds(g.rows), label="S") safe = [x for x in supersets(g.rows, s) if ri_ok(case.schema, apply_delete(case.db, x)) and g.group_closed(x)] @@ -504,9 +558,11 @@ def test_t4_minimality(case, data): assert all(d <= x for x in safe) -@given(case=cases(), data=st.data()) -def test_t5_read_boundary(case, data): +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t5_read_boundary(a1, data): """T5: U(S) is consistent on its own and holds every master-part instance it touches whole.""" + case = data.draw(cases(a1=a1), label="case") g = case.graph s = data.draw(seeds(g.rows), label="S") u = g.U(s) @@ -516,12 +572,18 @@ def test_t5_read_boundary(case, data): assert g.group_closed(u) -@given(case=tiny_cases, data=st.data()) -def test_t5_minimality(case, data): +def _self_contained(case, rows) -> bool: + return ri_ok(case.schema, subdatabase(case.db, rows)) and case.graph.group_closed(rows) + + +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t5_minimality(a1, data): """T5: U(S) is the least superset of S that is consistent on its own.""" + case = data.draw(tiny_cases(a1), label="case") g = case.graph s = data.draw(seeds(g.rows), label="S") - consistent = [x for x in supersets(g.rows, s) if ri_ok(case.schema, subdatabase(case.db, x)) and g.group_closed(x)] + consistent = [x for x in supersets(g.rows, s) if _self_contained(case, x)] u = g.U(s) assert u in consistent assert all(u <= x for x in consistent) @@ -532,16 +594,31 @@ def test_t5_minimality(case, data): # ============================================================================= -@given(case=cases(), data=st.data()) -def test_t6_both(case, data): - """T6: U(D(S)) is up-closed, group-closed and contains D(S).""" - g = case.graph - s = data.draw(seeds(g.rows), label="S") +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t6_both(a1, data): + """T6: U(D(S)) contains D(S), is up-closed and group-closed; both is extensive and monotone.""" + g = data.draw(cases(a1=a1), label="case").graph + big = data.draw(seeds(g.rows), label="B") + s = data.draw(seeds(big), label="S ⊆ B") b = g.both(s) assert g.D(s) <= b assert g.U(b) == b assert g.group_closed(b) - assert b <= g.both(b) # extensive, though not idempotent (next test) + assert b <= g.both(b) # extensive, though not idempotent (test_t6_both_is_not_idempotent) + assert b <= g.both(big) + + +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t6_minimality(a1, data): + """T6: U(D(S)) is the least superset of D(S) that is consistent on its own.""" + case = data.draw(tiny_cases(a1), label="case") + g = case.graph + d = g.D(data.draw(seeds(g.rows), label="S")) + consistent = [x for x in supersets(g.rows, d) if _self_contained(case, x)] + assert g.U(d) in consistent + assert all(g.U(d) <= x for x in consistent) def test_t6_both_is_not_idempotent(): @@ -557,14 +634,20 @@ def test_t6_both_is_not_idempotent(): assert g.both(g.both({b})) == {a, b, c} -@given(case=cases(), data=st.data()) -def test_t7_enforce(case, data): - """T7: D1(S) is lift-closed if and only if D1(S) = D(S).""" - g = case.graph +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_t7_enforce(a1, data): + """T7: D1(S) is lift-closed if and only if D1(S) = D(S); enforce deletes D1(S) or raises.""" + g = data.draw(cases(a1=a1), label="case").graph s = data.draw(seeds(g.rows), label="S") d1, d = g.D1(s), g.D(s) assert d1 <= d assert g.lift_closed(d1) == (d1 == d) + if d1 == d: + assert g.delete(s, "enforce") == d + else: + with pytest.raises(PartIntegrityError): + g.delete(s, "enforce") # ============================================================================= @@ -584,9 +667,10 @@ def assert_valid_order(schema: Schema, order: list[str], quote: str) -> None: assert set(block) == {name[master]} | {name[p] for p in parts}, (master, order) +@pytest.mark.parametrize("a1", A1_MODES) @pytest.mark.parametrize("quote", ["`", '"']) -@given(schema=schemas(a1_only=True)) -def test_t8_topo_sort(quote, schema): +@given(data=st.data()) +def test_t8_topo_sort(quote, a1, data): """ T8: under A1, topo_sort succeeds if and only if Q is acyclic, and its order is valid. @@ -594,6 +678,7 @@ def test_t8_topo_sort(quote, schema): fills it, and sorted by ``datajoint.dependencies.topo_sort``. Both MySQL and PostgreSQL quoting are used, since ``extract_master`` recognizes parts by name. """ + schema = data.draw(schemas(a1_only=True, a1=a1), label="schema") graph = dependency_graph(schema, quote) if q_acyclic(schema): assert_valid_order(schema, graph.topo_sort(), quote) @@ -678,14 +763,16 @@ def _rows_outside_parts(case: Case): return sorted(r for r in case.graph.rows if case.schema.table(r[0]).master is None) -@given(case=cases(), data=st.data()) -def test_virtual_upstream_does_not_need_the_row(case, data): +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_virtual_upstream_does_not_need_the_row(a1, data): """ ``self.upstream`` is the same before and after the row is inserted. Before the insert, the row, its parts and its descendants are absent: ``db - D({r})``. Its upstream is built from its primary FKs alone. """ + case = data.draw(cases(a1=a1), label="case") rows = _rows_outside_parts(case) assume(rows) r = data.draw(st.sampled_from(rows), label="r") @@ -697,12 +784,14 @@ def test_virtual_upstream_does_not_need_the_row(case, data): assert r not in upstream -@given(case=cases(), data=st.data()) -def test_virtual_upstream_matches_trace_of_the_inserted_row(case, data): +@pytest.mark.parametrize("a1", A1_MODES) +@given(data=st.data()) +def test_virtual_upstream_matches_trace_of_the_inserted_row(a1, data): """ Once the row exists, ``U({r})`` contains ``self.upstream``. They are equal, apart from r itself, when r is in no group and all its FKs are primary. """ + case = data.draw(cases(a1=a1), label="case") rows = _rows_outside_parts(case) assume(rows) r = data.draw(st.sampled_from(rows), label="r") @@ -747,3 +836,369 @@ def test_fk_value_follows_match_simple(): schema = Schema([TableSpec("parent", ("a", "b")), TableSpec("child", ("a", "c"), ("b",))], [fk]) assert fk_value(schema, fk, {"a": 1, "b": None, "c": 1}) is None assert fk_value(schema, fk, {"a": 1, "b": 2, "c": 1}) == (1, 2) + + +# ============================================================================= +# The one pass over Q, and the groups that iterate (diagram.md, Computation) +# ============================================================================= +# ``one_pass`` evaluates the engine's computation on rows: one visit per node of +# Q, with an iterating group repeating its group step. It must give D(S) and +# U(S). diagram.md decides which groups iterate by whether an FK inside the +# group renames the master's key ("spec"); the exact test is whether the FK +# maps the instance onto itself ("exact"). + + +@pytest.mark.parametrize("a1", A1_MODES) +@pytest.mark.parametrize("direction", ["down", "up"]) +@given(data=st.data()) +def test_one_pass_with_exact_iteration_equals_the_closure(direction, a1, data): + """Iterating every group that has an FK not preserving the instance, one pass gives D(S) and U(S).""" + case = data.draw(cases(a1=a1), label="case") + s = data.draw(seeds(case.graph.rows), label="S") + found, _ = one_pass(case.schema, case.db, s, direction, iterate="exact") + assert found == (case.graph.D(s) if direction == "down" else case.graph.U(s)) + + +def _inner_fks_carry_the_master_key(schema: Schema) -> bool: + return all( + set(schema.owning[fk.parent].values()) <= set(fk.attr_map.values()) + for master in schema.a1_parts + for fk in schema.intra_group_fks(master) + ) + + +@pytest.mark.parametrize("direction", ["down", "up"]) +@given(data=st.data()) +def test_one_pass_with_the_spec_iteration_rule(direction, data): + """ + The rule of diagram.md is exact when every FK inside a group carries the master's key. + + Then an FK either maps the key to itself, and stays in the instance, or + renames it, and the group iterates. The next test shows the case it misses. + """ + case = data.draw(cases(), label="case") + assume(_inner_fks_carry_the_master_key(case.schema)) + s = data.draw(seeds(case.graph.rows), label="S") + found, _ = one_pass(case.schema, case.db, s, direction, iterate="spec") + assert found == (case.graph.D(s) if direction == "down" else case.graph.U(s)) + + +def _sibling_fk_without_master_key(): + """ + Part S holds the master key below the line (A1 through a secondary ``-> master``); + part P references S by S's primary key, which does not contain the master key. + """ + schema = Schema( + [ + TableSpec("m", ("m_id",)), + TableSpec("m__s", ("s_id",), ("m_id",), master="m"), + TableSpec("m__p", ("m_id", "p_id"), ("s_id",), master="m"), + ], + [FK("m__s", "m", {"m_id": "m_id"}), FK("m__p", "m", {"m_id": "m_id"}), FK("m__p", "m__s", {"s_id": "s_id"})], + ) + # S 10 belongs to master 1; P (2, 1) of master 2 references it. + db = make_db(schema, {"m": [(1,), (2,)], "m__s": [(10, 1)], "m__p": [(2, 1, 10)]}) + return schema, db + + +def test_one_pass_iterates_for_a_sibling_fk_without_the_master_key(): + """With the exact test, the group iterates and one pass reaches master 2 through P (2, 1).""" + schema, db = _sibling_fk_without_master_key() + assert schema.a1_parts == {"m": ("m__s", "m__p")} + g = RowGraph(schema, db) + everything = all_rows(db) + assert g.D({("m", (1,))}) == everything + assert g.U({("m__p", (2, 1))}) == everything + assert one_pass(schema, db, {("m", (1,))}, "down", iterate="exact")[0] == everything + assert one_pass(schema, db, {("m__p", (2, 1))}, "up", iterate="exact")[0] == everything + + +@pytest.mark.xfail( + strict=True, + reason=( + "docs vs model: diagram.md (Groups that iterate) iterates a group only for an FK inside it that " + "renames the master's key. An FK to a sibling whose primary key lacks the master key (the sibling " + "holds it below the line) renames nothing, yet links instances, so one pass misses rows." + ), +) +def test_spec_iteration_rule_misses_a_sibling_fk_without_the_master_key(): + """traversal per diagram.md: the FK from P to S does not rename the master key, so the group does not iterate.""" + schema, db = _sibling_fk_without_master_key() + assert not any(renames_master_key(schema, fk) for fk in schema.intra_group_fks("m")) + g = RowGraph(schema, db) + assert one_pass(schema, db, {("m", (1,))}, "down", iterate="spec")[0] == g.D({("m", (1,))}) + + +# ============================================================================= +# R1: copying a condition (diagram.md, R1, the edge rule) +# ============================================================================= + + +VALUES = st.sampled_from([0, 1, 2, None]) + + +def key_dicts(names: list[str]) -> st.SearchStrategy: + """A dict over some of ``names``, with values that may be NULL.""" + return st.lists(st.sampled_from(names), unique=True, max_size=3).flatmap( + lambda keys: st.fixed_dictionaries({k: VALUES for k in keys}) + ) + + +def relations(names: list[str]) -> st.SearchStrategy: + """A query expression used as a condition: a few rows over some of ``names``.""" + + def over(heading): + rows = st.frozensets(st.tuples(*[VALUES] * len(heading)), max_size=4) if heading else st.just(frozenset({()})) + return rows.map(lambda r: Relation(tuple(heading), r)) + + return st.lists(st.sampled_from(names), unique=True, max_size=3).flatmap(over) + + +def conditions(names: list[str]) -> st.SearchStrategy: + """A dict, a list of dicts, a query expression (Relation) or a Not, over ``names``.""" + return st.one_of( + key_dicts(names), + st.lists(key_dicts(names), max_size=2), + relations(names), + relations(names).map(Not), + key_dicts(names).map(Not), + ) + + +def _edge_and_names(case: Case, data, copyable: bool): + fks = [fk for fk in case.schema.fks if fk.child != fk.parent] + if copyable: + fks = [fk for fk in fks if not fk.renamed and not set(fk.attr_map) & case.schema.nullable[fk.child]] + assume(fks) + fk = data.draw(st.sampled_from(fks), label="fk") + parent, child = case.schema.table(fk.parent), case.schema.table(fk.child) + # The FK's own attributes, three times as likely as the others. + names = list(fk.attr_map) * 3 + sorted(set(parent.heading) | set(child.heading)) + ["unrelated"] + return fk, names + + +@given(data=st.data()) +def test_r1_copy_rule(data): + """ + A condition copied under R1's rule selects the same child rows as the restriction. + + The rule: the attributes the condition names are among the FK's attributes, + the FK is not renamed, and none of its columns is nullable. + """ + case = data.draw(cases(), label="case") + fk, names = _edge_and_names(case, data, copyable=True) + cond = data.draw(conditions(names), label="condition") + assume(copy_allowed(case.schema, fk, cond)) + parents = restrict(case.schema, case.db, fk.parent, cond) + assert restrict(case.schema, case.db, fk.child, cond) == r1_down(case.schema, case.db, fk, parents) + + +# ============================================================================= +# Relabeling materialized keys (diagram.md, Seeds) +# ============================================================================= + + +def _relabel_down_exact(schema: Schema, fk: FK, keys: list[dict]) -> bool: + """ + When relabeling parent keys gives the same child rows as R1. + + Every key names, among the parent's attributes, only those the FK carries; + and if the FK has a nullable column, every key names all its attributes, + none of them NULL. + """ + parent = schema.table(fk.parent) + carried = set(fk.attr_map.values()) + if any(set(key) & set(parent.heading) - carried for key in keys): + return False + if not set(fk.attr_map) & schema.nullable[fk.child]: + return True + return all(carried <= set(key) and all(key[a] is not None for a in carried) for key in keys) + + +@given(data=st.data()) +def test_relabel_down_when_exact(data): + """Relabeled parent keys select the same child rows as R1 when ``_relabel_down_exact`` holds.""" + case = data.draw(cases(), label="case") + fk, names = _edge_and_names(case, data, copyable=False) + keys = data.draw(st.lists(key_dicts(names), max_size=3), label="keys") + assume(_relabel_down_exact(case.schema, fk, keys)) + parents = restrict(case.schema, case.db, fk.parent, keys) + relabeled = relabel_down(fk, keys) + assert restrict(case.schema, case.db, fk.child, relabeled) == r1_down(case.schema, case.db, fk, parents) + + +@given(data=st.data()) +def test_relabel_up_for_keys_read_from_the_child(data): + """Keys read from child rows that contain all the FK's columns, relabeled, select the referenced parents.""" + case = data.draw(cases(), label="case") + fk, _ = _edge_and_names(case, data, copyable=False) + child = case.schema.table(fk.child) + picked = data.draw(st.frozensets(st.sampled_from(sorted(case.db[fk.child])) if case.db[fk.child] else st.nothing())) + extra = data.draw(st.sets(st.sampled_from(child.heading)), label="other attributes") + attrs = set(fk.attr_map) | extra + keys = [{a: case.db[fk.child][k][a] for a in attrs} for k in picked] + relabeled = relabel_up(fk, keys) + referenced = r1_up(case.schema, case.db, fk, {(fk.child, k) for k in picked}) + assert restrict(case.schema, case.db, fk.parent, relabeled) == referenced + + +def _k_schema(): + schema = Schema( + [ + TableSpec("subject", ("subject_id",), ("species",)), + TableSpec("session", ("subject_id", "session_id"), ("quality",)), + TableSpec("trial", ("subject_id", "session_id", "trial_id")), + ], + [ + FK("session", "subject", {"subject_id": "subject_id"}), + FK("trial", "session", {"subject_id": "subject_id", "session_id": "session_id"}), + ], + ) + db = make_db( + schema, + { + "subject": [(1, "mouse"), (2, "rat")], + "session": [(1, 1, 0), (1, 2, 1), (2, 2, 0)], + "trial": [(1, 1, 1), (1, 2, 1), (2, 2, 1)], + }, + ) + return schema, db + + +@pytest.mark.xfail( + strict=True, + reason=( + "docs vs model: diagram.md (Seeds) relabels a dict seed downward whenever the FK has no nullable " + "column, keeping only the keys the FK carries. A dict that names a secondary attribute of the parent " + "loses that key, so `Session & {'quality': 0}` relabels to {} and selects every trial." + ), +) +def test_spec_relabel_rule_drops_a_secondary_attribute(): + """traversal per diagram.md: relabeling ``Session & {"quality": 0}`` across Trial's FK.""" + schema, db = _k_schema() + fk, keys = schema.fks_of("trial")[0], [{"quality": 0}] + assert relabel_down_allowed(schema, fk, keys) + assert relabel_down(fk, keys) == [{}] + r1 = r1_down(schema, db, fk, restrict(schema, db, "session", keys)) + assert r1 == {("trial", (1, 1, 1)), ("trial", (2, 2, 1))} + assert restrict(schema, db, "trial", relabel_down(fk, keys)) == r1 + + +@pytest.mark.xfail( + strict=True, + reason=( + "docs vs model: diagram.md (Seeds) relabels downward over a nullable FK when the keys name all its " + "columns. A key that names a nullable column as None selects no parent, but relabeled it selects the " + "child rows whose FK column is NULL." + ), +) +def test_spec_relabel_rule_with_a_null_key_value(): + """traversal per diagram.md: ``Surgery & {"subject_id": 1, "surgery_id": None}`` relabeled to Session (S3's schema).""" + schema = Schema( + [ + TableSpec("subject", ("subject_id",)), + TableSpec("surgery", ("subject_id", "surgery_id")), + TableSpec("session", ("subject_id", "session_id"), ("surgery_id",)), + ], + [ + FK("surgery", "subject", {"subject_id": "subject_id"}), + FK("session", "subject", {"subject_id": "subject_id"}), + FK("session", "surgery", {"subject_id": "subject_id", "surgery_id": "surgery_id"}, nullable=True), + ], + ) + db = make_db( + schema, + {"subject": [(1,)], "surgery": [(1, 1)], "session": [(1, 1, 1), (1, 2, None)]}, + ) + fk, keys = schema.fks_of("session")[1], [{"subject_id": 1, "surgery_id": None}] + assert relabel_down_allowed(schema, fk, keys) + r1 = r1_down(schema, db, fk, restrict(schema, db, "surgery", keys)) + assert r1 == set() + assert restrict(schema, db, "session", relabel_down(fk, keys)) == r1 + + +# ============================================================================= +# A1 generalized to owning chains with renames (#1429) +# ============================================================================= + + +def _issue_1429_case_1(a1: str): + """``PartB`` references ``Master.PartA.proj(src_master="master_id", src_part="part_a_id")`` and has no master_id.""" + schema = Schema( + [ + TableSpec("master", ("master_id",)), + TableSpec("master__part_a", ("master_id", "part_a_id"), master="master"), + TableSpec("master__part_b", ("src_master", "src_part", "part_b_id"), master="master"), + ], + [ + FK("master__part_a", "master", {"master_id": "master_id"}), + FK("master__part_b", "master__part_a", {"src_master": "master_id", "src_part": "part_a_id"}), + ], + a1=a1, + ) + db = make_db( + schema, + {"master": [(1,), (2,)], "master__part_a": [(1, 1), (2, 1)], "master__part_b": [(1, 1, 1), (2, 1, 1)]}, + ) + return schema, db + + +def test_issue_1429_case_1_under_strict_a1(): + """Under the specification's A1, PartB violates A1: no lift, and it is not part of an instance.""" + schema, db = _issue_1429_case_1("strict") + assert schema.a1_parts == {"master": ("master__part_a",)} + g = RowGraph(schema, db) + assert g.D({("master__part_b", (1, 1, 1))}) == {("master__part_b", (1, 1, 1))} + assert g.U({("master__part_a", (1, 1))}) == {("master__part_a", (1, 1)), ("master", (1,))} + + +def test_issue_1429_case_1_under_chain_a1(): + """ + Under the chain reading, PartB is a member whose owning attribute is src_master. + + The lift takes master_id from src_master, so R2's ``M & R(Pi).proj(*M.primary_key)`` + must rename it: PartB has no master_id. The FK from PartB to PartA renames the + master key yet preserves the instance, so the rule of diagram.md iterates the + group although one pass would be exact. + """ + schema, db = _issue_1429_case_1("chain") + assert schema.a1_parts == {"master": ("master__part_a", "master__part_b")} + assert schema.owning_attributes("master__part_b") == ("src_master",) + g = RowGraph(schema, db) + instance_1 = {("master", (1,)), ("master__part_a", (1, 1)), ("master__part_b", (1, 1, 1))} + assert g.D({("master__part_b", (1, 1, 1))}) == instance_1 + assert g.U({("master__part_a", (1, 1))}) == instance_1 + (fk,) = schema.intra_group_fks("master")[1:] + assert renames_master_key(schema, fk) and preserves_instance(schema, fk) + for direction in ("down", "up"): + assert one_pass(schema, db, {("master__part_b", (1, 1, 1))}, direction, iterate=None)[0] == instance_1 + + +def test_chain_a1_makes_g6_a_member(): + """ + Under the chain reading, G6's Probe.Channel satisfies A1 through ``-> master.proj(host_id="probe_id")``. + + The catalog's G6 rows then change: channel (1, 1) lifts probe 1 and brings channel (1, 2). + """ + tables = [TableSpec("probe", ("probe_id",)), TableSpec("probe__channel", ("host_id", "channel_id"), master="probe")] + fks = [FK("probe__channel", "probe", {"host_id": "probe_id"})] + data = {"probe": [(1,), (2,)], "probe__channel": [(1, 1), (1, 2), (2, 1)]} + strict, chain = Schema(tables, fks), Schema(tables, fks, a1="chain") + assert strict.a1_parts == {"probe": ()} + assert chain.a1_parts == {"probe": ("probe__channel",)} + seed = {("probe__channel", (1, 1))} + assert RowGraph(strict, make_db(strict, data)).D(seed) == seed + assert RowGraph(chain, make_db(chain, data)).D(seed) == { + ("probe__channel", (1, 1)), + ("probe", (1,)), + ("probe__channel", (1, 2)), + } + + +def test_generator_draws_chain_members(): + """Under the chain reading, the generator draws members whose owning attributes are renamed.""" + find( + schemas(a1="chain"), + lambda s: any(s.owning_attributes(p) != s.table(s.group[p]).pk for p in s.group), + settings=_FIND_SETTINGS, + ) From 6f0775e36a46ea9294c01a97dca55214ebf6849f Mon Sep 17 00:00:00 2001 From: Dimitri Yatsenko Date: Sat, 17 Oct 2026 17:46:46 -0500 Subject: [PATCH 4/4] test(diagram): define groups by ownership, as the revised spec does Follow the revised traversal specification (datajoint-docs#249): - A1 is ownership: a part is owned if an FK in its primary key references the master or an owned part of the same master, renames allowed. Its owning attributes come from that chain, preferring an unrenamed -> master and otherwise the first owning reference. This replaces the strict reading and the a1 switch; a part that references its master only below the line is no longer in its group. - The lift goes through the owning attributes, and a group iterates if and only if some FK inside it does not map owning attributes onto owning attributes. The relabel rule is the revised one. - The shape catalog gains the rewritten G6 (a part its master does not own) and G7 (an owned part-of-part with renamed attributes, #1429). The three xfails for the spec's iteration and relabel rules are gone: the docs now state the model's rules, and the tests assert them. Two strict xfails remain for T8: topo_sort still groups every part with its master by name, while the spec makes a part that is not owned a plain table (#1619). test_expand_spec.py opens with a description of the test layers and of the Hypothesis profiles. --- tests/unit/expand_model.py | 179 +++--- tests/unit/test_expand_shapes_model.py | 126 ++++- tests/unit/test_expand_spec.py | 750 +++++++++---------------- 3 files changed, 435 insertions(+), 620 deletions(-) diff --git a/tests/unit/expand_model.py b/tests/unit/expand_model.py index 54c1352c9..5c94862aa 100644 --- a/tests/unit/expand_model.py +++ b/tests/unit/expand_model.py @@ -22,7 +22,7 @@ The module also models parts of the specification's "Computation" section, so that its claims can be checked against the definitions: the table-level reach (the node set of an expanded diagram), the one pass over the group quotient with -its iterating groups, and the conditions under which R1 may copy a condition or +its iterating groups, and the rules under which R1 may copy a condition or relabel materialized keys. Model @@ -32,10 +32,10 @@ Parallel FKs between the same two tables are separate edges. - A2: an FK with any NULL column references nothing, as in SQL's MATCH SIMPLE. FKs can share columns, so partial NULLs occur. -- A1, the owning reference: a part's heading contains its master's primary key, - unrenamed and non-null, inherited through ``-> master`` directly or through a - sibling part. A part that violates A1 is traversed as a plain table. - ``Schema(..., a1="chain")`` selects a generalized A1, described in ``Schema``. +- A1, owned parts: a part is owned by its master if an FK in its primary key + references the master or an owned part of the same master, renames allowed. + The chain maps the part's owning attributes onto the master's key. A master + and its owned parts form its group; a part that is not owned is a plain table. - A database is ``{table: {pk_tuple: row_dict}}``, where ``pk_tuple`` lists the primary-key values in the order of ``TableSpec.pk``. """ @@ -130,16 +130,6 @@ class Schema: Tables in declaration order. fks : iterable of FK Foreign keys. Each child's FKs are listed in declaration order. - a1 : {"strict", "chain"}, optional - Which owning reference makes a part a member of its master's group. - - - ``"strict"`` (the specification): an FK to the master, or to a sibling - that is a member, that carries the master's key unrenamed and non-null. - - ``"chain"`` (a generalization, for #1429): also a primary-key FK, with - renames allowed, to the master or to a member whose owning attributes - lie in its primary key. The owning attributes are then composed along - the chain. When a part has several such FKs, an unrenamed one wins, - and otherwise the first in declaration order. Notes ----- @@ -149,18 +139,19 @@ class Schema: FK that includes it is nullable, as in ``declare.py``, where an FK adds only the attributes not declared yet. Primary-key attributes and attributes outside every FK are never NULL. - - **Groups.** Each master that has parts forms a group with its member - parts. ``group[t]`` names the master of table ``t`` for the master itself - and for its members. A part that is not a member is in no group. + - **Owned parts and groups** (A1). A part is owned if an FK in its primary + key (every attribute of the FK in the primary key, as ``Dependencies`` + marks it ``primary``) references the master or an owned part of the same + master, renamed or not. Each master that has parts forms a group with its + owned parts; ``group[t]`` names the master of table ``t`` for the master + and for its owned parts. A part that is not owned is in no group. - **Owning attributes.** ``owning[t]`` maps each attribute of the master's - primary key to the attribute of member ``t`` that holds it: the identity - for the master, and for every member under ``a1="strict"``. + key to the attribute of ``t`` that the owning chain maps onto it: the + identity for the master. If a part has several owning references, an + unrenamed ``-> master`` wins, and otherwise the first in its definition. """ - def __init__(self, tables: Iterable[TableSpec], fks: Iterable[FK], a1: str = "strict") -> None: - if a1 not in ("strict", "chain"): - raise ValueError(f"a1 must be 'strict' or 'chain', not {a1!r}") - self.a1 = a1 + def __init__(self, tables: Iterable[TableSpec], fks: Iterable[FK]) -> None: self.tables = tuple(tables) self.fks = tuple(fks) self._by_name = {t.name: t for t in self.tables} @@ -168,14 +159,14 @@ def __init__(self, tables: Iterable[TableSpec], fks: Iterable[FK], a1: str = "st raise ValueError("table names must be unique") self._validate() self.nullable = {t.name: self._nullable_attributes(t) for t in self.tables} - self.a1_parts = {} + self.owned_parts = {} self.group = {} self.owning = {} for t in self.tables: parts = [p for p in self.tables if p.master == t.name] if parts: - owning = self._find_members(t, parts) - self.a1_parts[t.name] = tuple(p.name for p in parts if p.name in owning) + owning = self._find_owned(t, parts) + self.owned_parts[t.name] = tuple(p.name for p in parts if p.name in owning) self.owning.update(owning) self.group.update({name: t.name for name in owning}) @@ -187,8 +178,7 @@ def __repr__(self) -> str: for fk in self.fks: null = ", nullable=True" if fk.nullable else "" lines.append(f" FK({fk.child!r}, {fk.parent!r}, {dict(fk.attr_map)!r}{null}),") - a1 = "" if self.a1 == "strict" else f" a1={self.a1!r},\n" - return "\n".join(lines) + "\n" + a1 + ")" + return "\n".join(lines) + "\n)" def table(self, name: str) -> TableSpec: """Return the table named ``name``.""" @@ -247,46 +237,20 @@ def _nullable_attributes(self, table: TableSpec) -> frozenset[str]: nullable.add(attr) return frozenset(nullable) - def _find_members(self, master: TableSpec, parts: list[TableSpec]) -> dict[str, dict[str, str]]: - # The strict owning reference is found first, to a fixed point, so that - # the result does not depend on the order in which the parts are listed. - # Under a1="chain", primary-key FKs with renames are then accepted for - # the parts left over. + def _find_owned(self, master: TableSpec, parts: list[TableSpec]) -> dict[str, dict[str, str]]: + # Parts are taken in declaration order, so an owning reference to another + # part is decided after that part (an FK references a table declared earlier). owning = {master.name: {a: a for a in master.pk}} - rules = [self._strict_owning] + ([self._chain_owning] if self.a1 == "chain" else []) - for rule in rules: - changed = True - while changed: - changed = False - for part in parts: - if part.name in owning: - continue - for fk in self.fks_of(part.name): - found = rule(fk, master.pk, owning) - if found is not None: - owning[part.name] = found - changed = True - break + for part in parts: + chains = [fk for fk in self.fks_of(part.name) if fk.parent in owning and self.is_primary(fk)] + if not chains: + continue + unrenamed = [fk for fk in chains if fk.parent == master.name and not fk.renamed] + fk = (unrenamed or chains)[0] + by_parent_attr = {p: c for c, p in fk.attr_map.items()} + owning[part.name] = {a: by_parent_attr[owning[fk.parent][a]] for a in master.pk} return owning - def _strict_owning(self, fk: FK, master_key: tuple, owning: dict) -> dict | None: - # Unrenamed and non-null: the part holds the master's key under its names. - if fk.parent == fk.child or fk.parent not in owning or any(owning[fk.parent][a] != a for a in master_key): - return None - if all(fk.attr_map.get(a) == a and a not in self.nullable[fk.child] for a in master_key): - return {a: a for a in master_key} - return None - - def _chain_owning(self, fk: FK, master_key: tuple, owning: dict) -> dict | None: - # A primary-key FK to a member whose owning attributes it carries, renamed or not. - if fk.parent == fk.child or fk.parent not in owning or not self.is_primary(fk): - return None - by_parent_attr = {p: c for c, p in fk.attr_map.items()} - parent_owning = owning[fk.parent] - if not all(parent_owning[a] in by_parent_attr for a in master_key): - return None # the parent's owning attributes are not in its primary key - return {a: by_parent_attr[parent_owning[a]] for a in master_key} - # ============================================================================= # Databases and references @@ -356,9 +320,9 @@ def master_of(schema: Schema, table: str, row: Mapping) -> Row | None: """ ``master(r)``: identifier of the master row of a row in a group. - The master row is named by the row's owning attributes, which A1 guarantees - are present and non-null. Returns None for a row in no group. The identifier - is returned whether or not the master row exists. + The master row is named by the row's owning attributes, which lie in the + primary key and so are never NULL. Returns None for a row in no group. The + identifier is returned whether or not the master row exists. """ attrs = schema.owning_attributes(table) if attrs is None: @@ -406,8 +370,8 @@ def group_whole(schema: Schema, db: DB) -> bool: delete must remove each master-part instance whole or not at all, which ``RowGraph.group_closed`` checks on the deleted rows. Removing one part row of an instance leaves a database that still passes this check, yet breaks - P2. Under A1 the check also follows from ``ri_ok``, since each part row - references its master directly or through a sibling. + P2. For owned parts the check also follows from ``ri_ok``, since each part + row references its master along its owning chain. """ for table, master in schema.group.items(): if table == master: @@ -423,8 +387,8 @@ def quotient(schema: Schema) -> nx.DiGraph: """ The group quotient Q: the FK graph with each master-part group contracted to one node. - FKs between two tables of the same group are left out. A part that violates - A1 is in no group and stays its own node. A self-referencing FK is a + FKs between two tables of the same group are left out. A part that is not + owned is in no group and stays its own node. A self-referencing FK is a self-loop, which counts as a cycle. """ node = {t.name: schema.group.get(t.name, t.name) for t in schema.tables} @@ -447,7 +411,7 @@ def table_reach(schema: Schema, tables: Iterable[str], direction: str = "down", The node set of an expanded diagram: the table-level reach of ``tables``. The tables reached by following FKs in the direction of travel, together - with the master and all member parts of every group that has a reached + with the master and all owned parts of every group that has a reached member. ``group_step=False`` leaves the group step out (``D1``). For ``"both"``, it is the upward reach of the downward reach. """ @@ -465,7 +429,7 @@ def table_reach(schema: Schema, tables: Iterable[str], direction: str = "down", nxt = {fk.parent for fk in schema.fks if fk.child == t} if group_step and t in schema.group: master = schema.group[t] - nxt |= {master, *schema.a1_parts[master]} + nxt |= {master, *schema.owned_parts[master]} for n in nxt - seen: seen.add(n) stack.append(n) @@ -497,8 +461,8 @@ class RowGraph: - ``child(r) = {c : ref_e(c) = r}`` and ``parent(r) = {ref_e(r)}``, over every FK edge e. - ``inst(r)`` is the master-part instance of a row in a group: the master - row and every member part row that carries the same master key. For a row - in no group, ``inst(r) = {r}``. + row and every owned part row whose owning attributes hold the same master + key. For a row in no group, ``inst(r) = {r}``. - ``down = child ∪ inst`` and ``up = parent ∪ inst``, so ``up`` is the converse of ``down``. """ @@ -635,30 +599,23 @@ def group_closed(self, rows: Iterable[Row]) -> bool: def preserves_instance(schema: Schema, fk: FK) -> bool: """ - True if an FK inside a group always references a row of the same instance. + True if an FK inside a group maps the referencing part's owning attributes + onto the referenced table's owning attributes, so it stays in the instance. - It does when it maps the child's owning attribute of each master-key - attribute onto the parent's owning attribute of the same one. + A group iterates if and only if some FK inside it does not. """ master = schema.group[fk.child] child_owning, parent_owning = schema.owning[fk.child], schema.owning[fk.parent] return all(fk.attr_map.get(child_owning[a]) == parent_owning[a] for a in schema.table(master).pk) -def renames_master_key(schema: Schema, fk: FK) -> bool: - """The specification's test for an iterating group: the FK renames an attribute of the master's key.""" - parent_owning = set(schema.owning[fk.parent].values()) - return any(c != p for c, p in fk.attr_map.items() if p in parent_owning) - - -ITERATION_TESTS = { - "spec": renames_master_key, - "exact": lambda schema, fk: not preserves_instance(schema, fk), -} +def iterates(schema: Schema, master: str) -> bool: + """Whether the group of ``master`` iterates: some FK inside it does not preserve the instance.""" + return not all(preserves_instance(schema, fk) for fk in schema.intra_group_fks(master)) def one_pass( - schema: Schema, db: DB, seed: Iterable[Row], direction: str = "down", iterate: str | None = "exact" + schema: Schema, db: DB, seed: Iterable[Row], direction: str = "down", iterate: bool = True ) -> tuple[frozenset[Row], dict]: """ The engine's computation, evaluated on rows: one visit per node of Q. @@ -666,10 +623,10 @@ def one_pass( The nodes of Q are visited in topological order for ``"down"`` and in reverse order for ``"up"``. A plain table takes its seed rows and the rows that R1 reaches from its neighbors on the incoming side. A group takes the - master keys of its seed rows and of the rows R1 reaches in any of its - tables, then all rows of those instances. A group that iterates repeats the - group step, following the FKs inside the group, until no new master key - appears. + master keys of its seed rows and of the rows R1 reaches in any of its tables + (the lift, through the owning attributes), then all rows of those instances + (the re-expansion). A group that iterates repeats the group step, following + the FKs inside the group, until no new master key appears. Parameters ---------- @@ -679,11 +636,9 @@ def one_pass( The seed rows. direction : {"down", "up"} The direction of travel. - iterate : {"exact", "spec"} or None - Which groups iterate: those with an FK inside the group that does not - preserve the instance (``"exact"``), those with an FK inside the group - that renames the master's key (``"spec"``, the rule in ``diagram.md``), - or none. + iterate : bool + Whether the groups that ``iterates`` selects repeat the group step. With + False, every group takes one step. Returns ------- @@ -704,7 +659,6 @@ def one_pass( members = defaultdict(list) for t in schema.tables: members[node_of[t.name]].append(t.name) - test = ITERATION_TESTS[iterate] if iterate else None def rows_of(table): return {(table, k) for k in db[table]} @@ -727,16 +681,16 @@ def crossing(fk, reached): outside = fk.parent if direction == "down" else fk.child if inside in mem and node_of[outside] != node: incoming |= crossing(fk, reached) - if node not in schema.a1_parts: + if node not in schema.owned_parts: reached |= incoming continue inner = schema.intra_group_fks(node) - iterates = test is not None and any(test(schema, fk) for fk in inner) + repeat = iterate and iterates(schema, node) keys = {graph.master(r) for r in incoming} history = [set(keys)] while True: instance = {r for t in mem for r in rows_of(t) if graph.master(r) in keys} - if not iterates: + if not repeat: break step = set().union(*(crossing(fk, instance) for fk in inner)) grown = keys | {graph.master(r) for r in step} @@ -866,9 +820,20 @@ def relabel_up(fk: FK, keys: list[dict]) -> list[dict]: def relabel_down_allowed(schema: Schema, fk: FK, keys: list[dict]) -> bool: - """The Seeds rule for relabeling downward: the FK has no nullable column, or every key names all its columns.""" - parent_attrs = set(fk.attr_map.values()) - return not set(fk.attr_map) & schema.nullable[fk.child] or all(parent_attrs <= set(key) for key in keys) + """ + The Seeds rule for relabeling parent keys downward. + + Every key names, among the parent's attributes, only attributes the FK + carries; and if the FK has a nullable column, every key names all of the + FK's attributes, none of them NULL. + """ + parent = schema.table(fk.parent) + carried = set(fk.attr_map.values()) + if any(set(key) & set(parent.heading) - carried for key in keys): + return False + if not set(fk.attr_map) & schema.nullable[fk.child]: + return True + return all(carried <= set(key) and all(key[a] is not None for a in carried) for key in keys) # ============================================================================= diff --git a/tests/unit/test_expand_shapes_model.py b/tests/unit/test_expand_shapes_model.py index 979990750..910bef785 100644 --- a/tests/unit/test_expand_shapes_model.py +++ b/tests/unit/test_expand_shapes_model.py @@ -41,13 +41,14 @@ copy_allowed, dependency_graph, group_whole, + iterates, make_db, one_pass, + preserves_instance, q_acyclic, r1_down, relabel_down_allowed, relabel_up, - renames_master_key, restrict, ri_ok, subdatabase, @@ -363,8 +364,11 @@ def test_G2_part_of_part_with_two_chains(): "up", {"master__p": [(1, 100)], "master": [1], "master__q": [(1, 10)], "ext": [1, 2]}, ) - # Why: the lift is one step on master_id, so the group needs no iteration. - assert not any(renames_master_key(s.schema, fk) for fk in s.schema.intra_group_fks("master")) + # Why: the lift is one step on master_id, which every part row carries; the + # references inside the group map owning attributes onto owning attributes, + # so the group does not iterate. + assert s.schema.owning_attributes("master__p") == ("master_id",) + assert not iterates(s.schema, "master") def test_G3_second_renamed_fk_to_its_own_master(): @@ -383,11 +387,14 @@ def test_G3_second_renamed_fk_to_its_own_master(): seed = s.seed("session__link", {"session_id": 1}, [(1, 1)]) s.check("session__link", seed, "down", {"session__link": [(1, 1), (3, 1)], "session": [1, 3]}) s.check("session__link", seed, "up", {"session__link": [(1, 1)], "session": [1, 2]}) - # Why: the renamed FK links one instance to another, so this group iterates. One - # pass alone would stop at session 1; with the iteration it reaches session 3. - assert renames_master_key(s.schema, s.fk("session__link", "session", "other_session")) - assert one_pass(s.schema, s.db, seed, "down", iterate=None)[0] == rows({"session__link": [(1, 1)], "session": [1]}) - assert one_pass(s.schema, s.db, seed, "down", iterate="spec")[0] == s.g.D(seed) + # Why: Link is owned through -> master, so its owning attribute is session_id. + # The second FK maps other_session, not an owning attribute, onto the master's + # key, so the group iterates. One pass alone stops at session 1. + assert s.schema.owning_attributes("session__link") == ("session_id",) + assert not preserves_instance(s.schema, s.fk("session__link", "session", "other_session")) + assert iterates(s.schema, "session") + assert one_pass(s.schema, s.db, seed, "down", iterate=False)[0] == rows({"session__link": [(1, 1)], "session": [1]}) + assert one_pass(s.schema, s.db, seed, "down")[0] == s.g.D(seed) # With part_integrity="enforce", deleting session 1 raises: link (3, 1) would go without session 3. with pytest.raises(PartIntegrityError, match=r"session__link.*\(3, 1\)"): s.g.delete(rows({"session": [1]}), "enforce") @@ -428,14 +435,16 @@ def test_G4_renamed_sibling_fk_chained_across_three_instances(): "both", {"run": [1, 2, 3], "run__step": [(1, 1), (2, 1), (3, 1)], "run__carry": [(2, 1), (3, 1)]}, ) - # Why: one pass over the group would stop at run 1. Iterating, the master keys grow - # as {1}, {1, 2}, {1, 2, 3}, and a fourth round adds nothing. Upward the chain runs - # the other way, from run 2 back to run 1. - assert one_pass(s.schema, s.db, run1, "down", iterate=None)[0] == rows({"run": [1], "run__step": [(1, 1)]}) - found, rounds = one_pass(s.schema, s.db, run1, "down", iterate="spec") + # Why: one pass over the group would stop at run 1. Carry's reference to Step maps + # prev_run, not Carry's owning attribute run_id, onto Step's owning attribute, so + # the group step repeats: the master keys grow as {1}, {1, 2}, {1, 2, 3}, and a + # fourth round adds nothing. Upward the chain runs the other way, from run 2 back to run 1. + assert not preserves_instance(s.schema, s.fk("run__carry", "run__step")) + assert one_pass(s.schema, s.db, run1, "down", iterate=False)[0] == rows({"run": [1], "run__step": [(1, 1)]}) + found, rounds = one_pass(s.schema, s.db, run1, "down") assert found == s.g.D(run1) assert rounds["run"] == [rows({"run": [1]}), rows({"run": [1, 2]}), rows({"run": [1, 2, 3]})] - found, rounds = one_pass(s.schema, s.db, run2, "up", iterate="spec") + found, rounds = one_pass(s.schema, s.db, run2, "up") assert found == s.g.U(run2) assert rounds["run"] == [rows({"run": [2]}), rows({"run": [1, 2]})] @@ -493,19 +502,86 @@ def test_G5_merge_table(): s.g.delete(seed, "enforce") -def test_G6_part_violating_a1(): - """traversal-shapes.md, G6. A part violating A1, traversed as a plain table.""" - s = Shape( +def test_G6_part_its_master_does_not_own(): + """traversal-shapes.md, G6. A part its master does not own, traversed as a plain table.""" + tables = [TableSpec("probe", ("probe_id",)), TableSpec("probe__channel", ("channel_id",), ("probe_id",), master="probe")] + data = {"probe": [(1,), (2,)], "probe__channel": [(1, 1), (2, 1), (3, 2)]} + s = Shape(tables, [FK("probe__channel", "probe", {"probe_id": "probe_id"})], data) + assert s.schema.owned_parts == {"probe": ()} + seed = s.seed("probe__channel", {"channel_id": 1}, [1]) + s.check("probe__channel", seed, "down", {"probe__channel": [1]}) + s.check("probe__channel", seed, "up", {"probe__channel": [1], "probe": [1]}) + seed = s.seed("probe", {"probe_id": 1}, [1]) + s.check("probe", seed, "down", {"probe": [1], "probe__channel": [1, 2]}) + # A nullable reference, -> [nullable] master, has the same effect. + nullable = Schema(tables, [FK("probe__channel", "probe", {"probe_id": "probe_id"}, nullable=True)]) + assert nullable.owned_parts == {"probe": ()} + # Moving the reference into the primary key, as -> master or renamed as + # -> master.proj(host_id="probe_id"), would make the part owned. + in_key = Schema( + [TableSpec("probe", ("probe_id",)), TableSpec("probe__channel", ("probe_id", "channel_id"), master="probe")], + [FK("probe__channel", "probe", {"probe_id": "probe_id"})], + ) + renamed = Schema( [TableSpec("probe", ("probe_id",)), TableSpec("probe__channel", ("host_id", "channel_id"), master="probe")], [FK("probe__channel", "probe", {"host_id": "probe_id"})], - {"probe": [(1,), (2,)], "probe__channel": [(1, 1), (1, 2), (2, 1)]}, ) - assert s.schema.a1_parts == {"probe": ()} - seed = s.seed("probe__channel", {"host_id": 1, "channel_id": 1}, [(1, 1)]) - s.check("probe__channel", seed, "down", {"probe__channel": [(1, 1)]}) - s.check("probe__channel", seed, "up", {"probe__channel": [(1, 1)], "probe": [1]}) - seed = s.seed("probe", {"probe_id": 1}, [1]) - s.check("probe", seed, "down", {"probe": [1], "probe__channel": [(1, 1), (1, 2)]}) + assert in_key.owned_parts == renamed.owned_parts == {"probe": ("probe__channel",)} + assert renamed.owning_attributes("probe__channel") == ("host_id",) + + +def test_G7_owned_part_of_part_with_renamed_attributes(): + """traversal-shapes.md, G7. Owned part-of-part with renamed attributes (#1429, Case 1).""" + s = Shape( + [ + TableSpec("master", ("master_id",)), + TableSpec("master__part_a", ("master_id", "part_a_id"), master="master"), + TableSpec("master__part_b", ("src_master", "src_part", "part_b_id"), master="master"), + TableSpec("report", ("master_id", "report_id")), + ], + [ + FK("master__part_a", "master", {"master_id": "master_id"}), + FK("master__part_b", "master__part_a", {"src_master": "master_id", "src_part": "part_a_id"}), + FK("report", "master", {"master_id": "master_id"}), + ], + { + "master": [(1,), (2,), (3,)], + "master__part_a": [(1, 10), (1, 11), (2, 20)], + "master__part_b": [(1, 10, 100), (1, 11, 110), (2, 20, 200)], + "report": [(1, 1), (2, 1)], + }, + ) + # PartB has no master_id; it is owned through PartA, and its owning attribute is src_master. + assert s.schema.owned_parts == {"master": ("master__part_a", "master__part_b")} + assert s.schema.owning_attributes("master__part_b") == ("src_master",) + seed = s.seed("master__part_b", {"part_b_id": 100}, [(1, 10, 100)]) + s.check( + "master__part_b", + seed, + "down", + { + "master__part_b": [(1, 10, 100), (1, 11, 110)], + "master": [1], + "master__part_a": [(1, 10), (1, 11)], + "report": [(1, 1)], + }, + ) + s.check( + "master__part_b", + seed, + "up", + {"master__part_b": [(1, 10, 100), (1, 11, 110)], "master": [1], "master__part_a": [(1, 10), (1, 11)]}, + ) + # Why: the lift renames the owning attribute onto the master's key, + # Master & R(PartB).proj(master_id="src_master"), and selects master 1 in one step. + lifted = Relation(("master_id",), frozenset({(s.db["master__part_b"][k]["src_master"],) for _, k in seed})) + assert restrict(s.schema, s.db, "master", lifted) == rows({"master": [1]}) + # PartB's reference to PartA maps src_master onto PartA's owning attribute, so the group does not iterate. + assert not iterates(s.schema, "master") + assert one_pass(s.schema, s.db, seed, "down", iterate=False)[0] == s.g.D(seed) + # With part_integrity="enforce", deleting the seed raises: part B row (1, 10, 100) would go without master 1. + with pytest.raises(PartIntegrityError, match=r"master__part_b.*\(1, 10, 100\)"): + s.g.delete(seed, "enforce") # ============================================================================= @@ -672,7 +748,7 @@ def test_X2_group_cycle(): FK("session__note", "review", {"session_id": "session_id"}), ], ) - assert after.a1_parts == {"session": ("session__trial", "session__note")} + assert after.owned_parts == {"session": ("session__trial", "session__note")} # Note is owned: -> master in its key assert not q_acyclic(after) # Session -> Review -> Session.Note, which belongs to Session's group with pytest.raises(nx.NetworkXUnfeasible): dependency_graph(after).topo_sort() diff --git a/tests/unit/test_expand_spec.py b/tests/unit/test_expand_spec.py index d4bb35088..e1cd31bf1 100644 --- a/tests/unit/test_expand_spec.py +++ b/tests/unit/test_expand_spec.py @@ -1,36 +1,50 @@ """ -Property tests of the ``Diagram.expand`` reference model against the specification. - -The specification states eight results, T1-T8, about the operations ``D``, -``D1``, ``U`` and ``both`` (see ``expand_model.py``). Each one is tested here on -random schemas and databases drawn by Hypothesis, with no database server: - -- T1 closure: D and U are extensive, monotone and idempotent. -- T2 additivity: ``X(A ∪ B) = X(A) ∪ X(B)`` for D, U and both. +Tests of the ``Diagram.expand`` specification, without a database. + +The traversal specification (the "Traversal specification" section of +``diagram.md`` and the shape catalog ``traversal-shapes.md`` in datajoint-docs) +is tested in three layers that need no database server: + +- ``expand_model.py`` is the reference model: an executable copy of the + definitions. It holds rows in Python dictionaries and computes ``D``, ``D1``, + ``U`` and ``both`` as plain closures over row identifiers. +- This module tests the model against the specification's derived results + T1-T8, on random schemas and databases drawn by Hypothesis, and checks the + specification's computation rules (the one pass over the group quotient, the + groups that iterate, R1's copy rule, relabeling) against the definitions. +- ``test_expand_shapes_model.py`` mirrors the shape catalog one to one: each + shape's schema, rows, seeds and the rows the catalog states, by hand. + +Later layers run the engine against the same model on MySQL and PostgreSQL. + +Running +------- +``pytest tests/unit/test_expand_spec.py tests/unit/test_expand_shapes_model.py``. +``HYPOTHESIS_PROFILE`` selects the number of examples (``tests/conftest.py``): + +- ``dev`` (the default outside CI): 50 examples per property, for quick runs; +- ``ci`` (the default when ``CI`` is set): 500 examples, the same on every run; +- ``thorough``: 2000 random examples; failing examples are saved under the + gitignored ``.hypothesis/`` directory and replayed first on the next run. + +The derived results +------------------- +- T1 closure: D, U and D1 are extensive, monotone and idempotent. +- T2 additivity: ``X(A ∪ B) = X(A) ∪ X(B)`` for D, U, D1 and both. - T3 duality: ``y ∈ D({x})`` if and only if ``x ∈ U({y})``. - T4 delete safety: removing D(S) leaves a consistent database consistent, and D(S) is the least such superset of S. - T5 read boundary: U(S) is consistent on its own and is the least such superset. -- T6 both: U(D(S)) is up-closed, group-closed and contains D(S); ``both`` is - not idempotent. +- T6 both: U(D(S)) contains D(S), is up-closed and group-closed, and is the least + such superset; ``both`` is extensive and monotone but not idempotent. - T7 enforce: D1(S) is lift-closed if and only if D1(S) = D(S). -- T8 ordering: under A1, ``datajoint.dependencies.topo_sort`` succeeds if and - only if the group quotient Q is acyclic, and its order is valid. - -T1-T8 run under both readings of A1: the specification's, and a generalization -to owning chains with renames (#1429; ``Schema(..., a1="chain")``). - -Then come checks of the specification's computation (``diagram.md``, -"Computation") against the definitions: the one pass over Q and the groups that -iterate, R1's rule for copying a condition, and the rule for relabeling -materialized keys. Each claim that does not hold is kept as a strict ``xfail`` -with a minimal counterexample. The tests of ``self.upstream`` for a row not -inserted yet, and of the chain reading of A1, come last. - -Drawn schemas may contain group cycles (Q cyclic), which E1b refuses at -declaration. T1-T7 and the other row-level properties are stated for acyclic Q -and discard the other schemas with ``assume``; T8 tests both cases. Select the -number of examples with ``HYPOTHESIS_PROFILE`` (see ``tests/conftest.py``). +- T8 ordering: ``datajoint.dependencies.topo_sort`` succeeds if and only if the + group quotient Q is acyclic, and its order is valid. + +Drawn schemas may contain group cycles (Q cyclic), which the declaration check +refuses (#1619). The row-level properties are stated for acyclic Q and discard +the other schemas with ``assume``; T8 tests both cases. A claim the engine does +not meet yet is kept as a strict ``xfail`` with a minimal example. """ from __future__ import annotations @@ -62,6 +76,7 @@ fk_value, full_table_name, group_whole, + iterates, make_db, one_pass, preserves_instance, @@ -71,7 +86,6 @@ relabel_down, relabel_down_allowed, relabel_up, - renames_master_key, restrict, ri_ok, subdatabase, @@ -194,21 +208,21 @@ def _finish(draw, builder: _TableBuilder, tables: list[TableSpec], fks: list, ow @st.composite -def schemas(draw, max_tables: int = 7, a1_only: bool = False, a1: str = "strict") -> Schema: +def schemas(draw, max_tables: int = 7, owned_only: bool = False) -> Schema: """ Draw a schema: tables in declaration order, so the FK graph is a DAG. - Each step declares a plain table, a master, or a part of a master declared earlier, so parts can be added late, after other tables. - - A master has 0-3 parts. A part's owning reference is direct (``-> master`` - in the primary key), through a sibling part, or secondary. Otherwise it is - missing, nullable, or renamed: to the master, or to a sibling as in #1429. - A renamed owning reference in the primary key violates A1 under - ``a1="strict"`` and satisfies it under ``a1="chain"``. With ``a1_only``, - every part satisfies A1. + - A master has 0-3 parts. A part is owned through ``-> master`` in its + primary key, a renamed one, or a primary-key FK to an owned sibling with + or without renames (#1429). Unless ``owned_only``, a part may also be not + owned: it references the master only below the line, or through a + nullable FK, or through a part that is not owned, or not at all. - Other FKs are primary or secondary, nullable, renamed and parallel, to any table declared earlier. Diamonds that share attributes, part -> part across - groups, renamed references within a group and group cycles all occur. + groups, references inside a group that link instances, and group cycles + all occur. - Some tables get a namesake attribute that belongs to no FK. """ fresh = itertools.count() @@ -227,55 +241,39 @@ def schemas(draw, max_tables: int = 7, a1_only: bool = False, a1: str = "strict" parts_of[spec.name] = [] else: master = draw(st.sampled_from(open_masters)) - mspec, mk = by_name[master], by_name[master].pk + mspec = by_name[master] builder = _TableBuilder(f"{master}__p{len(parts_of[master])}", master=master) - so_far = Schema(tables, fks, a1=a1) - members = [p for p in parts_of[master] if p in so_far.group] - # Siblings an FK can inherit the key from: unrenamed (key in the primary key under - # the master's names), or renamed (owning attributes in the primary key). - carrying = [p for p in members if so_far.owning_attributes(p) == mk and set(mk) <= set(by_name[p].pk)] - chainable = [p for p in members if set(so_far.owning_attributes(p)) <= set(by_name[p].pk)] - renamed_modes = ["renamed"] + (["renamed sibling"] if chainable else []) - owner_modes = ["direct", "secondary"] + (["sibling"] if carrying else []) - violations = ["missing", "nullable"] - if a1 == "chain": - owner_modes += renamed_modes - else: - violations += renamed_modes - mode = draw(st.sampled_from(owner_modes + ([] if a1_only else ["violate"]))) - if mode == "violate": - mode = draw(st.sampled_from(violations)) - owning = [] # the owning reference, when it is a secondary FK + owned = Schema(tables, fks).owned_parts.get(master, ()) + not_owned = [p for p in parts_of[master] if p not in owned] + owner_modes = ["direct", "renamed"] + (["sibling"] if owned else []) + ways_not_owned = ["secondary", "nullable", "missing"] + (["through a part not owned"] if not_owned else []) + mode = draw(st.sampled_from(owner_modes + ([] if owned_only else ["not owned"]))) + if mode == "not owned": + mode = draw(st.sampled_from(ways_not_owned)) + below_line = [] # a reference to the master below the line if mode == "direct": builder.add_fk(mspec, True, {}) - elif mode == "sibling": - sibling = by_name[draw(st.sampled_from(carrying))] - builder.add_fk(sibling, True, _draw_renames(draw, sibling, fresh, keep=mk)) - elif mode == "secondary": - owning = [(mspec, False, {}, False)] - elif mode == "nullable": - owning = [(mspec, False, {}, True)] elif mode == "renamed": - renamed = draw(st.sets(st.sampled_from(mk), min_size=1)) + renamed = draw(st.sets(st.sampled_from(mspec.pk), min_size=1)) builder.add_fk(mspec, True, {a: f"{a}_r{next(fresh)}" for a in sorted(renamed)}) - elif mode == "renamed sibling": - sibling = by_name[draw(st.sampled_from(chainable))] - key = so_far.owning_attributes(sibling.name) - renamed = draw(st.sets(st.sampled_from(key), min_size=1)) - renames = _draw_renames(draw, sibling, fresh, keep=key) - renames.update({a: f"{a}_r{next(fresh)}" for a in sorted(renamed)}) - builder.add_fk(sibling, True, renames) - solid = set(builder.pk) | {a for o in owning if not o[3] for a in o[0].pk} + elif mode == "sibling": + sibling = by_name[draw(st.sampled_from(owned))] + builder.add_fk(sibling, True, _draw_renames(draw, sibling, fresh)) + elif mode == "through a part not owned": + other = by_name[draw(st.sampled_from(not_owned))] + builder.add_fk(other, True, _draw_renames(draw, other, fresh)) + elif mode in ("secondary", "nullable"): + below_line = [(mspec, False, {}, mode == "nullable")] + solid = set(builder.pk) | {a for o in below_line if not o[3] for a in o[0].pk} group = [master] + parts_of[master] extra = _draw_fks(draw, tables, fks, fresh, draw(st.integers(0, 3)), solid, group) - # With a secondary owning reference the master key is not in the - # primary key, so the part needs a key attribute of its own. - spec = _finish(draw, builder, tables, owning + extra, own_pk=f"k{i}", force_own=bool(owning)) + # With the master referenced below the line, the part needs a key attribute of its own. + spec = _finish(draw, builder, tables, below_line + extra, own_pk=f"k{i}", force_own=bool(below_line)) parts_of[master].append(spec.name) tables.append(spec) fks.extend(builder.fks) - schema = Schema(tables, fks, a1=a1) - if a1_only: + schema = Schema(tables, fks) + if owned_only: assert all(t.name in schema.group for t in schema.tables if t.master), schema return schema @@ -333,11 +331,9 @@ def __repr__(self) -> str: @st.composite -def cases( - draw, max_tables: int = 7, max_rows: int = 4, acyclic: bool = True, max_total: int | None = None, a1: str = "strict" -) -> Case: +def cases(draw, max_tables: int = 7, max_rows: int = 4, acyclic: bool = True, max_total: int | None = None) -> Case: """Draw a schema and a database. With ``acyclic``, schemas whose Q has a cycle are discarded.""" - schema = draw(schemas(max_tables, a1=a1)) + schema = draw(schemas(max_tables)) if acyclic: assume(q_acyclic(schema)) db = draw(databases(schema, max_rows)) @@ -352,13 +348,8 @@ def seeds(rows) -> st.SearchStrategy: return st.frozensets(st.sampled_from(rows)) if rows else st.just(frozenset()) -#: Both readings of A1: the specification's, and the generalization for #1429. -A1_MODES = ["strict", "chain"] - - -def tiny_cases(a1: str) -> st.SearchStrategy: - """Tiny databases for the brute-force minimality checks (at most 2^8 supersets).""" - return cases(max_tables=4, max_rows=2, max_total=8, a1=a1) +#: Tiny databases for the brute-force minimality checks (at most 2^8 supersets). +tiny_cases = cases(max_tables=4, max_rows=2, max_total=8) def supersets(rows, seed): @@ -402,8 +393,7 @@ def _has_partial_null(case: Case) -> bool: def _crosses_instances(case: Case) -> bool: - # A part row references a row of its own group in another master instance, - # through an FK that renames the master key. + # A part row references a row of its own group in another master instance. g = case.graph for r in g.rows: for p in g.parent(r): @@ -413,36 +403,27 @@ def _crosses_instances(case: Case) -> bool: return False -def _group_parts(s: Schema): +def _owned(s: Schema): return [t for t in s.tables if t.master and t.name in s.group] -def _is_owning_direct(s: Schema, fk: FK) -> bool: - return fk.parent == s.table(fk.child).master and all(fk.attr_map.get(a) == a for a in s.table(fk.parent).pk) - - -def _renames_master_key(s: Schema, fk: FK) -> bool: +def _leaves_instance(s: Schema, fk: FK) -> bool: master = s.group.get(fk.child) - return ( - master is not None - and fk.child != master - and s.group.get(fk.parent) == master - and any(c != p for c, p in fk.attr_map.items() if p in s.table(master).pk) - ) + return master is not None and fk.child != master and fk in s.intra_group_fks(master) and not preserves_instance(s, fk) SCHEMA_FEATURES = { "group cycle": lambda s: not q_acyclic(s), - "part violating A1": lambda s: any(t.master and t.name not in s.group for t in s.tables), - "owning reference through a sibling": lambda s: any( - not any(_is_owning_direct(s, fk) for fk in s.fks_of(t.name)) for t in _group_parts(s) + "part its master does not own": lambda s: any(t.master and t.name not in s.group for t in s.tables), + "part owned through a sibling": lambda s: any( + not any(fk.parent == t.master and s.is_primary(fk) for fk in s.fks_of(t.name)) for t in _owned(s) ), - "secondary owning reference": lambda s: any(not set(s.table(t.master).pk) <= set(t.pk) for t in _group_parts(s)), - "renamed FK to the own master": lambda s: any( - _renames_master_key(s, fk) and fk.parent == s.group[fk.child] for fk in s.fks + "renamed owning attributes": lambda s: any(s.owning_attributes(t.name) != s.table(t.master).pk for t in _owned(s)), + "FK to the own master that leaves the instance": lambda s: any( + _leaves_instance(s, fk) and fk.parent == s.group[fk.child] for fk in s.fks ), - "sibling FK renaming the master key": lambda s: any( - _renames_master_key(s, fk) and fk.parent != s.group[fk.child] for fk in s.fks + "FK to a sibling that leaves the instance": lambda s: any( + _leaves_instance(s, fk) and fk.parent != s.group[fk.child] for fk in s.fks ), "part -> part across groups": lambda s: any( s.table(fk.child).master and s.table(fk.parent).master and s.table(fk.child).master != s.table(fk.parent).master @@ -487,14 +468,12 @@ def test_generator_draws_data_feature(feature): # ============================================================================= # T1-T3 # ============================================================================= -# Each property runs under both readings of A1 (see the A1 section at the end). -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t1_closure(a1, data): +@given(case=cases(), data=st.data()) +def test_t1_closure(case, data): """T1: D, U and D1 are extensive, monotone and idempotent.""" - g = data.draw(cases(a1=a1), label="case").graph + g = case.graph b = data.draw(seeds(g.rows), label="B") a = data.draw(seeds(b), label="A ⊆ B") for op in (g.D, g.U, g.D1): @@ -503,22 +482,20 @@ def test_t1_closure(a1, data): assert op(op(a)) == op(a) -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t2_additivity(a1, data): +@given(case=cases(), data=st.data()) +def test_t2_additivity(case, data): """T2: X(A ∪ B) = X(A) ∪ X(B) for X in D, U, D1 and both.""" - g = data.draw(cases(a1=a1), label="case").graph + g = case.graph a = data.draw(seeds(g.rows), label="A") b = data.draw(seeds(g.rows), label="B") for op in (g.D, g.U, g.D1, g.both): assert op(a | b) == op(a) | op(b) -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t3_duality(a1, data): +@given(case=cases()) +def test_t3_duality(case): """T3: y ∈ D({x}) if and only if x ∈ U({y}).""" - g = data.draw(cases(a1=a1), label="case").graph + g = case.graph up = {y: g.U({y}) for y in g.rows} for x in g.rows: down = g.D({x}) @@ -531,11 +508,9 @@ def test_t3_duality(a1, data): # ============================================================================= -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t4_delete_safety(a1, data): +@given(case=cases(), data=st.data()) +def test_t4_delete_safety(case, data): """T4: removing D(S) leaves the database consistent and takes every master-part instance whole.""" - case = data.draw(cases(a1=a1), label="case") g = case.graph s = data.draw(seeds(g.rows), label="S") d = g.D(s) @@ -545,11 +520,9 @@ def test_t4_delete_safety(a1, data): assert g.group_closed(d) -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t4_minimality(a1, data): +@given(case=tiny_cases, data=st.data()) +def test_t4_minimality(case, data): """T4: D(S) is the least superset of S whose removal is safe.""" - case = data.draw(tiny_cases(a1), label="case") g = case.graph s = data.draw(seeds(g.rows), label="S") safe = [x for x in supersets(g.rows, s) if ri_ok(case.schema, apply_delete(case.db, x)) and g.group_closed(x)] @@ -558,11 +531,9 @@ def test_t4_minimality(a1, data): assert all(d <= x for x in safe) -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t5_read_boundary(a1, data): +@given(case=cases(), data=st.data()) +def test_t5_read_boundary(case, data): """T5: U(S) is consistent on its own and holds every master-part instance it touches whole.""" - case = data.draw(cases(a1=a1), label="case") g = case.graph s = data.draw(seeds(g.rows), label="S") u = g.U(s) @@ -576,11 +547,9 @@ def _self_contained(case, rows) -> bool: return ri_ok(case.schema, subdatabase(case.db, rows)) and case.graph.group_closed(rows) -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t5_minimality(a1, data): +@given(case=tiny_cases, data=st.data()) +def test_t5_minimality(case, data): """T5: U(S) is the least superset of S that is consistent on its own.""" - case = data.draw(tiny_cases(a1), label="case") g = case.graph s = data.draw(seeds(g.rows), label="S") consistent = [x for x in supersets(g.rows, s) if _self_contained(case, x)] @@ -594,11 +563,10 @@ def test_t5_minimality(a1, data): # ============================================================================= -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t6_both(a1, data): +@given(case=cases(), data=st.data()) +def test_t6_both(case, data): """T6: U(D(S)) contains D(S), is up-closed and group-closed; both is extensive and monotone.""" - g = data.draw(cases(a1=a1), label="case").graph + g = case.graph big = data.draw(seeds(g.rows), label="B") s = data.draw(seeds(big), label="S ⊆ B") b = g.both(s) @@ -609,11 +577,9 @@ def test_t6_both(a1, data): assert b <= g.both(big) -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t6_minimality(a1, data): +@given(case=tiny_cases, data=st.data()) +def test_t6_minimality(case, data): """T6: U(D(S)) is the least superset of D(S) that is consistent on its own.""" - case = data.draw(tiny_cases(a1), label="case") g = case.graph d = g.D(data.draw(seeds(g.rows), label="S")) consistent = [x for x in supersets(g.rows, d) if _self_contained(case, x)] @@ -634,11 +600,10 @@ def test_t6_both_is_not_idempotent(): assert g.both(g.both({b})) == {a, b, c} -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_t7_enforce(a1, data): +@given(case=cases(), data=st.data()) +def test_t7_enforce(case, data): """T7: D1(S) is lift-closed if and only if D1(S) = D(S); enforce deletes D1(S) or raises.""" - g = data.draw(cases(a1=a1), label="case").graph + g = case.graph s = data.draw(seeds(g.rows), label="S") d1, d = g.D1(s), g.D(s) assert d1 <= d @@ -655,35 +620,35 @@ def test_t7_enforce(a1, data): # ============================================================================= -def assert_valid_order(schema: Schema, order: list[str], quote: str) -> None: - """Every parent precedes its children, and each master is followed at once by its parts.""" +def assert_valid_order(schema: Schema, order: list[str], quote: str = "`") -> None: + """Every parent precedes its children, and each master is followed at once by its owned parts.""" name = {t.name: full_table_name(t.name, quote) for t in schema.tables} assert sorted(order) == sorted(name.values()) pos = {n: i for i, n in enumerate(order)} for fk in schema.fks: assert pos[name[fk.parent]] < pos[name[fk.child]], fk - for master, parts in schema.a1_parts.items(): + for master, parts in schema.owned_parts.items(): block = order[pos[name[master]] : pos[name[master]] + 1 + len(parts)] assert set(block) == {name[master]} | {name[p] for p in parts}, (master, order) -@pytest.mark.parametrize("a1", A1_MODES) @pytest.mark.parametrize("quote", ["`", '"']) -@given(data=st.data()) -def test_t8_topo_sort(quote, a1, data): +@given(schema=schemas(owned_only=True)) +def test_t8_topo_sort(quote, schema): """ - T8: under A1, topo_sort succeeds if and only if Q is acyclic, and its order is valid. + T8: topo_sort succeeds if and only if Q is acyclic, and its order is valid. The graph is a ``Dependencies`` object filled the way ``Dependencies.load`` fills it, and sorted by ``datajoint.dependencies.topo_sort``. Both MySQL and PostgreSQL quoting are used, since ``extract_master`` recognizes parts by name. + Every part is owned here: ``topo_sort`` still groups a part with its master + by name, owned or not (see the two examples below). """ - schema = data.draw(schemas(a1_only=True, a1=a1), label="schema") graph = dependency_graph(schema, quote) if q_acyclic(schema): assert_valid_order(schema, graph.topo_sort(), quote) else: - # E1b will replace NetworkXUnfeasible with a DataJointError that names the cycle. + # The declaration check (#1619) refuses these schemas; topo_sort raises on them. with pytest.raises((nx.NetworkXUnfeasible, DataJointError)): graph.topo_sort() @@ -697,236 +662,87 @@ def test_model_part_names_follow_datajoint(schema): assert extract_master(full_table_name(t.name, quote)) == expected -def _violating_part_schema(*, nullable_owner: bool) -> Schema: - if not nullable_owner: - # M; X references M; part P of M has no reference to M and references X. - return Schema( - [TableSpec("m", ("m",)), TableSpec("x", ("x",), ("m",)), TableSpec("m__p", ("p",), ("x",), master="m")], - [FK("x", "m", {"m": "m"}), FK("m__p", "x", {"x": "x"})], - ) - # M; part P1 references M through a nullable FK (violates A1); part P2 references M and P1. - return Schema( - [ - TableSpec("m", ("m",)), - TableSpec("m__p1", ("p1",), ("m",), master="m"), - TableSpec("m__p2", ("m", "p2"), ("p1",), master="m"), - ], - [FK("m__p1", "m", {"m": "m"}, nullable=True), FK("m__p2", "m", {"m": "m"}), FK("m__p2", "m__p1", {"p1": "p1"})], - ) +TOPO_SORT_GROUPS_BY_NAME = ( + "engine vs docs: datajoint.dependencies.topo_sort groups every part with its master by name; " + "diagram.md (A1, T8) makes a part its master does not own a plain table in topo_sort (#1619)" +) -def test_t8_needs_a1_q_acyclic_but_topo_sort_fails(): +@pytest.mark.xfail(strict=True, reason=TOPO_SORT_GROUPS_BY_NAME) +def test_t8_part_not_owned_q_acyclic(): """ - Without A1, T8 fails in one direction: Q is acyclic but topo_sort raises. + Q is acyclic, so topo_sort must succeed. - P violates A1, so the traversal treats it as a plain table and Q is - ``M -> X -> P``. ``topo_sort`` still recognizes P as a part by its name and - adds the edge ``X -> M``, which closes the cycle ``M -> X -> M``. + M; X references M; part P of M does not reference M, and references X. P is + not owned, so it is a plain table and Q is ``M -> X -> P``. Today + ``topo_sort`` treats P as a part and adds the edge ``X -> M``, a cycle. """ - schema = _violating_part_schema(nullable_owner=False) - assert schema.a1_parts == {"m": ()} + schema = Schema( + [TableSpec("m", ("m",)), TableSpec("x", ("x",), ("m",)), TableSpec("m__p", ("p",), ("x",), master="m")], + [FK("x", "m", {"m": "m"}), FK("m__p", "x", {"x": "x"})], + ) + assert schema.owned_parts == {"m": ()} assert q_acyclic(schema) - with pytest.raises(nx.NetworkXUnfeasible): - dependency_graph(schema).topo_sort() + assert_valid_order(schema, dependency_graph(schema).topo_sort()) -def test_t8_needs_a1_q_cyclic_but_topo_sort_succeeds(): - """ - Without A1, T8 fails in the other direction: Q has a cycle but topo_sort succeeds. - - P1 references M through a nullable FK, so it violates A1 and is its own node - in Q. Q is then ``{M, P2} -> P1 -> {M, P2}``, a cycle, so Q has no - topological order for the one-pass plan to follow: a row of P1 belongs to - one master instance by its FK and is referenced by a part row of another. - ``topo_sort`` sees both P1 and P2 as parts of M by name, adds no edge, and - succeeds. The E1b rule as planned allows this schema, since P1 references - its master and P2 references its master and a sibling part. +@pytest.mark.xfail(strict=True, reason=TOPO_SORT_GROUPS_BY_NAME) +def test_t8_part_not_owned_q_cyclic(): """ - schema = _violating_part_schema(nullable_owner=True) - assert schema.a1_parts == {"m": ("m__p2",)} - assert not q_acyclic(schema) - # topo_sort keeps the parts by name after their master, so P1 sits inside the block of M. - assert dependency_graph(schema).topo_sort() == [full_table_name(n) for n in ("m", "m__p1", "m__p2")] - # The rows show the cycle: P1 row 5 belongs to master 1 by its FK, and P2 row (2, 1) of master 2 references it. - db = make_db(schema, {"m": [(1,), (2,)], "m__p1": [(5, 1)], "m__p2": [(2, 1, 5)]}) - g = RowGraph(schema, db) - assert g.D({("m", (1,))}) == {("m", (1,)), ("m__p1", (5,)), ("m__p2", (2, 1)), ("m", (2,))} - - -# ============================================================================= -# self.upstream for a row not inserted yet -# ============================================================================= - - -def _rows_outside_parts(case: Case): - # make() fills a plain table or a master (with its parts), never a part on its own. - return sorted(r for r in case.graph.rows if case.schema.table(r[0]).master is None) - + Q has a cycle, so topo_sort must raise. -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_virtual_upstream_does_not_need_the_row(a1, data): + M; part P1 references M only through a nullable FK, so it is not owned; part + P2 is owned and references P1. Q is ``{M, P2} -> P1 -> {M, P2}``. Today + ``topo_sort`` sees P1 as a part of M by name, adds no edge, and succeeds. + The declaration check refuses P2's reference, since P1 depends on M. """ - ``self.upstream`` is the same before and after the row is inserted. - - Before the insert, the row, its parts and its descendants are absent: - ``db - D({r})``. Its upstream is built from its primary FKs alone. - """ - case = data.draw(cases(a1=a1), label="case") - rows = _rows_outside_parts(case) - assume(rows) - r = data.draw(st.sampled_from(rows), label="r") - table = case.schema.table(r[0]) - key = dict(zip(table.pk, r[1])) - before = apply_delete(case.db, case.graph.D({r})) - upstream = virtual_upstream(case.schema, before, table.name, key) - assert upstream == virtual_upstream(case.schema, case.db, table.name, key) - assert r not in upstream - - -@pytest.mark.parametrize("a1", A1_MODES) -@given(data=st.data()) -def test_virtual_upstream_matches_trace_of_the_inserted_row(a1, data): - """ - Once the row exists, ``U({r})`` contains ``self.upstream``. They are equal, - apart from r itself, when r is in no group and all its FKs are primary. - """ - case = data.draw(cases(a1=a1), label="case") - rows = _rows_outside_parts(case) - assume(rows) - r = data.draw(st.sampled_from(rows), label="r") - schema, g = case.schema, case.graph - table = schema.table(r[0]) - upstream = virtual_upstream(schema, case.db, table.name, dict(zip(table.pk, r[1]))) - traced = g.U({r}) - assert upstream <= traced - if table.name not in schema.group and all(schema.is_primary(fk) for fk in schema.fks_of(table.name)): - assert upstream == traced - {r} - - -def test_virtual_upstream_of_a_row_not_inserted(): - """U(∅) = ∅, so the upstream of a row not inserted yet comes from its primary FKs.""" schema = Schema( [ - TableSpec("subject", ("subject_id",)), - TableSpec("rig", ("rig_id",)), - TableSpec("session", ("subject_id", "session_id"), ("rig_id",)), - TableSpec("analysis", ("subject_id", "session_id")), - ], - [ - FK("session", "subject", {"subject_id": "subject_id"}), - FK("session", "rig", {"rig_id": "rig_id"}), - FK("analysis", "session", {"subject_id": "subject_id", "session_id": "session_id"}), + TableSpec("m", ("m",)), + TableSpec("m__p1", ("p1",), ("m",), master="m"), + TableSpec("m__p2", ("m", "p2"), ("p1",), master="m"), ], + [FK("m__p1", "m", {"m": "m"}, nullable=True), FK("m__p2", "m", {"m": "m"}), FK("m__p2", "m__p1", {"p1": "p1"})], ) - db = make_db(schema, {"subject": [(1,), (2,)], "rig": [(7,)], "session": [(1, 1, 7), (2, 1, 7)]}) - assert RowGraph(schema, db).U(set()) == set() - assert virtual_upstream(schema, db, "analysis", {"subject_id": 1, "session_id": 1}) == { - ("session", (1, 1)), - ("subject", (1,)), - ("rig", (7,)), # reached through session's secondary FK, which is known - } - # A key with no matching parent row has an empty upstream. - assert virtual_upstream(schema, db, "analysis", {"subject_id": 1, "session_id": 9}) == set() - - -def test_fk_value_follows_match_simple(): - """A2: an FK value with any NULL names nothing, even when its other attributes match a row.""" - fk = FK("child", "parent", {"a": "a", "b": "b"}, nullable=True) - schema = Schema([TableSpec("parent", ("a", "b")), TableSpec("child", ("a", "c"), ("b",))], [fk]) - assert fk_value(schema, fk, {"a": 1, "b": None, "c": 1}) is None - assert fk_value(schema, fk, {"a": 1, "b": 2, "c": 1}) == (1, 2) + assert schema.owned_parts == {"m": ("m__p2",)} + assert not q_acyclic(schema) + with pytest.raises((nx.NetworkXUnfeasible, DataJointError)): + dependency_graph(schema).topo_sort() # ============================================================================= # The one pass over Q, and the groups that iterate (diagram.md, Computation) # ============================================================================= # ``one_pass`` evaluates the engine's computation on rows: one visit per node of -# Q, with an iterating group repeating its group step. It must give D(S) and -# U(S). diagram.md decides which groups iterate by whether an FK inside the -# group renames the master's key ("spec"); the exact test is whether the FK -# maps the instance onto itself ("exact"). +# Q, the lift through the owning attributes, and the group step repeated for a +# group that iterates. It must give D(S) and U(S). -@pytest.mark.parametrize("a1", A1_MODES) @pytest.mark.parametrize("direction", ["down", "up"]) -@given(data=st.data()) -def test_one_pass_with_exact_iteration_equals_the_closure(direction, a1, data): - """Iterating every group that has an FK not preserving the instance, one pass gives D(S) and U(S).""" - case = data.draw(cases(a1=a1), label="case") +@given(case=cases(), data=st.data()) +def test_one_pass_equals_the_closure(direction, case, data): + """One pass over Q, iterating the groups that iterate, gives D(S) and U(S).""" s = data.draw(seeds(case.graph.rows), label="S") - found, _ = one_pass(case.schema, case.db, s, direction, iterate="exact") + found, _ = one_pass(case.schema, case.db, s, direction) assert found == (case.graph.D(s) if direction == "down" else case.graph.U(s)) -def _inner_fks_carry_the_master_key(schema: Schema) -> bool: - return all( - set(schema.owning[fk.parent].values()) <= set(fk.attr_map.values()) - for master in schema.a1_parts - for fk in schema.intra_group_fks(master) - ) - - @pytest.mark.parametrize("direction", ["down", "up"]) -@given(data=st.data()) -def test_one_pass_with_the_spec_iteration_rule(direction, data): - """ - The rule of diagram.md is exact when every FK inside a group carries the master's key. - - Then an FK either maps the key to itself, and stays in the instance, or - renames it, and the group iterates. The next test shows the case it misses. - """ - case = data.draw(cases(), label="case") - assume(_inner_fks_carry_the_master_key(case.schema)) +@given(case=cases(), data=st.data()) +def test_one_step_suffices_when_no_group_iterates(direction, case, data): + """When every FK inside every group maps owning attributes onto owning attributes, one step per group suffices.""" + assume(not any(iterates(case.schema, m) for m in case.schema.owned_parts)) s = data.draw(seeds(case.graph.rows), label="S") - found, _ = one_pass(case.schema, case.db, s, direction, iterate="spec") + found, _ = one_pass(case.schema, case.db, s, direction, iterate=False) assert found == (case.graph.D(s) if direction == "down" else case.graph.U(s)) -def _sibling_fk_without_master_key(): - """ - Part S holds the master key below the line (A1 through a secondary ``-> master``); - part P references S by S's primary key, which does not contain the master key. - """ - schema = Schema( - [ - TableSpec("m", ("m_id",)), - TableSpec("m__s", ("s_id",), ("m_id",), master="m"), - TableSpec("m__p", ("m_id", "p_id"), ("s_id",), master="m"), - ], - [FK("m__s", "m", {"m_id": "m_id"}), FK("m__p", "m", {"m_id": "m_id"}), FK("m__p", "m__s", {"s_id": "s_id"})], - ) - # S 10 belongs to master 1; P (2, 1) of master 2 references it. - db = make_db(schema, {"m": [(1,), (2,)], "m__s": [(10, 1)], "m__p": [(2, 1, 10)]}) - return schema, db - - -def test_one_pass_iterates_for_a_sibling_fk_without_the_master_key(): - """With the exact test, the group iterates and one pass reaches master 2 through P (2, 1).""" - schema, db = _sibling_fk_without_master_key() - assert schema.a1_parts == {"m": ("m__s", "m__p")} - g = RowGraph(schema, db) - everything = all_rows(db) - assert g.D({("m", (1,))}) == everything - assert g.U({("m__p", (2, 1))}) == everything - assert one_pass(schema, db, {("m", (1,))}, "down", iterate="exact")[0] == everything - assert one_pass(schema, db, {("m__p", (2, 1))}, "up", iterate="exact")[0] == everything - - -@pytest.mark.xfail( - strict=True, - reason=( - "docs vs model: diagram.md (Groups that iterate) iterates a group only for an FK inside it that " - "renames the master's key. An FK to a sibling whose primary key lacks the master key (the sibling " - "holds it below the line) renames nothing, yet links instances, so one pass misses rows." - ), -) -def test_spec_iteration_rule_misses_a_sibling_fk_without_the_master_key(): - """traversal per diagram.md: the FK from P to S does not rename the master key, so the group does not iterate.""" - schema, db = _sibling_fk_without_master_key() - assert not any(renames_master_key(schema, fk) for fk in schema.intra_group_fks("m")) - g = RowGraph(schema, db) - assert one_pass(schema, db, {("m", (1,))}, "down", iterate="spec")[0] == g.D({("m", (1,))}) +@given(schema=schemas()) +def test_every_fk_inside_a_group_carries_a_master_key(schema): + """An FK references the whole primary key, which holds the referenced table's owning attributes.""" + for master in schema.owned_parts: + for fk in schema.intra_group_fks(master): + assert set(schema.owning[fk.parent].values()) <= set(fk.attr_map.values()) # ============================================================================= @@ -977,15 +793,15 @@ def _edge_and_names(case: Case, data, copyable: bool): return fk, names -@given(data=st.data()) -def test_r1_copy_rule(data): +@given(case=cases(), data=st.data()) +def test_r1_copy_rule(case, data): """ A condition copied under R1's rule selects the same child rows as the restriction. - The rule: the attributes the condition names are among the FK's attributes, - the FK is not renamed, and none of its columns is nullable. + The rule: the attributes the condition names (every key of a dict, the whole + heading of a query expression) are among the FK's attributes, the FK is not + renamed, and none of its columns is nullable. """ - case = data.draw(cases(), label="case") fk, names = _edge_and_names(case, data, copyable=True) cond = data.draw(conditions(names), label="condition") assume(copy_allowed(case.schema, fk, cond)) @@ -998,39 +814,26 @@ def test_r1_copy_rule(data): # ============================================================================= -def _relabel_down_exact(schema: Schema, fk: FK, keys: list[dict]) -> bool: +@given(case=cases(), data=st.data()) +def test_relabel_down_rule(case, data): """ - When relabeling parent keys gives the same child rows as R1. + Parent keys relabeled under the Seeds rule select the same child rows as R1. - Every key names, among the parent's attributes, only those the FK carries; - and if the FK has a nullable column, every key names all its attributes, - none of them NULL. + The rule: every key names, among the parent's attributes, only attributes + the FK carries; and if the FK has a nullable column, every key names all of + the FK's attributes, none of them NULL. """ - parent = schema.table(fk.parent) - carried = set(fk.attr_map.values()) - if any(set(key) & set(parent.heading) - carried for key in keys): - return False - if not set(fk.attr_map) & schema.nullable[fk.child]: - return True - return all(carried <= set(key) and all(key[a] is not None for a in carried) for key in keys) - - -@given(data=st.data()) -def test_relabel_down_when_exact(data): - """Relabeled parent keys select the same child rows as R1 when ``_relabel_down_exact`` holds.""" - case = data.draw(cases(), label="case") fk, names = _edge_and_names(case, data, copyable=False) keys = data.draw(st.lists(key_dicts(names), max_size=3), label="keys") - assume(_relabel_down_exact(case.schema, fk, keys)) + assume(relabel_down_allowed(case.schema, fk, keys)) parents = restrict(case.schema, case.db, fk.parent, keys) relabeled = relabel_down(fk, keys) assert restrict(case.schema, case.db, fk.child, relabeled) == r1_down(case.schema, case.db, fk, parents) -@given(data=st.data()) -def test_relabel_up_for_keys_read_from_the_child(data): +@given(case=cases(), data=st.data()) +def test_relabel_up_for_keys_read_from_the_child(case, data): """Keys read from child rows that contain all the FK's columns, relabeled, select the referenced parents.""" - case = data.draw(cases(), label="case") fk, _ = _edge_and_names(case, data, copyable=False) child = case.schema.table(fk.child) picked = data.draw(st.frozensets(st.sampled_from(sorted(case.db[fk.child])) if case.db[fk.child] else st.nothing())) @@ -1042,7 +845,8 @@ def test_relabel_up_for_keys_read_from_the_child(data): assert restrict(case.schema, case.db, fk.parent, relabeled) == referenced -def _k_schema(): +def test_relabel_rule_refuses_a_key_on_a_secondary_attribute(): + """diagram.md, Seeds: ``Session & {"quality": 0}`` relabels to ``{}`` across Trial's FK and would select every trial.""" schema = Schema( [ TableSpec("subject", ("subject_id",), ("species",)), @@ -1062,38 +866,18 @@ def _k_schema(): "trial": [(1, 1, 1), (1, 2, 1), (2, 2, 1)], }, ) - return schema, db - - -@pytest.mark.xfail( - strict=True, - reason=( - "docs vs model: diagram.md (Seeds) relabels a dict seed downward whenever the FK has no nullable " - "column, keeping only the keys the FK carries. A dict that names a secondary attribute of the parent " - "loses that key, so `Session & {'quality': 0}` relabels to {} and selects every trial." - ), -) -def test_spec_relabel_rule_drops_a_secondary_attribute(): - """traversal per diagram.md: relabeling ``Session & {"quality": 0}`` across Trial's FK.""" - schema, db = _k_schema() fk, keys = schema.fks_of("trial")[0], [{"quality": 0}] - assert relabel_down_allowed(schema, fk, keys) + assert not relabel_down_allowed(schema, fk, keys) assert relabel_down(fk, keys) == [{}] - r1 = r1_down(schema, db, fk, restrict(schema, db, "session", keys)) - assert r1 == {("trial", (1, 1, 1)), ("trial", (2, 2, 1))} - assert restrict(schema, db, "trial", relabel_down(fk, keys)) == r1 + assert restrict(schema, db, "trial", [{}]) == {("trial", k) for k in db["trial"]} # every trial + assert r1_down(schema, db, fk, restrict(schema, db, "session", keys)) == {("trial", (1, 1, 1)), ("trial", (2, 2, 1))} -@pytest.mark.xfail( - strict=True, - reason=( - "docs vs model: diagram.md (Seeds) relabels downward over a nullable FK when the keys name all its " - "columns. A key that names a nullable column as None selects no parent, but relabeled it selects the " - "child rows whose FK column is NULL." - ), -) -def test_spec_relabel_rule_with_a_null_key_value(): - """traversal per diagram.md: ``Surgery & {"subject_id": 1, "surgery_id": None}`` relabeled to Session (S3's schema).""" +def test_relabel_rule_refuses_a_null_key_value(): + """ + diagram.md, Seeds, on S3's schema: ``Surgery & {"subject_id": 1, "surgery_id": None}`` + selects no surgery but, relabeled, would select session (1, 2; NULL). + """ schema = Schema( [ TableSpec("subject", ("subject_id",)), @@ -1106,99 +890,89 @@ def test_spec_relabel_rule_with_a_null_key_value(): FK("session", "surgery", {"subject_id": "subject_id", "surgery_id": "surgery_id"}, nullable=True), ], ) - db = make_db( - schema, - {"subject": [(1,)], "surgery": [(1, 1)], "session": [(1, 1, 1), (1, 2, None)]}, - ) + db = make_db(schema, {"subject": [(1,)], "surgery": [(1, 1)], "session": [(1, 1, 1), (1, 2, None)]}) fk, keys = schema.fks_of("session")[1], [{"subject_id": 1, "surgery_id": None}] - assert relabel_down_allowed(schema, fk, keys) - r1 = r1_down(schema, db, fk, restrict(schema, db, "surgery", keys)) - assert r1 == set() - assert restrict(schema, db, "session", relabel_down(fk, keys)) == r1 + assert not relabel_down_allowed(schema, fk, keys) + assert restrict(schema, db, "surgery", keys) == set() + assert restrict(schema, db, "session", relabel_down(fk, keys)) == {("session", (1, 2))} # ============================================================================= -# A1 generalized to owning chains with renames (#1429) +# self.upstream for a row not inserted yet # ============================================================================= -def _issue_1429_case_1(a1: str): - """``PartB`` references ``Master.PartA.proj(src_master="master_id", src_part="part_a_id")`` and has no master_id.""" - schema = Schema( - [ - TableSpec("master", ("master_id",)), - TableSpec("master__part_a", ("master_id", "part_a_id"), master="master"), - TableSpec("master__part_b", ("src_master", "src_part", "part_b_id"), master="master"), - ], - [ - FK("master__part_a", "master", {"master_id": "master_id"}), - FK("master__part_b", "master__part_a", {"src_master": "master_id", "src_part": "part_a_id"}), - ], - a1=a1, - ) - db = make_db( - schema, - {"master": [(1,), (2,)], "master__part_a": [(1, 1), (2, 1)], "master__part_b": [(1, 1, 1), (2, 1, 1)]}, - ) - return schema, db - - -def test_issue_1429_case_1_under_strict_a1(): - """Under the specification's A1, PartB violates A1: no lift, and it is not part of an instance.""" - schema, db = _issue_1429_case_1("strict") - assert schema.a1_parts == {"master": ("master__part_a",)} - g = RowGraph(schema, db) - assert g.D({("master__part_b", (1, 1, 1))}) == {("master__part_b", (1, 1, 1))} - assert g.U({("master__part_a", (1, 1))}) == {("master__part_a", (1, 1)), ("master", (1,))} +def _rows_outside_parts(case: Case): + # make() fills a plain table or a master (with its parts), never a part on its own. + return sorted(r for r in case.graph.rows if case.schema.table(r[0]).master is None) -def test_issue_1429_case_1_under_chain_a1(): +@given(case=cases(), data=st.data()) +def test_virtual_upstream_does_not_need_the_row(case, data): """ - Under the chain reading, PartB is a member whose owning attribute is src_master. + ``self.upstream`` is the same before and after the row is inserted. - The lift takes master_id from src_master, so R2's ``M & R(Pi).proj(*M.primary_key)`` - must rename it: PartB has no master_id. The FK from PartB to PartA renames the - master key yet preserves the instance, so the rule of diagram.md iterates the - group although one pass would be exact. + Before the insert, the row, its parts and its descendants are absent: + ``db - D({r})``. Its upstream is built from its primary FKs alone. """ - schema, db = _issue_1429_case_1("chain") - assert schema.a1_parts == {"master": ("master__part_a", "master__part_b")} - assert schema.owning_attributes("master__part_b") == ("src_master",) - g = RowGraph(schema, db) - instance_1 = {("master", (1,)), ("master__part_a", (1, 1)), ("master__part_b", (1, 1, 1))} - assert g.D({("master__part_b", (1, 1, 1))}) == instance_1 - assert g.U({("master__part_a", (1, 1))}) == instance_1 - (fk,) = schema.intra_group_fks("master")[1:] - assert renames_master_key(schema, fk) and preserves_instance(schema, fk) - for direction in ("down", "up"): - assert one_pass(schema, db, {("master__part_b", (1, 1, 1))}, direction, iterate=None)[0] == instance_1 + rows = _rows_outside_parts(case) + assume(rows) + r = data.draw(st.sampled_from(rows), label="r") + table = case.schema.table(r[0]) + key = dict(zip(table.pk, r[1])) + before = apply_delete(case.db, case.graph.D({r})) + upstream = virtual_upstream(case.schema, before, table.name, key) + assert upstream == virtual_upstream(case.schema, case.db, table.name, key) + assert r not in upstream -def test_chain_a1_makes_g6_a_member(): +@given(case=cases(), data=st.data()) +def test_virtual_upstream_matches_trace_of_the_inserted_row(case, data): """ - Under the chain reading, G6's Probe.Channel satisfies A1 through ``-> master.proj(host_id="probe_id")``. - - The catalog's G6 rows then change: channel (1, 1) lifts probe 1 and brings channel (1, 2). + Once the row exists, ``U({r})`` contains ``self.upstream``. They are equal, + apart from r itself, when r is in no group and all its FKs are primary. """ - tables = [TableSpec("probe", ("probe_id",)), TableSpec("probe__channel", ("host_id", "channel_id"), master="probe")] - fks = [FK("probe__channel", "probe", {"host_id": "probe_id"})] - data = {"probe": [(1,), (2,)], "probe__channel": [(1, 1), (1, 2), (2, 1)]} - strict, chain = Schema(tables, fks), Schema(tables, fks, a1="chain") - assert strict.a1_parts == {"probe": ()} - assert chain.a1_parts == {"probe": ("probe__channel",)} - seed = {("probe__channel", (1, 1))} - assert RowGraph(strict, make_db(strict, data)).D(seed) == seed - assert RowGraph(chain, make_db(chain, data)).D(seed) == { - ("probe__channel", (1, 1)), - ("probe", (1,)), - ("probe__channel", (1, 2)), - } + rows = _rows_outside_parts(case) + assume(rows) + r = data.draw(st.sampled_from(rows), label="r") + schema, g = case.schema, case.graph + table = schema.table(r[0]) + upstream = virtual_upstream(schema, case.db, table.name, dict(zip(table.pk, r[1]))) + traced = g.U({r}) + assert upstream <= traced + if table.name not in schema.group and all(schema.is_primary(fk) for fk in schema.fks_of(table.name)): + assert upstream == traced - {r} -def test_generator_draws_chain_members(): - """Under the chain reading, the generator draws members whose owning attributes are renamed.""" - find( - schemas(a1="chain"), - lambda s: any(s.owning_attributes(p) != s.table(s.group[p]).pk for p in s.group), - settings=_FIND_SETTINGS, +def test_virtual_upstream_of_a_row_not_inserted(): + """U(∅) = ∅, so the upstream of a row not inserted yet comes from its primary FKs.""" + schema = Schema( + [ + TableSpec("subject", ("subject_id",)), + TableSpec("rig", ("rig_id",)), + TableSpec("session", ("subject_id", "session_id"), ("rig_id",)), + TableSpec("analysis", ("subject_id", "session_id")), + ], + [ + FK("session", "subject", {"subject_id": "subject_id"}), + FK("session", "rig", {"rig_id": "rig_id"}), + FK("analysis", "session", {"subject_id": "subject_id", "session_id": "session_id"}), + ], ) + db = make_db(schema, {"subject": [(1,), (2,)], "rig": [(7,)], "session": [(1, 1, 7), (2, 1, 7)]}) + assert RowGraph(schema, db).U(set()) == set() + assert virtual_upstream(schema, db, "analysis", {"subject_id": 1, "session_id": 1}) == { + ("session", (1, 1)), + ("subject", (1,)), + ("rig", (7,)), # reached through session's secondary FK, which is known + } + # A key with no matching parent row has an empty upstream. + assert virtual_upstream(schema, db, "analysis", {"subject_id": 1, "session_id": 9}) == set() + + +def test_fk_value_follows_match_simple(): + """A2: an FK value with any NULL names nothing, even when its other attributes match a row.""" + fk = FK("child", "parent", {"a": "a", "b": "b"}, nullable=True) + schema = Schema([TableSpec("parent", ("a", "b")), TableSpec("child", ("a", "c"), ("b",))], [fk]) + assert fk_value(schema, fk, {"a": 1, "b": None, "c": 1}) is None + assert fk_value(schema, fk, {"a": 1, "b": 2, "c": 1}) == (1, 2)