diff --git a/.flake8 b/.flake8 index f5adf03..f944589 100644 --- a/.flake8 +++ b/.flake8 @@ -1,3 +1,5 @@ [flake8] ignore = W191,W503,E203,E741 max-line-length = 240 +per-file-ignores = + **/conftest.py:F811 \ No newline at end of file diff --git a/.github/workflows/pants.yml b/.github/workflows/pants.yml index b99cb60..ab09be5 100644 --- a/.github/workflows/pants.yml +++ b/.github/workflows/pants.yml @@ -46,7 +46,7 @@ jobs: env: integration_test_secrets: ${{secrets.integration_test_secrets}} - name: Upload test coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} files: dist/coverage/python/coverage.xml diff --git a/cicd/python-default.lock b/cicd/python-default.lock index 007f109..6f61962 100644 --- a/cicd/python-default.lock +++ b/cicd/python-default.lock @@ -11,14 +11,19 @@ // "generated_with_requirements": [ // "azure-identity<2.0.0,>=1.4.0", // "click~=8.0", +// "fastapi~=0.110.0", // "hypothesis<7,>=6", // "mypy>=1.5.0", +// "psycopg~=3.1", +// "pydantic-settings>=2.0", // "pydantic>=2.0", // "pytest<8,>7", // "pyyaml~=6.0", // "requests<3,>=2", +// "testcontainers<5,>=3", // "types-PyYAML", -// "types-requests" +// "types-requests", +// "uvicorn~=0.29" // ], // "manylinux": "manylinux2014", // "requirement_constraints": [], @@ -60,30 +65,68 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", - "url": "https://files.pythonhosted.org/packages/f0/eb/fcb708c7bf5056045e9e98f62b93bd7467eb718b0202e7698eb11d66416c/attrs-23.1.0-py3-none-any.whl" + "hash": "048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", + "url": "https://files.pythonhosted.org/packages/14/fd/2f20c40b45e4fb4324834aea24bd4afdf1143390242c0b33774da0e2e34f/anyio-4.3.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015", - "url": "https://files.pythonhosted.org/packages/97/90/81f95d5f705be17872843536b1868f351805acf6971251ff07c1b8334dbb/attrs-23.1.0.tar.gz" + "hash": "f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6", + "url": "https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz" + } + ], + "project_name": "anyio", + "requires_dists": [ + "Sphinx>=7; extra == \"doc\"", + "anyio[trio]; extra == \"test\"", + "coverage[toml]>=7; extra == \"test\"", + "exceptiongroup>=1.0.2; python_version < \"3.11\"", + "exceptiongroup>=1.2.0; extra == \"test\"", + "hypothesis>=4.0; extra == \"test\"", + "idna>=2.8", + "packaging; extra == \"doc\"", + "psutil>=5.9; extra == \"test\"", + "pytest-mock>=3.6.1; extra == \"test\"", + "pytest>=7.0; extra == \"test\"", + "sniffio>=1.1", + "sphinx-autodoc-typehints>=1.2.0; extra == \"doc\"", + "sphinx-rtd-theme; extra == \"doc\"", + "trio>=0.23; extra == \"trio\"", + "trustme; extra == \"test\"", + "typing-extensions>=4.1; python_version < \"3.11\"", + "uvloop>=0.17; (platform_python_implementation == \"CPython\" and platform_system != \"Windows\") and extra == \"test\"" + ], + "requires_python": ">=3.8", + "version": "4.3.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1", + "url": "https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", + "url": "https://files.pythonhosted.org/packages/e3/fc/f800d51204003fa8ae392c4e8278f256206e7a919b708eef054f5f4b650d/attrs-23.2.0.tar.gz" } ], "project_name": "attrs", "requires_dists": [ - "attrs[docs,tests]; extra == \"dev\"", + "attrs[tests-mypy]; extra == \"tests-no-zope\"", "attrs[tests-no-zope]; extra == \"tests\"", "attrs[tests]; extra == \"cov\"", + "attrs[tests]; extra == \"dev\"", "cloudpickle; platform_python_implementation == \"CPython\" and extra == \"tests-no-zope\"", "coverage[toml]>=5.3; extra == \"cov\"", "furo; extra == \"docs\"", "hypothesis; extra == \"tests-no-zope\"", "importlib-metadata; python_version < \"3.8\"", - "mypy>=1.1.1; platform_python_implementation == \"CPython\" and extra == \"tests-no-zope\"", + "mypy>=1.6; (platform_python_implementation == \"CPython\" and python_version >= \"3.8\") and extra == \"tests-mypy\"", "myst-parser; extra == \"docs\"", "pre-commit; extra == \"dev\"", "pympler; extra == \"tests-no-zope\"", - "pytest-mypy-plugins; platform_python_implementation == \"CPython\" and python_version < \"3.11\" and extra == \"tests-no-zope\"", + "pytest-mypy-plugins; (platform_python_implementation == \"CPython\" and python_version >= \"3.8\") and extra == \"tests-mypy\"", "pytest-xdist[psutil]; extra == \"tests-no-zope\"", "pytest>=4.3.0; extra == \"tests-no-zope\"", "sphinx-notfound-page; extra == \"docs\"", @@ -94,30 +137,30 @@ "zope-interface; extra == \"tests\"" ], "requires_python": ">=3.7", - "version": "23.1.0" + "version": "23.2.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "0fa04b7b1f7d44a4fb8468c4093deb2ea01fdf4faddbf802ed9205615f99d68c", - "url": "https://files.pythonhosted.org/packages/9c/f8/1cf23a75cb8c2755c539ac967f3a7f607887c4979d073808134803720f0f/azure_core-1.29.5-py3-none-any.whl" + "hash": "7c5ee397e48f281ec4dd773d67a0a47a0962ed6fa833036057f9ea067f688e74", + "url": "https://files.pythonhosted.org/packages/d7/70/180df3b43ebc7a1ec957d9e5c2c76e6c54398ec61a67dff88d3e0131be80/azure_core-1.30.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "52983c89d394c6f881a121e5101c5fa67278ca3b1f339c8fb2ef39230c70e9ac", - "url": "https://files.pythonhosted.org/packages/e3/39/328faea9f656075dbb8ecf70f1a4697bc80510fcc70e3e8f0090c34fc00c/azure-core-1.29.5.tar.gz" + "hash": "26273a254131f84269e8ea4464f3560c731f29c0c1f69ac99010845f239c1a8f", + "url": "https://files.pythonhosted.org/packages/51/0d/b76383f028aa3570419edf97ab504cb84b839e3cbc8c8b2048f16bbea2d3/azure-core-1.30.1.tar.gz" } ], "project_name": "azure-core", "requires_dists": [ "aiohttp>=3.0; extra == \"aio\"", - "requests>=2.18.4", + "requests>=2.21.0", "six>=1.11.0", "typing-extensions>=4.6.0" ], "requires_python": ">=3.7", - "version": "1.29.5" + "version": "1.30.1" }, { "artifacts": [ @@ -146,19 +189,50 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474", - "url": "https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl" + "hash": "7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9", + "url": "https://files.pythonhosted.org/packages/1a/ab/3e941e3fcf1b7d3ab3d0233194d99d6a0ed6b24f8f956fc81e47edc8c079/backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987", + "url": "https://files.pythonhosted.org/packages/4a/6d/eca004eeadcbf8bd64cc96feb9e355536147f0577420b44d80c7cac70767/backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2", + "url": "https://files.pythonhosted.org/packages/ad/85/475e514c3140937cf435954f78dedea1861aeab7662d11de232bdaa90655/backports.zoneinfo-0.2.1.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1", + "url": "https://files.pythonhosted.org/packages/c1/8f/9b1b920a6a95652463143943fa3b8c000cb0b932ab463764a6f2a2416560/backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl" + } + ], + "project_name": "backports-zoneinfo", + "requires_dists": [ + "importlib-resources; python_version < \"3.7\"", + "tzdata; extra == \"tzdata\"" + ], + "requires_python": ">=3.6", + "version": "0.2.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1", + "url": "https://files.pythonhosted.org/packages/ba/06/a07f096c664aeb9f01624f858c3add0a4e913d6c96257acb4fce61e7de14/certifi-2024.2.2-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", - "url": "https://files.pythonhosted.org/packages/d4/91/c89518dd4fe1f3a4e3f6ab7ff23cb00ef2e8c9adf99dacc618ad5e068e28/certifi-2023.11.17.tar.gz" + "hash": "0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", + "url": "https://files.pythonhosted.org/packages/71/da/e94e26401b62acd6d91df2b52954aceb7f561743aa5ccc32152886c76c96/certifi-2024.2.2.tar.gz" } ], "project_name": "certifi", "requires_dists": [], "requires_python": ">=3.6", - "version": "2023.11.17" + "version": "2024.2.2" }, { "artifacts": [ @@ -748,102 +822,143 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723", - "url": "https://files.pythonhosted.org/packages/26/41/e5ebaad8b27f8662c92a7d4cb9bf16e488450cb4b6ee0fce5b78b3327679/cryptography-41.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl" + "hash": "f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac", + "url": "https://files.pythonhosted.org/packages/50/be/92ce909d5d5b361780e21e0216502f72e5d8f9b2d73bcfde1ca5f791630b/cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc", + "url": "https://files.pythonhosted.org/packages/0e/1d/62a2324882c0db89f64358dadfb95cae024ee3ba9fde3d5fd4d2f58af9f5/cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548", - "url": "https://files.pythonhosted.org/packages/05/40/ade6e708e6e90528dc50b215adce495fec49286f199bc11e4199b1666505/cryptography-41.0.5-cp37-abi3-musllinux_1_1_aarch64.whl" + "hash": "6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1", + "url": "https://files.pythonhosted.org/packages/13/9e/a55763a32d340d7b06d045753c186b690e7d88780cafce5f88cb931536be/cryptography-42.0.5.tar.gz" }, { "algorithm": "sha256", - "hash": "da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797", - "url": "https://files.pythonhosted.org/packages/0b/c1/2f1e8abb31ec0bf8b004052bbe0face0a8be386ed5ea30e5e300bfffd51a/cryptography-41.0.5-cp37-abi3-macosx_10_12_universal2.whl" + "hash": "2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da", + "url": "https://files.pythonhosted.org/packages/2c/9c/821ef6144daf80360cf6093520bf07eec7c793103ed4b1bf3fa17d2b55d8/cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7", - "url": "https://files.pythonhosted.org/packages/16/a7/38fdcdd634515f589c8c723608c0f0b38d66c6c2320b3095967486f3045a/cryptography-41.0.5.tar.gz" + "hash": "e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e", + "url": "https://files.pythonhosted.org/packages/3f/ae/61d7c256bd8285263cdb5c9ebebcf66261bd0765ed255a074dc8d5304362/cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72", - "url": "https://files.pythonhosted.org/packages/1b/30/24cf09530df7ee5d85a3070b5ef8de5810b49130d955ae8bce11720b8b2a/cryptography-41.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl" + "hash": "cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a", + "url": "https://files.pythonhosted.org/packages/48/c8/c0962598c43d3cff2c9d6ac66d0c612bdfb1975be8d87b8889960cf8c81d/cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1", - "url": "https://files.pythonhosted.org/packages/2a/6d/33e42b8595da059bf10beb1529e501d5817ed4480b1d285303d155cdfccd/cryptography-41.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl" + "hash": "e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1", + "url": "https://files.pythonhosted.org/packages/50/26/248cd8b6809635ed412159791c0d3869d8ec9dfdc57d428d500a14d425b7/cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88", - "url": "https://files.pythonhosted.org/packages/2e/92/720491aae578d21d23934d816ef0620bd1081a1bfdc015f228cc8abccaf1/cryptography-41.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl" + "hash": "ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2", + "url": "https://files.pythonhosted.org/packages/59/48/519ecd6b65dc9ea7c8111dfde7c9ed61aeb90fe59c6b4454900bcd3e3286/cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696", - "url": "https://files.pythonhosted.org/packages/3e/1b/1703679eface155413730f4a2313aebf846ae7496c15083ae9c07e7324b2/cryptography-41.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1", + "url": "https://files.pythonhosted.org/packages/5b/3d/c3c21e3afaf43bacccc3ebf61d1a0d47cef6e2607dbba01662f6f9d8fc40/cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e", - "url": "https://files.pythonhosted.org/packages/48/4b/ef0a674e8ea1d7946dfed0fd3a683bd3a8134af7c7b1e2b0d49205bb494b/cryptography-41.0.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl" + "hash": "f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7", + "url": "https://files.pythonhosted.org/packages/64/f7/d3c83c79947cc6807e6acd3b2d9a1cbd312042777bc7eec50c869913df79/cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d", - "url": "https://files.pythonhosted.org/packages/4b/3d/081af0b323a8efd6cd9d9c5b248049b91a7c2cf5473fc67bae374f016781/cryptography-41.0.5-cp37-abi3-musllinux_1_1_x86_64.whl" + "hash": "a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7", + "url": "https://files.pythonhosted.org/packages/69/f6/630eb71f246208103ffee754b8375b6b334eeedb28620b3ae57be815eeeb/cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86", - "url": "https://files.pythonhosted.org/packages/4d/47/f8f1a8f762e4e7b772d1c9898caec7fa0a1ed4de5b9a536d7997fbb7133c/cryptography-41.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl" + "hash": "5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8", + "url": "https://files.pythonhosted.org/packages/6d/4d/f7c14c7a49e35df829e04d451a57b843208be7442c8e087250c195775be1/cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl" }, { "algorithm": "sha256", - "hash": "5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179", - "url": "https://files.pythonhosted.org/packages/59/34/d3023b52daacea96c4cb0514bd9712011cac444ee45d166919386f7ac13f/cryptography-41.0.5-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl" + "hash": "ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c", + "url": "https://files.pythonhosted.org/packages/6e/8d/6cce88bdeb26b4ec14b23ab9f0c2c7c0bf33ef4904bfa952c5db1749fd37/cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl" }, { "algorithm": "sha256", - "hash": "b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5", - "url": "https://files.pythonhosted.org/packages/76/77/e5ed12b40bbb710137bec76dd43efa6151b43fdece233b647463349e38fa/cryptography-41.0.5-cp37-abi3-macosx_10_12_x86_64.whl" + "hash": "c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922", + "url": "https://files.pythonhosted.org/packages/7d/bc/b6c691c960b5dcd54c5444e73af7f826e62af965ba59b6d7e9928b6489a2/cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20", - "url": "https://files.pythonhosted.org/packages/85/62/48bcebd955945d8da3fe9b84a679dbf4bf179e1ac36e583b7eaa47506758/cryptography-41.0.5-cp37-abi3-manylinux_2_28_x86_64.whl" + "hash": "b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278", + "url": "https://files.pythonhosted.org/packages/8c/50/9185cca136596448d9cc595ae22a9bd4412ad35d812550c37c1390d54673/cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl" }, { "algorithm": "sha256", - "hash": "d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147", - "url": "https://files.pythonhosted.org/packages/bb/36/5af9ca6e0b00bd0c40b0d0e3d95a1bfc4fb7e0b94e522d1394ff4f7505cc/cryptography-41.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8", + "url": "https://files.pythonhosted.org/packages/9f/c3/3d2d9bb2ff9e15b5ababc370ae85b377eacc8e3d54fcb03225471e41a1d8/cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da", - "url": "https://files.pythonhosted.org/packages/e3/21/958e33e2c149461e0a93ca358b794771d55f781ca808efcadb86a4c08e49/cryptography-41.0.5-cp37-abi3-manylinux_2_28_aarch64.whl" + "hash": "3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc", + "url": "https://files.pythonhosted.org/packages/c2/40/c7cb9d6819b90640ffc3c4028b28f46edc525feaeaa0d98ea23e843d446d/cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl" }, { "algorithm": "sha256", - "hash": "22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8", - "url": "https://files.pythonhosted.org/packages/ea/04/a9b58dbaccbc226c3d81f8edfc3cb91497dc295f0cc28d693c4f524169a4/cryptography-41.0.5-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl" + "hash": "1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30", + "url": "https://files.pythonhosted.org/packages/ca/2e/9f2c49bd6a18d46c05ec098b040e7d4599c61f50ced40a39adfae3f68306/cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1", - "url": "https://files.pythonhosted.org/packages/ed/d9/97ca3b4ee56a77fee0ec7ecb2a354c260a11ad5bc50d1e3de165e71f2ec4/cryptography-41.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl" + "hash": "a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16", + "url": "https://files.pythonhosted.org/packages/d1/f1/fd98e6e79242d9aeaf6a5d49639a7e85f05741575af14d3f4a1d477f572e/cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e", + "url": "https://files.pythonhosted.org/packages/d4/fa/057f9d7a5364c86ccb6a4bd4e5c58920dcb66532be0cc21da3f9c7617ec3/cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d", + "url": "https://files.pythonhosted.org/packages/d8/b1/127ecb373d02db85a7a7de5093d7ac7b7714b8907d631f0591e8f002998d/cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec", + "url": "https://files.pythonhosted.org/packages/d9/f9/27dda069a9f9bfda7c75305e222d904cc2445acf5eab5c696ade57d36f1b/cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb", + "url": "https://files.pythonhosted.org/packages/e2/59/61b2364f2a4d3668d933531bc30d012b9b2de1e534df4805678471287d57/cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee", + "url": "https://files.pythonhosted.org/packages/e5/61/67e090a41c70ee526bd5121b1ccabab85c727574332d03326baaedea962d/cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6", + "url": "https://files.pythonhosted.org/packages/ea/fa/b0cd7f1cd011b52196e01195581119d5e2b802a35e21f08f342d6640aaae/cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4", + "url": "https://files.pythonhosted.org/packages/fb/0b/14509319a1b49858425553d2fb3808579cfdfe98c1d71a3f046c1b4e0108/cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" } ], "project_name": "cryptography", "requires_dists": [ "bcrypt>=3.1.5; extra == \"ssh\"", - "black; extra == \"pep8test\"", "build; extra == \"sdist\"", - "cffi>=1.12", + "certifi; extra == \"test\"", + "cffi>=1.12; platform_python_implementation != \"PyPy\"", "check-sdist; extra == \"pep8test\"", + "click; extra == \"pep8test\"", "mypy; extra == \"pep8test\"", "nox; extra == \"nox\"", "pretend; extra == \"test\"", @@ -853,14 +968,59 @@ "pytest-randomly; extra == \"test-randomorder\"", "pytest-xdist; extra == \"test\"", "pytest>=6.2.0; extra == \"test\"", + "readme-renderer; extra == \"docstest\"", "ruff; extra == \"pep8test\"", "sphinx-rtd-theme>=1.1.1; extra == \"docs\"", "sphinx>=5.3.0; extra == \"docs\"", - "sphinxcontrib-spelling>=4.0.1; extra == \"docstest\"", - "twine>=1.12.0; extra == \"docstest\"" + "sphinxcontrib-spelling>=4.0.1; extra == \"docstest\"" ], "requires_python": ">=3.7", - "version": "41.0.5" + "version": "42.0.5" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", + "url": "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", + "url": "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz" + } + ], + "project_name": "deprecation", + "requires_dists": [ + "packaging" + ], + "requires_python": null, + "version": "2.1.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "12ba681f2777a0ad28ffbcc846a69c31b4dfd9752b47eb425a274ee269c5e14b", + "url": "https://files.pythonhosted.org/packages/18/bd/9706c10bb12e05043ef138dc8d412cfd17f29c8df0fb28ad71c96a98785d/docker-7.0.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "323736fb92cd9418fc5e7133bc953e11a9da04f4483f828b527db553f1e7e5a3", + "url": "https://files.pythonhosted.org/packages/25/14/7d40f8f64ceca63c741ee5b5611ead4fb8d3bcaf3e6ab57d2ab0f01712bc/docker-7.0.0.tar.gz" + } + ], + "project_name": "docker", + "requires_dists": [ + "packaging>=14.0", + "paramiko>=2.4.3; extra == \"ssh\"", + "pywin32>=304; sys_platform == \"win32\"", + "requests>=2.26.0", + "urllib3>=1.26.0", + "websocket-client>=1.3.0; extra == \"websockets\"" + ], + "requires_python": ">=3.8", + "version": "7.0.0" }, { "artifacts": [ @@ -886,18 +1046,71 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "4d7d3d3d5e4e4a9954b448fc8220cd73573e3e32adb00059f6907de6b55dcd5e", - "url": "https://files.pythonhosted.org/packages/5a/e9/d49f3a13d1bcc173eff044850b90dd2ff0628c70ff96960ecbf02d2662de/hypothesis-6.90.0-py3-none-any.whl" + "hash": "87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b", + "url": "https://files.pythonhosted.org/packages/f0/f7/ea860cb8aa18e326f411e32ab537424690a53db20de6bad73d70da611fae/fastapi-0.110.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3", + "url": "https://files.pythonhosted.org/packages/61/53/326977db62bf34bbdfc64acb9414e1881af7ea14e8a062fd1c11a8697616/fastapi-0.110.0.tar.gz" + } + ], + "project_name": "fastapi", + "requires_dists": [ + "email-validator>=2.0.0; extra == \"all\"", + "httpx>=0.23.0; extra == \"all\"", + "itsdangerous>=1.1.0; extra == \"all\"", + "jinja2>=2.11.2; extra == \"all\"", + "orjson>=3.2.1; extra == \"all\"", + "pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4", + "pydantic-extra-types>=2.0.0; extra == \"all\"", + "pydantic-settings>=2.0.0; extra == \"all\"", + "python-multipart>=0.0.7; extra == \"all\"", + "pyyaml>=5.3.1; extra == \"all\"", + "starlette<0.37.0,>=0.36.3", + "typing-extensions>=4.8.0", + "ujson!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,>=4.0.1; extra == \"all\"", + "uvicorn[standard]>=0.12.0; extra == \"all\"" + ], + "requires_python": ">=3.8", + "version": "0.110.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", + "url": "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "0ab33900b9362318bd03d911a77a0dda8629c1877420074d87ae466919f6e4c0", - "url": "https://files.pythonhosted.org/packages/57/2f/a22ec98e4c338a61ae98a3fd247c88b136ae9682ac263ab7079889f7aae6/hypothesis-6.90.0.tar.gz" + "hash": "8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", + "url": "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz" + } + ], + "project_name": "h11", + "requires_dists": [ + "typing-extensions; python_version < \"3.8\"" + ], + "requires_python": ">=3.7", + "version": "0.14.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "b538df1d22365df84f94c38fb2d9c41a222373594c2a910cc8f4ddc68240a62f", + "url": "https://files.pythonhosted.org/packages/f7/5c/02b1360a6fd4b31cdea03422af6873d0d5293ea0af3215656c580df76640/hypothesis-6.99.13-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "e425e8a3f1912e44f62ff3e2768dca19c79f46d43ec70fa56e96e2d7194ccd2d", + "url": "https://files.pythonhosted.org/packages/66/c6/8896a3dbe1732b29f419d3442df9fe920cf912aa05e399bf460ddfc6fa5c/hypothesis-6.99.13.tar.gz" } ], "project_name": "hypothesis", "requires_dists": [ - "attrs>=19.2.0", + "attrs>=22.2.0", "backports.zoneinfo>=0.2.1; python_version < \"3.9\" and extra == \"all\"", "backports.zoneinfo>=0.2.1; python_version < \"3.9\" and extra == \"zoneinfo\"", "black>=19.10b0; extra == \"all\"", @@ -905,11 +1118,15 @@ "black>=19.10b0; extra == \"ghostwriter\"", "click>=7.0; extra == \"all\"", "click>=7.0; extra == \"cli\"", + "crosshair-tool>=0.0.53; extra == \"all\"", + "crosshair-tool>=0.0.53; extra == \"crosshair\"", "django>=3.2; extra == \"all\"", "django>=3.2; extra == \"django\"", "dpcontracts>=0.4; extra == \"all\"", "dpcontracts>=0.4; extra == \"dpcontracts\"", "exceptiongroup>=1.0.0; python_version < \"3.11\"", + "hypothesis-crosshair>=0.0.2; extra == \"all\"", + "hypothesis-crosshair>=0.0.2; extra == \"crosshair\"", "lark>=0.10.1; extra == \"all\"", "lark>=0.10.1; extra == \"lark\"", "libcst>=0.3.16; extra == \"all\"", @@ -929,11 +1146,11 @@ "rich>=9.0.0; extra == \"all\"", "rich>=9.0.0; extra == \"cli\"", "sortedcontainers<3.0.0,>=2.1.0", - "tzdata>=2023.3; sys_platform == \"win32\" and extra == \"all\"", - "tzdata>=2023.3; sys_platform == \"win32\" and extra == \"zoneinfo\"" + "tzdata>=2024.1; (sys_platform == \"win32\" or sys_platform == \"emscripten\") and extra == \"all\"", + "tzdata>=2024.1; (sys_platform == \"win32\" or sys_platform == \"emscripten\") and extra == \"zoneinfo\"" ], "requires_python": ">=3.8", - "version": "6.90.0" + "version": "6.99.13" }, { "artifacts": [ @@ -975,162 +1192,159 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "386df621becb506bc315a713ec3d4d5b5d6163116955c7dde23622f156b81af6", - "url": "https://files.pythonhosted.org/packages/2a/45/d80a35ce701c1b3b53ab57a585813636acba39f3a8ed87ac01e0f1dfa3c1/msal-1.25.0-py2.py3-none-any.whl" + "hash": "3064f80221a21cd535ad8c3fafbb3a3582cd9c7e9af0bb789ae14f726a0ca99b", + "url": "https://files.pythonhosted.org/packages/40/41/646c00154efa437bf01b30444421285fb29ef624e86b2446e71eff50b7a9/msal-1.28.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "f44329fdb59f4f044c779164a34474b8a44ad9e4940afbc4c3a3a2bbe90324d9", - "url": "https://files.pythonhosted.org/packages/df/55/2e3047c723a2e3ed880b8a37ab020419c2bae1c0ba3b994fefe0508cb351/msal-1.25.0.tar.gz" + "hash": "80bbabe34567cb734efd2ec1869b2d98195c927455369d8077b3c542088c5c9d", + "url": "https://files.pythonhosted.org/packages/b3/2a/76e64e6a5f0d3d12f4b3ab2cfc8b5e4fcb6982d15213aad9c8e6b20ebeae/msal-1.28.0.tar.gz" } ], "project_name": "msal", "requires_dists": [ "PyJWT[crypto]<3,>=1.0.0", - "cryptography<44,>=0.6", - "mock; python_version < \"3.3\"", - "pymsalruntime<0.14,>=0.13.2; (python_version >= \"3.6\" and platform_system == \"Windows\") and extra == \"broker\"", + "cryptography<45,>=0.6", + "pymsalruntime<0.15,>=0.13.2; (python_version >= \"3.6\" and platform_system == \"Windows\") and extra == \"broker\"", "requests<3,>=2.0.0" ], - "requires_python": ">=2.7", - "version": "1.25.0" + "requires_python": ">=3.7", + "version": "1.28.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "91e3db9620b822d0ed2b4d1850056a0f133cba04455e62f11612e40f5502f2ee", - "url": "https://files.pythonhosted.org/packages/52/34/a8995d6f0fa626ff6b28dbd9c90f6c2a46bd484bc7ab343d078b0c6ff1a7/msal_extensions-1.0.0-py2.py3-none-any.whl" + "hash": "01be9711b4c0b1a151450068eeb2c4f0997df3bba085ac299de3a66f585e382f", + "url": "https://files.pythonhosted.org/packages/78/8d/ecd0eb93196f25c722ba1b923fd54d190366feccfa5b159d48dacf2b1fee/msal_extensions-1.1.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "c676aba56b0cce3783de1b5c5ecfe828db998167875126ca4b47dc6436451354", - "url": "https://files.pythonhosted.org/packages/33/5e/2e23593c67df0b21ffb141c485ca0ae955569203d7ff5064040af968cb81/msal-extensions-1.0.0.tar.gz" + "hash": "6ab357867062db7b253d0bd2df6d411c7891a0ee7308d54d1e4317c1d1c54252", + "url": "https://files.pythonhosted.org/packages/cb/ba/618771542cdc4bc5314c395076c397d67e2bdcd88564c6ca712a2497d1c6/msal-extensions-1.1.0.tar.gz" } ], "project_name": "msal-extensions", "requires_dists": [ "msal<2.0.0,>=0.4.1", - "pathlib2; python_version < \"3.0\"", - "portalocker<2,>=1.0; python_version == \"2.7\" and platform_system != \"Windows\"", - "portalocker<2,>=1.6; python_version == \"2.7\" and platform_system == \"Windows\"", - "portalocker<3,>=1.0; python_version >= \"3.5\" and platform_system != \"Windows\"", - "portalocker<3,>=1.6; python_version >= \"3.5\" and platform_system == \"Windows\"" + "packaging", + "portalocker<3,>=1.0; platform_system != \"Windows\"", + "portalocker<3,>=1.6; platform_system == \"Windows\"" ], - "requires_python": null, - "version": "1.0.0" + "requires_python": ">=3.7", + "version": "1.1.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea", - "url": "https://files.pythonhosted.org/packages/10/df/92bb67911c6c1d3faa46e4c9a5d0a93dd343dcf56022d1fb97a0c0ee65eb/mypy-1.7.1-py3-none-any.whl" + "hash": "a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e", + "url": "https://files.pythonhosted.org/packages/60/db/0ba2eaedca52bf5276275e8489951c26206030b3d31bf06f00875ae75d5d/mypy-1.9.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9", - "url": "https://files.pythonhosted.org/packages/02/d8/782be41850c227cfcbc5e65a911fb76724161736aecd8a53a4537d41f9e2/mypy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl" + "hash": "f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f", + "url": "https://files.pythonhosted.org/packages/1a/a7/0b180ef81daebabd6ef011f12ecd1ba4c0747aa8c460a8caf79f38789b90/mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51", - "url": "https://files.pythonhosted.org/packages/10/ee/8d3add501af4905f986fc54b761b227d245b853c254e24fedda26c9152c9/mypy-1.7.1-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913", + "url": "https://files.pythonhosted.org/packages/1c/1b/3e962a201d2f0f57c9fa1990e0dd6076f4f2f94954ab56e4a701ec3cc070/mypy-1.9.0-cp312-cp312-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d", - "url": "https://files.pythonhosted.org/packages/29/6d/8ffee8037d5371008d729d28ae7e700984db96ed2b6b4cbcc49318b73fda/mypy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl" + "hash": "e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf", + "url": "https://files.pythonhosted.org/packages/36/35/b5ca875ce1a1aa724916ea4bcb7cc0bb53fda2f135915426f8ea077c1984/mypy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200", - "url": "https://files.pythonhosted.org/packages/2a/01/fcd9d04f5d37fe04c7ab75fed028debd9fdf5b26636bac25adfd4366c02e/mypy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl" + "hash": "b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04", + "url": "https://files.pythonhosted.org/packages/3c/95/352a56a3fb8373bde12c2f2a55fbdd643644033ac0294184c63ade3ab97b/mypy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5", - "url": "https://files.pythonhosted.org/packages/44/ae/e45078b06648e42d61461faf1070c7615fe39f904dcef741431beb410b39/mypy-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374", + "url": "https://files.pythonhosted.org/packages/3d/23/b4282a2b59b74a3bf4a16713491348f72d843e218a73a12399bc98754c48/mypy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28", - "url": "https://files.pythonhosted.org/packages/67/47/5bee43b465abd613cd21fd5220a6a35547a42ce0d312c7a5e887bb11e9c2/mypy-1.7.1-cp311-cp311-musllinux_1_1_x86_64.whl" + "hash": "f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4", + "url": "https://files.pythonhosted.org/packages/61/e9/d18add3d83a363fb890944c95de9bf7ac89dceb265edb2304a50099866ee/mypy-1.9.0-cp39-cp39-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce", - "url": "https://files.pythonhosted.org/packages/72/d4/097f61229f6eb148f34b6a61b8d1536fc7de32e46d0d4021ec740278f4d3/mypy-1.7.1-cp39-cp39-macosx_11_0_arm64.whl" + "hash": "653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3", + "url": "https://files.pythonhosted.org/packages/6d/ce/c62c0c0d83b8a936ad6d5e0294e956e881acc5d680deb4929ea259fb50f6/mypy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea", - "url": "https://files.pythonhosted.org/packages/83/9c/51effe3396740868649364ebea5001c694af29ec891bc90aae6944886c45/mypy-1.7.1-cp312-cp312-musllinux_1_1_x86_64.whl" + "hash": "aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd", + "url": "https://files.pythonhosted.org/packages/6e/96/40f0f605b1d4e2ad1fb11d21988ce3a3e205886c0fcbd35c9789a214de9a/mypy-1.9.0-cp312-cp312-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1", - "url": "https://files.pythonhosted.org/packages/88/43/2a04b1cc1a27b1fe623cae9c6840f333e47422598a3556d3824a07d8c2c4/mypy-1.7.1-cp312-cp312-macosx_10_9_x86_64.whl" + "hash": "3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974", + "url": "https://files.pythonhosted.org/packages/72/1e/a587a862c766a755a58b62d8c00aed11b74a15dc415c1bf5da7b607b0efd/mypy-1.9.0.tar.gz" }, { "algorithm": "sha256", - "hash": "75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a", - "url": "https://files.pythonhosted.org/packages/90/f6/7a5cd1e2b4095249efae755f0bc2b5fd518da6626050e1d76a00fbcd598b/mypy-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185", + "url": "https://files.pythonhosted.org/packages/85/a5/b7dc7eb69eda899fd07e71403b51b598a1f4df0f452d1da5844374082bcd/mypy-1.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7", - "url": "https://files.pythonhosted.org/packages/a2/bd/16ba65d605058ba897a707db78f6d939dd7cda4eaffe3d53e48d83e8e3e9/mypy-1.7.1-cp38-cp38-macosx_11_0_arm64.whl" + "hash": "2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129", + "url": "https://files.pythonhosted.org/packages/a1/81/97e8539d6cdcfb3a8ae7eb1438c6983a9fc434ef9664572bfa7fd285cab9/mypy-1.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49", - "url": "https://files.pythonhosted.org/packages/a6/8c/034b199b5b07cfa1adbe3b629fced2549cbb1b95919de7a3bd3b349ad425/mypy-1.7.1-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e", + "url": "https://files.pythonhosted.org/packages/c7/9f/a2cec898515478f69a5996eb9df74513dd1d9658e7e83fb224d3a0b2cf0f/mypy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e", - "url": "https://files.pythonhosted.org/packages/aa/5e/eaee23820d6ca6d7fc75a894839bce98594bb80821161e37a70b489bfc88/mypy-1.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b", + "url": "https://files.pythonhosted.org/packages/cb/a3/c6d971f07b312117073ca77f006337fc8b074eb304bdd43fbf9971cacbb3/mypy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2", - "url": "https://files.pythonhosted.org/packages/ae/30/05a7c016431b3fdbaf0bcf663aee7c5e4b3d2293cd4e0568140cecae4967/mypy-1.7.1.tar.gz" + "hash": "d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed", + "url": "https://files.pythonhosted.org/packages/d0/41/87f727fdbb43a1f975df5fe5d038dad552440b1e5c21f999bce0d83fd847/mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a", - "url": "https://files.pythonhosted.org/packages/af/9a/ad4b219cf27496653c2312407d6a47593f25f5b53e2d163ade5961cac78c/mypy-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d", + "url": "https://files.pythonhosted.org/packages/d5/61/0433cb518d7f0eb1978834d23bcc178036e9629449cab9cecd2b2a46f0b3/mypy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb", - "url": "https://files.pythonhosted.org/packages/b6/ab/39f476f18a45b2a74b38722107ac5c8e50a5ee41f39f7ac46ac50caa630e/mypy-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02", + "url": "https://files.pythonhosted.org/packages/d6/3f/213223cab830d9d593bb8764db51c00e528e6c20c2a48bb2f69e6dc3c003/mypy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl" }, { "algorithm": "sha256", - "hash": "ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120", - "url": "https://files.pythonhosted.org/packages/cb/ed/a96d30f4d5b9d78508765b267058305043985818f771963a455adca73af3/mypy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl" + "hash": "0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6", + "url": "https://files.pythonhosted.org/packages/d7/d2/072e40384b53051106b4fcf03537fb88e2a6ad0757d2ab7f6c8c2f188a69/mypy-1.9.0-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33", - "url": "https://files.pythonhosted.org/packages/cc/b7/5ae852edccf9eba4d71b3df212928f15759142894a4bb84c1d3cb0ade729/mypy-1.7.1-cp312-cp312-macosx_11_0_arm64.whl" + "hash": "3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc", + "url": "https://files.pythonhosted.org/packages/da/e2/1864612774cf8a445f6d42ce73ce0f1492a37ed2af1c908e989f1ec7d349/mypy-1.9.0-cp311-cp311-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe", - "url": "https://files.pythonhosted.org/packages/de/0e/07025ca7d3fa0c89456cd62b470473341693e205a2cd99add370b22c4012/mypy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl" + "hash": "49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150", + "url": "https://files.pythonhosted.org/packages/e1/87/b508b34309359daa00e0e76d9a0dbe43031866af49b279861f69c76e5d70/mypy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7", - "url": "https://files.pythonhosted.org/packages/e1/c6/99b845e9eaf7290ff6bdab40e3bbbb5134bebfa56ca622003c24596325c7/mypy-1.7.1-cp311-cp311-macosx_10_9_x86_64.whl" + "hash": "5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2", + "url": "https://files.pythonhosted.org/packages/ee/2d/a081526f63444e6520dfcc0810326c44052b9d7e93d46549132f86b929e0/mypy-1.9.0-cp38-cp38-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340", - "url": "https://files.pythonhosted.org/packages/ea/a1/4d821de78ad8c78b2e159359faabd70cc85dcccb0f1c9cd71d5ea5ae332a/mypy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl" + "hash": "68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612", + "url": "https://files.pythonhosted.org/packages/ef/cf/43c1e29b9d3b2bf6c75e32d021d7db4631c516e4c0bd72b75bc8836680d8/mypy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl" } ], "project_name": "mypy", @@ -1144,7 +1358,7 @@ "typing-extensions>=4.1.0" ], "requires_python": ">=3.8", - "version": "1.7.1" + "version": "1.9.0" }, { "artifacts": [ @@ -1168,31 +1382,31 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", - "url": "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl" + "hash": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", + "url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", - "url": "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz" + "hash": "eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9", + "url": "https://files.pythonhosted.org/packages/ee/b5/b43a27ac7472e1818c4bafd44430e69605baefe1f34440593e0332ec8b4d/packaging-24.0.tar.gz" } ], "project_name": "packaging", "requires_dists": [], "requires_python": ">=3.7", - "version": "23.2" + "version": "24.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7", - "url": "https://files.pythonhosted.org/packages/05/b8/42ed91898d4784546c5f06c60506400548db3f7a4b3fb441cba4e5c17952/pluggy-1.3.0-py3-none-any.whl" + "hash": "7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981", + "url": "https://files.pythonhosted.org/packages/a5/5b/0cc789b59e8cc1bf288b38111d002d8c5917123194d45b29dcdac64723cc/pluggy-1.4.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", - "url": "https://files.pythonhosted.org/packages/36/51/04defc761583568cae5fd533abda3d40164cbdcf22dee5b7126ffef68a40/pluggy-1.3.0.tar.gz" + "hash": "8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be", + "url": "https://files.pythonhosted.org/packages/54/c6/43f9d44d92aed815e781ca25ba8c174257e27253a94630d21be8725a2b59/pluggy-1.4.0.tar.gz" } ], "project_name": "pluggy", @@ -1203,7 +1417,7 @@ "tox; extra == \"dev\"" ], "requires_python": ">=3.8", - "version": "1.3.0" + "version": "1.4.0" }, { "artifacts": [ @@ -1238,413 +1452,442 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "url": "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl" + "hash": "4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e", + "url": "https://files.pythonhosted.org/packages/fc/ca/dff5452be0b31fab93808b1ff0047bebc08d1ac65939be104c98e081b069/psycopg-3.1.18-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206", - "url": "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz" + "hash": "31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b", + "url": "https://files.pythonhosted.org/packages/31/4d/43deb2a892b95875672df8fb34fcbff1345214f96d94ff49206871576fc0/psycopg-3.1.18.tar.gz" + } + ], + "project_name": "psycopg", + "requires_dists": [ + "Sphinx>=5.0; extra == \"docs\"", + "anyio<4.0,>=3.6.2; extra == \"test\"", + "backports.zoneinfo>=0.2.0; python_version < \"3.9\"", + "black>=24.1.0; extra == \"dev\"", + "codespell>=2.2; extra == \"dev\"", + "dnspython>=2.1; extra == \"dev\"", + "flake8>=4.0; extra == \"dev\"", + "furo==2022.6.21; extra == \"docs\"", + "mypy>=1.4.1; extra == \"dev\"", + "mypy>=1.4.1; extra == \"test\"", + "pproxy>=2.7; extra == \"test\"", + "psycopg-binary==3.1.18; implementation_name != \"pypy\" and extra == \"binary\"", + "psycopg-c==3.1.18; implementation_name != \"pypy\" and extra == \"c\"", + "psycopg-pool; extra == \"pool\"", + "pytest-cov>=3.0; extra == \"test\"", + "pytest-randomly>=3.5; extra == \"test\"", + "pytest>=6.2.5; extra == \"test\"", + "sphinx-autobuild>=2021.3.14; extra == \"docs\"", + "sphinx-autodoc-typehints>=1.12; extra == \"docs\"", + "types-setuptools>=57.4; extra == \"dev\"", + "typing-extensions>=4.1", + "tzdata; sys_platform == \"win32\"", + "wheel>=0.37; extra == \"dev\"" + ], + "requires_python": ">=3.7", + "version": "3.1.18" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", + "url": "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "url": "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz" } ], "project_name": "pycparser", "requires_dists": [], - "requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7", - "version": "2.21" + "requires_python": ">=3.8", + "version": "2.22" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0", - "url": "https://files.pythonhosted.org/packages/0a/2b/64066de1c4cf3d4ed623beeb3bbf3f8d0cc26661f1e7d180ec5eb66b75a5/pydantic-2.5.2-py3-none-any.whl" + "hash": "cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5", + "url": "https://files.pythonhosted.org/packages/e5/f3/8296f550276194a58c5500d55b19a27ae0a5a3a51ffef66710c58544b32d/pydantic-2.6.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd", - "url": "https://files.pythonhosted.org/packages/b7/41/3c8108f79fb7da2d2b17f35744232af4ffcd9e764ebe1e3fd4b26669b325/pydantic-2.5.2.tar.gz" + "hash": "b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6", + "url": "https://files.pythonhosted.org/packages/4b/de/38b517edac45dd022e5d139aef06f9be4762ec2e16e2b14e1634ba28886b/pydantic-2.6.4.tar.gz" } ], "project_name": "pydantic", "requires_dists": [ "annotated-types>=0.4.0", "email-validator>=2.0.0; extra == \"email\"", - "importlib-metadata; python_version == \"3.7\"", - "pydantic-core==2.14.5", + "pydantic-core==2.16.3", "typing-extensions>=4.6.1" ], - "requires_python": ">=3.7", - "version": "2.5.2" + "requires_python": ">=3.8", + "version": "2.6.4" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe", - "url": "https://files.pythonhosted.org/packages/ef/e9/ffaec12924f90d4f2f589b0f6f510b671a561b02dce47ce9fad559b41ac3/pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl" + "hash": "ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc", + "url": "https://files.pythonhosted.org/packages/5b/ff/8d5c1b17206098a92e57751c4fea3cefa6c3f215a32765faadce832012d1/pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0", - "url": "https://files.pythonhosted.org/packages/00/47/88baa62574f06e2dd5b9c0285b5b9b300c79e3d808c5d5a81f04e0817b82/pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl" + "hash": "0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff", + "url": "https://files.pythonhosted.org/packages/03/c8/9afd3a316123806d7bff177beba7906ab9dd267845ae42f98f051d2250a0/pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789", - "url": "https://files.pythonhosted.org/packages/03/99/f7eb0cc34ea21e94aa0610a9c0794064847adc38ab824c8722e9fe35ebba/pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e", + "url": "https://files.pythonhosted.org/packages/0d/84/5e157e382cf8e2a5854802211ab954662841a82e3d3b9ff1be08b3fd7298/pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07", - "url": "https://files.pythonhosted.org/packages/05/7b/9083133f247b9f712f5718c66b3e39194ea679fbe85567bf4dc9d08557bb/pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241", + "url": "https://files.pythonhosted.org/packages/10/72/7574e1ef407fde0aa70fc02acdd09ea791366f69194827096a7072fa88a0/pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4", - "url": "https://files.pythonhosted.org/packages/0b/32/0a6ee79ed34e8934a54548495883017dfaf3fc742b0d0d02afa154f1f49d/pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl" + "hash": "4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c", + "url": "https://files.pythonhosted.org/packages/12/11/e7ababda30c736572300058c4b52cb2323f4d2bdabf26ec895361439792d/pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d", - "url": "https://files.pythonhosted.org/packages/10/89/bbb9bb3bd59b1cb36a87c2f6b6e3b2858fdb6ac438539f67a6c93a91ba5e/pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl" + "hash": "a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e", + "url": "https://files.pythonhosted.org/packages/18/0e/1e39cfbffa57e92ab9f1f0869b32ead8a48ab11e4a373421d625f25fcb26/pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459", - "url": "https://files.pythonhosted.org/packages/12/00/bd693e0bf24fa016c7194ac9ca671903b0938a5aa2603f7b5779070a15a0/pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl" + "hash": "0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979", + "url": "https://files.pythonhosted.org/packages/1d/fd/a59e201dc75125a91328e90b9156f31562c11075fffc9399cb9072a3a148/pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209", - "url": "https://files.pythonhosted.org/packages/19/1c/d9ba54c20c76706eb04491187d2d22ce56982ec3d999c6915ceb16755ebd/pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl" + "hash": "a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b", + "url": "https://files.pythonhosted.org/packages/1e/d6/64d0b9569d8d176d1df5ff74e87aaf460093a57f93bae2aa4140c015cbab/pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5", - "url": "https://files.pythonhosted.org/packages/1a/b8/7f1ca7c80dcb44bd525ba5e5feba5e45be686daeee535b434628be0f6cd7/pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl" + "hash": "5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f", + "url": "https://files.pythonhosted.org/packages/21/af/ff8366baf078e9b14fb780c004d1a5bf00d2c4fee3d04319991a66ace636/pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8", - "url": "https://files.pythonhosted.org/packages/1d/0f/bb0bd20e5bbabdf99d0a25858cf77b74926826a75d0458dc4842cf360ea5/pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9", + "url": "https://files.pythonhosted.org/packages/37/c7/d006cca0650d39b887489f5b0c62c767d6614d2eceef12b378e25f0f68ec/pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3", - "url": "https://files.pythonhosted.org/packages/1f/f0/a588fd5d66c9c3bf16d63cac3437e2260cbddd7df7a089ca58b8e94dcb3e/pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183", + "url": "https://files.pythonhosted.org/packages/39/ac/bb3fe0960707ba7ef18eb242ca193df59bc7eec925adbda1dc28df723c03/pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937", - "url": "https://files.pythonhosted.org/packages/22/11/3f332887a888217e28b23c115c343ef89ccf5f49bbbd88d9317c707b00ac/pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c", + "url": "https://files.pythonhosted.org/packages/3a/49/895d128c60d6af044c3c0d6a6850fd3bae35d07d2ab5bd03ab7364429eb2/pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd", - "url": "https://files.pythonhosted.org/packages/28/27/83ad40b64e8503b0eaeb88f6206225d0a3be1bd1d852dfdc4437f7e02a69/pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2", + "url": "https://files.pythonhosted.org/packages/3c/82/b79a75a6f5b19f7f43b08671f6b818a335b5d970b9e50a39acd3f07aed32/pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada", - "url": "https://files.pythonhosted.org/packages/28/81/f5452ccf3b15aa280188fbf2b6ab39ed700623df4fcc28675f19eee9634a/pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl" + "hash": "287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6", + "url": "https://files.pythonhosted.org/packages/41/d4/484a806ce1108c531e1ebfc08cd6d08e42b879c4dc54121309f6fa52c1dc/pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3", - "url": "https://files.pythonhosted.org/packages/2a/83/05756b6656c3478e34e5dd5fcb693034f586bb1d437365928f6989bb0050/pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01", + "url": "https://files.pythonhosted.org/packages/45/8b/3e9105cf60fcdd59cb130cd807331d66b04d4f6a438142469b0a827d80bc/pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db", - "url": "https://files.pythonhosted.org/packages/2a/b7/f85e5fd4504fae0df3eadd4bf9e0c495ecbdb804dc9be65653119454571e/pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca", + "url": "https://files.pythonhosted.org/packages/46/28/cb10d96904bd7483a6237855e427876e72c369ec100d6c946d468257bbb8/pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997", - "url": "https://files.pythonhosted.org/packages/2c/43/d94f10d82ccffc86bd69bfac73c54589703008236d63965dd40005a80af9/pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl" + "hash": "13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5", + "url": "https://files.pythonhosted.org/packages/4b/8c/569a5db6be43934d7ed88d0f64b9746217c5eca26cb7b6d70fcc01e81b9c/pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911", - "url": "https://files.pythonhosted.org/packages/36/53/d4ae1f5273cbc83d5a4c158916a9235c1bfc8194be63958b4b5ff11bf838/pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972", + "url": "https://files.pythonhosted.org/packages/4c/59/98f7929c35df068b872070bdb257404a60d5ddc8dabb9ac06af079576acd/pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093", - "url": "https://files.pythonhosted.org/packages/3a/dd/fc81e3ea962a356a705fa06965a7dbc0b204da014f238df95f1cd276bfab/pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl" + "hash": "cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad", + "url": "https://files.pythonhosted.org/packages/4e/08/cf75dd8f8a87220f428cd03023369c9645a6005f88f9bf423cfa1825f746/pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f", - "url": "https://files.pythonhosted.org/packages/3c/5e/2a822aa3f3dd68fa45129d4d50290625e97b9b223cf76bafeb765430a0bc/pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl" + "hash": "99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137", + "url": "https://files.pythonhosted.org/packages/51/98/22a3052e4fc2189c3eda813eaad173139963cc84dfded0cc0695282f68aa/pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f", - "url": "https://files.pythonhosted.org/packages/41/0a/1c0372929f3723587d66c188cbdd0c47d269447e0ac8f231f0db0f9bb03c/pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c", + "url": "https://files.pythonhosted.org/packages/51/b2/ecf41e6e365c946145a4e88efa7e60e6c1173cb93e1cb3a107166bb09efc/pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66", - "url": "https://files.pythonhosted.org/packages/46/df/5159aa30c4b2128f14634f3b3e9e19df228364c2107cda7910d058cc1bca/pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda", + "url": "https://files.pythonhosted.org/packages/54/18/7dd9308ad022d0b47b41f5506e179e563e7cf04a04d1574598e756c83b2a/pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144", - "url": "https://files.pythonhosted.org/packages/47/85/190ee74d99149a6d16bf14016d0011b629702d37b955070a5fabaa3be8a8/pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl" + "hash": "8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b", + "url": "https://files.pythonhosted.org/packages/54/c0/7ecafb2dad658078bf28e4045a29a7b2de76319ebbc8cf7ef177d17e4d9e/pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2", - "url": "https://files.pythonhosted.org/packages/4a/5c/cc41dad06acd213f093581454812d6bb20311524ecf265f893e05e4fbe84/pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8", + "url": "https://files.pythonhosted.org/packages/59/7b/de628f7afabf4c0d5cd3bbc871b90843c08b3ca1a5d3e465f70428c7eaab/pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b", - "url": "https://files.pythonhosted.org/packages/4f/10/c44d89cb2fa31a27766aeb39b11380ad2e01bdab7f4bf63b18dfea20ec00/pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120", + "url": "https://files.pythonhosted.org/packages/60/7e/5bdb72aa8f1de0a0e38194dd261b5335747ef8d9bf3421fc960498442830/pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c", - "url": "https://files.pythonhosted.org/packages/57/03/0f238853ad2c93ba344ad702234ee02ff8daa10b7cd680523a40a851499d/pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1", + "url": "https://files.pythonhosted.org/packages/62/0a/f4c40eccecd08677b3b7b96dc87c6705a56f546c2a5404241de01ffa9da9/pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc", - "url": "https://files.pythonhosted.org/packages/5a/cf/1348242330768c4014ba26c51a847c23db105da6b21bdcefbc9087926af3/pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl" + "hash": "b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8", + "url": "https://files.pythonhosted.org/packages/62/c1/c0e7984c1e06d53dc48231f052699ba62ec97a1429413295f883c66bfda8/pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec", - "url": "https://files.pythonhosted.org/packages/62/5c/de43c71edd1cda67e5cc194873ee84483230ac9cf576d6020ee945e0494e/pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl" + "hash": "b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805", + "url": "https://files.pythonhosted.org/packages/63/5a/c38280dff597924f0962195bbb11318d710271e76f12661f8a4de3f9738f/pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26", - "url": "https://files.pythonhosted.org/packages/63/e6/8887679b7f923290db2638bf80733c609aaefaae29b9fe99b83f800c1910/pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48", + "url": "https://files.pythonhosted.org/packages/64/b7/b6ae4b8d37e4695e74e0578dd842c94cef406f4ebad9c98a2f248a0057d2/pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71", - "url": "https://files.pythonhosted.org/packages/64/26/cffb93fe9c6b5a91c497f37fae14a4b073ecbc47fc36a9979c7aa888b245/pydantic_core-2.14.5.tar.gz" + "hash": "1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad", + "url": "https://files.pythonhosted.org/packages/77/3f/65dbe5231946fe02b4e6ea92bc303d2462f45d299890fd5e8bfe4d1c3d66/pydantic_core-2.16.3.tar.gz" }, { "algorithm": "sha256", - "hash": "61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093", - "url": "https://files.pythonhosted.org/packages/66/11/f3e35b74745b5167df5f1dc15bd2368dbaa9e70d2ad8438a0c9485b78da5/pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl" + "hash": "162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45", + "url": "https://files.pythonhosted.org/packages/78/7e/e8d64c813b1a632c8d545b0208182361597973ad8a4f5082cc66dcdcef51/pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520", - "url": "https://files.pythonhosted.org/packages/66/44/ed210be2a055e612d58146be167017e43a76ff79807c753a264d7084d24d/pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl" + "hash": "dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a", + "url": "https://files.pythonhosted.org/packages/7a/48/6853dfcf23693ac14af1ff381e17f318c2ef381db1fedb157b30fd540644/pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8", - "url": "https://files.pythonhosted.org/packages/6c/ba/f3eee66c90f2e4f468fc01cace46ec633f9d47d53e1610ef3bc6003fc936/pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl" + "hash": "cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e", + "url": "https://files.pythonhosted.org/packages/7c/6e/3c188b11eef09d15702f3808bf6d0b2828a4268fb4be19ac7a2ef4f6a8c7/pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4", - "url": "https://files.pythonhosted.org/packages/75/cf/2f6e6410ae735c11df32c391948a6c601a22f40f414b5dfc24f2def8c064/pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b", + "url": "https://files.pythonhosted.org/packages/7d/d2/66392daf79515c506a1742f935d01861b945e023dcfc5c8cea14bd6aa6c1/pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124", - "url": "https://files.pythonhosted.org/packages/76/b3/54001e0b49c3eb135cccb1d353c8bd758b77b60d3c610b47888ac1e12fa6/pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl" + "hash": "75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4", + "url": "https://files.pythonhosted.org/packages/87/07/7f0e613e287376a7a2673c31fa24e1891f750972290465bd2d8a73d1ba07/pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9", - "url": "https://files.pythonhosted.org/packages/78/ef/4fd3b40a82ea729a2566575aeec119449b0bf1b4c13d9255e8ac2a40a58b/pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256", + "url": "https://files.pythonhosted.org/packages/8d/0c/91c3a51e5bd1480e1799322392d52f2c3164277051c9b22d581f2a85bbcc/pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e", - "url": "https://files.pythonhosted.org/packages/79/73/d1d3846f19b11a7d62e93e5c38c5386c42f3e42abad46c0d1904ccdf8fef/pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820", + "url": "https://files.pythonhosted.org/packages/8e/a4/00f38508c29cb7256388dc5c08bbb105f626d47a50c1be69521109b87d5e/pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113", - "url": "https://files.pythonhosted.org/packages/7c/f5/3e59681bd53955da311a7f4efbb6315d01006e9d18b8a06b527a22d3d923/pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4", + "url": "https://files.pythonhosted.org/packages/8e/c7/d89b2692eaaebadc9aa792a8e22f085b7fc7ed11f4cff791a9572c3fae3f/pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl" }, { "algorithm": "sha256", - "hash": "ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3", - "url": "https://files.pythonhosted.org/packages/7d/de/df454233c7960a899846f037209204df1d8ab761bb81a7561abb4daf2288/pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23", + "url": "https://files.pythonhosted.org/packages/90/49/a8b478aaccd9cf9e93b2c492695fe626d376b32f0c5fad5431ed92e8ef25/pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d", - "url": "https://files.pythonhosted.org/packages/7e/ff/72d57544a70f4f37a06c40cfe1c4a038bc21db308e916a277faa1854a1d8/pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl" + "hash": "be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074", + "url": "https://files.pythonhosted.org/packages/94/18/56735aa19a265916684cda6b77c716ff51a37d4cba79685853d0203cc928/pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706", - "url": "https://files.pythonhosted.org/packages/80/ee/c1ce56f63f08bf261f243d7f5faed5b1d2215d231996e74f7dd89559e9e5/pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89", + "url": "https://files.pythonhosted.org/packages/9a/d8/7bd2fdf14e33ab0c16f45c4099917f30b37ad206bb2e0f6bb1b2d9c0f395/pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d", - "url": "https://files.pythonhosted.org/packages/84/01/079cd694491f1e05a1caae15a2ee32321a8fa748a34a183f6a38bf885af9/pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97", + "url": "https://files.pythonhosted.org/packages/9d/1a/b550381063265588e7c54ff56a642a725ac3bfbb3c8a5a08409ccac1e810/pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189", - "url": "https://files.pythonhosted.org/packages/89/5c/e0584d534863639757e05479a3c1172550e3d3dab0c39b79e41692d1804d/pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + "hash": "a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8", + "url": "https://files.pythonhosted.org/packages/a7/6b/c9cfc165e18222b226daedb2940889f3c3a22fdc11ebf29249bf8704b8b8/pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc", - "url": "https://files.pythonhosted.org/packages/8f/af/b202d44845f89e9c997f2f351be35a76ff78304eb926b1bdb33929de40db/pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf", + "url": "https://files.pythonhosted.org/packages/af/9b/3eb4c9dc8712543424b1731c44d3597f56ed4be3bdfbec3f9a45111b774a/pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69", - "url": "https://files.pythonhosted.org/packages/90/6f/52cb83061430628878c34fdb199ccc8313a104f1390d99bff4a29b2ff6fe/pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl" + "hash": "c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8", + "url": "https://files.pythonhosted.org/packages/b1/01/1e562fd1f8ebce512cb785964de1b14cdd09ce047c573488dcc8a19111c1/pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8", - "url": "https://files.pythonhosted.org/packages/94/cd/de236ed3c5a2a0f5545cf78e7a6aaa04d8ee10dc3b738cc516bfc59dfb18/pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl" + "hash": "00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a", + "url": "https://files.pythonhosted.org/packages/b2/b4/7b0b21e39542db4abeb9a3470c4dfa528e5131ae1c5f487bfe46e60284f2/pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7", - "url": "https://files.pythonhosted.org/packages/9a/e1/c33fcdbdad7f5c29376fa2e57f8d60f966c44fc77fc36a70d0ae03bbe813/pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl" + "hash": "9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1", + "url": "https://files.pythonhosted.org/packages/b3/9b/bab93756eb12a10e3db425d5e6bd603aa7089e596202713020bbb91b00e4/pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef", - "url": "https://files.pythonhosted.org/packages/9c/52/2fc8b7e07f360993bc3d5f9ea743aac9f59287002035887c7d4f45bc6fb6/pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db", + "url": "https://files.pythonhosted.org/packages/b5/d4/c26689ac08b4b935d11e395516403a7b77e68e94f4861300447d1b1c8de5/pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed", - "url": "https://files.pythonhosted.org/packages/9e/f3/9e3d334976b5051cd18e3feef06516ead3230efb8b9af8514bc52b2795b1/pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f", + "url": "https://files.pythonhosted.org/packages/b8/be/a3c2edde00afcf5cdc0fb710ce0289f5af776273f420b4486cf005c94b57/pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b", - "url": "https://files.pythonhosted.org/packages/a4/09/90f5a03ab19e21601c6fec11fc9dea30e3228731e12b2f75f58d02430b85/pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187", + "url": "https://files.pythonhosted.org/packages/bc/e7/e387bf771fac18e41893dc7e08f07dc3e93143b1befebc7af71cbd847004/pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca", - "url": "https://files.pythonhosted.org/packages/a6/c6/01758bde5022817fd202ee9de506ea5ba3cedc9aa4b421edabda0d1b9fa4/pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl" + "hash": "e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f", + "url": "https://files.pythonhosted.org/packages/be/31/5f6b46d10f7624963630a38cf3ac97f5d62982000a656aa1976d2f84edbd/pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00", - "url": "https://files.pythonhosted.org/packages/a7/be/6be1245f78b72da970cf52cf4c55d8abcfd1655114d122ee6cf5641fc3f5/pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl" + "hash": "1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340", + "url": "https://files.pythonhosted.org/packages/ce/68/50bfcf8fc9e51a9ca7e914bfcf8902008511e63f9922694474161ed028b9/pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe", - "url": "https://files.pythonhosted.org/packages/a9/2b/f1dca235271785f19e0f3696b31140d6a69ff5349970253c034f9c603b8e/pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + "hash": "fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a", + "url": "https://files.pythonhosted.org/packages/d1/43/430e8a0be9dfec1ff9fb7f2289da9bd684fdb8d15796888a53b540c5e3d6/pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl" }, { "algorithm": "sha256", - "hash": "ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88", - "url": "https://files.pythonhosted.org/packages/ab/43/77d8f56eb332e84097f18fc294346d214e9f0d22fb9ec67ebed4b8e90e35/pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl" + "hash": "72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d", + "url": "https://files.pythonhosted.org/packages/d7/70/c05ec1dc13e2ec4247309ba1fe1b37847c24cdd7929e7116a55da62a25ad/pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1", - "url": "https://files.pythonhosted.org/packages/ab/a6/e6e660299765ae03a55375935d5c6edc9d3e4798e63642f6c3030e15fddf/pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac", + "url": "https://files.pythonhosted.org/packages/d7/ce/666885ab07e5184825b081095071297057b77c9dccd62616bf5b85a26365/pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" }, { "algorithm": "sha256", - "hash": "853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda", - "url": "https://files.pythonhosted.org/packages/af/ab/79c2126e5504a3f0ecc0b1d97768594f9baa090134b0053309a2d938efaa/pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl" + "hash": "aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053", + "url": "https://files.pythonhosted.org/packages/d7/d9/b3d217a092bf23b143e59a691d61598c308386293c310ff6746a0c8ed6a5/pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" }, { "algorithm": "sha256", - "hash": "3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6", - "url": "https://files.pythonhosted.org/packages/b2/83/ae5698f7a8121599b239ea547f58f7b135e299e87cfe1a88fb1e6319d57c/pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + "hash": "716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec", + "url": "https://files.pythonhosted.org/packages/d8/f1/831ee552713474daf89997b56f3c0e7157ad40fe599172b444750f50ca66/pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, { "algorithm": "sha256", - "hash": "531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331", - "url": "https://files.pythonhosted.org/packages/ba/95/d1104b88d5e3ad42db30935a4c258da2385139dd216ec8dfbc347a32dbff/pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade", + "url": "https://files.pythonhosted.org/packages/dc/df/cd1cdd79a307c06fbea11be2cd8f361604b82f9b28c7712bd1220c44f226/pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de", - "url": "https://files.pythonhosted.org/packages/ba/9b/5246600a17467ad8071174250d7727b34f5dc0dfe74abf3e99dbdf1beee1/pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl" + "hash": "c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975", + "url": "https://files.pythonhosted.org/packages/e7/b2/b6eef8d0a914e44826785cc99cd7a1711c2eea2dfc69bc3aefc3be507234/pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863", - "url": "https://files.pythonhosted.org/packages/bf/d2/4820db26970effb5d6fdee68f578585448b2eb6dd7344ab55b20958a0874/pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl" + "hash": "7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9", + "url": "https://files.pythonhosted.org/packages/eb/20/c44de400d4906f75c11e8e447d1dba24ee7273fec02073686af8866f6e38/pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" }, { "algorithm": "sha256", - "hash": "96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6", - "url": "https://files.pythonhosted.org/packages/bf/ed/ee221482b51f368884ea6453f3784eeaeb17f5b737589d39d68a89bffde7/pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + "hash": "d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a", + "url": "https://files.pythonhosted.org/packages/ee/16/9f724d8841bef4509f667143501529c75091760a4248c2e2459f64378b55/pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" }, { "algorithm": "sha256", - "hash": "7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd", - "url": "https://files.pythonhosted.org/packages/c0/d2/b31c030802f29c35fa0c8ab92891bee9dcedd2793df560041b6d38f5fd49/pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl" + "hash": "2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7", + "url": "https://files.pythonhosted.org/packages/f0/d6/a8914af00eeb62444609f3c7acda8fd92b23ed5f14d272ad0f3fbe103730/pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl" }, { "algorithm": "sha256", - "hash": "cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e", - "url": "https://files.pythonhosted.org/packages/cb/96/27421976cde52555eb20636d59743621d4fa3bba278a0e4dbb4751e3f5c1/pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99", + "url": "https://files.pythonhosted.org/packages/f1/35/a081d16848d303abaf2fdd98c65b3da0593455e5867c61d211626b5e8139/pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl" }, { "algorithm": "sha256", - "hash": "3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955", - "url": "https://files.pythonhosted.org/packages/d2/d7/0f13f8cce749c4c5484ddfe60239bcce21a2a6cdcea250f13ae471cb86cb/pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl" + "hash": "33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba", + "url": "https://files.pythonhosted.org/packages/fe/18/ced020e55c75cfc514957bbe8fefe61d591673098c4385c53bcad183928f/pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl" }, { "algorithm": "sha256", - "hash": "88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4", - "url": "https://files.pythonhosted.org/packages/d4/bb/923eeeb3e87ba9024e311e0f3d1f0a4baad609ed7bfc7da7341e95981bd4/pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl" - }, - { - "algorithm": "sha256", - "hash": "074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b", - "url": "https://files.pythonhosted.org/packages/e5/15/5ccdb37835f710819305024fb07512bf202da1a247b4ffdbdb82a6c34f7a/pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl" - }, - { - "algorithm": "sha256", - "hash": "038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b", - "url": "https://files.pythonhosted.org/packages/e6/bc/e5cd49beafe7bf0f640bfd0a1b42e00b17b81ab072dea77c4a60cf986127/pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl" - }, - { - "algorithm": "sha256", - "hash": "6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb", - "url": "https://files.pythonhosted.org/packages/eb/45/5eef8d36c2bf4c63e73e598fe523a0bc15069a97994481e27bef933ff423/pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl" - }, - { - "algorithm": "sha256", - "hash": "40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68", - "url": "https://files.pythonhosted.org/packages/ed/b0/afd8f57e4ac5eaa4f1562b6f04cf10140cd6596c97d378aae2af6a236234/pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl" - }, - { - "algorithm": "sha256", - "hash": "3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a", - "url": "https://files.pythonhosted.org/packages/f2/a4/fcb082e0723f9e4fcdbc5564879255c7f6de1f3d4d6acdd1b8799a86aa97/pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl" - }, + "hash": "b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd", + "url": "https://files.pythonhosted.org/packages/ff/c7/e14e6ce2fe221d1046a7cc190b26b2bde2b1076d901154cdb8c20d88e6e0/pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl" + } + ], + "project_name": "pydantic-core", + "requires_dists": [ + "typing-extensions!=4.7.0,>=4.6.0" + ], + "requires_python": ">=3.8", + "version": "2.16.3" + }, + { + "artifacts": [ { "algorithm": "sha256", - "hash": "d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7", - "url": "https://files.pythonhosted.org/packages/fb/84/f7e4556343ea0a483fa4e18505efaf10002581d2e980867a5b1ed22bfd21/pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl" + "hash": "0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091", + "url": "https://files.pythonhosted.org/packages/99/ee/24ec87e3a91426497c5a2b9880662d19cfd640342d477334ebc60fc2c276/pydantic_settings-2.2.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf", - "url": "https://files.pythonhosted.org/packages/fd/83/65e9db6549a01a369202fadac682c1a9f5ec57a637e672554ee50ef7f625/pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl" + "hash": "00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed", + "url": "https://files.pythonhosted.org/packages/00/a4/89191c3cce6e6f79b734bfe81d3a8f176d21b57b034689cfbdc57d61c412/pydantic_settings-2.2.1.tar.gz" } ], - "project_name": "pydantic-core", + "project_name": "pydantic-settings", "requires_dists": [ - "typing-extensions!=4.7.0,>=4.6.0" + "pydantic>=2.3.0", + "python-dotenv>=0.21.0", + "pyyaml>=6.0.1; extra == \"yaml\"", + "tomli>=2.0.1; extra == \"toml\"" ], - "requires_python": ">=3.7", - "version": "2.14.5" + "requires_python": ">=3.8", + "version": "2.2.1" }, { "artifacts": [ @@ -1683,13 +1926,13 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", - "url": "https://files.pythonhosted.org/packages/f3/8c/f16efd81ca8e293b2cc78f111190a79ee539d0d5d36ccd49975cb3beac60/pytest-7.4.3-py3-none-any.whl" + "hash": "b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", + "url": "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5", - "url": "https://files.pythonhosted.org/packages/38/d4/174f020da50c5afe9f5963ad0fc5b56a4287e3586e3de5b3c8bce9c547b4/pytest-7.4.3.tar.gz" + "hash": "2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", + "url": "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz" } ], "project_name": "pytest", @@ -1712,7 +1955,27 @@ "xmlschema; extra == \"testing\"" ], "requires_python": ">=3.7", - "version": "7.4.3" + "version": "7.4.4" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", + "url": "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "url": "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz" + } + ], + "project_name": "python-dotenv", + "requires_dists": [ + "click>=5.0; extra == \"cli\"" + ], + "requires_python": ">=3.8", + "version": "1.0.1" }, { "artifacts": [ @@ -1831,6 +2094,11 @@ "hash": "28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", "url": "https://files.pythonhosted.org/packages/c1/39/47ed4d65beec9ce07267b014be85ed9c204fa373515355d3efa62d19d892/PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl" }, + { + "algorithm": "sha256", + "hash": "a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", + "url": "https://files.pythonhosted.org/packages/c7/4c/4a2908632fc980da6d918b9de9c1d9d7d7e70b2672b1ad5166ed27841ef7/PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, { "algorithm": "sha256", "hash": "7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", @@ -1905,6 +2173,24 @@ "requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7", "version": "1.16.0" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "url": "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", + "url": "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz" + } + ], + "project_name": "sniffio", + "requires_dists": [], + "requires_python": ">=3.7", + "version": "1.3.1" + }, { "artifacts": [ { @@ -1923,6 +2209,68 @@ "requires_python": null, "version": "2.4.0" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "13d429aa93a61dc40bf503e8c801db1f1bca3dc706b10ef2434a36123568f044", + "url": "https://files.pythonhosted.org/packages/eb/f7/372e3953b6e6fbfe0b70a1bb52612eae16e943f4288516480860fcd4ac41/starlette-0.36.3-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "90a671733cfb35771d8cc605e0b679d23b992f8dcfad48cc60b38cb29aeb7080", + "url": "https://files.pythonhosted.org/packages/be/47/1bba49d42d63f4453f0a64a20acbf2d0bd2f5a8cde6a166ee66c074a08f8/starlette-0.36.3.tar.gz" + } + ], + "project_name": "starlette", + "requires_dists": [ + "anyio<5,>=3.4.0", + "httpx>=0.22.0; extra == \"full\"", + "itsdangerous; extra == \"full\"", + "jinja2; extra == \"full\"", + "python-multipart>=0.0.7; extra == \"full\"", + "pyyaml; extra == \"full\"", + "typing-extensions>=3.10.0; python_version < \"3.10\"" + ], + "requires_python": ">=3.8", + "version": "0.36.3" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "7f48cef4bf0ccd78f1a4534d4b701a003a3bace851f24eae58a32f9e3f0aeba0", + "url": "https://files.pythonhosted.org/packages/b3/37/38c595414d764cb1d9f3a0c907878c4146a21505ab974c63bcf3d8145807/testcontainers-3.7.1-py2.py3-none-any.whl" + } + ], + "project_name": "testcontainers", + "requires_dists": [ + "azure-storage-blob; extra == \"azurite\"", + "clickhouse-driver; extra == \"clickhouse\"", + "cx-Oracle; extra == \"oracle\"", + "deprecation", + "docker-compose; extra == \"docker-compose\"", + "docker>=4.0.0", + "google-cloud-pubsub<2; extra == \"google-cloud-pubsub\"", + "kafka-python; extra == \"kafka\"", + "neo4j; extra == \"neo4j\"", + "pika; extra == \"rabbitmq\"", + "psycopg2-binary; extra == \"postgresql\"", + "pymongo; extra == \"mongo\"", + "pymssql; extra == \"mssqlserver\"", + "pymysql; extra == \"mysql\"", + "python-arango; extra == \"arangodb\"", + "python-keycloak; extra == \"keycloak\"", + "redis; extra == \"redis\"", + "selenium; extra == \"selenium\"", + "sqlalchemy; extra == \"mysql\"", + "sqlalchemy; extra == \"oracle\"", + "sqlalchemy; extra == \"postgresql\"", + "wrapt" + ], + "requires_python": ">=3.7", + "version": "3.7.1" + }, { "artifacts": [ { @@ -1945,80 +2293,328 @@ "artifacts": [ { "algorithm": "sha256", - "hash": "c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24", - "url": "https://files.pythonhosted.org/packages/9d/df/aabb870a04254ceb8a406b0a4222c1b14f7fdf3d2d7633ba49364aca27f3/types_PyYAML-6.0.12.12-py3-none-any.whl" + "hash": "b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6", + "url": "https://files.pythonhosted.org/packages/b3/9a/2b75087549910ebd2be9894bfd89450668b2455094a8f2ba2b67072f15a5/types_PyYAML-6.0.12.20240311-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062", - "url": "https://files.pythonhosted.org/packages/af/48/b3bbe63a129a80911b60f57929c5b243af909bc1c9590917434bca61a4a3/types-PyYAML-6.0.12.12.tar.gz" + "hash": "a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342", + "url": "https://files.pythonhosted.org/packages/0a/3c/6f4c97d9eb2b58f57fc595c105ae0a53a851747cddfb7df30f3d7192c837/types-PyYAML-6.0.12.20240311.tar.gz" } ], "project_name": "types-pyyaml", "requires_dists": [], - "requires_python": null, - "version": "6.0.12.12" + "requires_python": ">=3.8", + "version": "6.0.12.20240311" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc", - "url": "https://files.pythonhosted.org/packages/1d/d6/1281c1d7b03a127562d6644ebff081e85045f0025b1fe26dcdd82811ad1a/types_requests-2.31.0.10-py3-none-any.whl" + "hash": "47872893d65a38e282ee9f277a4ee50d1b28bd592040df7d1fdaffdf3779937d", + "url": "https://files.pythonhosted.org/packages/05/22/21c7918c9bb842faa92fd26108e9f669c3dee9b6b239e8f45dd5f673e6cf/types_requests-2.31.0.20240311-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92", - "url": "https://files.pythonhosted.org/packages/0d/a0/578870e05da99902bf6b75bc37f845cc359bb0278eb0d926c6f3f10bb869/types-requests-2.31.0.10.tar.gz" + "hash": "b1c1b66abfb7fa79aae09097a811c4aa97130eb8831c60e47aee4ca344731ca5", + "url": "https://files.pythonhosted.org/packages/d1/bb/05c62e972a5a89318ee014aed52af921800e3bdd9a0eabfb3851d9bf0beb/types-requests-2.31.0.20240311.tar.gz" } ], "project_name": "types-requests", "requires_dists": [ "urllib3>=2" ], - "requires_python": ">=3.7", - "version": "2.31.0.10" + "requires_python": ">=3.8", + "version": "2.31.0.20240311" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", - "url": "https://files.pythonhosted.org/packages/24/21/7d397a4b7934ff4028987914ac1044d3b7d52712f30e2ac7a2ae5bc86dd0/typing_extensions-4.8.0-py3-none-any.whl" + "hash": "69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", + "url": "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef", - "url": "https://files.pythonhosted.org/packages/1f/7a/8b94bb016069caa12fc9f587b28080ac33b4fbb8ca369b98bc0a4828543e/typing_extensions-4.8.0.tar.gz" + "hash": "b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", + "url": "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz" } ], "project_name": "typing-extensions", "requires_dists": [], "requires_python": ">=3.8", - "version": "4.8.0" + "version": "4.10.0" }, { "artifacts": [ { "algorithm": "sha256", - "hash": "55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", - "url": "https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl" + "hash": "450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "url": "https://files.pythonhosted.org/packages/a2/73/a68704750a7679d0b6d3ad7aa8d4da8e14e151ae82e6fee774e6e0d05ec8/urllib3-2.2.1-py3-none-any.whl" }, { "algorithm": "sha256", - "hash": "df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54", - "url": "https://files.pythonhosted.org/packages/36/dd/a6b232f449e1bc71802a5b7950dc3675d32c6dbc2a1bd6d71f065551adb6/urllib3-2.1.0.tar.gz" + "hash": "d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19", + "url": "https://files.pythonhosted.org/packages/7a/50/7fd50a27caa0652cd4caf224aa87741ea41d3265ad13f010886167cfcc79/urllib3-2.2.1.tar.gz" } ], "project_name": "urllib3", "requires_dists": [ "brotli>=1.0.9; platform_python_implementation == \"CPython\" and extra == \"brotli\"", "brotlicffi>=0.8.0; platform_python_implementation != \"CPython\" and extra == \"brotli\"", + "h2<5,>=4; extra == \"h2\"", "pysocks!=1.5.7,<2.0,>=1.5.6; extra == \"socks\"", "zstandard>=0.18.0; extra == \"zstd\"" ], "requires_python": ">=3.8", - "version": "2.1.0" + "version": "2.2.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de", + "url": "https://files.pythonhosted.org/packages/73/f5/cbb16fcbe277c1e0b8b3ddd188f2df0e0947f545c49119b589643632d156/uvicorn-0.29.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0", + "url": "https://files.pythonhosted.org/packages/49/8d/5005d39cd79c9ae87baf7d7aafdcdfe0b13aa69d9a1e3b7f1c984a2ac6d2/uvicorn-0.29.0.tar.gz" + } + ], + "project_name": "uvicorn", + "requires_dists": [ + "click>=7.0", + "colorama>=0.4; sys_platform == \"win32\" and extra == \"standard\"", + "h11>=0.8", + "httptools>=0.5.0; extra == \"standard\"", + "python-dotenv>=0.13; extra == \"standard\"", + "pyyaml>=5.1; extra == \"standard\"", + "typing-extensions>=4.0; python_version < \"3.11\"", + "uvloop!=0.15.0,!=0.15.1,>=0.14.0; (sys_platform != \"win32\" and (sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\")) and extra == \"standard\"", + "watchfiles>=0.13; extra == \"standard\"", + "websockets>=10.4; extra == \"standard\"" + ], + "requires_python": ">=3.8", + "version": "0.29.0" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", + "url": "https://files.pythonhosted.org/packages/ff/21/abdedb4cdf6ff41ebf01a74087740a709e2edb146490e4d9beea054b0b7a/wrapt-1.16.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", + "url": "https://files.pythonhosted.org/packages/07/44/359e4724a92369b88dbf09878a7cde7393cf3da885567ea898e5904049a3/wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", + "url": "https://files.pythonhosted.org/packages/0f/16/ea627d7817394db04518f62934a5de59874b587b792300991b3c347ff5e0/wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", + "url": "https://files.pythonhosted.org/packages/0f/ef/0ecb1fa23145560431b970418dce575cfaec555ab08617d82eb92afc7ccf/wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", + "url": "https://files.pythonhosted.org/packages/11/fb/18ec40265ab81c0e82a934de04596b6ce972c27ba2592c8b53d5585e6bcd/wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", + "url": "https://files.pythonhosted.org/packages/14/26/93a9fa02c6f257df54d7570dfe8011995138118d11939a4ecd82cb849613/wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", + "url": "https://files.pythonhosted.org/packages/15/4e/081f59237b620a124b035f1229f55db40841a9339fdb8ef60b4decc44df9/wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", + "url": "https://files.pythonhosted.org/packages/19/d4/cd33d3a82df73a064c9b6401d14f346e1d2fb372885f0295516ec08ed2ee/wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", + "url": "https://files.pythonhosted.org/packages/25/62/cd284b2b747f175b5a96cbd8092b32e7369edab0644c45784871528eb852/wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", + "url": "https://files.pythonhosted.org/packages/28/d3/4f079f649c515727c127c987b2ec2e0816b80d95784f2d28d1a57d2a1029/wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", + "url": "https://files.pythonhosted.org/packages/32/12/e11adfde33444986135d8881b401e4de6cbb4cced046edc6b464e6ad7547/wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", + "url": "https://files.pythonhosted.org/packages/34/49/589db6fa2d5d428b71716815bca8b39196fdaeea7c247a719ed2f93b0ab4/wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", + "url": "https://files.pythonhosted.org/packages/49/4e/5d2f6d7b57fc9956bf06e944eb00463551f7d52fc73ca35cfc4c2cdb7aed/wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", + "url": "https://files.pythonhosted.org/packages/49/83/b40bc1ad04a868b5b5bcec86349f06c1ee1ea7afe51dc3e46131e4f39308/wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", + "url": "https://files.pythonhosted.org/packages/4a/cc/3402bcc897978be00fef608cd9e3e39ec8869c973feeb5e1e277670e5ad2/wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", + "url": "https://files.pythonhosted.org/packages/58/43/d72e625edb5926483c9868214d25b5e7d5858ace6a80c9dfddfbadf4d8f9/wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", + "url": "https://files.pythonhosted.org/packages/62/62/30ca2405de6a20448ee557ab2cd61ab9c5900be7cbd18a2639db595f0b98/wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", + "url": "https://files.pythonhosted.org/packages/69/21/b2ba809bafc9b6265e359f9c259c6d9a52a16cf6be20c72d95e76da609dd/wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", + "url": "https://files.pythonhosted.org/packages/6a/d7/cfcd73e8f4858079ac59d9db1ec5a1349bc486ae8e9ba55698cc1f4a1dff/wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", + "url": "https://files.pythonhosted.org/packages/6e/52/2da48b35193e39ac53cfb141467d9f259851522d0e8c87153f0ba4205fb1/wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", + "url": "https://files.pythonhosted.org/packages/70/7d/3dcc4a7e96f8d3e398450ec7703db384413f79bd6c0196e0e139055ce00f/wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", + "url": "https://files.pythonhosted.org/packages/70/cc/b92e1da2cad6a9f8ee481000ece07a35e3b24e041e60ff8b850c079f0ebf/wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", + "url": "https://files.pythonhosted.org/packages/72/b5/0c9be75f826c8e8d583a4ab312552d63d9f7c0768710146a22ac59bda4a9/wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", + "url": "https://files.pythonhosted.org/packages/7e/79/5ff0a5c54bda5aec75b36453d06be4f83d5cd4932cc84b7cb2b52cee23e2/wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", + "url": "https://files.pythonhosted.org/packages/7f/a7/f1212ba098f3de0fd244e2de0f8791ad2539c03bef6c05a9fcb03e45b089/wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", + "url": "https://files.pythonhosted.org/packages/92/17/224132494c1e23521868cdd57cd1e903f3b6a7ba6996b7b8f077ff8ac7fe/wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", + "url": "https://files.pythonhosted.org/packages/95/4c/063a912e20bcef7124e0df97282a8af3ff3e4b603ce84c481d6d7346be0a/wrapt-1.16.0.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", + "url": "https://files.pythonhosted.org/packages/96/e8/27ef35cf61e5147c1c3abcb89cfbb8d691b2bb8364803fcc950140bc14d8/wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", + "url": "https://files.pythonhosted.org/packages/a3/1c/226c2a4932e578a2241dcb383f425995f80224b446f439c2e112eb51c3a6/wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", + "url": "https://files.pythonhosted.org/packages/a6/9b/c2c21b44ff5b9bf14a83252a8b973fb84923764ff63db3e6dfc3895cf2e0/wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4", + "url": "https://files.pythonhosted.org/packages/a8/c6/5375258add3777494671d8cec27cdf5402abd91016dee24aa2972c61fedf/wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", + "url": "https://files.pythonhosted.org/packages/b1/e7/459a8a4f40f2fa65eb73cb3f339e6d152957932516d18d0e996c7ae2d7ae/wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", + "url": "https://files.pythonhosted.org/packages/b6/ad/7a0766341081bfd9f18a7049e4d6d45586ae5c5bb0a640f05e2f558e849c/wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", + "url": "https://files.pythonhosted.org/packages/b7/96/bb5e08b3d6db003c9ab219c487714c13a237ee7dcc572a555eaf1ce7dc82/wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", + "url": "https://files.pythonhosted.org/packages/c4/81/e799bf5d419f422d8712108837c1d9bf6ebe3cb2a81ad94413449543a923/wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", + "url": "https://files.pythonhosted.org/packages/c5/40/3eabe06c8dc54fada7364f34e8caa562efe3bf3f769bf3258de9c785a27f/wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", + "url": "https://files.pythonhosted.org/packages/d1/c4/8dfdc3c2f0b38be85c8d9fdf0011ebad2f54e40897f9549a356bebb63a97/wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", + "url": "https://files.pythonhosted.org/packages/da/6f/6d0b3c4983f1fc764a422989dabc268ee87d937763246cd48aa92f1eed1e/wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", + "url": "https://files.pythonhosted.org/packages/ef/58/2fde309415b5fa98fd8f5f4a11886cbf276824c4c64d45a39da342fff6fe/wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", + "url": "https://files.pythonhosted.org/packages/ef/c6/56e718e2c58a4078518c14d97e531ef1e9e8a5c1ddafdc0d264a92be1a1a/wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", + "url": "https://files.pythonhosted.org/packages/fd/03/c188ac517f402775b90d6f312955a5e53b866c964b32119f2ed76315697e/wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", + "url": "https://files.pythonhosted.org/packages/fe/9e/d3bc95e75670ba15c5b25ecf07fc49941843e2678d777ca59339348d1c96/wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl" + } + ], + "project_name": "wrapt", + "requires_dists": [], + "requires_python": ">=3.6", + "version": "1.16.0" } ], "platform_tag": null @@ -2031,14 +2627,19 @@ "requirements": [ "azure-identity<2.0.0,>=1.4.0", "click~=8.0", + "fastapi~=0.110.0", "hypothesis<7,>=6", "mypy>=1.5.0", + "psycopg~=3.1", + "pydantic-settings>=2.0", "pydantic>=2.0", "pytest<8,>7", "pyyaml~=6.0", "requests<3,>=2", + "testcontainers<5,>=3", "types-PyYAML", - "types-requests" + "types-requests", + "uvicorn~=0.29" ], "requires_python": [ ">=3.8" diff --git a/llamazure/azgraph/integration_test.py b/llamazure/azgraph/integration_test.py index b3275f9..0ad767c 100644 --- a/llamazure/azgraph/integration_test.py +++ b/llamazure/azgraph/integration_test.py @@ -1,37 +1,19 @@ """Integration test against a real, live Azure""" # pylint: disable=redefined-outer-name -import os -from typing import Any - import pytest -import yaml -from azure.identity import ClientSecretCredential from llamazure.azgraph.azgraph import Graph from llamazure.azgraph.models import Req, Res, ResErr - - -def print_output(name: str, output: Any): - should_print = os.environ.get("INTEGRATION_PRINT_OUTPUT", "False") == "True" - if should_print: - print(name, output) +from llamazure.test.credentials import load_credentials +from llamazure.test.inspect import print_output @pytest.fixture() @pytest.mark.integration def graph(): """Run integration test""" - - secrets = os.environ.get("integration_test_secrets") - if not secrets: - with open("cicd/secrets.yml", mode="r", encoding="utf-8") as f: - secrets = f.read() - secrets = yaml.safe_load(secrets) - client = secrets["azgraph"] - - credential = ClientSecretCredential(tenant_id=client["tenant"], client_id=client["appId"], client_secret=client["password"]) - + credential = load_credentials() g = Graph.from_credential(credential) return g diff --git a/llamazure/history/BUILD b/llamazure/history/BUILD new file mode 100644 index 0000000..be4ebd1 --- /dev/null +++ b/llamazure/history/BUILD @@ -0,0 +1,34 @@ +python_sources( + name="history", +) + +python_tests( + name="tests", +) + +python_distribution( + name="llamazure.history", + dependencies=[":history"], + long_description_path="llamazure/history/readme.md", + provides=python_artifact( + name="llamazure.history", + version="0.0.1", + description="Build a history of an Azure tenancy", + author="Daniel Goldman", + classifiers=[ + "Development Status :: 3 - Alpha", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Utilities", + "Topic :: Internet :: Log Analysis", + ], + license="Round Robin 2.0.0", + long_description_content_type="text/markdown", + ), +) + +python_test_utils( + name="test_utils", +) diff --git a/llamazure/history/__init__.py b/llamazure/history/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/llamazure/history/app.py b/llamazure/history/app.py new file mode 100644 index 0000000..bd1bd32 --- /dev/null +++ b/llamazure/history/app.py @@ -0,0 +1,112 @@ +"""The llamazure.history application, a webserver to collect and present the history of Azure tenancies""" +from __future__ import annotations + +import datetime +from typing import Generator, List, Optional, TypeVar +from uuid import UUID + +from azure.identity import DefaultAzureCredential +from fastapi import Depends, FastAPI +from pydantic import BaseModel +from pydantic_settings import BaseSettings, SettingsConfigDict + +from llamazure.azgraph import azgraph +from llamazure.history.collect import Collector, CredentialCache +from llamazure.history.data import DB, TSDB, Res + +T = TypeVar("T") +Provider = Generator[T, None, None] + + +class CredentialCacheDefault(CredentialCache): + """Load Azure credentials with default loader""" + + @staticmethod + def credential(): + """Get the Default Azure credential""" + return DefaultAzureCredential() + + def azgraph(self, tenant_id: UUID) -> azgraph.Graph: + return azgraph.Graph.from_credential(self.credential()) + + +class Settings(BaseSettings): + """Settings for llamazure.history""" + + model_config = SettingsConfigDict(env_nested_delimiter="__") + + class DB(BaseModel): + """Settings for DB""" + + connstr: str + + db: Settings.DB + + +settings = Settings() # type: ignore + + +def get_collector() -> Provider[Collector]: + """FastAPI Dependency for Collector""" + yield Collector( + CredentialCacheDefault(), + DB(TSDB(settings.db.connstr)), + ) + + +def get_db() -> Provider[DB]: + """FastAPI Dependency for DB""" + yield DB(TSDB(settings.db.connstr)) + + +app = FastAPI() + + +@app.post("/collect/snapshots") +async def collect_snapshot(tenant_id: UUID, collector: Collector = Depends(get_collector)): + """Dispatch the collection of a snapshot""" + collector.take_snapshot(tenant_id) + + +@app.post("/collect/delta") +async def collect_delta(tenant_id: UUID, delta: dict, collector: Collector = Depends(get_collector)): + """Insert a single delta""" + collector.insert_deltas(tenant_id, [delta]) + + +@app.post("/collect/deltas") +async def collect_deltas(tenant_id: UUID, deltas: List[dict], collector: Collector = Depends(get_collector)): + """Insert multiple deltas""" + collector.insert_deltas(tenant_id, deltas) + + +@app.get("/history") +async def read_history(at: Optional[datetime.datetime] = None, db: DB = Depends(get_db)) -> Res: + """Read history at a point in time""" + if at is None: + return db.read_latest() + else: + return db.read_at(at) + + +@app.get("/resources/{resource_id}") +async def read_resource( + resource_id: str, + ti: Optional[datetime.datetime] = None, + tf: Optional[datetime.datetime] = None, + db: DB = Depends(get_db), +) -> Res: + return db.read_resource(resource_id, ti, tf) + + +@app.get("/ping") +async def ping() -> str: + """PING this service for up check""" + now = datetime.datetime.now(datetime.timezone.utc).isoformat() + return f"PONG {now}" + + +@app.post("/admin/init_db") +async def init_db(db: DB = Depends(get_db)): + """Initialize the tables in the database""" + db.create_tables() diff --git a/llamazure/history/collect.py b/llamazure/history/collect.py new file mode 100644 index 0000000..935da15 --- /dev/null +++ b/llamazure/history/collect.py @@ -0,0 +1,63 @@ +import datetime +from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Dict, Generator, List, Tuple, cast +from uuid import UUID + +from llamazure.azgraph import azgraph +from llamazure.history.data import DB +from llamazure.rid import mp +from llamazure.tresource.mp import MPData, TresourceMPData + + +def reformat_resources_for_tresource(resources): + """Reformat mp_resources for TresourceMPData""" + for r in resources: + path, azobj = mp.parse(r["id"]) + mpdata = MPData(azobj, r) + yield path, mpdata + + +def reformat_resources_for_db(tree: TresourceMPData) -> Generator[Tuple[str, Dict], None, None]: + return ((cast(str, path), mpdata.data) for path, mpdata in tree.resources.items() if mpdata.data is not None) + + +class CredentialCache(ABC): + @abstractmethod + def azgraph(self, tenant_id: UUID) -> azgraph.Graph: + """Get the azgraph.Graph instance for this tenant""" + + +@dataclass +class Collector: + """Load data from Azure Resource Manager and put into the DB""" + + credentials: CredentialCache + db: DB + + def take_snapshot(self, tenant_id: UUID): + """Take a snapshot and insert it into the DB""" + resources = self.credentials.azgraph(tenant_id).q("Resources") + if isinstance(resources, azgraph.ResErr): + raise RuntimeError(azgraph.ResErr) + + tree: TresourceMPData[Dict] = TresourceMPData() + tree.add_many(reformat_resources_for_tresource(resources)) + + self.db.insert_snapshot( + time=self.snapshot_time(), + azure_tenant=tenant_id, + resources=reformat_resources_for_db(tree), + ) + + def insert_deltas(self, tenant_id: UUID, deltas: List[Dict]): + tree: TresourceMPData[Dict] = TresourceMPData() + tree.add_many(reformat_resources_for_tresource(deltas)) + + request_snapshot_time = self.snapshot_time() + for rid, data in reformat_resources_for_db(tree): + self.db.insert_delta(time=request_snapshot_time, azure_tenant=tenant_id, rid=rid, data=data) + + @staticmethod + def snapshot_time() -> datetime.datetime: + return datetime.datetime.now(datetime.timezone.utc) diff --git a/llamazure/history/conftest.py b/llamazure/history/conftest.py new file mode 100644 index 0000000..7810a97 --- /dev/null +++ b/llamazure/history/conftest.py @@ -0,0 +1,198 @@ +"""Test fixtures for History""" +import datetime +import random +import string +import uuid +from dataclasses import dataclass, field +from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from uuid import UUID + +import psycopg +import pytest +from psycopg.conninfo import make_conninfo +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_for + +from llamazure.azgraph import azgraph +from llamazure.history.collect import CredentialCache +from llamazure.history.data import DB, TSDB, Res +from llamazure.test.credentials import load_credentials +from llamazure.test.util import Fixture + + +class TimescaledbContainer(DockerContainer): + """TimescaleDB Testcontainer""" + + _PORT = 5432 + _ADMIN_USER = "llamazure" + _ADMIN_PASSWORD = "".join(random.SystemRandom().choices(string.ascii_letters + string.digits + string.punctuation, k=32)) + _DB = "llamazure" + _IMAGE = "timescale/timescaledb:latest-pg16" + + def __init__( + self, + image: str = _IMAGE, + admin_user: str = _ADMIN_USER, + admin_password: str = _ADMIN_PASSWORD, + db: str = _DB, + config_overrides: Optional[Dict[str, Any]] = None, + **kwargs, + ): + super().__init__(image, **kwargs) + self.conf = {} + # port + self.with_exposed_ports(self._PORT) + self.db = db + self._set_conf("POSTGRES_DB", db) + self.user = admin_user + self._set_conf("POSTGRES_USER", admin_user) + self.password = admin_password + self._set_conf("POSTGRES_PASSWORD", admin_password) + + if config_overrides: + self.conf.update(config_overrides) + + self._apply_conf() + + @property + def port(self) -> int: + return int(self.get_exposed_port(self._PORT)) + + def _set_conf(self, k, v): + self.conf[k] = v + + def _apply_conf(self): + for k, v in self.conf.items(): + self.with_env(k, v) + + def connstr(self, db: str) -> str: + """Get the connstr for connecting""" + return make_conninfo( + "", + **{ + "host": "localhost", + "user": self.user, + "port": self.port, + "dbname": db, + "password": self.password, + }, + ) + + def try_connecting(self) -> bool: + """Attempt to connect to this container""" + db = TSDB(connstr=self.connstr(self.db)) + if not db.ping(): + raise ConnectionError() + return True + + def start(self): + """Start the container""" + ret = super().start() + wait_for(self.try_connecting) + return ret + + def new_db(self, db_name: Optional[str] = None) -> DB: + """Create a new DB and return the connection info""" + if db_name is None: + db_name = "".join(random.choice(string.ascii_lowercase) for i in range(10)) + + with psycopg.connect(self.connstr(self.db), autocommit=True) as conn: + cur = conn.cursor() + cur.execute(f"""CREATE DATABASE {db_name};""") + cur.execute(f"""GRANT ALL PRIVILEGES ON DATABASE {db_name} TO {self.user}""") + conn.commit() + + db = DB(TSDB(connstr=(self.connstr(db_name)))) + db.create_tables() + return db + + +@pytest.fixture(scope="module") +def timescaledb_container() -> Fixture[TimescaledbContainer]: + """A running TimescaledbContainer fixture""" + with TimescaledbContainer() as tsdb: + yield tsdb + + +@pytest.fixture(scope="function") +def newdb(timescaledb_container: TimescaledbContainer) -> DB: + """Fixture for DB""" + return timescaledb_container.new_db() + + +@pytest.fixture +def now() -> datetime.datetime: + """Fixture for current time""" + return datetime.datetime.now(tz=datetime.timezone.utc) + + +@dataclass +class CredentialCacheIntegrationTest(CredentialCache): + """Load credentials from the integration test secrets""" + + def azgraph(self, tenant_id: UUID) -> azgraph.Graph: + return azgraph.Graph.from_credential(load_credentials()) + + +@dataclass +class FakeDataFactory: + """ + All the fake data you need. + + Generator functions accept a kw-only parameter `idx`. + This key is used to store the value generated. + Re-invoke this function to retrieve the generated value + """ + + tenants: dict = field(default_factory=dict) + resources: dict = field(default_factory=dict) + snapshots: dict = field(default_factory=dict) + + def _get_or_gen(self, db: dict, gen: Callable, idx: Union[str, int]): + if idx in db: + return db[idx] + else: + v = gen() + db[idx] = v + return v + + def tenant(self, *, idx: Union[str, int]) -> UUID: + """A fake tenant""" + return self._get_or_gen(self.tenants, uuid.uuid4, idx) + + def _resource(self, name: str, rev=0) -> dict: + return {"id": f"/subscriptions/s0/fakeResource/{name}", "k0": rev} + + def resource(self, name: str = "res", rev=0, *, idx: Union[str, int]) -> dict: + """A single fake resource""" + return self._get_or_gen(self.resources, lambda: self._resource(name, rev), idx) + + def snapshot(self, count=4, rev=0, *, idx: Union[str, int]) -> List[Tuple[str, dict]]: + """A fake snapshot""" + + def _mk_snapshot(): + resources = [self._resource(str(i), rev) for i in range(0, count)] + return [(e["id"], e) for e in resources] + + return self._get_or_gen(self.snapshots, _mk_snapshot, idx) + + def res2snapshot(self, res: Res) -> List[Tuple[str, dict]]: + """Convert a Res into the original snapshot""" + return [(e[res.cols["rid"]], e[res.cols["data"]]) for e in res.rows] + + def compare_snapshot(self, r: Res, *idxs) -> bool: + """Assert that a result of reading a snapshot is the same as the snapshot inserted""" + merged = sum((self.snapshot(idx=idx) for idx in idxs), start=[]) + assert self.res2snapshot(r) == merged + return True + + def assert_snapshot_at(self, r: Res, at: datetime.datetime) -> bool: + """Assert that a snapshot happened at the given time""" + assert all(e[r.cols["time"]] == at for e in r.rows) + return True + + +@pytest.fixture(scope="function") +def fdf() -> FakeDataFactory: + """Fixture for FakeDataFactory""" + return FakeDataFactory() diff --git a/llamazure/history/data.py b/llamazure/history/data.py new file mode 100644 index 0000000..e1c6a0d --- /dev/null +++ b/llamazure/history/data.py @@ -0,0 +1,213 @@ +"""Interface with the TimescaleDB""" +from __future__ import annotations + +import datetime +from dataclasses import dataclass +from textwrap import dedent +from typing import Any, Dict, Iterable, List, Optional, Tuple +from uuid import UUID + +import psycopg +from psycopg import Cursor, OperationalError +from psycopg.types.json import Jsonb + + +@dataclass(frozen=True) +class Res: + """Result of a Postgres query""" + + cols: Dict[str, int] + rows: List[Tuple] + + @staticmethod + def decode(cursor: Cursor, result) -> Res: + """Convert a cursor into a Res""" + assert cursor.description is not None + return Res( + cols={desc[0]: i for i, desc in enumerate(cursor.description)}, + rows=result, + ) + + +class TSDB: + """TimescaleDB connection""" + + def __init__(self, connstr: str): + self.connstr = connstr + + def exec(self, q, data: Optional[Tuple] = None): + """Execute a query""" + with psycopg.connect(self.connstr) as conn: + cur = conn.cursor() + cur.execute(q, data) + conn.commit() + return cur + + def exec_returning(self, q, data: Optional[Tuple] = None) -> Any: + """Execute a query""" + with psycopg.connect(self.connstr) as conn: + cur = conn.cursor() + cur.execute(q, data) + res = cur.fetchone() + if res is not None: + res = res[0] + conn.commit() + return res + + def create_hypertable(self, name: str, time_col: str): + """Convert a table into a hypertable""" + self.exec(f"""SELECT create_hypertable('{name}', by_range('{time_col}'), if_not_exists => TRUE)""") + + def ping(self) -> bool: + """Check connectivity to postgresql""" + try: + r = self.exec_returning("select version();") + assert r + return True + except OperationalError: + return False + + +class DB: + """Store, load, and create tables""" + + def __init__(self, db: TSDB): + self.db = db + + def create_tables(self): + """Create the tables in TimescaleDB""" + self.db.exec( + dedent( + """\ + CREATE TABLE IF NOT EXISTS snapshot ( + id SERIAL PRIMARY KEY, + time TIMESTAMPTZ NOT NULL, + azure_tenant UUID + ) + """ + ) + ) + + self.db.exec( + dedent( + """\ + CREATE TABLE IF NOT EXISTS res ( + time TIMESTAMPTZ NOT NULL, + snapshot INTEGER, + azure_tenant UUID, + rid VARCHAR, + data JSONB, + FOREIGN KEY (snapshot) REFERENCES snapshot (id) + ) + """ + ) + ) + self.db.create_hypertable("res", "time") + + def insert_resource(self, time: datetime.datetime, azure_tenant: UUID, snapshot_id, rid: str, data: dict): + """Insert a resource into the DB""" + self.db.exec( + """INSERT INTO res (time, snapshot, azure_tenant, rid, data) VALUES (%s, %s, %s, %s, %s)""", + (time, snapshot_id, azure_tenant, rid, Jsonb(data)), + ) + + def insert_snapshot(self, time: datetime.datetime, azure_tenant: UUID, resources: Iterable[Tuple[str, dict]]): + """Insert a complete snapshot into the DB""" + snapshot_id = self.db.exec_returning("""INSERT INTO snapshot (time, azure_tenant) VALUES (%s, %s) RETURNING id""", (time, azure_tenant)) + for rid, data in resources: + self.insert_resource(time, azure_tenant, snapshot_id, rid, data) + + def insert_delta(self, time: datetime.datetime, azure_tenant: UUID, rid: str, data: dict): + """Insert a single delta into the DB""" + return self.insert_resource(time, azure_tenant, None, rid, data) + + def list_snapshots(self) -> Res: + """List snapshots""" + cur = self.db.exec("""SELECT * FROM snapshot;""") + return Res.decode(cur, cur.fetchall()) + + def read_snapshot(self, time: datetime.datetime) -> Res: + """ + Read a complete snapshot. Does not include any deltas + + ```prql + let latest_snapshots = ( + from snapshot + filter s"time" <= $1 + group azure_tenant ( + sort { - s"time" } + take 1 + ) + select id + ) + + from res + join side:inner latest_snapshots (res.snapshot == latest_snapshots.id) + ``` + """ + + cur = self.db.exec( + dedent( + """\ + WITH table_0 AS ( + SELECT + id, + ROW_NUMBER() OVER ( + PARTITION BY azure_tenant + ORDER BY + time DESC + ) AS _expr_0 + FROM + snapshot + WHERE + time <= %s + ), + latest_snapshots AS ( + SELECT + id + FROM + table_0 + WHERE + _expr_0 <= 1 + ) + SELECT + res.*, + latest_snapshots.id + FROM + res + JOIN latest_snapshots ON res.snapshot = latest_snapshots.id + + -- Generated by PRQL compiler version:0.11.3 (https://prql-lang.org) + """ + ), + (time,), + ) + return Res.decode(cur, cur.fetchall()) + + def read_latest(self) -> Res: + """Read the latest information for all resources. Includes deltas.""" + cur = self.db.exec("""SELECT DISTINCT ON (rid) * FROM res ORDER BY rid, time DESC;""") + return Res.decode(cur, cur.fetchall()) + + def read_at(self, time: datetime.datetime) -> Res: + """Read the information for all resources at a point in time. Includes deltas.""" + cur = self.db.exec("""SELECT DISTINCT ON (rid) * FROM res WHERE time <= %s ORDER BY rid, time DESC;""", (time,)) + return Res.decode(cur, cur.fetchall()) + + def read_resource(self, rid: str, ti: Optional[datetime.datetime] = None, tf: Optional[datetime.datetime] = None) -> Res: + """Read all the history of a resource, between specified time""" + data: tuple + if ti is None and tf is None: + q = """SELECT * FROM res WHERE rid = %s ORDER BY time ASC;""" + data = (rid,) + elif ti is not None and tf is None: + q = """SELECT * FROM res WHERE rid = %s AND time >= %s ORDER BY time ASC;""" + data = (rid, ti) + elif ti is None and tf is not None: + q = """SELECT * FROM res WHERE rid = %s AND time < %s ORDER BY time ASC;""" + data = (rid, tf) + else: + q = """SELECT * FROM res WHERE rid = %s AND time >= %s and time < %s ORDER BY time ASC;""" + data = (rid, ti, tf) + cur = self.db.exec(q, data) + return Res.decode(cur, cur.fetchall()) diff --git a/llamazure/history/data_test.py b/llamazure/history/data_test.py new file mode 100644 index 0000000..62ebac3 --- /dev/null +++ b/llamazure/history/data_test.py @@ -0,0 +1,78 @@ +"""Tests for DB functions""" +import datetime + +from llamazure.history.conftest import FakeDataFactory +from llamazure.history.data import DB + + +class TestSnapshotsSingleTenant: + """Test for reading snapshots where all snapshots are the same tenant""" + + def test_single(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + """Test reading a single snapshot""" + newdb.insert_snapshot(now, fdf.tenant(idx=0), fdf.snapshot(idx=0)) + r = newdb.read_snapshot(now) + assert fdf.compare_snapshot(r, 0) + + def test_in_past(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + """Test that we read the snapshot even if we're after the latest one""" + newdb.insert_snapshot(now, fdf.tenant(idx=0), fdf.snapshot(idx=0)) + r = newdb.read_snapshot(now + datetime.timedelta(days=1)) + assert fdf.compare_snapshot(r, 0) + + def test_multiple(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + """Test with multiple snapshots""" + newdb.insert_snapshot(now, fdf.tenant(idx=0), fdf.snapshot(idx=0)) + time_2 = now + datetime.timedelta(days=2) + newdb.insert_snapshot(time_2, fdf.tenant(idx=0), fdf.snapshot(idx=1)) + + r_between = newdb.read_snapshot(now + datetime.timedelta(days=1)) + assert fdf.compare_snapshot(r_between, 0) + assert fdf.assert_snapshot_at(r_between, now) + + r_after = newdb.read_snapshot(now + datetime.timedelta(days=3)) + assert fdf.compare_snapshot(r_after, 1) + assert fdf.assert_snapshot_at(r_after, time_2) + + +class TestSnapshotMultiTenant: + def test_single_per_tenant(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + """Test that the latest is retrieved for each of multiple tenants""" + newdb.insert_snapshot(now, fdf.tenant(idx=0), fdf.snapshot(idx=0)) + newdb.insert_snapshot(now, fdf.tenant(idx=1), fdf.snapshot(idx=1)) + + r = newdb.read_snapshot(now) + assert fdf.compare_snapshot(r, 0, 1) + + +class TestReadResource: + name = "readresource" + + def seed_db(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + datas = [fdf.resource(name=self.name, rev=i, idx=i) for i in range(3)] + for i, d in enumerate(datas): + newdb.insert_delta(now + datetime.timedelta(days=i * 2), fdf.tenant(idx=0), d["id"], d) + + def test_no_bounds(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + self.seed_db(fdf, newdb, now) + + r = newdb.read_resource(fdf.resource(idx=0)["id"]) + assert len(r.rows) == 3 + + def test_ti(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + self.seed_db(fdf, newdb, now) + + r = newdb.read_resource(fdf.resource(idx=0)["id"], ti=now + datetime.timedelta(days=1)) + assert len(r.rows) == 2 + + def test_tf(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + self.seed_db(fdf, newdb, now) + + r = newdb.read_resource(fdf.resource(idx=0)["id"], tf=now + datetime.timedelta(days=3)) + assert len(r.rows) == 2 + + def test_both(self, fdf: FakeDataFactory, newdb: DB, now: datetime.datetime): + self.seed_db(fdf, newdb, now) + + r = newdb.read_resource(fdf.resource(idx=0)["id"], ti=now + datetime.timedelta(days=1), tf=now + datetime.timedelta(days=3)) + assert len(r.rows) == 1 diff --git a/llamazure/history/docker-compose.yml b/llamazure/history/docker-compose.yml new file mode 100644 index 0000000..7744737 --- /dev/null +++ b/llamazure/history/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3' + +services: + timescaledb: + image: timescale/timescaledb:latest-pg16 + container_name: timescaledb + env_file: + - .env + environment: + - POSTGRES_DB=llamazure + ports: + - "5432:5432" + networks: + - timescaledb_network + +networks: + timescaledb_network: + driver: bridge diff --git a/llamazure/history/integration_test.py b/llamazure/history/integration_test.py new file mode 100644 index 0000000..b83159d --- /dev/null +++ b/llamazure/history/integration_test.py @@ -0,0 +1,86 @@ +import datetime +from collections import defaultdict +from typing import Dict, Set +from uuid import UUID + +import pytest + +from llamazure.azgraph.azgraph import Graph +from llamazure.azgraph.models import ResErr +from llamazure.azrest.azrest import AzRest +from llamazure.azrest.models import AzList +from llamazure.azrest.models import Req as AzReq +from llamazure.history.collect import Collector +from llamazure.history.conftest import CredentialCacheIntegrationTest, TimescaledbContainer +from llamazure.history.data import Res +from llamazure.test.credentials import load_credentials + + +def group_by_time(snapshot: Res) -> Dict[datetime.datetime, Set[str]]: + out = defaultdict(set) + for r in snapshot.rows: + out[r[snapshot.cols["time"]]].add(r[snapshot.cols["rid"]]) + return out + + +def test_nothing(): + """Prevent collection problems for partitions""" + + +def _rids_in_res(res: Res) -> Set[str]: + return {e[res.cols["rid"]] for e in res.rows} + + +@pytest.mark.integration +def test_integration(timescaledb_container: TimescaledbContainer) -> None: + """ + End-to-end test that: + - creates tables + - loads data from azure + - converts to a tresource + - inserts into the tsdb + - synthesises a delta + - inserts a delta + """ + db = timescaledb_container.new_db() + + credential = load_credentials() + g = Graph.from_credential(credential) + azr = AzRest.from_credential(credential) + + tenants = azr.call(AzReq.get("GetTenants", "/tenants", "2022-12-01", AzList[dict])) + tenant_id = UUID(tenants[0]["tenantId"]) + + history = Collector(CredentialCacheIntegrationTest(), db) + history.take_snapshot(tenant_id) + original_resources = db.read_latest() + + delta_q = g.q("Resources | take(1)") + if isinstance(delta_q, ResErr): + raise RuntimeError(ResErr) + history.insert_deltas(tenant_id, delta_q) + + latest = db.read_latest() + found_resources = {e[latest.cols["rid"]]: e for e in latest.rows} + + delta_id = delta_q[0]["id"].lower() + + # assert that our delta is correctly given to us + assert delta_id in found_resources, "did not find delta in resources" + found_delta = found_resources[delta_id] + found_by_time = group_by_time(latest) + found_delta_time = found_delta[latest.cols["time"]] + assert found_by_time[found_delta_time] == {delta_id} + + # assert that all the resources were included in the + resources = g.q("Resources") + if isinstance(delta_q, ResErr): + raise RuntimeError(ResErr) + assert isinstance(resources, list) + assert len(resources) == len(found_resources), "snapshot and resources had different count" + assert {e["id"].lower() for e in resources} == _rids_in_res(latest), "snapshot did not contain same resources" + + # assert that read_at in the past returns the resources from that time + original_time = original_resources.rows[0][original_resources.cols["time"]] + past_resources = db.read_at(original_time) + assert _rids_in_res(original_resources) == _rids_in_res(past_resources), "snapshot of past did not contain same contents as in past" \ No newline at end of file diff --git a/llamazure/history/models.py b/llamazure/history/models.py new file mode 100644 index 0000000..e69de29 diff --git a/llamazure/history/readme.md b/llamazure/history/readme.md new file mode 100644 index 0000000..986eeaa --- /dev/null +++ b/llamazure/history/readme.md @@ -0,0 +1,32 @@ +# llamazure.history + +Build a history of an Azure tenancy. + +The `llamazure.history` provides an application that can keep track of the history of Azure tenancies. +`llamazure.history` uses Postgres as a backend to store the resources. You can use the HTTP query interface in the app, or you can extend the app to run in-DB queries. You can also expose the DB directly for analytical queries. + +`llamazure.history` views changes in 2 phases: +- snapshots : coherent and complete views of a tenancy +- deltas : change events to individual resources + +Deltas keep the model up-to-date, and snapshots make up for missed deltas and provide a complete view of a tenancy. Snapshots can also improve performance if exact timing isn't necessary. For example, deltas allow a user to know exactly when a resource changed. But, if the delta message is missed, a snapshot ensures that change is noticed at some point. Since snapshots are complete, a user could request a snapshot to get all resources instantly, rather than the DB having to compute the latest version of every resource. + +## Usage + +### Writing + + + +### Customisation + +The app uses the `DefaultAzureCredential` for its Azure credential. You can implement an alternative `CredentialCache`. + +## Examples + +## References + +### FastAPI + +- [Getting started](https://fastapi.tiangolo.com/) : the basics for using fastapi +- [FastAPI Settings](https://fastapi.tiangolo.com/advanced/settings/) : FastAPI using Pydantic settings +- [Pydantic Settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) : doc \ No newline at end of file diff --git a/llamazure/rbac/conftest.py b/llamazure/rbac/conftest.py index a1d772d..225067e 100644 --- a/llamazure/rbac/conftest.py +++ b/llamazure/rbac/conftest.py @@ -1,4 +1,5 @@ """Conftest""" +# noqa: F811 import logging # pylint: disable=redefined-outer-name @@ -8,29 +9,16 @@ import pytest import yaml -from azure.identity import AzureCliCredential, ClientSecretCredential, CredentialUnavailableError from llamazure.azrest.azrest import AzRest from llamazure.msgraph.msgraph import Graph from llamazure.rbac.resources import Groups, Users from llamazure.rbac.roles import RoleAssignments, RoleDefinitions, RoleOps +from llamazure.test.conftest import credentials as credentials # noqa: F401 l = logging.getLogger(__name__) -@pytest.fixture -def credential(): - """Azure credential""" - try: - cli_credential = AzureCliCredential() - cli_credential.get_token("https://management.azure.com//.default") - return cli_credential - except CredentialUnavailableError: - secrets = yaml.safe_load(os.environ.get("integration_test_secrets")) - client = secrets["azgraph"] - return ClientSecretCredential(tenant_id=client["tenant"], client_id=client["appId"], client_secret=client["password"]) - - @pytest.fixture def scopes(): """Fixture: subscriptions and ids for testing""" @@ -42,33 +30,33 @@ def scopes(): @pytest.fixture -def rds(credential) -> RoleDefinitions: +def rds(credentials) -> RoleDefinitions: """Fixture: RoleDefinitions""" - return RoleDefinitions(AzRest.from_credential(credential)) + return RoleDefinitions(AzRest.from_credential(credentials)) @pytest.fixture -def ras(credential) -> RoleAssignments: +def ras(credentials) -> RoleAssignments: """Fixture: RoleAssignments""" - return RoleAssignments(AzRest.from_credential(credential)) + return RoleAssignments(AzRest.from_credential(credentials)) @pytest.fixture -def role_ops(credential) -> RoleOps: +def role_ops(credentials) -> RoleOps: """Fixture: RoleOps""" - return RoleOps(AzRest.from_credential(credential)) + return RoleOps(AzRest.from_credential(credentials)) @pytest.fixture -def users(credential) -> Users: +def users(credentials) -> Users: """Fixture: Users""" - return Users(Graph.from_credential(credential)) + return Users(Graph.from_credential(credentials)) @pytest.fixture -def groups(credential) -> Groups: +def groups(credentials) -> Groups: """Fixture: Users""" - return Groups(Graph.from_credential(credential)) + return Groups(Graph.from_credential(credentials)) @pytest.fixture diff --git a/llamazure/rbac/integration_test.py b/llamazure/rbac/integration_test.py index 69c0031..f080617 100644 --- a/llamazure/rbac/integration_test.py +++ b/llamazure/rbac/integration_test.py @@ -1,7 +1,5 @@ """Integration tests for roles""" import logging -import os -from typing import Any import pytest @@ -16,13 +14,6 @@ attempts = 5 -def print_output(name: str, output: Any): - """Print output if requested, so we don't always print potentially sensitive information to the logs""" - should_print = os.environ.get("INTEGRATION_PRINT_OUTPUT", "False") == "True" - if should_print: - print(name, output) - - class TestRoles: """Test combined aspects of roles""" diff --git a/llamazure/rid/BUILD b/llamazure/rid/BUILD index 34c8aa0..354938a 100644 --- a/llamazure/rid/BUILD +++ b/llamazure/rid/BUILD @@ -20,6 +20,7 @@ python_distribution( "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Utilities", "Topic :: Internet :: Log Analysis", ], diff --git a/llamazure/test/BUILD b/llamazure/test/BUILD new file mode 100644 index 0000000..d4cea96 --- /dev/null +++ b/llamazure/test/BUILD @@ -0,0 +1 @@ +python_test_utils(sources=["*.py"]) diff --git a/llamazure/test/__init__.py b/llamazure/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/llamazure/test/conftest.py b/llamazure/test/conftest.py new file mode 100644 index 0000000..4b39639 --- /dev/null +++ b/llamazure/test/conftest.py @@ -0,0 +1,9 @@ +import pytest + +from llamazure.test.credentials import load_credentials + + +@pytest.fixture() +@pytest.mark.integration +def credentials(): + return load_credentials() diff --git a/llamazure/test/credentials.py b/llamazure/test/credentials.py new file mode 100644 index 0000000..1260239 --- /dev/null +++ b/llamazure/test/credentials.py @@ -0,0 +1,23 @@ +import os + +import yaml +from azure.identity import AzureCliCredential, ClientSecretCredential, CredentialUnavailableError + + +def load_credentials(): + """Load credentials for a Service Principal""" + try: + cli_credential = AzureCliCredential() + cli_credential.get_token("https://management.azure.com//.default") + return cli_credential + except CredentialUnavailableError: + secrets = os.environ.get("integration_test_secrets") + if not secrets: + with open("cicd/secrets.yml", mode="r", encoding="utf-8") as f: + secrets = f.read() + secrets = yaml.safe_load(secrets) + + client = secrets["auth"] + + credential = ClientSecretCredential(tenant_id=client["tenant"], client_id=client["appId"], client_secret=client["password"]) + return credential diff --git a/llamazure/test/inspect.py b/llamazure/test/inspect.py new file mode 100644 index 0000000..5b3523b --- /dev/null +++ b/llamazure/test/inspect.py @@ -0,0 +1,25 @@ +import datetime +import json +import os +from dataclasses import asdict, is_dataclass +from typing import Any +from uuid import UUID + + +def print_output(name: str, output: Any): + should_print = os.environ.get("INTEGRATION_PRINT_OUTPUT", "False") == "True" + if should_print: + print(name, output) + + +class MyEncoder(json.JSONEncoder): + """Encoder for more types""" + + def default(self, o): + if is_dataclass(o): + return asdict(o) + if isinstance(o, datetime.datetime): + return o.isoformat() + if isinstance(o, UUID): + return str(o) + return super().default(o) diff --git a/llamazure/test/util.py b/llamazure/test/util.py new file mode 100644 index 0000000..04b51f6 --- /dev/null +++ b/llamazure/test/util.py @@ -0,0 +1,4 @@ +from typing import Generator, TypeVar + +T = TypeVar("T") +Fixture = Generator[T, None, None] diff --git a/llamazure/tresource/readme.md b/llamazure/tresource/readme.md index ef1387f..0433eca 100644 --- a/llamazure/tresource/readme.md +++ b/llamazure/tresource/readme.md @@ -11,4 +11,22 @@ There are several variants of Tresources. ## Examples +Load all resources into a tresource, indexable by rid, including data: + +```python +from azure.identity import DefaultAzureCredential + +from llamazure.azgraph.azgraph import Graph +from llamazure.rid import mp +from llamazure.tresource.mp import TresourceMPData + +g = Graph.from_credential(DefaultAzureCredential()) +resources = g.q("Resources") + +t = TresourceMPData() + +for resource in resources: + t.set_data(mp.parse(resource["id"])[1], resource) +``` + ## Design notes diff --git a/pants.toml b/pants.toml index d4e9234..b00a5e3 100644 --- a/pants.toml +++ b/pants.toml @@ -6,6 +6,7 @@ backend_packages = [ "pants.backend.python.lint.black", "pants.backend.python.lint.isort", "pants.backend.python.lint.flake8", + "pants.backend.python.lint.autoflake", "pants.backend.python.lint.pylint", "pants.backend.python.typecheck.mypy", "pants.backend.experimental.adhoc", @@ -36,7 +37,7 @@ interpreter_constraints = ["CPython>=3.8"] [test] use_coverage = true -extra_env_vars = ["integration_test_secrets", "INTEGRATION_PRINT_OUTPUT", "AZURE_CONFIG_DIR"] +extra_env_vars = ["integration_test_secrets", "INTEGRATION_PRINT_OUTPUT", "AZURE_CONFIG_DIR", "DOCKER_HOST"] [coverage-py] report = ["xml", "html"] diff --git a/pyproject.toml b/pyproject.toml index e82f226..f7c3b43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ module = [ "ansible.errors", "azure.cli.*", "knack.*", + "testcontainers.*", ] ignore_missing_imports = true @@ -35,3 +36,7 @@ markers = [ "integration: integration tests", "admin: tests that need some amount of admin", ] + +pytest_plugins = [ + "llamazure.tests", +] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0697d7a..332c69b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,9 @@ azure-identity>=1.4.0,<2.0.0 +click~=8.0 +fastapi~=0.110.0 +psycopg~=3.1 pydantic>=2.0 +pydantic-settings>=2.0 pyyaml~=6.0 requests>=2,<3 -click~=8.0 +uvicorn~=0.29 diff --git a/requirements_test.txt b/requirements_test.txt index 185f4a1..d7ff48b 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,2 +1,3 @@ hypothesis>=6,<7 pytest>7,<8 +testcontainers>=3,<5