diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2c26806e..39f22887d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: - master pull_request: branches: - - '**' + - "**" jobs: tests: @@ -54,7 +54,7 @@ jobs: - name: Install dependencies shell: bash - run: poetry install -E compiler + run: poetry install --with dev,test - name: Generate code from proto files shell: bash diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 519980702..47d6755c5 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -6,13 +6,23 @@ on: - master pull_request: branches: - - '**' + - "**" jobs: check-formatting: name: Check code/doc formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - - uses: pre-commit/action@v2.0.3 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + - uses: pre-commit/action@v2.0.3 + - run: pipx install poetry + - uses: actions/setup-python@v4 + with: + cache: "poetry" + + - run: poetry install --with dev,test + - run: echo "$(poetry env info --path)/bin" >> $GITHUB_PATH + - uses: jakebailey/pyright-action@v1 + with: + extra-args: "--exclude tests" diff --git a/poetry.lock b/poetry.lock index c20859ad2..7a10747b6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -71,17 +71,18 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte [[package]] name = "babel" -version = "2.13.0" +version = "2.13.1" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ - {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, - {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, + {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"}, + {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} +setuptools = {version = "*", markers = "python_version >= \"3.12\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] @@ -219,13 +220,13 @@ files = [ [[package]] name = "cachetools" -version = "5.3.1" +version = "5.3.2" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, - {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, + {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, + {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, ] [[package]] @@ -263,101 +264,101 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.0" +version = "3.3.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"}, - {file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"}, - {file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"}, - {file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"}, - {file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"}, - {file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"}, - {file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"}, - {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, + {file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-win32.whl", hash = "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"}, + {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, ] [[package]] @@ -558,73 +559,68 @@ testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "p [[package]] name = "greenlet" -version = "3.0.0" +version = "3.0.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6"}, - {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a"}, - {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c"}, - {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a68d670c8f89ff65c82b936275369e532772eebc027c3be68c6b87ad05ca695"}, - {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ad562a104cd41e9d4644f46ea37167b93190c6d5e4048fcc4b80d34ecb278f"}, - {file = "greenlet-3.0.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02a807b2a58d5cdebb07050efe3d7deaf915468d112dfcf5e426d0564aa3aa4a"}, - {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1660a15a446206c8545edc292ab5c48b91ff732f91b3d3b30d9a915d5ec4779"}, - {file = "greenlet-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:813720bd57e193391dfe26f4871186cf460848b83df7e23e6bef698a7624b4c9"}, - {file = "greenlet-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:aa15a2ec737cb609ed48902b45c5e4ff6044feb5dcdfcf6fa8482379190330d7"}, - {file = "greenlet-3.0.0-cp310-universal2-macosx_11_0_x86_64.whl", hash = "sha256:7709fd7bb02b31908dc8fd35bfd0a29fc24681d5cc9ac1d64ad07f8d2b7db62f"}, - {file = "greenlet-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7"}, - {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6512592cc49b2c6d9b19fbaa0312124cd4c4c8a90d28473f86f92685cc5fef8e"}, - {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:871b0a8835f9e9d461b7fdaa1b57e3492dd45398e87324c047469ce2fc9f516c"}, - {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b505fcfc26f4148551826a96f7317e02c400665fa0883fe505d4fcaab1dabfdd"}, - {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14"}, - {file = "greenlet-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:96d9ea57292f636ec851a9bb961a5cc0f9976900e16e5d5647f19aa36ba6366b"}, - {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b72b802496cccbd9b31acea72b6f87e7771ccfd7f7927437d592e5c92ed703c"}, - {file = "greenlet-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362"}, - {file = "greenlet-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:37f60b3a42d8b5499be910d1267b24355c495064f271cfe74bf28b17b099133c"}, - {file = "greenlet-3.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383"}, - {file = "greenlet-3.0.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f"}, - {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73b2f1922a39d5d59cc0e597987300df3396b148a9bd10b76a058a2f2772fc04"}, - {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1e22c22f7826096ad503e9bb681b05b8c1f5a8138469b255eb91f26a76634f2"}, - {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99"}, - {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:334ef6ed8337bd0b58bb0ae4f7f2dcc84c9f116e474bb4ec250a8bb9bd797a66"}, - {file = "greenlet-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6672fdde0fd1a60b44fb1751a7779c6db487e42b0cc65e7caa6aa686874e79fb"}, - {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:952256c2bc5b4ee8df8dfc54fc4de330970bf5d79253c863fb5e6761f00dda35"}, - {file = "greenlet-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:269d06fa0f9624455ce08ae0179430eea61085e3cf6457f05982b37fd2cefe17"}, - {file = "greenlet-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9adbd8ecf097e34ada8efde9b6fec4dd2a903b1e98037adf72d12993a1c80b51"}, - {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c"}, - {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecf94aa539e97a8411b5ea52fc6ccd8371be9550c4041011a091eb8b3ca1d810"}, - {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80dcd3c938cbcac986c5c92779db8e8ce51a89a849c135172c88ecbdc8c056b7"}, - {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52a712c38e5fb4fd68e00dc3caf00b60cb65634d50e32281a9d6431b33b4af1"}, - {file = "greenlet-3.0.0-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5539f6da3418c3dc002739cb2bb8d169056aa66e0c83f6bacae0cd3ac26b423"}, - {file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:343675e0da2f3c69d3fb1e894ba0a1acf58f481f3b9372ce1eb465ef93cf6fed"}, - {file = "greenlet-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:abe1ef3d780de56defd0c77c5ba95e152f4e4c4e12d7e11dd8447d338b85a625"}, - {file = "greenlet-3.0.0-cp37-cp37m-win32.whl", hash = "sha256:e693e759e172fa1c2c90d35dea4acbdd1d609b6936115d3739148d5e4cd11947"}, - {file = "greenlet-3.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705"}, - {file = "greenlet-3.0.0-cp37-universal2-macosx_11_0_x86_64.whl", hash = "sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353"}, - {file = "greenlet-3.0.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9"}, - {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c"}, - {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b822fab253ac0f330ee807e7485769e3ac85d5eef827ca224feaaefa462dc0d0"}, - {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8060b32d8586e912a7b7dac2d15b28dbbd63a174ab32f5bc6d107a1c4143f40b"}, - {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365"}, - {file = "greenlet-3.0.0-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb36985f606a7c49916eff74ab99399cdfd09241c375d5a820bb855dfb4af9f"}, - {file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d"}, - {file = "greenlet-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f351479a6914fd81a55c8e68963609f792d9b067fb8a60a042c585a621e0de4f"}, - {file = "greenlet-3.0.0-cp38-cp38-win32.whl", hash = "sha256:9de687479faec7db5b198cc365bc34addd256b0028956501f4d4d5e9ca2e240a"}, - {file = "greenlet-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:3fd2b18432e7298fcbec3d39e1a0aa91ae9ea1c93356ec089421fabc3651572b"}, - {file = "greenlet-3.0.0-cp38-universal2-macosx_11_0_x86_64.whl", hash = "sha256:3c0d36f5adc6e6100aedbc976d7428a9f7194ea79911aa4bf471f44ee13a9464"}, - {file = "greenlet-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4"}, - {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5b2d4cdaf1c71057ff823a19d850ed5c6c2d3686cb71f73ae4d6382aaa7a06"}, - {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e7dcdfad252f2ca83c685b0fa9fba00e4d8f243b73839229d56ee3d9d219314"}, - {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870"}, - {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad6fb737e46b8bd63156b8f59ba6cdef46fe2b7db0c5804388a2d0519b8ddb99"}, - {file = "greenlet-3.0.0-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d55db1db455c59b46f794346efce896e754b8942817f46a1bada2d29446e305a"}, - {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692"}, - {file = "greenlet-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a812224a5fb17a538207e8cf8e86f517df2080c8ee0f8c1ed2bdaccd18f38f4"}, - {file = "greenlet-3.0.0-cp39-cp39-win32.whl", hash = "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9"}, - {file = "greenlet-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:831d6f35037cf18ca5e80a737a27d822d87cd922521d18ed3dbc8a6967be50ce"}, - {file = "greenlet-3.0.0-cp39-universal2-macosx_11_0_x86_64.whl", hash = "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355"}, - {file = "greenlet-3.0.0.tar.gz", hash = "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b"}, + {file = "greenlet-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63"}, + {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e"}, + {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846"}, + {file = "greenlet-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9"}, + {file = "greenlet-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234"}, + {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884"}, + {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94"}, + {file = "greenlet-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c"}, + {file = "greenlet-3.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5"}, + {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d"}, + {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445"}, + {file = "greenlet-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de"}, + {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166"}, + {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36"}, + {file = "greenlet-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1"}, + {file = "greenlet-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8"}, + {file = "greenlet-3.0.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9"}, + {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e"}, + {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a"}, + {file = "greenlet-3.0.1-cp38-cp38-win32.whl", hash = "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd"}, + {file = "greenlet-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6"}, + {file = "greenlet-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d"}, + {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8"}, + {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546"}, + {file = "greenlet-3.0.1-cp39-cp39-win32.whl", hash = "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57"}, + {file = "greenlet-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619"}, + {file = "greenlet-3.0.1.tar.gz", hash = "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b"}, ] [package.extras] @@ -961,16 +957,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -1488,7 +1474,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1496,15 +1481,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1521,7 +1499,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1529,7 +1506,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1860,13 +1836,13 @@ files = [ [[package]] name = "urllib3" -version = "2.0.6" +version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.6-py3-none-any.whl", hash = "sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2"}, - {file = "urllib3-2.0.6.tar.gz", hash = "sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564"}, + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [package.extras] @@ -1877,13 +1853,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.24.5" +version = "20.24.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, - {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, + {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, + {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index dc85cd8b0..0a05b4ca9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,8 @@ isort = {version = "^5.11.5", optional = true} typing-extensions = "^4.7.1" betterproto-rust-codec = { version = "0.1.0", optional = true } +[tool.poetry.group.dev] +optional = true [tool.poetry.group.dev.dependencies] asv = "^0.4.2" bpython = "^0.19" @@ -33,6 +35,8 @@ pre-commit = "^2.17.0" grpcio-tools = "^1.54.2" tox = "^4.0.0" +[tool.poetry.group.test] +optional = true [tool.poetry.group.test.dependencies] poethepoet = ">=0.9.0" pytest = "^6.2.5" @@ -63,27 +67,27 @@ cmd = "pytest" help = "Run tests" [tool.poe.tasks.types] -cmd = "mypy src --ignore-missing-imports" -help = "Check types with mypy" +cmd = "pyright --exclude tests" +help = "Check types with pyright" [tool.poe.tasks] -_black = "black . --exclude tests/output_ --target-version py310" +_black = "black . --extend-exclude tests/output_ --target-version py310" _isort = "isort . --extend-skip-glob 'tests/output_*/**/*'" [tool.poe.tasks.format] -sequence = ["_black", "_isort"] +sequence = ["_black", "_isort"] help = "Apply black and isort formatting to source code" [tool.poe.tasks.docs] -cmd = "sphinx-build docs docs/build" +cmd = "sphinx-build docs docs/build" help = "Build the sphinx docs" [tool.poe.tasks.bench] shell = "asv run master^! && asv run HEAD^! && asv compare master HEAD" -help = "Benchmark current commit vs. master branch" +help = "Benchmark current commit vs. master branch" [tool.poe.tasks.clean] -cmd = """ +cmd = """ rm -rf .asv .coverage .mypy_cache .pytest_cache dist betterproto.egg-info **/__pycache__ testsoutput_* @@ -111,6 +115,10 @@ help = "Run tests with multiple pythons" cmd = "black . --check --diff" help = "Check if code style is correct" +[tool.pyright] +typeCheckingMode = "basic" +reportSelfClsParameterName = false + [tool.isort] py_version = 37 profile = "black" @@ -130,7 +138,7 @@ max_line_length = 88 [tool.doc8.ignore_path_errors] "docs/migrating.rst" = [ - "D001", # contains table which is longer than 88 characters long + "D001", # contains table which is longer than 88 characters long ] [tool.coverage.run] diff --git a/src/betterproto/__init__.py b/src/betterproto/__init__.py index c16e2cdd9..001452fe2 100644 --- a/src/betterproto/__init__.py +++ b/src/betterproto/__init__.py @@ -1,14 +1,13 @@ from __future__ import annotations import dataclasses -import enum as builtin_enum import json import math import struct import sys +import types import typing import warnings -from abc import ABC from base64 import ( b64decode, b64encode, @@ -26,23 +25,18 @@ Any, Callable, ClassVar, - Dict, Generator, Iterable, Mapping, - Optional, - Set, - Tuple, - Type, Union, + cast, get_type_hints, ) from dateutil.parser import isoparse from typing_extensions import Self -from ._types import T -from ._version import __version__ +from ._version import __version__ as __version__ from .casing import ( camel_case, safe_snake_case, @@ -58,6 +52,7 @@ if TYPE_CHECKING: from _typeshed import ( + DataclassInstance, SupportsRead, SupportsWrite, ) @@ -154,11 +149,11 @@ def datetime_default_gen() -> datetime: NAN = "NaN" -class Casing(builtin_enum.Enum): +class Casing: """Casing constants for serialization.""" - CAMEL = camel_case #: A camelCase sterilization function. - SNAKE = snake_case #: A snake_case sterilization function. + CAMEL = staticmethod(camel_case) #: A camelCase sterilization function. + SNAKE = staticmethod(snake_case) #: A snake_case sterilization function. PLACEHOLDER: Any = object() @@ -173,16 +168,16 @@ class FieldMetadata: # Protobuf type name proto_type: str # Map information if the proto_type is a map - map_types: Optional[Tuple[str, str]] = None + map_types: tuple[str, str] | None = None # Groups several "one-of" fields together - group: Optional[str] = None + group: str | None = None # Describes the wrapped type (e.g. when using google.protobuf.BoolValue) - wraps: Optional[str] = None + wraps: str | None = None # Is the field optional - optional: Optional[bool] = False + optional: bool | None = False @staticmethod - def get(field: dataclasses.Field) -> "FieldMetadata": + def get(field: dataclasses.Field) -> FieldMetadata: """Returns the field metadata for a dataclass field.""" return field.metadata["betterproto"] @@ -191,14 +186,14 @@ def dataclass_field( number: int, proto_type: str, *, - map_types: Optional[Tuple[str, str]] = None, - group: Optional[str] = None, - wraps: Optional[str] = None, + map_types: tuple[str, str] | None = None, + group: str | None = None, + wraps: str | None = None, optional: bool = False, -) -> dataclasses.Field: +) -> dataclasses.Field[Any]: """Creates a dataclass field with attached protobuf metadata.""" return dataclasses.field( - default=None if optional else PLACEHOLDER, + default=None if optional else PLACEHOLDER, # type: ignore metadata={ "betterproto": FieldMetadata( number, proto_type, map_types, group, wraps, optional @@ -212,102 +207,78 @@ def dataclass_field( # out at runtime. The generated dataclass variables are still typed correctly. -def enum_field(number: int, group: Optional[str] = None, optional: bool = False) -> Any: +def enum_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_ENUM, group=group, optional=optional) -def bool_field(number: int, group: Optional[str] = None, optional: bool = False) -> Any: +def bool_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_BOOL, group=group, optional=optional) -def int32_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def int32_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_INT32, group=group, optional=optional) -def int64_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def int64_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_INT64, group=group, optional=optional) -def uint32_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def uint32_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_UINT32, group=group, optional=optional) -def uint64_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def uint64_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_UINT64, group=group, optional=optional) -def sint32_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def sint32_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_SINT32, group=group, optional=optional) -def sint64_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def sint64_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_SINT64, group=group, optional=optional) -def float_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def float_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_FLOAT, group=group, optional=optional) -def double_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def double_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_DOUBLE, group=group, optional=optional) -def fixed32_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def fixed32_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_FIXED32, group=group, optional=optional) -def fixed64_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def fixed64_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_FIXED64, group=group, optional=optional) def sfixed32_field( - number: int, group: Optional[str] = None, optional: bool = False + number: int, group: str | None = None, optional: bool = False ) -> Any: return dataclass_field(number, TYPE_SFIXED32, group=group, optional=optional) def sfixed64_field( - number: int, group: Optional[str] = None, optional: bool = False + number: int, group: str | None = None, optional: bool = False ) -> Any: return dataclass_field(number, TYPE_SFIXED64, group=group, optional=optional) -def string_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def string_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_STRING, group=group, optional=optional) -def bytes_field( - number: int, group: Optional[str] = None, optional: bool = False -) -> Any: +def bytes_field(number: int, group: str | None = None, optional: bool = False) -> Any: return dataclass_field(number, TYPE_BYTES, group=group, optional=optional) def message_field( number: int, - group: Optional[str] = None, - wraps: Optional[str] = None, + group: str | None = None, + wraps: str | None = None, optional: bool = False, ) -> Any: return dataclass_field( @@ -316,7 +287,7 @@ def message_field( def map_field( - number: int, key_type: str, value_type: str, group: Optional[str] = None + number: int, key_type: str, value_type: str, group: str | None = None ) -> Any: return dataclass_field( number, TYPE_MAP, map_types=(key_type, value_type), group=group @@ -335,7 +306,7 @@ def _pack_fmt(proto_type: str) -> str: }[proto_type] -def dump_varint(value: int, stream: "SupportsWrite[bytes]") -> None: +def dump_varint(value: int, stream: SupportsWrite[bytes]) -> None: """Encodes a single varint and dumps it into the provided stream.""" if value < -(1 << 63): raise ValueError( @@ -522,7 +493,7 @@ def _parse_float(value: Any) -> float: return float(value) -def _dump_float(value: float) -> Union[float, str]: +def _dump_float(value: float) -> float | str: """Dump the given float to JSON Parameters @@ -544,7 +515,7 @@ def _dump_float(value: float) -> Union[float, str]: return value -def load_varint(stream: "SupportsRead[bytes]") -> Tuple[int, bytes]: +def load_varint(stream: SupportsRead[bytes]) -> tuple[int, bytes]: """ Load a single varint value from a stream. Returns the value and the raw bytes read. """ @@ -562,8 +533,10 @@ def load_varint(stream: "SupportsRead[bytes]") -> Tuple[int, bytes]: if not (b_int & 0x80): return result, raw + raise RuntimeError("unreachable code reached") + -def decode_varint(buffer: bytes, pos: int) -> Tuple[int, int]: +def decode_varint(buffer: bytes, pos: int) -> tuple[int, int]: """ Decode a single varint value from a byte buffer. Returns the value and the new position in the buffer. @@ -582,7 +555,7 @@ class ParsedField: raw: bytes -def load_fields(stream: "SupportsRead[bytes]") -> Generator[ParsedField, None, None]: +def load_fields(stream: SupportsRead[bytes]) -> Generator[ParsedField, None, None]: while True: try: num_wire, raw = load_varint(stream) @@ -646,21 +619,23 @@ class ProtoClassMetadata: "sorted_field_names", ) - oneof_group_by_field: Dict[str, str] - oneof_field_by_group: Dict[str, Set[dataclasses.Field]] - field_name_by_number: Dict[int, str] - meta_by_field_name: Dict[str, FieldMetadata] - sorted_field_names: Tuple[str, ...] - default_gen: Dict[str, Callable[[], Any]] - cls_by_field: Dict[str, Type] + oneof_group_by_field: dict[str, str] + oneof_field_by_group: dict[str, set[dataclasses.Field]] + field_name_by_number: dict[int, str] + meta_by_field_name: dict[str, FieldMetadata] + sorted_field_names: tuple[str, ...] + default_gen: dict[str, Callable[[], Any]] + cls_by_field: dict[str, type] - def __init__(self, cls: Type["Message"]): + def __init__(self, cls: type[Message]): by_field = {} - by_group: Dict[str, Set] = {} + by_group: dict[str, set] = {} by_field_name = {} by_field_number = {} - fields = dataclasses.fields(cls) + fields = dataclasses.fields( + cast("DataclassInstance", cls) + ) # cls is always a dataclass for field in fields: meta = FieldMetadata.get(field) @@ -685,14 +660,14 @@ def __init__(self, cls: Type["Message"]): @staticmethod def _get_default_gen( - cls: Type["Message"], fields: Iterable[dataclasses.Field] - ) -> Dict[str, Callable[[], Any]]: + cls: type[Message], fields: Iterable[dataclasses.Field] + ) -> dict[str, Callable[[], Any]]: return {field.name: cls._get_field_default_gen(field) for field in fields} @staticmethod def _get_cls_by_field( - cls: Type["Message"], fields: Iterable[dataclasses.Field] - ) -> Dict[str, Type]: + cls: type[Message], fields: Iterable[dataclasses.Field] + ) -> dict[str, type]: field_cls = {} for field in fields: @@ -716,7 +691,15 @@ def _get_cls_by_field( return field_cls -class Message(ABC): +def _is_optional(t: Any) -> bool: + return ( + t.__origin__ is Union + or hasattr(types, "UnionType") + and isinstance(t, types.UnionType) + ) and t.__args__[1] is type(None) + + +class Message: """ The base class for protobuf messages, all generated messages will inherit from this. This class registers the message fields which are used by the serializers and @@ -735,7 +718,7 @@ class Message(ABC): _serialized_on_wire: bool _unknown_fields: bytes - _group_current: Dict[str, str] + _group_current: dict[str, str] _betterproto_meta: ClassVar[ProtoClassMetadata] def __post_init__(self) -> None: @@ -743,7 +726,7 @@ def __post_init__(self) -> None: all_sentinel = True # Set current field of each group after `__init__` has already been run. - group_current: Dict[str, Optional[str]] = {} + group_current: dict[str, str | None] = {} for field_name, meta in self._betterproto.meta_by_field_name.items(): if meta.group: group_current.setdefault(meta.group) @@ -804,7 +787,7 @@ def __repr__(self) -> str: ] return f"{self.__class__.__name__}({', '.join(parts)})" - def __rich_repr__(self) -> Iterable[Tuple[str, Any, Any]]: + def __rich_repr__(self) -> Iterable[tuple[str, Any, Any]]: for field_name in self._betterproto.sorted_field_names: yield field_name, self.__raw_get(field_name), PLACEHOLDER @@ -874,7 +857,7 @@ def __bool__(self) -> bool: for field_name in self._betterproto.meta_by_field_name ) - def __deepcopy__(self: T, _: Any = {}) -> T: + def __deepcopy__(self, _: Any = {}) -> Self: kwargs = {} for name in self._betterproto.sorted_field_names: value = self.__raw_get(name) @@ -882,7 +865,7 @@ def __deepcopy__(self: T, _: Any = {}) -> T: kwargs[name] = deepcopy(value) return self.__class__(**kwargs) # type: ignore - def __copy__(self: T, _: Any = {}) -> T: + def __copy__(self, _: Any = {}) -> Self: kwargs = {} for name in self._betterproto.sorted_field_names: value = self.__raw_get(name) @@ -903,7 +886,7 @@ def _betterproto(cls: type[Self]) -> ProtoClassMetadata: # type: ignore cls._betterproto_meta = meta = ProtoClassMetadata(cls) return meta - def dump(self, stream: "SupportsWrite[bytes]", delimit: bool = False) -> None: + def dump(self, stream: SupportsWrite[bytes], delimit: bool = False) -> None: """ Dumps the binary encoded Protobuf message to the stream. @@ -1114,7 +1097,7 @@ def __len__(self) -> int: return size # For compatibility with other libraries - def SerializeToString(self: T) -> bytes: + def SerializeToString(self) -> bytes: """ Get the binary encoded Protobuf representation of this message instance. @@ -1132,23 +1115,23 @@ def SerializeToString(self: T) -> bytes: def __getstate__(self) -> bytes: return bytes(self) - def __setstate__(self: T, pickled_bytes: bytes) -> T: + def __setstate__(self, pickled_bytes: bytes) -> Self: return self.parse(pickled_bytes) - def __reduce__(self) -> Tuple[Any, ...]: + def __reduce__(self) -> tuple[Any, ...]: return (self.__class__.FromString, (bytes(self),)) @classmethod - def _type_hint(cls, field_name: str) -> Type: + def _type_hint(cls, field_name: str) -> Any: return cls._type_hints()[field_name] @classmethod - def _type_hints(cls) -> Dict[str, Type]: + def _type_hints(cls) -> dict[str, Any]: module = sys.modules[cls.__module__] return get_type_hints(cls, module.__dict__, {}) @classmethod - def _cls_for(cls, field: dataclasses.Field, index: int = 0) -> Type: + def _cls_for(cls, field: dataclasses.Field, index: int = 0) -> type: """Get the message class for a field from the type hints.""" field_cls = cls._type_hint(field.name) if hasattr(field_cls, "__args__") and index >= 0: @@ -1173,7 +1156,7 @@ def _get_field_default_gen(cls, field: dataclasses.Field) -> Any: elif t.__origin__ is list: # This is some kind of list (repeated) field. return list - elif t.__origin__ is Union and t.__args__[1] is type(None): + elif _is_optional(t): # This is an optional field (either wrapped, or using proto3 # field presence). For setting the default we really don't care # what kind of field it is. @@ -1209,7 +1192,9 @@ def _postprocess_single( value = value > 0 elif meta.proto_type == TYPE_ENUM: # Convert enum ints to python enum instances - value = self._betterproto.cls_by_field[field_name].try_value(value) + enum_cls = self._betterproto.cls_by_field[field_name] + assert issubclass(enum_cls, Enum) + value = enum_cls.try_value(value) elif wire_type in (WIRE_FIXED_32, WIRE_FIXED_64): fmt = _pack_fmt(meta.proto_type) value = struct.unpack(fmt, value)[0] @@ -1243,10 +1228,10 @@ def _include_default_value_for_oneof( ) def load( - self: T, - stream: "SupportsRead[bytes]", - size: Optional[int] = None, - ) -> T: + self, + stream: SupportsRead[bytes], + size: int | None = None, + ) -> Self: """ Load the binary encoded Protobuf from a stream into this message instance. This returns the instance itself and is therefore assignable and chainable. @@ -1341,7 +1326,7 @@ def load( return self - def parse(self: T, data: bytes) -> T: + def parse(self, data: bytes) -> Self: """ Parse the binary encoded Protobuf into this message instance. This returns the instance itself and is therefore assignable and chainable. @@ -1361,7 +1346,7 @@ def parse(self: T, data: bytes) -> T: # For compatibility with other libraries. @classmethod - def FromString(cls: Type[T], data: bytes) -> T: + def FromString(cls, data: bytes) -> Self: """ Parse the binary encoded Protobuf into this message instance. This returns the instance itself and is therefore assignable and chainable. @@ -1384,14 +1369,16 @@ def FromString(cls: Type[T], data: bytes) -> T: return cls().parse(data) def to_dict( - self, casing: Casing = Casing.CAMEL, include_default_values: bool = False - ) -> Dict[str, Any]: + self, + casing: Callable[[str], str] = Casing.CAMEL, + include_default_values: bool = False, + ) -> dict[str, Any]: """ Returns a JSON serializable dict representation of this object. Parameters ----------- - casing: :class:`Casing` + casing: Callable[[str], str] The casing to use for key values. Default is :attr:`Casing.CAMEL` for compatibility purposes. include_default_values: :class:`bool` @@ -1404,7 +1391,7 @@ def to_dict( Dict[:class:`str`, Any] The JSON serializable dict representation of this object. """ - output: Dict[str, Any] = {} + output: dict[str, Any] = {} field_types = self._type_hints() defaults = self._betterproto.default_gen for field_name, meta in self._betterproto.meta_by_field_name.items(): @@ -1522,7 +1509,7 @@ def to_dict( @classmethod def _from_dict_init(cls, mapping: Mapping[str, Any]) -> Mapping[str, Any]: - init_kwargs: Dict[str, Any] = {} + init_kwargs: dict[str, Any] = {} for key, value in mapping.items(): field_name = safe_snake_case(key) try: @@ -1534,6 +1521,7 @@ def _from_dict_init(cls, mapping: Mapping[str, Any]) -> Mapping[str, Any]: if meta.proto_type == TYPE_MESSAGE: sub_cls = cls._betterproto.cls_by_field[field_name] + assert isinstance(sub_cls, Message) if sub_cls == datetime: value = ( [isoparse(item) for item in value] @@ -1554,6 +1542,7 @@ def _from_dict_init(cls, mapping: Mapping[str, Any]) -> Mapping[str, Any]: ) elif meta.map_types and meta.map_types[1] == TYPE_MESSAGE: sub_cls = cls._betterproto.cls_by_field[f"{field_name}.value"] + assert isinstance(sub_cls, Message) value = {k: sub_cls.from_dict(v) for k, v in value.items()} else: if meta.proto_type in INT_64_TYPES: @@ -1570,6 +1559,7 @@ def _from_dict_init(cls, mapping: Mapping[str, Any]) -> Mapping[str, Any]: ) elif meta.proto_type == TYPE_ENUM: enum_cls = cls._betterproto.cls_by_field[field_name] + assert isinstance(enum_cls, Enum) if isinstance(value, list): value = [enum_cls.from_string(e) for e in value] elif isinstance(value, str): @@ -1626,9 +1616,9 @@ def from_dict(self, value: Mapping[str, Any]) -> Self: def to_json( self, - indent: Union[None, int, str] = None, + indent: None | int | str = None, include_default_values: bool = False, - casing: Casing = Casing.CAMEL, + casing: Callable[[str], str] = Casing.CAMEL, ) -> str: """A helper function to parse the message instance into its JSON representation. @@ -1647,7 +1637,7 @@ def to_json( E.g. an ``int32`` field will be included with a value of ``0`` if this is set to ``True``, otherwise this would be ignored. - casing: :class:`Casing` + casing: Callable[[str], str] The casing to use for key values. Default is :attr:`Casing.CAMEL` for compatibility purposes. @@ -1661,7 +1651,7 @@ def to_json( indent=indent, ) - def from_json(self: T, value: Union[str, bytes]) -> T: + def from_json(self, value: str | bytes) -> Self: """A helper function to return the message instance from its JSON representation. This returns the instance itself and is therefore assignable and chainable. @@ -1683,14 +1673,16 @@ def from_json(self: T, value: Union[str, bytes]) -> T: return self.from_dict(json.loads(value)) def to_pydict( - self, casing: Casing = Casing.CAMEL, include_default_values: bool = False - ) -> Dict[str, Any]: + self, + casing: Callable[[str], str] = Casing.CAMEL, + include_default_values: bool = False, + ) -> dict[str, Any]: """ Returns a python dict representation of this object. Parameters ----------- - casing: :class:`Casing` + casing: Callable[[str], str] The casing to use for key values. Default is :attr:`Casing.CAMEL` for compatibility purposes. include_default_values: :class:`bool` @@ -1703,7 +1695,7 @@ def to_pydict( Dict[:class:`str`, Any] The python dict representation of this object. """ - output: Dict[str, Any] = {} + output: dict[str, Any] = {} defaults = self._betterproto.default_gen for field_name, meta in self._betterproto.meta_by_field_name.items(): field_is_repeated = defaults[field_name] is list @@ -1764,7 +1756,7 @@ def to_pydict( output[cased_name] = value return output - def from_pydict(self: T, value: Mapping[str, Any]) -> T: + def from_pydict(self, value: Mapping[str, Any]) -> Self: """ Parse the key/value pairs into the current message instance. This returns the instance itself and is therefore assignable and chainable. @@ -1866,7 +1858,9 @@ def _validate_field_groups(cls, values): return values -Message.__annotations__ = {} # HACK to avoid typing.get_type_hints breaking :) +Message.__annotations__ = ( + {} +) # HACK to avoid typing.get_type_hints breaking because we have to manually pass globals # monkey patch (de-)serialization functions of class `Message` # with functions from `betterproto-rust-codec` if available @@ -1900,7 +1894,7 @@ def serialized_on_wire(message: Message) -> bool: return message._serialized_on_wire -def which_one_of(message: Message, group_name: str) -> Tuple[str, Optional[Any]]: +def which_one_of(message: Message, group_name: str) -> tuple[str, Any | None]: """ Return the name and value of a message's one-of field group. @@ -1936,7 +1930,7 @@ class _Duration(Duration): @classmethod def from_timedelta( cls, delta: timedelta, *, _1_microsecond: timedelta = timedelta(microseconds=1) - ) -> "_Duration": + ) -> _Duration: total_ms = delta // _1_microsecond seconds = int(total_ms / 1e6) nanos = int((total_ms % 1e6) * 1e3) @@ -1956,7 +1950,7 @@ def delta_to_json(delta: timedelta) -> str: class _Timestamp(Timestamp): @classmethod - def from_datetime(cls, dt: datetime) -> "_Timestamp": + def from_datetime(cls, dt: datetime) -> _Timestamp: # manual epoch offset calulation to avoid rounding errors, # to support negative timestamps (before 1970) and skirt # around datetime bugs (apparently 0 isn't a year in [0, 9999]??) @@ -1998,7 +1992,7 @@ def timestamp_to_json(dt: datetime) -> str: return f"{result}.{nanos:09d}" -def _get_wrapper(proto_type: str) -> Type: +def _get_wrapper(proto_type: str) -> type: """Get the wrapper message class for a wrapped type.""" # TODO: include ListValue and NullValue? diff --git a/src/betterproto/_types.py b/src/betterproto/_types.py deleted file mode 100644 index 616d550de..000000000 --- a/src/betterproto/_types.py +++ /dev/null @@ -1,14 +0,0 @@ -from typing import ( - TYPE_CHECKING, - TypeVar, -) - - -if TYPE_CHECKING: - from grpclib._typing import IProtoMessage - - from . import Message - -# Bound type variable to allow methods to return `self` of subclasses -T = TypeVar("T", bound="Message") -ST = TypeVar("ST", bound="IProtoMessage") diff --git a/src/betterproto/enum.py b/src/betterproto/enum.py index 529832251..9c87bf671 100644 --- a/src/betterproto/enum.py +++ b/src/betterproto/enum.py @@ -1,6 +1,10 @@ from __future__ import annotations import sys +from collections.abc import ( + Generator, + Mapping, +) from enum import ( EnumMeta, IntEnum, @@ -9,22 +13,13 @@ from typing import ( TYPE_CHECKING, Any, - Dict, - Optional, - Tuple, + cast, ) - -if TYPE_CHECKING: - from collections.abc import ( - Generator, - Mapping, - ) - - from typing_extensions import ( - Never, - Self, - ) +from typing_extensions import ( + Never, + Self, +) def _is_descriptor(obj: object) -> bool: @@ -38,20 +33,23 @@ class EnumType(EnumMeta if TYPE_CHECKING else type): _member_map_: Mapping[str, Enum] def __new__( - mcs, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any] + mcs, name: str, bases: tuple[type, ...], namespace: dict[str, Any] ) -> Self: value_map = {} member_map = {} - new_mcs = type( - f"{name}Type", - tuple( - dict.fromkeys( - [base.__class__ for base in bases if base.__class__ is not type] - + [EnumType, type] - ) - ), # reorder the bases so EnumType and type are last to avoid conflicts - {"_value_map_": value_map, "_member_map_": member_map}, + new_mcs = cast( + "type[Self]", + type( + f"{name}Type", + tuple( + dict.fromkeys( + [base.__class__ for base in bases if base.__class__ is not type] + + [EnumType, type] + ) + ), # reorder the bases so EnumType and type are last to avoid conflicts + {"_value_map_": value_map, "_member_map_": member_map}, + ), ) members = { @@ -60,11 +58,14 @@ def __new__( if not _is_descriptor(value) and not name.startswith("__") } - cls = type.__new__( - new_mcs, - name, - bases, - {key: value for key, value in namespace.items() if key not in members}, + cls = cast( + "type[Enum]", + type.__new__( + new_mcs, + name, + bases, + {key: value for key, value in namespace.items() if key not in members}, + ), ) # this allows us to disallow member access from other members as # members become proper class variables @@ -72,7 +73,7 @@ def __new__( for name, value in members.items(): member = value_map.get(value) if member is None: - member = cls.__new__(cls, name=name, value=value) # type: ignore + member = cls._new_member(name=name, value=value) value_map[value] = member member_map[name] = member type.__setattr__(new_mcs, name, member) @@ -120,7 +121,14 @@ def __delattr__(cls, name: str) -> Never: raise AttributeError(f"{cls.__name__}: cannot delete Enum members.") def __contains__(cls, member: object) -> bool: - return isinstance(member, cls) and member.name in cls._member_map_ + return ( + isinstance(member, Enum) + and isinstance(member, cls) + and member.name in cls._member_map_ + ) + + def __dir__(self) -> list[str]: + return super().__dir__() + list(self._member_map_) class Enum(IntEnum if TYPE_CHECKING else int, metaclass=EnumType): @@ -129,16 +137,15 @@ class Enum(IntEnum if TYPE_CHECKING else int, metaclass=EnumType): inherit from this. Emulates `enum.IntEnum`. """ - name: Optional[str] + name: str | None value: int - if not TYPE_CHECKING: - - def __new__(cls, *, name: Optional[str], value: int) -> Self: - self = super().__new__(cls, value) - super().__setattr__(self, "name", name) - super().__setattr__(self, "value", value) - return self + @classmethod + def _new_member(cls, *, name: str | None, value: int) -> Self: + self = super().__new__(cls, value) + super().__setattr__(self, "name", name) + super().__setattr__(self, "value", value) + return self def __str__(self) -> str: return self.name or "None" @@ -174,7 +181,7 @@ def try_value(cls, value: int = 0) -> Self: try: return cls._value_map_[value] except (KeyError, TypeError): - return cls.__new__(cls, name=None, value=value) + return cls._new_member(name=None, value=value) @classmethod def from_string(cls, name: str) -> Self: diff --git a/src/betterproto/grpc/grpclib_client.py b/src/betterproto/grpc/grpclib_client.py index b19e80615..cc3461916 100644 --- a/src/betterproto/grpc/grpclib_client.py +++ b/src/betterproto/grpc/grpclib_client.py @@ -1,50 +1,49 @@ +from __future__ import annotations + import asyncio -from abc import ABC -from typing import ( - TYPE_CHECKING, +from collections.abc import ( AsyncIterable, AsyncIterator, Collection, Iterable, Mapping, - Optional, - Tuple, - Type, - Union, +) +from typing import ( + TYPE_CHECKING, + Any, + TypeVar, ) import grpclib.const +from typing_extensions import TypeAlias if TYPE_CHECKING: + from grpclib._typing import IProtoMessage from grpclib.client import Channel from grpclib.metadata import Deadline - from .._types import ( - ST, - IProtoMessage, - Message, - T, - ) + from .. import Message +T = TypeVar("T", bound="Message") -Value = Union[str, bytes] -MetadataLike = Union[Mapping[str, Value], Collection[Tuple[str, Value]]] -MessageSource = Union[Iterable["IProtoMessage"], AsyncIterable["IProtoMessage"]] +Value: TypeAlias = "str | bytes" +MetadataLike: TypeAlias = "Mapping[str, Value] | Collection[tuple[str, Value]]" +MessageSource: TypeAlias = "Iterable[IProtoMessage] | AsyncIterable[IProtoMessage]" -class ServiceStub(ABC): +class ServiceStub: """ Base class for async gRPC clients. """ def __init__( self, - channel: "Channel", + channel: Channel, *, - timeout: Optional[float] = None, - deadline: Optional["Deadline"] = None, - metadata: Optional[MetadataLike] = None, + timeout: float | None = None, + deadline: Deadline | None = None, + metadata: MetadataLike | None = None, ) -> None: self.channel = channel self.timeout = timeout @@ -53,10 +52,10 @@ def __init__( def __resolve_request_kwargs( self, - timeout: Optional[float], - deadline: Optional["Deadline"], - metadata: Optional[MetadataLike], - ): + timeout: float | None, + deadline: Deadline | None, + metadata: MetadataLike | None, + ) -> dict[str, Any]: return { "timeout": self.timeout if timeout is None else timeout, "deadline": self.deadline if deadline is None else deadline, @@ -66,13 +65,13 @@ def __resolve_request_kwargs( async def _unary_unary( self, route: str, - request: "IProtoMessage", - response_type: Type["T"], + request: IProtoMessage, + response_type: type[T], *, - timeout: Optional[float] = None, - deadline: Optional["Deadline"] = None, - metadata: Optional[MetadataLike] = None, - ) -> "T": + timeout: float | None = None, + deadline: Deadline | None = None, + metadata: MetadataLike | None = None, + ) -> T: """Make a unary request and return the response.""" async with self.channel.request( route, @@ -89,13 +88,13 @@ async def _unary_unary( async def _unary_stream( self, route: str, - request: "IProtoMessage", - response_type: Type["T"], + request: IProtoMessage, + response_type: type[T], *, - timeout: Optional[float] = None, - deadline: Optional["Deadline"] = None, - metadata: Optional[MetadataLike] = None, - ) -> AsyncIterator["T"]: + timeout: float | None = None, + deadline: Deadline | None = None, + metadata: MetadataLike | None = None, + ) -> AsyncIterator[T]: """Make a unary request and return the stream response iterator.""" async with self.channel.request( route, @@ -112,13 +111,13 @@ async def _stream_unary( self, route: str, request_iterator: MessageSource, - request_type: Type["IProtoMessage"], - response_type: Type["T"], + request_type: type[IProtoMessage], + response_type: type[T], *, - timeout: Optional[float] = None, - deadline: Optional["Deadline"] = None, - metadata: Optional[MetadataLike] = None, - ) -> "T": + timeout: float | None = None, + deadline: Deadline | None = None, + metadata: MetadataLike | None = None, + ) -> T: """Make a stream request and return the response.""" async with self.channel.request( route, @@ -137,13 +136,13 @@ async def _stream_stream( self, route: str, request_iterator: MessageSource, - request_type: Type["IProtoMessage"], - response_type: Type["T"], + request_type: type[IProtoMessage], + response_type: type[T], *, - timeout: Optional[float] = None, - deadline: Optional["Deadline"] = None, - metadata: Optional[MetadataLike] = None, - ) -> AsyncIterator["T"]: + timeout: float | None = None, + deadline: Deadline | None = None, + metadata: MetadataLike | None = None, + ) -> AsyncIterator[T]: """ Make a stream request and return an AsyncIterator to iterate over response messages. @@ -167,7 +166,7 @@ async def _stream_stream( raise @staticmethod - async def _send_messages(stream, messages: MessageSource): + async def _send_messages(stream, messages: MessageSource) -> None: if isinstance(messages, AsyncIterable): async for message in messages: await stream.send_message(message) diff --git a/src/betterproto/grpc/grpclib_server.py b/src/betterproto/grpc/grpclib_server.py index 3e2803113..61d4710b9 100644 --- a/src/betterproto/grpc/grpclib_server.py +++ b/src/betterproto/grpc/grpclib_server.py @@ -3,7 +3,6 @@ from typing import ( Any, Callable, - Dict, ) import grpclib diff --git a/src/betterproto/grpc/util/async_channel.py b/src/betterproto/grpc/util/async_channel.py index 9f18dbfd2..a95400e05 100644 --- a/src/betterproto/grpc/util/async_channel.py +++ b/src/betterproto/grpc/util/async_channel.py @@ -1,11 +1,12 @@ +from __future__ import annotations + import asyncio from typing import ( AsyncIterable, AsyncIterator, Iterable, - Optional, TypeVar, - Union, + cast, ) @@ -32,6 +33,7 @@ class AsyncChannel(AsyncIterable[T]): This makes decoupled bidirectional steaming gRPC requests easy if used like: .. code-block:: python + client = GeneratedStub(grpclib_chan) request_channel = await AsyncChannel() # We can start be sending all the requests we already have @@ -119,8 +121,8 @@ def done(self) -> bool: return self._closed and self._queue.qsize() <= self._waiting_receivers async def send_from( - self, source: Union[Iterable[T], AsyncIterable[T]], close: bool = False - ) -> "AsyncChannel[T]": + self, source: Iterable[T] | AsyncIterable[T], close: bool = False + ) -> AsyncChannel[T]: """ Iterates the given [Async]Iterable and sends all the resulting items. If close is set to True then subsequent send calls will be rejected with a @@ -143,7 +145,7 @@ async def send_from( self.close() return self - async def send(self, item: T) -> "AsyncChannel[T]": + async def send(self, item: T) -> AsyncChannel[T]: """ Send a single item over this channel. :param item: The item to send @@ -153,7 +155,7 @@ async def send(self, item: T) -> "AsyncChannel[T]": await self._queue.put(item) return self - async def receive(self) -> Optional[T]: + async def receive(self) -> T | None: """ Returns the next item from this channel when it becomes available, or None if the channel is closed before another item is sent. @@ -171,14 +173,14 @@ async def receive(self) -> Optional[T]: self._waiting_receivers -= 1 self._queue.task_done() - def close(self): + def close(self) -> None: """ Close this channel to new items """ self._closed = True asyncio.ensure_future(self._flush_queue()) - async def _flush_queue(self): + async def _flush_queue(self) -> None: """ To be called after the channel is closed. Pushes a number of self.__flush objects to the queue to ensure no waiting consumers get deadlocked. @@ -190,4 +192,4 @@ async def _flush_queue(self): await self._queue.put(self.__flush) # A special signal object for flushing the queue when the channel is closed - __flush = object() + __flush = cast(T, object()) diff --git a/src/betterproto/plugin/models.py b/src/betterproto/plugin/models.py index 93b2aa8cf..b0cea6aaa 100644 --- a/src/betterproto/plugin/models.py +++ b/src/betterproto/plugin/models.py @@ -29,34 +29,30 @@ reference to `A` to `B`'s `fields` attribute. """ +from __future__ import annotations import builtins import re import textwrap +from collections.abc import ( + Iterable, + Iterator, +) from dataclasses import ( dataclass, field, ) -from typing import ( - Dict, - Iterable, - Iterator, - List, - Optional, - Set, - Type, - Union, -) +from typing import Any import betterproto from betterproto import which_one_of -from betterproto.casing import sanitize_name from betterproto.compile.importing import ( get_type_reference, parse_source_type_name, ) from betterproto.compile.naming import ( pythonize_class_name, + pythonize_enum_member_name, pythonize_field_name, pythonize_method_name, ) @@ -69,24 +65,14 @@ FieldDescriptorProtoType, FileDescriptorProto, MethodDescriptorProto, + ServiceDescriptorProto, ) from betterproto.lib.google.protobuf.compiler import CodeGeneratorRequest -from ..compile.importing import ( - get_type_reference, - parse_source_type_name, -) -from ..compile.naming import ( - pythonize_class_name, - pythonize_enum_member_name, - pythonize_field_name, - pythonize_method_name, -) - # Create a unique placeholder to deal with # https://stackoverflow.com/questions/51575931/class-inheritance-in-python-3-7-dataclasses -PLACEHOLDER = object() +PLACEHOLDER: Any = object() # Organize proto types into categories PROTO_FLOAT_TYPES = ( @@ -152,7 +138,7 @@ def monkey_patch_oneof_index(): def get_comment( - proto_file: "FileDescriptorProto", path: List[int], indent: int = 4 + proto_file: FileDescriptorProto, path: list[int], indent: int = 4 ) -> str: pad = " " * indent for sci_loc in proto_file.source_code_info.location: @@ -173,11 +159,11 @@ class ProtoContentBase: """Methods common to MessageCompiler, ServiceCompiler and ServiceMethodCompiler.""" source_file: FileDescriptorProto - path: List[int] + path: list[int] comment_indent: int = 4 - parent: Union["betterproto.Message", "OutputTemplate"] + parent: betterproto.Message | OutputTemplate - __dataclass_fields__: Dict[str, object] + __dataclass_fields__: dict[str, object] def __post_init__(self) -> None: """Checks that no fake default fields were left as placeholders.""" @@ -186,14 +172,14 @@ def __post_init__(self) -> None: raise ValueError(f"`{field_name}` is a required field.") @property - def output_file(self) -> "OutputTemplate": + def output_file(self) -> OutputTemplate: current = self while not isinstance(current, OutputTemplate): current = current.parent return current @property - def request(self) -> "PluginRequestCompiler": + def request(self) -> PluginRequestCompiler: current = self while not isinstance(current, OutputTemplate): current = current.parent @@ -212,10 +198,10 @@ def comment(self) -> str: @dataclass class PluginRequestCompiler: plugin_request_obj: CodeGeneratorRequest - output_packages: Dict[str, "OutputTemplate"] = field(default_factory=dict) + output_packages: dict[str, OutputTemplate] = field(default_factory=dict) @property - def all_messages(self) -> List["MessageCompiler"]: + def all_messages(self) -> list[MessageCompiler]: """All of the messages in this request. Returns @@ -239,16 +225,16 @@ class OutputTemplate: parent_request: PluginRequestCompiler package_proto_obj: FileDescriptorProto - input_files: List[str] = field(default_factory=list) - imports: Set[str] = field(default_factory=set) - datetime_imports: Set[str] = field(default_factory=set) - typing_imports: Set[str] = field(default_factory=set) - pydantic_imports: Set[str] = field(default_factory=set) + input_files: list[FileDescriptorProto] = field(default_factory=list) + imports: set[str] = field(default_factory=set) + datetime_imports: set[str] = field(default_factory=set) + typing_imports: set[str] = field(default_factory=set) + pydantic_imports: set[str] = field(default_factory=set) builtins_import: bool = False - messages: List["MessageCompiler"] = field(default_factory=list) - enums: List["EnumDefinitionCompiler"] = field(default_factory=list) - services: List["ServiceCompiler"] = field(default_factory=list) - imports_type_checking_only: Set[str] = field(default_factory=set) + messages: list[MessageCompiler] = field(default_factory=list) + enums: list[EnumDefinitionCompiler] = field(default_factory=list) + services: list[ServiceCompiler] = field(default_factory=list) + imports_type_checking_only: set[str] = field(default_factory=set) pydantic_dataclasses: bool = False output: bool = True @@ -275,7 +261,7 @@ def input_filenames(self) -> Iterable[str]: return sorted(f.name for f in self.input_files) @property - def python_module_imports(self) -> Set[str]: + def python_module_imports(self) -> set[str]: imports = set() if any(x for x in self.messages if any(x.deprecated_fields)): imports.add("warnings") @@ -289,14 +275,12 @@ class MessageCompiler(ProtoContentBase): """Representation of a protobuf message.""" source_file: FileDescriptorProto - parent: Union["MessageCompiler", OutputTemplate] = PLACEHOLDER + parent: MessageCompiler | OutputTemplate = PLACEHOLDER proto_obj: DescriptorProto = PLACEHOLDER - path: List[int] = PLACEHOLDER - fields: List[Union["FieldCompiler", "MessageCompiler"]] = field( - default_factory=list - ) + path: list[int] = PLACEHOLDER + fields: list[FieldCompiler | MessageCompiler] = field(default_factory=list) deprecated: bool = field(default=False, init=False) - builtins_types: Set[str] = field(default_factory=set) + builtins_types: set[str] = field(default_factory=set) def __post_init__(self) -> None: # Add message to output file @@ -316,6 +300,10 @@ def proto_name(self) -> str: def py_name(self) -> str: return pythonize_class_name(self.proto_name) + @property + def repeated(self) -> bool: + raise NotImplementedError + @property def annotation(self) -> str: if self.repeated: @@ -339,20 +327,19 @@ def has_oneof_fields(self) -> bool: @property def has_message_field(self) -> bool: return any( - ( - field.proto_obj.type in PROTO_MESSAGE_TYPES - for field in self.fields - if isinstance(field.proto_obj, FieldDescriptorProto) - ) + field.proto_obj.type in PROTO_MESSAGE_TYPES + for field in self.fields + if isinstance(field.proto_obj, FieldDescriptorProto) ) def is_map( - proto_field_obj: FieldDescriptorProto, parent_message: DescriptorProto + proto_field_obj: FieldDescriptorProto, + parent_message: DescriptorProto | MessageCompiler, ) -> bool: """True if proto_field_obj is a map, otherwise False.""" if proto_field_obj.type == FieldDescriptorProtoType.TYPE_MESSAGE: - if not hasattr(parent_message, "nested_type"): + if not isinstance(parent_message, DescriptorProto): return False # This might be a map... @@ -415,16 +402,16 @@ def get_field_string(self, indent: int = 4) -> str: return f"{name}{annotations} = {betterproto_field_type}" @property - def betterproto_field_args(self) -> List[str]: + def betterproto_field_args(self) -> list[str]: args = [] if self.field_wraps: args.append(f"wraps={self.field_wraps}") if self.optional: - args.append(f"optional=True") + args.append("optional=True") return args @property - def datetime_imports(self) -> Set[str]: + def datetime_imports(self) -> set[str]: imports = set() annotation = self.annotation # FIXME: false positives - e.g. `MyDatetimedelta` @@ -435,7 +422,7 @@ def datetime_imports(self) -> Set[str]: return imports @property - def typing_imports(self) -> Set[str]: + def typing_imports(self) -> set[str]: imports = set() annotation = self.annotation if "Optional[" in annotation: @@ -447,7 +434,7 @@ def typing_imports(self) -> Set[str]: return imports @property - def pydantic_imports(self) -> Set[str]: + def pydantic_imports(self) -> set[str]: return set() @property @@ -463,7 +450,7 @@ def add_imports_to(self, output_file: OutputTemplate) -> None: output_file.builtins_import = output_file.builtins_import or self.use_builtins @property - def field_wraps(self) -> Optional[str]: + def field_wraps(self) -> str | None: """Returns betterproto wrapped field type or None.""" match_wrapper = re.match( r"\.google\.protobuf\.(.+)Value$", self.proto_obj.type_name @@ -581,7 +568,7 @@ def annotation(self) -> str: @dataclass class OneOfFieldCompiler(FieldCompiler): @property - def betterproto_field_args(self) -> List[str]: + def betterproto_field_args(self) -> list[str]: args = super().betterproto_field_args group = self.parent.proto_obj.oneof_decl[self.proto_obj.oneof_index].name args.append(f'group="{group}"') @@ -598,14 +585,14 @@ def optional(self) -> bool: return True @property - def pydantic_imports(self) -> Set[str]: + def pydantic_imports(self) -> set[str]: return {"root_validator"} @dataclass class MapEntryCompiler(FieldCompiler): - py_k_type: Type = PLACEHOLDER - py_v_type: Type = PLACEHOLDER + py_k_type: str = PLACEHOLDER + py_v_type: str = PLACEHOLDER proto_k_type: str = PLACEHOLDER proto_v_type: str = PLACEHOLDER @@ -635,7 +622,7 @@ def __post_init__(self) -> None: super().__post_init__() # call FieldCompiler-> MessageCompiler __post_init__ @property - def betterproto_field_args(self) -> List[str]: + def betterproto_field_args(self) -> list[str]: return [f"betterproto.{self.proto_k_type}", f"betterproto.{self.proto_v_type}"] @property @@ -656,7 +643,7 @@ class EnumDefinitionCompiler(MessageCompiler): """Representation of a proto Enum definition.""" proto_obj: EnumDescriptorProto = PLACEHOLDER - entries: List["EnumDefinitionCompiler.EnumEntry"] = PLACEHOLDER + entries: list[EnumDefinitionCompiler.EnumEntry] = PLACEHOLDER @dataclass(unsafe_hash=True) class EnumEntry: @@ -694,9 +681,9 @@ def default_value_string(self) -> str: @dataclass class ServiceCompiler(ProtoContentBase): parent: OutputTemplate = PLACEHOLDER - proto_obj: DescriptorProto = PLACEHOLDER - path: List[int] = PLACEHOLDER - methods: List["ServiceMethodCompiler"] = field(default_factory=list) + proto_obj: ServiceDescriptorProto = PLACEHOLDER + path: list[int] = PLACEHOLDER + methods: list[ServiceMethodCompiler] = field(default_factory=list) def __post_init__(self) -> None: # Add service to output file @@ -717,7 +704,7 @@ def py_name(self) -> str: class ServiceMethodCompiler(ProtoContentBase): parent: ServiceCompiler proto_obj: MethodDescriptorProto - path: List[int] = PLACEHOLDER + path: list[int] = PLACEHOLDER comment_indent: int = 8 def __post_init__(self) -> None: @@ -768,7 +755,7 @@ def route(self) -> str: return f"/{package_part}{self.parent.proto_name}/{self.proto_name}" @property - def py_input_message(self) -> Optional[MessageCompiler]: + def py_input_message(self) -> MessageCompiler | None: """Find the input message object. Returns diff --git a/src/betterproto/plugin/parser.py b/src/betterproto/plugin/parser.py index f48533338..11e00af35 100644 --- a/src/betterproto/plugin/parser.py +++ b/src/betterproto/plugin/parser.py @@ -1,12 +1,8 @@ +from __future__ import annotations + import pathlib import sys -from typing import ( - Generator, - List, - Set, - Tuple, - Union, -) +from collections.abc import Generator from betterproto.lib.google.protobuf import ( DescriptorProto, @@ -41,17 +37,13 @@ def traverse( proto_file: FileDescriptorProto, -) -> Generator[ - Tuple[Union[EnumDescriptorProto, DescriptorProto], List[int]], None, None -]: +) -> Generator[tuple[EnumDescriptorProto | DescriptorProto, list[int]], None, None]: # Todo: Keep information about nested hierarchy def _traverse( - path: List[int], - items: Union[List[EnumDescriptorProto], List[DescriptorProto]], + path: list[int], + items: list[EnumDescriptorProto] | list[DescriptorProto], prefix: str = "", - ) -> Generator[ - Tuple[Union[EnumDescriptorProto, DescriptorProto], List[int]], None, None - ]: + ) -> Generator[tuple[EnumDescriptorProto | DescriptorProto, list[int]], None, None]: for i, item in enumerate(items): # Adjust the name since we flatten the hierarchy. # Todo: don't change the name, but include full name in returned tuple @@ -118,7 +110,7 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse: read_protobuf_service(service, index, output_package) # Generate output files - output_paths: Set[pathlib.Path] = set() + output_paths: set[pathlib.Path] = set() for output_package_name, output_package in request_data.output_packages.items(): if not output_package.output: continue @@ -154,10 +146,10 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse: def _make_one_of_field_compiler( output_package: OutputTemplate, - source_file: "FileDescriptorProto", + source_file: FileDescriptorProto, parent: MessageCompiler, - proto_obj: "FieldDescriptorProto", - path: List[int], + proto_obj: FieldDescriptorProto, + path: list[int], ) -> FieldCompiler: pydantic = output_package.pydantic_dataclasses Cls = PydanticOneOfFieldCompiler if pydantic else OneOfFieldCompiler @@ -170,9 +162,9 @@ def _make_one_of_field_compiler( def read_protobuf_type( - item: DescriptorProto, - path: List[int], - source_file: "FileDescriptorProto", + item: DescriptorProto | EnumDescriptorProto, + path: list[int], + source_file: FileDescriptorProto, output_package: OutputTemplate, ) -> None: if isinstance(item, DescriptorProto): diff --git a/src/betterproto/utils.py b/src/betterproto/utils.py index b977fc713..a5bf0edb2 100644 --- a/src/betterproto/utils.py +++ b/src/betterproto/utils.py @@ -36,7 +36,7 @@ def instancemethod(self, func: Callable[Concatenate[SelfT, P], HybridT]) -> Self return self def __get__( - self, instance: Optional[SelfT], owner: Type[SelfT] + self, instance: SelfT | None, owner: type[SelfT] ) -> Callable[P, HybridT]: if instance is None or self.instance_func is None: # either bound to the class, or no instance method available