diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..12ddc6a --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,19 @@ +name: Publish Python Package + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + - uses: abatilo/actions-poetry@v2 + - name: Poetry build + run: poetry build -n + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml new file mode 100644 index 0000000..6f6ff03 --- /dev/null +++ b/.github/workflows/pull-request.yaml @@ -0,0 +1,53 @@ +name: Check On Pull Request + +on: + pull_request: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [ "3.9", "3.10", "3.11", "3.12" ] + protoc-version: [ "26.1" ] + buf-version: [ "1.31.0" ] + steps: + - uses: actions/checkout@v4 + - name: Install Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + # see details (matrix, python-version, python-version-file, etc.) + # https://github.com/actions/setup-python + - name: Install poetry + uses: abatilo/actions-poetry@v2 + - name: Setup a local virtual environment (if no poetry.toml file) + run: | + poetry config virtualenvs.create true --local + poetry config virtualenvs.in-project true --local + - uses: actions/cache@v3 + name: Define a cache for the virtual environment based on the dependencies lock file + with: + path: ./.venv + key: venv-${{ hashFiles('poetry.lock') }} + - name: Install the project dependencies + run: poetry install --all-extras + - name: Download protoc ${{ matrix.protoc-version }} + run: wget -q -O protoc.zip "https://github.com/protocolbuffers/protobuf/releases/download/v${{ matrix.protoc-version }}/protoc-${{ matrix.protoc-version }}-linux-x86_64.zip" + - name: Unzip protoc ${{ matrix.protoc-version }} + run: unzip -q -n protoc.zip 'bin/*' 'include/google/protobuf/*' -d .venv + - name: Download buf ${{ matrix.buf-version }} + run: wget -q -O .venv/bin/buf "https://github.com/bufbuild/buf/releases/download/v${{ matrix.buf-version }}/buf-Linux-x86_64" + - name: Allow run buf ${{ matrix.buf-version }} + run: chmod +x .venv/bin/buf + - name: Run mypy + run: poetry run mypy + - name: Run pytest + run: poetry run pytest + - name: Run ruff format + run: poetry run ruff format --check + - name: Run ruff format + run: poetry run ruff format --check diff --git a/README.md b/README.md index 4c548a4..a0c3272 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,24 @@ install poetry add pyprotostuben ``` -available executables: +### protoc-gen-pyprotostuben -* `protoc-gen-pyprotostuben` -- a protoc plugin that generates MyPy stubs +a protoc plugin that generates MyPy stubs + +#### features + +* choose message structure immutability / mutability +* choose async / sync grpc module stubs +* grpc servicer abstract methods have full signature (with appropriate type args in generics), thus it is easier to + implement methods in IDE + +#### flags + +* `message-mutable` -- add setters for fields, use mutable containers +* `message-all-init-args-optional` -- each field is optional in message constructor (event if required) +* `grpc-sync` -- use sync grpc stubs instead of grpc.aio module and async defs +* `grpc-skip-servicer` -- don't generate code for servicers +* `grpc-skip-stub` -- don't generate code for stubs ## examples @@ -25,16 +40,22 @@ available executables: * / * src/ - * foo.proto + * greeting.proto ```protobuf syntax = "proto3"; - - package bar; - - message Spam { - string eggs = 1; - repeated int64 pizza = 2; - optional string apple = 3; + + package greeting; + + message GreetRequest { + string name = 1; + } + + message GreetResponse { + string text = 1; + } + + service Greeter { + rpc Greet(GreetRequest) returns (GreetResponse) {} } ``` * buf.yaml @@ -63,30 +84,56 @@ buf generate #### output -##### src/foo_pb2.pyi +##### src/greeting_pb2.pyi ```python import builtins +import google.protobuf.message import typing -import google.protobuf.message +class GreetRequest(google.protobuf.message.Message): + + def __init__(self, *, name: builtins.str) -> None:... + @builtins.property + def name(self) -> builtins.str:... -class Spam(google.protobuf.message.Message): + def HasField(self, field_name: typing.NoReturn) -> typing.NoReturn:... - def __init__(self, *, eggs: builtins.str, pizza: typing.Sequence[builtins.int], - apple: typing.Optional[builtins.str] = None) -> None: ... + def WhichOneof(self, oneof_group: typing.NoReturn) -> typing.NoReturn:... - @builtins.property - def eggs(self) -> builtins.str: ... +class GreetResponse(google.protobuf.message.Message): - @builtins.property - def pizza(self) -> typing.Sequence[builtins.int]: ... + def __init__(self, *, text: builtins.str) -> None:... @builtins.property - def apple(self) -> builtins.str: ... + def text(self) -> builtins.str:... + + def HasField(self, field_name: typing.NoReturn) -> typing.NoReturn:... + + def WhichOneof(self, oneof_group: typing.NoReturn) -> typing.NoReturn:... +``` + +##### src/greeting_pb2_grpc.pyi + +```python +import abc +import builtins +import greeting_pb2 +import grpc +import grpc.aio +import typing + +class GreeterServicer(metaclass=abc.ABCMeta): + + @abc.abstractmethod + async def Greet(self, request: greeting_pb2.GreetRequest, context: grpc.aio.ServicerContext[greeting_pb2.GreetRequest, greeting_pb2.GreetResponse]) -> greeting_pb2.GreetResponse:... + +def add_GreeterServicer_to_server(servicer: GreeterServicer, server: grpc.aio.Server) -> None:... + +class GreeterStub: - def HasField(self, field_name: typing.Literal['apple']) -> bool: ... + def __init__(self, channel: grpc.aio.Channel) -> None:... - def WhichOneof(self, oneof_group: typing.NoReturn) -> typing.NoReturn: ... + async def Greet(self, request: greeting_pb2.GreetRequest, *, timeout: typing.Optional[builtins.float]=None, metadata: typing.Optional[grpc.aio.MetadataType]=None, credentials: typing.Optional[grpc.CallCredentials]=None, wait_for_ready: typing.Optional[builtins.bool]=None, compression: typing.Optional[grpc.Compression]=None) -> grpc.aio.UnaryUnaryCall[greeting_pb2.GreetRequest, greeting_pb2.GreetResponse]:... ``` diff --git a/poetry.lock b/poetry.lock index 154e68a..9b68b98 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,64 +14,84 @@ files = [ [[package]] name = "coverage" -version = "7.5.3" +version = "7.6.1" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] [package.dependencies] @@ -82,14 +102,14 @@ toml = ["tomli"] [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -112,62 +132,62 @@ grpcio = "*" [[package]] name = "grpcio" -version = "1.64.1" +version = "1.66.0" description = "HTTP/2-based RPC framework" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, - {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, - {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, - {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, - {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, - {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, - {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, - {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, - {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, - {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, - {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, - {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, - {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, - {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, - {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, - {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, - {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, - {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, - {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, - {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, - {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, - {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, - {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, - {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, - {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, - {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, - {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, - {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, - {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, - {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, - {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, + {file = "grpcio-1.66.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:ad7256f224437b2c29c2bef98ddd3130454c5b1ab1f0471fc11794cefd4dbd3d"}, + {file = "grpcio-1.66.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:5f4b3357e59dfba9140a51597287297bc638710d6a163f99ee14efc19967a821"}, + {file = "grpcio-1.66.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e8d20308eeae15b3e182f47876f05acbdec1eebd9473a9814a44e46ec4a84c04"}, + {file = "grpcio-1.66.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1eb03524d0f55b965d6c86aa44e5db9e5eaa15f9ed3b164621e652e5b927f4b8"}, + {file = "grpcio-1.66.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37514b68a42e9cf24536345d3cf9e580ffd29117c158b4eeea34625200256067"}, + {file = "grpcio-1.66.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:516fdbc8e156db71a004bc431a6303bca24cfde186babe96dde7bd01e8f0cc70"}, + {file = "grpcio-1.66.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d0439a970d65327de21c299ea0e0c2ad0987cdaf18ba5066621dea5f427f922b"}, + {file = "grpcio-1.66.0-cp310-cp310-win32.whl", hash = "sha256:5f93fc84b72bbc7b84a42f3ca9dc055fa00d2303d9803be011ebf7a10a4eb833"}, + {file = "grpcio-1.66.0-cp310-cp310-win_amd64.whl", hash = "sha256:8fc5c710ddd51b5a0dc36ef1b6663430aa620e0ce029b87b150dafd313b978c3"}, + {file = "grpcio-1.66.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:dd614370e939f9fceeeb2915111a0795271b4c11dfb5fc0f58449bee40c726a5"}, + {file = "grpcio-1.66.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:245b08f9b3c645a6a623f3ed4fa43dcfcd6ad701eb9c32511c1bb7380e8c3d23"}, + {file = "grpcio-1.66.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:aaf30c75cbaf30e561ca45f21eb1f729f0fab3f15c592c1074795ed43e3ff96f"}, + {file = "grpcio-1.66.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49234580a073ce7ac490112f6c67c874cbcb27804c4525978cdb21ba7f3f193c"}, + {file = "grpcio-1.66.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de9e20a0acb709dcfa15a622c91f584f12c9739a79c47999f73435d2b3cc8a3b"}, + {file = "grpcio-1.66.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc008c6afa1e7c8df99bd9154abc4f0470d26b7730ca2521122e99e771baa8c7"}, + {file = "grpcio-1.66.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:50cea8ce2552865b87e3dffbb85eb21e6b98d928621600c0feda2f02449cd837"}, + {file = "grpcio-1.66.0-cp311-cp311-win32.whl", hash = "sha256:508411df1f2b7cfa05d4d7dbf3d576fe4f949cd61c03f3a6f0378c84e3d7b963"}, + {file = "grpcio-1.66.0-cp311-cp311-win_amd64.whl", hash = "sha256:6d586a95c05c82a5354be48bb4537e1accaf2472d8eb7e9086d844cbff934482"}, + {file = "grpcio-1.66.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:5ea27f4ce8c0daccfdd2c7961e6ba404b6599f47c948415c4cca5728739107a3"}, + {file = "grpcio-1.66.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:296a45ea835e12a1cc35ab0c57e455346c272af7b0d178e29c67742167262b4c"}, + {file = "grpcio-1.66.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:e36fa838ac1d6c87198ca149cbfcc92e1af06bb8c8cd852622f8e58f33ea3324"}, + {file = "grpcio-1.66.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:684a4c07883cbd4ac864f0d08d927267404f5f0c76f31c85f9bbe05f2daae2f2"}, + {file = "grpcio-1.66.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3084e590e857ba7585ae91078e4c9b6ef55aaf1dc343ce26400ba59a146eada"}, + {file = "grpcio-1.66.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:526d4f6ca19f31b25606d5c470ecba55c0b22707b524e4de8987919e8920437d"}, + {file = "grpcio-1.66.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:423ae18637cd99ddcf2e5a6851c61828c49e9b9d022d0442d979b4f230109787"}, + {file = "grpcio-1.66.0-cp312-cp312-win32.whl", hash = "sha256:7bc9d823e05d63a87511fb456dcc48dc0fced86c282bf60229675e7ee7aac1a1"}, + {file = "grpcio-1.66.0-cp312-cp312-win_amd64.whl", hash = "sha256:230cdd696751e7eb1395718cd308234749daa217bb8d128f00357dc4df102558"}, + {file = "grpcio-1.66.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:0f3010bf46b2a01c9e40644cb9ed91b4b8435e5c500a275da5f9f62580e31e80"}, + {file = "grpcio-1.66.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ba18cfdc09312eb2eea6fa0ce5d2eec3cf345ea78f6528b2eaed6432105e0bd0"}, + {file = "grpcio-1.66.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:53d4c6706b49e358a2a33345dbe9b6b3bb047cecd7e8c07ba383bd09349bfef8"}, + {file = "grpcio-1.66.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:643d8d9632a688ae69661e924b862e23c83a3575b24e52917ec5bcc59543d212"}, + {file = "grpcio-1.66.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba60ae3b465b3e85080ae3bfbc36fd0305ae495ab16fcf8022fc7d7a23aac846"}, + {file = "grpcio-1.66.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9d5251578767fe44602688c851c2373b5513048ac84c21a0fe946590a8e7933d"}, + {file = "grpcio-1.66.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5e8140b39f10d7be2263afa2838112de29374c5c740eb0afd99146cb5bdbd990"}, + {file = "grpcio-1.66.0-cp38-cp38-win32.whl", hash = "sha256:5b15ef1b296c4e78f15f64fc65bf8081f8774480ffcac45642f69d9d753d9c6b"}, + {file = "grpcio-1.66.0-cp38-cp38-win_amd64.whl", hash = "sha256:c072f90a1f0409f827ae86266984cba65e89c5831a0726b9fc7f4b5fb940b853"}, + {file = "grpcio-1.66.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:a639d3866bfb5a678b5c0b92cd7ab543033ed8988854290fd86145e71731fd4c"}, + {file = "grpcio-1.66.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ed35bf7da3fb3b1949e32bdf47a8b5ffe0aed11722d948933bd068531cd4682"}, + {file = "grpcio-1.66.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:1c5466222470cb7fbc9cc898af1d48eefd297cb2e2f59af6d4a851c862fa90ac"}, + {file = "grpcio-1.66.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:921b8f7f25d5300d7c6837a1e0639ef145fbdbfb728e0a5db2dbccc9fc0fd891"}, + {file = "grpcio-1.66.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3f6feb0dc8456d025e566709f7dd02885add99bedaac50229013069242a1bfd"}, + {file = "grpcio-1.66.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748452dbd5a047475d5413bdef08b0b9ceb2c0c0e249d4ee905a5fb82c6328dc"}, + {file = "grpcio-1.66.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:832945e64176520520317b50d64ec7d79924429528d5747669b52d0bf2c7bd78"}, + {file = "grpcio-1.66.0-cp39-cp39-win32.whl", hash = "sha256:8096a922eb91bc97c839f675c3efa1257c6ef181ae1b25d3fb97f2cae4c57c01"}, + {file = "grpcio-1.66.0-cp39-cp39-win_amd64.whl", hash = "sha256:375b58892301a5fc6ca7d7ff689c9dc9d00895f5d560604ace9f4f0573013c63"}, + {file = "grpcio-1.66.0.tar.gz", hash = "sha256:c1ea4c528e7db6660718e4165fd1b5ac24b79a70c870a7bc0b7bdb9babab7c1e"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.64.1)"] +protobuf = ["grpcio-tools (>=1.66.0)"] [[package]] name = "iniconfig" @@ -183,45 +203,45 @@ files = [ [[package]] name = "mypy" -version = "1.10.0" +version = "1.11.1" description = "Optional static typing for Python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, - {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, - {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, - {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, - {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, - {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, - {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, - {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, - {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, - {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, - {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, - {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, - {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, - {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, - {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, - {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, - {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, - {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, - {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, + {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, + {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, + {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, + {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, + {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, + {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, + {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, + {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, + {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, + {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, + {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, + {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, + {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, + {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, + {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, + {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, + {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" +typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] @@ -271,35 +291,35 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "protobuf" -version = "5.27.1" +version = "5.27.3" description = "" category = "main" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.27.1-cp310-abi3-win32.whl", hash = "sha256:3adc15ec0ff35c5b2d0992f9345b04a540c1e73bfee3ff1643db43cc1d734333"}, - {file = "protobuf-5.27.1-cp310-abi3-win_amd64.whl", hash = "sha256:25236b69ab4ce1bec413fd4b68a15ef8141794427e0b4dc173e9d5d9dffc3bcd"}, - {file = "protobuf-5.27.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4e38fc29d7df32e01a41cf118b5a968b1efd46b9c41ff515234e794011c78b17"}, - {file = "protobuf-5.27.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:917ed03c3eb8a2d51c3496359f5b53b4e4b7e40edfbdd3d3f34336e0eef6825a"}, - {file = "protobuf-5.27.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:ee52874a9e69a30271649be88ecbe69d374232e8fd0b4e4b0aaaa87f429f1631"}, - {file = "protobuf-5.27.1-cp38-cp38-win32.whl", hash = "sha256:7a97b9c5aed86b9ca289eb5148df6c208ab5bb6906930590961e08f097258107"}, - {file = "protobuf-5.27.1-cp38-cp38-win_amd64.whl", hash = "sha256:f6abd0f69968792da7460d3c2cfa7d94fd74e1c21df321eb6345b963f9ec3d8d"}, - {file = "protobuf-5.27.1-cp39-cp39-win32.whl", hash = "sha256:dfddb7537f789002cc4eb00752c92e67885badcc7005566f2c5de9d969d3282d"}, - {file = "protobuf-5.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:39309898b912ca6febb0084ea912e976482834f401be35840a008da12d189340"}, - {file = "protobuf-5.27.1-py3-none-any.whl", hash = "sha256:4ac7249a1530a2ed50e24201d6630125ced04b30619262f06224616e0030b6cf"}, - {file = "protobuf-5.27.1.tar.gz", hash = "sha256:df5e5b8e39b7d1c25b186ffdf9f44f40f810bbcc9d2b71d9d3156fee5a9adf15"}, + {file = "protobuf-5.27.3-cp310-abi3-win32.whl", hash = "sha256:dcb307cd4ef8fec0cf52cb9105a03d06fbb5275ce6d84a6ae33bc6cf84e0a07b"}, + {file = "protobuf-5.27.3-cp310-abi3-win_amd64.whl", hash = "sha256:16ddf3f8c6c41e1e803da7abea17b1793a97ef079a912e42351eabb19b2cffe7"}, + {file = "protobuf-5.27.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:68248c60d53f6168f565a8c76dc58ba4fa2ade31c2d1ebdae6d80f969cdc2d4f"}, + {file = "protobuf-5.27.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b8a994fb3d1c11156e7d1e427186662b64694a62b55936b2b9348f0a7c6625ce"}, + {file = "protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:a55c48f2a2092d8e213bd143474df33a6ae751b781dd1d1f4d953c128a415b25"}, + {file = "protobuf-5.27.3-cp38-cp38-win32.whl", hash = "sha256:043853dcb55cc262bf2e116215ad43fa0859caab79bb0b2d31b708f128ece035"}, + {file = "protobuf-5.27.3-cp38-cp38-win_amd64.whl", hash = "sha256:c2a105c24f08b1e53d6c7ffe69cb09d0031512f0b72f812dd4005b8112dbe91e"}, + {file = "protobuf-5.27.3-cp39-cp39-win32.whl", hash = "sha256:c84eee2c71ed83704f1afbf1a85c3171eab0fd1ade3b399b3fad0884cbcca8bf"}, + {file = "protobuf-5.27.3-cp39-cp39-win_amd64.whl", hash = "sha256:af7c0b7cfbbb649ad26132e53faa348580f844d9ca46fd3ec7ca48a1ea5db8a1"}, + {file = "protobuf-5.27.3-py3-none-any.whl", hash = "sha256:8572c6533e544ebf6899c360e91d6bcbbee2549251643d32c52cf8a5de295ba5"}, + {file = "protobuf-5.27.3.tar.gz", hash = "sha256:82460903e640f2b7e34ee81a947fdaad89de796d324bcbc38ff5430bcdead82c"}, ] [[package]] name = "pytest" -version = "8.2.2" +version = "8.3.2" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, - {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [package.dependencies] @@ -307,7 +327,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] @@ -334,29 +354,29 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "ruff" -version = "0.4.9" +version = "0.4.10" description = "An extremely fast Python linter and code formatter, written in Rust." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.4.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b262ed08d036ebe162123170b35703aaf9daffecb698cd367a8d585157732991"}, - {file = "ruff-0.4.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:98ec2775fd2d856dc405635e5ee4ff177920f2141b8e2d9eb5bd6efd50e80317"}, - {file = "ruff-0.4.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4555056049d46d8a381f746680db1c46e67ac3b00d714606304077682832998e"}, - {file = "ruff-0.4.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e91175fbe48f8a2174c9aad70438fe9cb0a5732c4159b2a10a3565fea2d94cde"}, - {file = "ruff-0.4.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8e7b95673f22e0efd3571fb5b0cf71a5eaaa3cc8a776584f3b2cc878e46bff"}, - {file = "ruff-0.4.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2d45ddc6d82e1190ea737341326ecbc9a61447ba331b0a8962869fcada758505"}, - {file = "ruff-0.4.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78de3fdb95c4af084087628132336772b1c5044f6e710739d440fc0bccf4d321"}, - {file = "ruff-0.4.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06b60f91bfa5514bb689b500a25ba48e897d18fea14dce14b48a0c40d1635893"}, - {file = "ruff-0.4.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88bffe9c6a454bf8529f9ab9091c99490578a593cc9f9822b7fc065ee0712a06"}, - {file = "ruff-0.4.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:673bddb893f21ab47a8334c8e0ea7fd6598ecc8e698da75bcd12a7b9d0a3206e"}, - {file = "ruff-0.4.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8c1aff58c31948cc66d0b22951aa19edb5af0a3af40c936340cd32a8b1ab7438"}, - {file = "ruff-0.4.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:784d3ec9bd6493c3b720a0b76f741e6c2d7d44f6b2be87f5eef1ae8cc1d54c84"}, - {file = "ruff-0.4.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:732dd550bfa5d85af8c3c6cbc47ba5b67c6aed8a89e2f011b908fc88f87649db"}, - {file = "ruff-0.4.9-py3-none-win32.whl", hash = "sha256:8064590fd1a50dcf4909c268b0e7c2498253273309ad3d97e4a752bb9df4f521"}, - {file = "ruff-0.4.9-py3-none-win_amd64.whl", hash = "sha256:e0a22c4157e53d006530c902107c7f550b9233e9706313ab57b892d7197d8e52"}, - {file = "ruff-0.4.9-py3-none-win_arm64.whl", hash = "sha256:5d5460f789ccf4efd43f265a58538a2c24dbce15dbf560676e430375f20a8198"}, - {file = "ruff-0.4.9.tar.gz", hash = "sha256:f1cb0828ac9533ba0135d148d214e284711ede33640465e706772645483427e3"}, + {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, + {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1dd1681dfa90a41b8376a61af05cc4dc5ff32c8f14f5fe20dba9ff5deb80cd6"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c75c53bb79d71310dc79fb69eb4902fba804a81f374bc86a9b117a8d077a1784"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18238c80ee3d9100d3535d8eb15a59c4a0753b45cc55f8bf38f38d6a597b9739"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d8f71885bce242da344989cae08e263de29752f094233f932d4f5cfb4ef36a81"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:330421543bd3222cdfec481e8ff3460e8702ed1e58b494cf9d9e4bf90db52b9d"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e9b6fb3a37b772628415b00c4fc892f97954275394ed611056a4b8a2631365e"}, + {file = "ruff-0.4.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f54c481b39a762d48f64d97351048e842861c6662d63ec599f67d515cb417f6"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:67fe086b433b965c22de0b4259ddfe6fa541c95bf418499bedb9ad5fb8d1c631"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:acfaaab59543382085f9eb51f8e87bac26bf96b164839955f244d07125a982ef"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:3cea07079962b2941244191569cf3a05541477286f5cafea638cd3aa94b56815"}, + {file = "ruff-0.4.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:338a64ef0748f8c3a80d7f05785930f7965d71ca260904a9321d13be24b79695"}, + {file = "ruff-0.4.10-py3-none-win32.whl", hash = "sha256:ffe3cd2f89cb54561c62e5fa20e8f182c0a444934bf430515a4b422f1ab7b7ca"}, + {file = "ruff-0.4.10-py3-none-win_amd64.whl", hash = "sha256:67f67cef43c55ffc8cc59e8e0b97e9e60b4837c8f21e8ab5ffd5d66e196e25f7"}, + {file = "ruff-0.4.10-py3-none-win_arm64.whl", hash = "sha256:dd1fcee327c20addac7916ca4e2653fbbf2e8388d8a6477ce5b4e986b68ae6c0"}, + {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, ] [[package]] @@ -373,14 +393,14 @@ files = [ [[package]] name = "types-protobuf" -version = "5.26.0.20240422" +version = "5.27.0.20240626" description = "Typing stubs for protobuf" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "types-protobuf-5.26.0.20240422.tar.gz", hash = "sha256:e6074178109f97efe9f0b20a035ba61d7c3b03e867eb47d254d2b2ab6a805e36"}, - {file = "types_protobuf-5.26.0.20240422-py3-none-any.whl", hash = "sha256:e4dc2554d342501d5aebc3c71203868b51118340e105fc190e3a64ca1be43831"}, + {file = "types-protobuf-5.27.0.20240626.tar.gz", hash = "sha256:683ba14043bade6785e3f937a7498f243b37881a91ac8d81b9202ecf8b191e9c"}, + {file = "types_protobuf-5.27.0.20240626-py3-none-any.whl", hash = "sha256:688e8f7e8d9295db26bc560df01fb731b27a25b77cbe4c1ce945647f7024f5c1"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 2f49dc7..3a76fd8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,20 @@ build-backend = "poetry.core.masonry.api" include = ["src/**/*.py", "tests/**/*.py"] line-length = 120 +[tool.ruff.lint] +select = ["ALL"] +ignore = [ + "ANN", # because we use mypy + "D", # TODO: add docstrings to public code + "FA", # TODO: consider should we use __annotations__ + "TD", # no task tracking + "FIX", # TODO: consider enable it against new code on pull requests + "COM812", # because ruff format suggests to skip it + "ISC001", # because ruff format suggests to skip it + "RET505", # clashes with mypy exhaustiveness check + "S101", # allow asserts for tests checks and mypy help +] + [tool.mypy] files = ["src", "tests"] strict = true diff --git a/src/pyprotostuben/codegen/abc.py b/src/pyprotostuben/codegen/abc.py index 90f75eb..9f3e0fe 100644 --- a/src/pyprotostuben/codegen/abc.py +++ b/src/pyprotostuben/codegen/abc.py @@ -16,4 +16,4 @@ def run(self, request: CodeGeneratorRequest) -> CodeGeneratorResponse: class ProtoFileGenerator(metaclass=abc.ABCMeta): @abc.abstractmethod def run(self, file: ProtoFile) -> t.Iterable[t.Tuple[ProtoFile, Path, str]]: - raise NotImplementedError() + raise NotImplementedError diff --git a/src/pyprotostuben/codegen/echo.py b/src/pyprotostuben/codegen/echo.py index c230768..194a733 100644 --- a/src/pyprotostuben/codegen/echo.py +++ b/src/pyprotostuben/codegen/echo.py @@ -28,7 +28,8 @@ def run(self, request: CodeGeneratorRequest) -> CodeGeneratorResponse: content = MessageToJson(request, preserving_proto_field_name=True, sort_keys=True) else: - raise ValueError("unsupported format", format_) + msg = "unsupported format" + raise ValueError(msg, format_) log.info("request handled", dest=dest) diff --git a/src/pyprotostuben/codegen/module_ast.py b/src/pyprotostuben/codegen/module_ast.py index 91a4006..a692436 100644 --- a/src/pyprotostuben/codegen/module_ast.py +++ b/src/pyprotostuben/codegen/module_ast.py @@ -14,7 +14,7 @@ class ModuleASTProtoVisitorDecoratorFactory(metaclass=abc.ABCMeta): @abc.abstractmethod def create_proto_visitor_decorator(self, modules: t.MutableMapping[Path, ast.Module]) -> ProtoVisitorDecorator: - raise NotImplementedError() + raise NotImplementedError class ModuleASTBasedProtoFileGenerator(ProtoFileGenerator, LoggerMixin): diff --git a/src/pyprotostuben/codegen/mypy/context.py b/src/pyprotostuben/codegen/mypy/context.py index 4d76e10..1459794 100644 --- a/src/pyprotostuben/codegen/mypy/context.py +++ b/src/pyprotostuben/codegen/mypy/context.py @@ -3,7 +3,7 @@ from dataclasses import dataclass, field from pyprotostuben.protobuf.builder.grpc import GRPCASTBuilder, MethodInfo -from pyprotostuben.protobuf.builder.message import MessageASTBuilder, FieldInfo +from pyprotostuben.protobuf.builder.message import FieldInfo, MessageASTBuilder from pyprotostuben.protobuf.file import ProtoFile from pyprotostuben.python.info import ModuleInfo diff --git a/src/pyprotostuben/codegen/mypy/generator.py b/src/pyprotostuben/codegen/mypy/generator.py index d8f26d2..3eaeb36 100644 --- a/src/pyprotostuben/codegen/mypy/generator.py +++ b/src/pyprotostuben/codegen/mypy/generator.py @@ -3,27 +3,25 @@ from pathlib import Path from google.protobuf.descriptor_pb2 import ( - FileDescriptorProto, + DescriptorProto, EnumDescriptorProto, EnumValueDescriptorProto, - DescriptorProto, - OneofDescriptorProto, FieldDescriptorProto, - ServiceDescriptorProto, + FileDescriptorProto, MethodDescriptorProto, + OneofDescriptorProto, + ServiceDescriptorProto, ) from pyprotostuben.codegen.module_ast import ModuleASTProtoVisitorDecoratorFactory -from pyprotostuben.codegen.mypy.context import MessageContext, GRPCContext +from pyprotostuben.codegen.mypy.context import GRPCContext, MessageContext from pyprotostuben.logging import LoggerMixin from pyprotostuben.protobuf.builder.grpc import GRPCASTBuilder, MethodInfo -from pyprotostuben.protobuf.builder.grpc_aio import GRPCAioASTBuilder -from pyprotostuben.protobuf.builder.message import MessageASTBuilder, FieldInfo -from pyprotostuben.protobuf.builder.message_immutable import ImmutableMessageASTBuilder +from pyprotostuben.protobuf.builder.message import FieldInfo, MessageASTBuilder from pyprotostuben.protobuf.builder.resolver import ProtoDependencyResolver from pyprotostuben.protobuf.context import CodeGeneratorContext from pyprotostuben.protobuf.file import ProtoFile -from pyprotostuben.protobuf.registry import TypeRegistry, MapEntryInfo +from pyprotostuben.protobuf.registry import MapEntryInfo, TypeRegistry from pyprotostuben.protobuf.visitor.decorator import ProtoVisitorDecorator from pyprotostuben.python.ast_builder import ASTBuilder from pyprotostuben.python.info import ModuleInfo @@ -69,28 +67,21 @@ def create_file_grpc_context(self, file: ProtoFile) -> GRPCContext: def __create_message_ast_builder(self, module: ModuleInfo, deps: t.Set[ModuleInfo]) -> MessageASTBuilder: inner = ASTBuilder(ProtoDependencyResolver(module, deps)) - mode = self.__context.params.get_raw_by_name("messages", "immutable") - if mode == "mutable": - return MessageASTBuilder(inner) - - elif mode == "immutable": - return ImmutableMessageASTBuilder(inner) - - else: - raise ValueError("unsupported message mode", mode) + return MessageASTBuilder( + inner, + mutable=self.__context.params.has_flag("message-mutable"), + all_init_args_optional=self.__context.params.has_flag("message-all-init-args-optional"), + ) def __create_grpc_ast_builder(self, module: ModuleInfo, deps: t.Set[ModuleInfo]) -> GRPCASTBuilder: inner = ASTBuilder(ProtoDependencyResolver(module, deps)) - mode = self.__context.params.get_raw_by_name("grpc", "aio") - if mode == "aio": - return GRPCAioASTBuilder(inner) - - elif mode == "default": - return GRPCASTBuilder(inner) - - else: - raise ValueError("unsupported grpc mode", mode) + return GRPCASTBuilder( + inner, + is_sync=self.__context.params.has_flag("grpc-sync"), + skip_servicer=self.__context.params.has_flag("grpc-skip-servicer"), + skip_stub=self.__context.params.has_flag("grpc-skip-stub"), + ) class MypyStubASTGenerator(ProtoVisitorDecorator, LoggerMixin): @@ -112,7 +103,7 @@ def enter_file_descriptor_proto(self, proto: FileDescriptorProto) -> None: self.__messages.put(self.__factory.create_file_message_context(file)) self.__grpcs.put(self.__factory.create_file_grpc_context(file)) - def leave_file_descriptor_proto(self, proto: FileDescriptorProto) -> None: + def leave_file_descriptor_proto(self, _: FileDescriptorProto) -> None: message = self.__messages.pop() grpc = self.__grpcs.pop() @@ -125,7 +116,7 @@ def leave_file_descriptor_proto(self, proto: FileDescriptorProto) -> None: grpc.nested, ) - def enter_enum_descriptor_proto(self, proto: EnumDescriptorProto) -> None: + def enter_enum_descriptor_proto(self, _: EnumDescriptorProto) -> None: parent = self.__messages.get_last() self.__messages.put(parent.sub()) @@ -146,7 +137,7 @@ def leave_enum_value_descriptor_proto(self, proto: EnumValueDescriptorProto) -> parent.nested.append(builder.build_protobuf_enum_value_def(proto.name, proto.number)) - def enter_descriptor_proto(self, proto: DescriptorProto) -> None: + def enter_descriptor_proto(self, _: DescriptorProto) -> None: parent = self.__messages.get_last() self.__messages.put(parent.sub()) @@ -165,7 +156,7 @@ def leave_descriptor_proto(self, proto: DescriptorProto) -> None: name=proto.name, fields=message.fields, nested=message.nested, - ) + ), ) def enter_oneof_descriptor_proto(self, proto: OneofDescriptorProto) -> None: @@ -187,7 +178,7 @@ def leave_field_descriptor_proto(self, proto: FieldDescriptorProto) -> None: annotation = builder.build_protobuf_type_ref(info) if not isinstance(info, MapEntryInfo) and proto.label == FieldDescriptorProto.Label.LABEL_REPEATED: - annotation = builder.build_protobuf_repeated_ref(info, annotation) + annotation = builder.build_protobuf_repeated_ref(annotation) message.fields.append( FieldInfo( @@ -195,15 +186,14 @@ def leave_field_descriptor_proto(self, proto: FieldDescriptorProto) -> None: annotation=annotation, optional=is_optional, default=None, - # TODO: support default value. - # default=proto.default_value, + # TODO: support proto.default_value oneof_group=message.oneof_groups[proto.oneof_index] if not is_optional and proto.HasField("oneof_index") else None, - ) + ), ) - def enter_service_descriptor_proto(self, proto: ServiceDescriptorProto) -> None: + def enter_service_descriptor_proto(self, _: ServiceDescriptorProto) -> None: parent = self.__grpcs.get_last() self.__grpcs.put(parent.sub()) @@ -230,5 +220,5 @@ def leave_method_descriptor_proto(self, proto: MethodDescriptorProto) -> None: client_streaming=proto.client_streaming, server_output=builder.build_grpc_message_ref(self.__registry.resolve_proto_method_server_output(proto)), server_streaming=proto.server_streaming, - ) + ), ) diff --git a/src/pyprotostuben/codegen/mypy/plugin.py b/src/pyprotostuben/codegen/mypy/plugin.py index 220c5c9..c5cbf14 100644 --- a/src/pyprotostuben/codegen/mypy/plugin.py +++ b/src/pyprotostuben/codegen/mypy/plugin.py @@ -7,10 +7,10 @@ from pyprotostuben.codegen.abc import ProtocPlugin, ProtoFileGenerator from pyprotostuben.codegen.module_ast import ModuleASTBasedProtoFileGenerator from pyprotostuben.codegen.mypy.generator import MypyStubASTGeneratorFactory -from pyprotostuben.logging import LoggerMixin, Logger +from pyprotostuben.logging import Logger, LoggerMixin from pyprotostuben.pool.abc import Pool -from pyprotostuben.pool.process import SingleProcessPool, MultiProcessPool -from pyprotostuben.protobuf.context import ContextBuilder, CodeGeneratorContext +from pyprotostuben.pool.process import MultiProcessPool, SingleProcessPool +from pyprotostuben.protobuf.context import CodeGeneratorContext, ContextBuilder # TODO: support such options (disabled by default): diff --git a/src/pyprotostuben/codegen/run.py b/src/pyprotostuben/codegen/run.py index 490294f..680ffb9 100644 --- a/src/pyprotostuben/codegen/run.py +++ b/src/pyprotostuben/codegen/run.py @@ -22,7 +22,7 @@ def run_codegen( response = gen.run(request) except Exception as err: - log.warning("generator error occurred", exc_info=err) + log.exception("generator error occurred", exc_info=err) response = CodeGeneratorResponse( error=repr(err), diff --git a/src/pyprotostuben/logging.py b/src/pyprotostuben/logging.py index 61bc83b..18bd426 100644 --- a/src/pyprotostuben/logging.py +++ b/src/pyprotostuben/logging.py @@ -4,10 +4,12 @@ import re import typing as t from functools import cached_property -from logging import LogRecord, LoggerAdapter, getLogger, Formatter +from logging import Formatter, LoggerAdapter, LogRecord, getLogger from logging.config import dictConfig from pathlib import Path +from typing_extensions import Self + class Logger( LoggerAdapter, # type: ignore[type-arg] @@ -25,7 +27,8 @@ def configure(cls) -> None: }, "verbose": { "()": SoftFormatter, - "fmt": "%(asctime)-25s %(levelname)-10s %(process)-10s [%(name)s %(self)s %(funcName)s] %(message)s %(details)s", + "fmt": "%(asctime)-25s %(levelname)-10s %(process)-10s [%(name)s %(self)s %(funcName)s] " + "%(message)s %(details)s", }, }, "handlers": { @@ -50,7 +53,12 @@ def configure(cls) -> None: # noinspection PyMethodParameters @classmethod - def get(__cls, __name: str, **kwargs: object) -> "Logger": + def get( + # allow callee to pass custom `cls` kwarg + __cls, # noqa: N804 + __name: str, + **kwargs: object, + ) -> "Logger": return __cls(getLogger(__name), kwargs) def process( @@ -61,18 +69,21 @@ def process( exc_info = kwargs.pop("exc_info", None) return msg, { "exc_info": exc_info, - "extra": {**self.extra, "details": {**self.details, **kwargs}}, + "extra": _merge_mappings(self.extra, {"details": _merge_mappings(self.details, kwargs)}), } @ft.cached_property def details(self) -> t.Mapping[str, object]: + if self.extra is None: + return {} + value = self.extra.get("details") return value if isinstance(value, dict) else {} - def bind(self, **kwargs: object) -> "Logger": - return self.__class__(self.logger, {**self.extra, **kwargs}) if kwargs else self + def bind(self, **kwargs: object) -> Self: + return self.__class__(self.logger, _merge_mappings(self.extra, kwargs)) if kwargs else self - def bind_details(self, **kwargs: object) -> "Logger": + def bind_details(self, **kwargs: object) -> Self: return self.bind(details=kwargs) if kwargs else self @@ -93,6 +104,19 @@ def __init__( self.__defaults = defaults or {} self.__fields: t.Sequence[str] = re.findall(r"%\((?P\w+)\)", fmt) if fmt is not None else [] - def formatMessage(self, record: LogRecord) -> str: + def formatMessage(self, record: LogRecord) -> str: # noqa: N802 assert self._fmt is not None return self._fmt % {field: getattr(record, field, self.__defaults.get(field, "")) for field in self.__fields} + + +def _merge_mappings( + left: t.Optional[t.Mapping[str, object]], + right: t.Optional[t.Mapping[str, object]], +) -> t.Mapping[str, object]: + if not right: + return left or {} + + if not left: + return right or {} + + return {**left, **right} diff --git a/src/pyprotostuben/pool/abc.py b/src/pyprotostuben/pool/abc.py index 5e8e8ce..0576bbf 100644 --- a/src/pyprotostuben/pool/abc.py +++ b/src/pyprotostuben/pool/abc.py @@ -6,5 +6,6 @@ class Pool(metaclass=abc.ABCMeta): + @abc.abstractmethod def run(self, func: t.Callable[[U_contra], V_co], args: t.Iterable[U_contra]) -> t.Iterable[V_co]: - raise NotImplementedError() + raise NotImplementedError diff --git a/src/pyprotostuben/protobuf/builder/grpc.py b/src/pyprotostuben/protobuf/builder/grpc.py index 7a9574a..74e6ac3 100644 --- a/src/pyprotostuben/protobuf/builder/grpc.py +++ b/src/pyprotostuben/protobuf/builder/grpc.py @@ -5,7 +5,7 @@ from pyprotostuben.protobuf.registry import MessageInfo from pyprotostuben.python.ast_builder import ASTBuilder -from pyprotostuben.python.info import ModuleInfo, TypeInfo +from pyprotostuben.python.info import ModuleInfo, PackageInfo, TypeInfo @dataclass(frozen=True) @@ -18,65 +18,76 @@ class MethodInfo: class GRPCASTBuilder: - def __init__(self, inner: ASTBuilder) -> None: + def __init__( + self, + inner: ASTBuilder, + *, + is_sync: bool, + skip_servicer: bool, + skip_stub: bool, + ) -> None: self.inner = inner + self.__is_sync = is_sync + self.__skip_servicer = skip_servicer + self.__skip_stub = skip_stub + + self.__base_grpc_module = ( + ModuleInfo(None, "grpc") if self.__is_sync else ModuleInfo(PackageInfo(None, "grpc"), "aio") + ) @cached_property def grpc_streaming_generic(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "typing"), "Iterator") - - @property - def is_grpc_servicer_async(self) -> bool: - return False - - @property - def is_grpc_stub_async(self) -> bool: - return False + return TypeInfo.build(ModuleInfo(None, "typing"), "Iterator" if self.__is_sync else "AsyncIterator") @cached_property def grpc_server_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "grpc"), "Server") + return TypeInfo.build(self.__base_grpc_module, "Server") @cached_property def grpc_servicer_context_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "grpc"), "ServicerContext") + return TypeInfo.build(self.__base_grpc_module, "ServicerContext") @cached_property def grpc_channel_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "grpc"), "Channel") + return TypeInfo.build(self.__base_grpc_module, "Channel") @cached_property def grpc_metadata_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "grpc"), "MetadataType") + return TypeInfo.build(self.__base_grpc_module, "MetadataType") @cached_property def grpc_call_credentials_ref(self) -> TypeInfo: + # always grpc module return TypeInfo.build(ModuleInfo(None, "grpc"), "CallCredentials") @cached_property def grpc_compression_ref(self) -> TypeInfo: + # always grpc module return TypeInfo.build(ModuleInfo(None, "grpc"), "Compression") @cached_property def grpc_unary_unary_call_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "grpc"), "UnaryUnaryCall") + return TypeInfo.build(self.__base_grpc_module, "UnaryUnaryCall") @cached_property def grpc_unary_stream_call_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "grpc"), "UnaryStreamCall") + return TypeInfo.build(self.__base_grpc_module, "UnaryStreamCall") @cached_property def grpc_stream_unary_call_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "grpc"), "StreamUnaryCall") + return TypeInfo.build(self.__base_grpc_module, "StreamUnaryCall") @cached_property def grpc_stream_stream_call_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "grpc"), "StreamStreamCall") + return TypeInfo.build(self.__base_grpc_module, "StreamStreamCall") def build_grpc_module(self, deps: t.Collection[ModuleInfo], body: t.Sequence[ast.stmt]) -> ast.Module: return self.inner.build_module(deps, body) if body else self.inner.build_module([], []) def build_grpc_servicer_defs(self, name: str, methods: t.Sequence[MethodInfo]) -> t.Sequence[ast.stmt]: + if self.__skip_servicer: + return [] + return [ self.inner.build_abstract_class_def( name=name, @@ -86,6 +97,9 @@ def build_grpc_servicer_defs(self, name: str, methods: t.Sequence[MethodInfo]) - ] def build_grpc_stub_defs(self, name: str, methods: t.Sequence[MethodInfo]) -> t.Sequence[ast.stmt]: + if self.__skip_stub: + return [] + return [ self.inner.build_class_def( name=name, @@ -122,7 +136,7 @@ def build_grpc_servicer_method_def(self, info: MethodInfo) -> ast.stmt: ), ], returns=response, - is_async=self.is_grpc_servicer_async, + is_async=not self.__is_sync, ) def build_grpc_servicer_method_request_response_refs(self, info: MethodInfo) -> t.Tuple[ast.expr, ast.expr]: @@ -132,26 +146,26 @@ def build_grpc_servicer_method_request_response_refs(self, info: MethodInfo) -> info.server_output, ) - elif not info.client_streaming and info.server_streaming: + if not info.client_streaming and info.server_streaming: return ( info.client_input, self.build_grpc_streaming_ref(info.server_output), ) - elif info.client_streaming and not info.server_streaming: + if info.client_streaming and not info.server_streaming: return ( self.build_grpc_streaming_ref(info.client_input), info.server_output, ) - elif info.client_streaming and info.server_streaming: + if info.client_streaming and info.server_streaming: return ( self.build_grpc_streaming_ref(info.client_input), self.build_grpc_streaming_ref(info.server_output), ) - else: - raise ValueError("invalid method streaming options", info) + msg = "invalid method streaming options" + raise ValueError(msg, info) def build_grpc_servicer_registrator_def(self, name: str, servicer: ast.expr) -> ast.stmt: return self.inner.build_func_stub( @@ -213,7 +227,7 @@ def build_grpc_stub_method_def(self, info: MethodInfo) -> ast.stmt: ), ], returns=response, - is_async=self.is_grpc_stub_async, + is_async=not self.__is_sync, ) def build_grpc_stub_method_request_response_refs(self, info: MethodInfo) -> t.Tuple[ast.expr, ast.expr]: @@ -223,23 +237,23 @@ def build_grpc_stub_method_request_response_refs(self, info: MethodInfo) -> t.Tu self.inner.build_generic_ref(self.grpc_unary_unary_call_ref, info.client_input, info.server_output), ) - elif not info.client_streaming and info.server_streaming: + if not info.client_streaming and info.server_streaming: return ( info.client_input, self.inner.build_generic_ref(self.grpc_unary_stream_call_ref, info.client_input, info.server_output), ) - elif info.client_streaming and not info.server_streaming: + if info.client_streaming and not info.server_streaming: return ( self.build_grpc_streaming_ref(info.client_input), self.inner.build_generic_ref(self.grpc_stream_unary_call_ref, info.client_input, info.server_output), ) - elif info.client_streaming and info.server_streaming: + if info.client_streaming and info.server_streaming: return ( self.build_grpc_streaming_ref(info.client_input), self.inner.build_generic_ref(self.grpc_stream_stream_call_ref, info.client_input, info.server_output), ) - else: - raise ValueError("invalid method streaming options", info) + msg = "invalid method streaming options" + raise ValueError(msg, info) diff --git a/src/pyprotostuben/protobuf/builder/grpc_aio.py b/src/pyprotostuben/protobuf/builder/grpc_aio.py deleted file mode 100644 index 5591202..0000000 --- a/src/pyprotostuben/protobuf/builder/grpc_aio.py +++ /dev/null @@ -1,50 +0,0 @@ -from functools import cached_property - -from pyprotostuben.protobuf.builder.grpc import GRPCASTBuilder -from pyprotostuben.python.info import ModuleInfo, PackageInfo, TypeInfo - - -class GRPCAioASTBuilder(GRPCASTBuilder): - @cached_property - def grpc_streaming_generic(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(None, "typing"), "AsyncIterator") - - @property - def is_grpc_servicer_async(self) -> bool: - return True - - @property - def is_grpc_stub_async(self) -> bool: - return True - - @cached_property - def grpc_server_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(PackageInfo(None, "grpc"), "aio"), "Server") - - @cached_property - def grpc_servicer_context_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(PackageInfo(None, "grpc"), "aio"), "ServicerContext") - - @cached_property - def grpc_channel_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(PackageInfo(None, "grpc"), "aio"), "Channel") - - @cached_property - def grpc_metadata_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(PackageInfo(None, "grpc"), "aio"), "MetadataType") - - @cached_property - def grpc_unary_unary_call_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(PackageInfo(None, "grpc"), "aio"), "UnaryUnaryCall") - - @cached_property - def grpc_unary_stream_call_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(PackageInfo(None, "grpc"), "aio"), "UnaryStreamCall") - - @cached_property - def grpc_stream_unary_call_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(PackageInfo(None, "grpc"), "aio"), "StreamUnaryCall") - - @cached_property - def grpc_stream_stream_call_ref(self) -> TypeInfo: - return TypeInfo.build(ModuleInfo(PackageInfo(None, "grpc"), "aio"), "StreamStreamCall") diff --git a/src/pyprotostuben/protobuf/builder/message.py b/src/pyprotostuben/protobuf/builder/message.py index 3eee2d0..564db96 100644 --- a/src/pyprotostuben/protobuf/builder/message.py +++ b/src/pyprotostuben/protobuf/builder/message.py @@ -3,8 +3,9 @@ from collections import defaultdict from dataclasses import dataclass from functools import cached_property +from itertools import chain -from pyprotostuben.protobuf.registry import ProtoInfo, MapEntryInfo +from pyprotostuben.protobuf.registry import MapEntryInfo, ProtoInfo from pyprotostuben.python.ast_builder import ASTBuilder, TypeRef from pyprotostuben.python.info import ModuleInfo, PackageInfo, TypeInfo @@ -19,8 +20,16 @@ class FieldInfo: class MessageASTBuilder: - def __init__(self, inner: ASTBuilder) -> None: + def __init__( + self, + inner: ASTBuilder, + *, + mutable: bool, + all_init_args_optional: bool, + ) -> None: self.inner = inner + self.__mutable = mutable + self.__all_init_args_optional = all_init_args_optional @cached_property def protobuf_message_ref(self) -> TypeInfo: @@ -28,15 +37,17 @@ def protobuf_message_ref(self) -> TypeInfo: @cached_property def protobuf_enum_ref(self) -> TypeInfo: + # TODO: actual type is not fully compatible with IntEnum. + # See: https://github.com/nipunn1313/mypy-protobuf/issues/484 return TypeInfo.build(ModuleInfo(None, "enum"), "IntEnum") @cached_property def protobuf_field_repeated_ref(self) -> TypeInfo: - return TypeInfo.build(self.inner.typing_module, "MutableSequence") + return TypeInfo.build(self.inner.typing_module, "MutableSequence" if self.__mutable else "Sequence") @cached_property def protobuf_map_entry_ref(self) -> TypeInfo: - return TypeInfo.build(self.inner.typing_module, "MutableMapping") + return TypeInfo.build(self.inner.typing_module, "MutableMapping" if self.__mutable else "Mapping") def build_protobuf_message_module(self, deps: t.Collection[ModuleInfo], body: t.Sequence[ast.stmt]) -> ast.Module: return self.inner.build_module(deps, body) if body else self.inner.build_module([], []) @@ -65,7 +76,7 @@ def build_protobuf_type_ref(self, info: ProtoInfo) -> ast.expr: return self.inner.build_ref(info) - def build_protobuf_repeated_ref(self, info: ProtoInfo, inner: TypeRef) -> ast.expr: + def build_protobuf_repeated_ref(self, inner: TypeRef) -> ast.expr: return self.inner.build_generic_ref(self.protobuf_field_repeated_ref, inner) def build_protobuf_enum_def( @@ -83,7 +94,8 @@ def build_protobuf_enum_value_def(self, name: str, value: object) -> ast.stmt: return ast.Assign( targets=[ast.Name(id=name)], value=ast.Constant(value=value), - lineno=None, + # Seems like it is allowed to pass `None`, but ast typing says it isn't + lineno=t.cast(int, None), ) def build_protobuf_message_init_stub(self, fields: t.Sequence[FieldInfo]) -> ast.stmt: @@ -91,22 +103,31 @@ def build_protobuf_message_init_stub(self, fields: t.Sequence[FieldInfo]) -> ast [ self.inner.build_kw_arg( name=field.name, - annotation=self.inner.build_optional_ref(field.annotation), - default=field.default if field.default is not None else self.inner.build_none_ref(), + annotation=self.inner.build_optional_ref(field.annotation) + if self.__all_init_args_optional or field.optional or field.oneof_group is not None + else field.annotation, + default=field.default + if field.default is not None + else self.inner.build_none_ref() + if self.__all_init_args_optional or field.optional or field.oneof_group is not None + else None, ) for field in fields - ] + ], ) def build_protobuf_message_field_stubs(self, fields: t.Sequence[FieldInfo]) -> t.Sequence[ast.stmt]: - return [ - self.inner.build_attr_stub( - name=field.name, - annotation=field.annotation, - default=field.default, - ) - for field in fields - ] + return list( + chain.from_iterable( + ( + self.inner.build_property_getter_stub(name=field.name, annotation=field.annotation), + self.inner.build_property_setter_stub(name=field.name, annotation=field.annotation), + ) + if self.__mutable + else (self.inner.build_property_getter_stub(name=field.name, annotation=field.annotation),) + for field in fields + ), + ) def build_protobuf_message_has_field_method_stub(self, fields: t.Sequence[FieldInfo]) -> ast.stmt: optional_field_names = [ast.Constant(value=field.name) for field in fields if field.optional] @@ -117,7 +138,7 @@ def build_protobuf_message_has_field_method_stub(self, fields: t.Sequence[FieldI self.inner.build_pos_arg( name="field_name", annotation=self.inner.build_literal_ref(*optional_field_names), - ) + ), ], returns=self.inner.build_bool_ref() if optional_field_names else self.inner.build_no_return_ref(), ) diff --git a/src/pyprotostuben/protobuf/builder/message_immutable.py b/src/pyprotostuben/protobuf/builder/message_immutable.py deleted file mode 100644 index d6a778e..0000000 --- a/src/pyprotostuben/protobuf/builder/message_immutable.py +++ /dev/null @@ -1,43 +0,0 @@ -import ast -import typing as t -from functools import cached_property - -from pyprotostuben.protobuf.builder.message import MessageASTBuilder, FieldInfo -from pyprotostuben.python.info import TypeInfo - - -class ImmutableMessageASTBuilder(MessageASTBuilder): - @cached_property - def protobuf_field_repeated_ref(self) -> TypeInfo: - return TypeInfo.build(self.inner.typing_module, "Sequence") - - @cached_property - def protobuf_map_entry_ref(self) -> TypeInfo: - return TypeInfo.build(self.inner.typing_module, "Mapping") - - def build_protobuf_message_init_stub(self, fields: t.Sequence[FieldInfo]) -> ast.stmt: - return self.inner.build_init_stub( - [ - self.inner.build_kw_arg( - name=field.name, - annotation=self.inner.build_optional_ref(field.annotation) - if field.optional or field.oneof_group is not None - else field.annotation, - default=field.default - if field.default is not None - else self.inner.build_none_ref() - if field.optional or field.oneof_group is not None - else None, - ) - for field in fields - ] - ) - - def build_protobuf_message_field_stubs(self, fields: t.Sequence[FieldInfo]) -> t.Sequence[ast.stmt]: - return [ - self.inner.build_property_stub( - name=field.name, - returns=field.annotation, - ) - for field in fields - ] diff --git a/src/pyprotostuben/protobuf/context.py b/src/pyprotostuben/protobuf/context.py index 97e1e43..969b124 100644 --- a/src/pyprotostuben/protobuf/context.py +++ b/src/pyprotostuben/protobuf/context.py @@ -3,24 +3,24 @@ from google.protobuf.compiler.plugin_pb2 import CodeGeneratorRequest from google.protobuf.descriptor_pb2 import ( - FileDescriptorProto, + DescriptorProto, EnumDescriptorProto, EnumValueDescriptorProto, - DescriptorProto, - OneofDescriptorProto, + FieldDescriptorProto, + FileDescriptorProto, MethodDescriptorProto, + OneofDescriptorProto, ServiceDescriptorProto, - FieldDescriptorProto, ) from pyprotostuben.logging import LoggerMixin from pyprotostuben.protobuf.file import ProtoFile -from pyprotostuben.protobuf.parser import Parameters, ParameterParser +from pyprotostuben.protobuf.parser import ParameterParser, Parameters from pyprotostuben.protobuf.registry import ( - MessageInfo, EnumInfo, - TypeRegistry, MapEntryPlaceholder, + MessageInfo, + TypeRegistry, ) from pyprotostuben.protobuf.visitor.abc import Proto, visit from pyprotostuben.protobuf.visitor.decorator import ProtoVisitorDecorator @@ -71,21 +71,21 @@ def enter_file_descriptor_proto(self, proto: FileDescriptorProto) -> None: self.__files[proto.name] = file self.__file_stack.put(file) - def leave_file_descriptor_proto(self, proto: FileDescriptorProto) -> None: + def leave_file_descriptor_proto(self, _: FileDescriptorProto) -> None: file = self.__file_stack.pop() self._log.debug("visited", file=file) def enter_enum_descriptor_proto(self, proto: EnumDescriptorProto) -> None: self.__proto_stack.put(proto) - def leave_enum_descriptor_proto(self, proto: EnumDescriptorProto) -> None: + def leave_enum_descriptor_proto(self, _: EnumDescriptorProto) -> None: self.__register_enum() self.__proto_stack.pop() def enter_enum_value_descriptor_proto(self, proto: EnumValueDescriptorProto) -> None: self.__proto_stack.put(proto) - def leave_enum_value_descriptor_proto(self, proto: EnumValueDescriptorProto) -> None: + def leave_enum_value_descriptor_proto(self, _: EnumValueDescriptorProto) -> None: self.__proto_stack.pop() def enter_descriptor_proto(self, proto: DescriptorProto) -> None: @@ -106,25 +106,25 @@ def leave_descriptor_proto(self, proto: DescriptorProto) -> None: def enter_oneof_descriptor_proto(self, proto: OneofDescriptorProto) -> None: self.__proto_stack.put(proto) - def leave_oneof_descriptor_proto(self, proto: OneofDescriptorProto) -> None: + def leave_oneof_descriptor_proto(self, _: OneofDescriptorProto) -> None: self.__proto_stack.pop() def enter_field_descriptor_proto(self, proto: FieldDescriptorProto) -> None: self.__proto_stack.put(proto) - def leave_field_descriptor_proto(self, proto: FieldDescriptorProto) -> None: + def leave_field_descriptor_proto(self, _: FieldDescriptorProto) -> None: self.__proto_stack.pop() def enter_service_descriptor_proto(self, proto: ServiceDescriptorProto) -> None: self.__proto_stack.put(proto) - def leave_service_descriptor_proto(self, proto: ServiceDescriptorProto) -> None: + def leave_service_descriptor_proto(self, _: ServiceDescriptorProto) -> None: self.__proto_stack.pop() def enter_method_descriptor_proto(self, proto: MethodDescriptorProto) -> None: self.__proto_stack.put(proto) - def leave_method_descriptor_proto(self, proto: MethodDescriptorProto) -> None: + def leave_method_descriptor_proto(self, _: MethodDescriptorProto) -> None: self.__proto_stack.pop() def __register_enum(self) -> None: @@ -157,4 +157,5 @@ def __find_field_by_name(self, fields: t.Sequence[FieldDescriptorProto], name: s if field.name == name: return field - raise ValueError("field not found", name, fields) + msg = "field not found" + raise ValueError(msg, name, fields) diff --git a/src/pyprotostuben/protobuf/file.py b/src/pyprotostuben/protobuf/file.py index 8aab392..36df8c1 100644 --- a/src/pyprotostuben/protobuf/file.py +++ b/src/pyprotostuben/protobuf/file.py @@ -4,7 +4,7 @@ from google.protobuf.descriptor_pb2 import FileDescriptorProto -from pyprotostuben.python.info import PackageInfo, ModuleInfo +from pyprotostuben.python.info import ModuleInfo, PackageInfo class ProtoFile: @@ -35,7 +35,6 @@ def pb2_package(self) -> t.Optional[PackageInfo]: return None return PackageInfo.build(*package_parts) - # return PackageInfo.build(*(package_parts[1:] if package_parts[0] == "" else package_parts)) @ft.cached_property def pb2_message(self) -> ModuleInfo: diff --git a/src/pyprotostuben/protobuf/registry.py b/src/pyprotostuben/protobuf/registry.py index 1395bdd..9fc7e42 100644 --- a/src/pyprotostuben/protobuf/registry.py +++ b/src/pyprotostuben/protobuf/registry.py @@ -1,6 +1,5 @@ import typing as t from dataclasses import dataclass -from functools import cache from google.protobuf.descriptor_pb2 import FieldDescriptorProto, MethodDescriptorProto @@ -39,6 +38,22 @@ class MapEntryPlaceholder: value: FieldDescriptorProto +class RegistryError(Exception): + pass + + +class InvalidMessageInfoError(RegistryError): + pass + + +class InvalidMapEntryKeyError(RegistryError): + pass + + +class InvalidMapEntryValueError(RegistryError): + pass + + class TypeRegistry: def __init__( self, @@ -77,21 +92,20 @@ def resolve_proto_method_server_output(self, method: MethodDescriptorProto) -> M def resolve_proto_message(self, ref: str) -> MessageInfo: info = self.__user_types[ref] if not isinstance(info, MessageInfo): - raise ValueError("invalid method input type", info, ref) + raise InvalidMessageInfoError(info, ref) return info - @cache def resolve_proto_map_entry(self, ref: str) -> MapEntryInfo: map_entry = self.__map_entries[ref] key = self.resolve_proto_field(map_entry.key) if not isinstance(key, (ScalarInfo, EnumInfo, MessageInfo)): - raise ValueError("invalid key info", key) + raise InvalidMapEntryKeyError(key, ref) value = self.resolve_proto_field(map_entry.value) if not isinstance(value, (ScalarInfo, EnumInfo, MessageInfo)): - raise ValueError("invalid value info", value) + raise InvalidMapEntryValueError(value, ref) return MapEntryInfo(map_entry.module, key, value) diff --git a/src/pyprotostuben/protobuf/visitor/abc.py b/src/pyprotostuben/protobuf/visitor/abc.py index e60447a..8888a7a 100644 --- a/src/pyprotostuben/protobuf/visitor/abc.py +++ b/src/pyprotostuben/protobuf/visitor/abc.py @@ -2,14 +2,14 @@ import typing as t from google.protobuf.descriptor_pb2 import ( - FileDescriptorProto, + DescriptorProto, EnumDescriptorProto, EnumValueDescriptorProto, - DescriptorProto, - OneofDescriptorProto, FieldDescriptorProto, - ServiceDescriptorProto, + FileDescriptorProto, MethodDescriptorProto, + OneofDescriptorProto, + ServiceDescriptorProto, ) Proto = t.Union[ @@ -27,35 +27,35 @@ class ProtoVisitor(metaclass=abc.ABCMeta): @abc.abstractmethod def visit_file_descriptor_proto(self, proto: FileDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def visit_enum_descriptor_proto(self, proto: EnumDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def visit_enum_value_descriptor_proto(self, proto: EnumValueDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def visit_descriptor_proto(self, proto: DescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def visit_oneof_descriptor_proto(self, proto: OneofDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def visit_field_descriptor_proto(self, proto: FieldDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def visit_service_descriptor_proto(self, proto: ServiceDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def visit_method_descriptor_proto(self, proto: MethodDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError def visit(visitor: ProtoVisitor, *protos: Proto) -> None: diff --git a/src/pyprotostuben/protobuf/visitor/decorator.py b/src/pyprotostuben/protobuf/visitor/decorator.py index 80711d7..585adc5 100644 --- a/src/pyprotostuben/protobuf/visitor/decorator.py +++ b/src/pyprotostuben/protobuf/visitor/decorator.py @@ -1,14 +1,14 @@ import abc from google.protobuf.descriptor_pb2 import ( - MethodDescriptorProto, - ServiceDescriptorProto, - FieldDescriptorProto, - OneofDescriptorProto, DescriptorProto, - EnumValueDescriptorProto, EnumDescriptorProto, + EnumValueDescriptorProto, + FieldDescriptorProto, FileDescriptorProto, + MethodDescriptorProto, + OneofDescriptorProto, + ServiceDescriptorProto, ) from pyprotostuben.protobuf.visitor.abc import ProtoVisitor @@ -17,67 +17,67 @@ class ProtoVisitorDecorator(metaclass=abc.ABCMeta): @abc.abstractmethod def enter_file_descriptor_proto(self, proto: FileDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def leave_file_descriptor_proto(self, proto: FileDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def enter_enum_descriptor_proto(self, proto: EnumDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def leave_enum_descriptor_proto(self, proto: EnumDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def enter_enum_value_descriptor_proto(self, proto: EnumValueDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def leave_enum_value_descriptor_proto(self, proto: EnumValueDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def enter_descriptor_proto(self, proto: DescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def leave_descriptor_proto(self, proto: DescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def enter_oneof_descriptor_proto(self, proto: OneofDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def leave_oneof_descriptor_proto(self, proto: OneofDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def enter_field_descriptor_proto(self, proto: FieldDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def leave_field_descriptor_proto(self, proto: FieldDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def enter_service_descriptor_proto(self, proto: ServiceDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def leave_service_descriptor_proto(self, proto: ServiceDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def enter_method_descriptor_proto(self, proto: MethodDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def leave_method_descriptor_proto(self, proto: MethodDescriptorProto) -> None: - raise NotImplementedError() + raise NotImplementedError class EnterProtoVisitorDecorator(ProtoVisitorDecorator): diff --git a/src/pyprotostuben/protobuf/visitor/dfs.py b/src/pyprotostuben/protobuf/visitor/dfs.py index f0a130c..83d2279 100644 --- a/src/pyprotostuben/protobuf/visitor/dfs.py +++ b/src/pyprotostuben/protobuf/visitor/dfs.py @@ -1,12 +1,12 @@ from google.protobuf.descriptor_pb2 import ( - FileDescriptorProto, + DescriptorProto, EnumDescriptorProto, EnumValueDescriptorProto, - DescriptorProto, - OneofDescriptorProto, FieldDescriptorProto, - ServiceDescriptorProto, + FileDescriptorProto, MethodDescriptorProto, + OneofDescriptorProto, + ServiceDescriptorProto, ) from pyprotostuben.logging import LoggerMixin diff --git a/src/pyprotostuben/python/ast_builder.py b/src/pyprotostuben/python/ast_builder.py index 31701c0..21fe250 100644 --- a/src/pyprotostuben/python/ast_builder.py +++ b/src/pyprotostuben/python/ast_builder.py @@ -1,3 +1,6 @@ +# Skip `too many arguments rule`, because it is a builder. Methods are invoked with key only args. +# ruff: noqa: PLR0913 + import abc import ast import enum @@ -25,10 +28,10 @@ class Kind(enum.Enum): class DependencyResolver(metaclass=abc.ABCMeta): @abc.abstractmethod def resolve(self, info: TypeInfo) -> TypeInfo: - raise NotImplementedError() + raise NotImplementedError -class ASTBuilder(metaclass=abc.ABCMeta): +class ASTBuilder: def __init__(self, resolver: DependencyResolver) -> None: self.__resolver = resolver @@ -80,41 +83,27 @@ def build_func_stub( returns: TypeRef, is_async: bool = False, ) -> ast.stmt: - return (ast.AsyncFunctionDef if is_async else ast.FunctionDef)( + if is_async: + return ast.AsyncFunctionDef( # type: ignore[call-overload,no-any-return,unused-ignore] + # type_comment and type_params has default value each in 3.12 and not available in 3.9 + name=name, + args=self._build_func_args(args), + body=self._build_stub_body(), + decorator_list=self._build_decorators(decorators), + returns=self.build_ref(returns), + # Seems like it is allowed to pass `None`, but ast typing says it isn't + lineno=t.cast(int, None), + ) + + return ast.FunctionDef( # type: ignore[call-overload,no-any-return,unused-ignore] + # type_comment and type_params has default value each in 3.12 and not available in 3.9 name=name, - decorator_list=[self.build_ref(dec) for dec in (decorators or ())], - args=ast.arguments( - posonlyargs=[], - args=[ - ast.arg( - arg=arg.name, - annotation=self.build_ref(arg.annotation) if arg.annotation is not None else None, - ) - for arg in (args or []) - if arg.kind is FuncArgInfo.Kind.POS - ], - defaults=[ - self.build_ref(arg.default) - for arg in (args or []) - if arg.kind is FuncArgInfo.Kind.POS and arg.default is not None - ], - kwonlyargs=[ - ast.arg( - arg=arg.name, - annotation=self.build_ref(arg.annotation) if arg.annotation is not None else None, - ) - for arg in (args or []) - if arg.kind is FuncArgInfo.Kind.KW_ONLY - ], - kw_defaults=[ - self.build_ref(arg.default) if arg.default is not None else None - for arg in (args or []) - if arg.kind is FuncArgInfo.Kind.KW_ONLY - ], - ), + decorator_list=self._build_decorators(decorators), + args=self._build_func_args(args), returns=self.build_ref(returns), - body=[ast.Ellipsis()], - lineno=None, + body=self._build_stub_body(), + # Seems like it is allowed to pass `None`, but ast typing says it isn't + lineno=t.cast(int, None), ) def build_class_def( @@ -126,12 +115,13 @@ def build_class_def( keywords: t.Optional[t.Mapping[str, TypeRef]] = None, body: t.Optional[t.Sequence[ast.stmt]] = None, ) -> ast.ClassDef: - return ast.ClassDef( + # type_params has default value in 3.12 and not available in 3.9 + return ast.ClassDef( # type: ignore[call-arg,unused-ignore] name=name, - decorator_list=[self.build_ref(dec) for dec in (decorators or ())], + decorator_list=self._build_decorators(decorators), bases=[self.build_ref(base) for base in (bases or ())], keywords=[ast.keyword(arg=key, value=self.build_ref(value)) for key, value in (keywords or {}).items()], - body=body or [], + body=list(body or []), ) def build_abstract_class_def( @@ -185,16 +175,30 @@ def build_abstract_method_stub( is_async=is_async, ) - def build_property_stub( + def build_property_getter_stub( self, *, name: str, - returns: TypeRef, + annotation: TypeRef, ) -> ast.stmt: return self.build_method_stub( name=name, decorators=[TypeInfo.build(self.builtins_module, "property")], - returns=returns, + returns=annotation, + is_async=False, + ) + + def build_property_setter_stub( + self, + *, + name: str, + annotation: TypeRef, + ) -> ast.stmt: + return self.build_method_stub( + name=name, + decorators=[TypeInfo.build(None, name, "setter")], + args=[self.build_pos_arg(name="value", annotation=annotation)], + returns=self.build_none_ref(), is_async=False, ) @@ -215,8 +219,9 @@ def build_attr_stub( ) -> ast.stmt: return ast.AnnAssign( target=ast.Name(id=name), - annotation=annotation, + annotation=self.build_ref(annotation), value=default, + simple=1, ) def build_generic_ref(self, generic: TypeRef, *args: TypeRef) -> ast.expr: @@ -228,14 +233,14 @@ def build_generic_ref(self, generic: TypeRef, *args: TypeRef) -> ast.expr: return ast.Subscript(value=self.build_ref(generic), slice=ast.Tuple(elts=[self.build_ref(arg) for arg in args])) - def build_mapping_ref(self, key: TypeRef, value: TypeRef, mutable: bool = False) -> ast.expr: + def build_mapping_ref(self, key: TypeRef, value: TypeRef, *, mutable: bool = False) -> ast.expr: return self.build_generic_ref( TypeInfo.build(self.typing_module, "MutableMapping" if mutable else "Mapping"), key, value, ) - def build_sequence_ref(self, inner: TypeRef, mutable: bool = False) -> ast.expr: + def build_sequence_ref(self, inner: TypeRef, *, mutable: bool = False) -> ast.expr: return self.build_generic_ref( TypeInfo.build(self.typing_module, "MutableSequence" if mutable else "Sequence"), inner, @@ -285,3 +290,49 @@ def build_module(self, deps: t.Collection[ModuleInfo], body: t.Sequence[ast.stmt ], type_ignores=[], ) + + def _build_decorators(self, decorators: t.Optional[t.Sequence[t.Union[ast.expr, TypeInfo]]]) -> t.List[ast.expr]: + return [self.build_ref(dec) for dec in (decorators or ())] + + def _build_func_args(self, args: t.Optional[t.Sequence[FuncArgInfo]]) -> ast.arguments: + return ast.arguments( + posonlyargs=[], + args=[ + ast.arg( + arg=arg.name, + annotation=self.build_ref(arg.annotation) if arg.annotation is not None else None, + ) + for arg in (args or []) + if arg.kind is FuncArgInfo.Kind.POS + ], + defaults=[ + self.build_ref(arg.default) + for arg in (args or []) + if arg.kind is FuncArgInfo.Kind.POS and arg.default is not None + ], + kwonlyargs=[ + ast.arg( + arg=arg.name, + annotation=self.build_ref(arg.annotation) if arg.annotation is not None else None, + ) + for arg in (args or []) + if arg.kind is FuncArgInfo.Kind.KW_ONLY + ], + kw_defaults=[ + self.build_ref(arg.default) if arg.default is not None else None + for arg in (args or []) + if arg.kind is FuncArgInfo.Kind.KW_ONLY + ], + ) + + def _build_stub_body(self) -> t.List[ast.stmt]: + return [ + # Ellipsis is ok for function body, but ast typing says it isnt't + t.cast( + ast.stmt, + ast.Ellipsis( + # ast typing says that `value` is required position arg, but no + # type: ignore[call-arg] + ), + ), + ] diff --git a/src/pyprotostuben/python/info.py b/src/pyprotostuben/python/info.py index ffd80c6..e0abb90 100644 --- a/src/pyprotostuben/python/info.py +++ b/src/pyprotostuben/python/info.py @@ -57,7 +57,7 @@ def package(self) -> t.Optional[PackageInfo]: @ft.cached_property def file(self) -> Path: return ((self.package.directory / self.name) if self.package is not None else Path(self.name)).with_suffix( - ".py" + ".py", ) @ft.cached_property diff --git a/src/pyprotostuben/stack.py b/src/pyprotostuben/stack.py index 9fbfa9e..28b7142 100644 --- a/src/pyprotostuben/stack.py +++ b/src/pyprotostuben/stack.py @@ -10,11 +10,11 @@ class Stack(t.Generic[V_co], t.Sequence[V_co], metaclass=abc.ABCMeta): @abc.abstractmethod def get_head(self) -> V_co: - raise NotImplementedError() + raise NotImplementedError @abc.abstractmethod def get_last(self) -> V_co: - raise NotImplementedError() + raise NotImplementedError class MutableStack(Stack[T]): @@ -38,6 +38,9 @@ def __getitem__(self, index: t.Union[int, slice]) -> t.Union[T, t.Sequence[T]]: # deque does not support slice :shrug: return list(islice(self.__impl, index.start, index.stop, index.step)) + else: + raise TypeError(index) + def index( self, value: T, diff --git a/tests/integration/cases/000_greeting/expected_gen/greeting_pb2.pyi b/tests/integration/cases/000_greeting/expected_gen/greeting_pb2.pyi new file mode 100644 index 0000000..2b19efd --- /dev/null +++ b/tests/integration/cases/000_greeting/expected_gen/greeting_pb2.pyi @@ -0,0 +1,25 @@ +import builtins +import google.protobuf.message +import typing + +class GreetRequest(google.protobuf.message.Message): + + def __init__(self, *, name: builtins.str) -> None:... + + @builtins.property + def name(self) -> builtins.str:... + + def HasField(self, field_name: typing.NoReturn) -> typing.NoReturn:... + + def WhichOneof(self, oneof_group: typing.NoReturn) -> typing.NoReturn:... + +class GreetResponse(google.protobuf.message.Message): + + def __init__(self, *, text: builtins.str) -> None:... + + @builtins.property + def text(self) -> builtins.str:... + + def HasField(self, field_name: typing.NoReturn) -> typing.NoReturn:... + + def WhichOneof(self, oneof_group: typing.NoReturn) -> typing.NoReturn:... \ No newline at end of file diff --git a/tests/integration/cases/000_greeting/expected_gen/greeting_pb2_grpc.pyi b/tests/integration/cases/000_greeting/expected_gen/greeting_pb2_grpc.pyi new file mode 100644 index 0000000..3247858 --- /dev/null +++ b/tests/integration/cases/000_greeting/expected_gen/greeting_pb2_grpc.pyi @@ -0,0 +1,19 @@ +import abc +import builtins +import greeting_pb2 +import grpc +import grpc.aio +import typing + +class GreeterServicer(metaclass=abc.ABCMeta): + + @abc.abstractmethod + async def Greet(self, request: greeting_pb2.GreetRequest, context: grpc.aio.ServicerContext[greeting_pb2.GreetRequest, greeting_pb2.GreetResponse]) -> greeting_pb2.GreetResponse:... + +def add_GreeterServicer_to_server(servicer: GreeterServicer, server: grpc.aio.Server) -> None:... + +class GreeterStub: + + def __init__(self, channel: grpc.aio.Channel) -> None:... + + async def Greet(self, request: greeting_pb2.GreetRequest, *, timeout: typing.Optional[builtins.float]=None, metadata: typing.Optional[grpc.aio.MetadataType]=None, credentials: typing.Optional[grpc.CallCredentials]=None, wait_for_ready: typing.Optional[builtins.bool]=None, compression: typing.Optional[grpc.Compression]=None) -> grpc.aio.UnaryUnaryCall[greeting_pb2.GreetRequest, greeting_pb2.GreetResponse]:... \ No newline at end of file diff --git a/tests/integration/cases/000_greeting/proto/greeting.proto b/tests/integration/cases/000_greeting/proto/greeting.proto new file mode 100644 index 0000000..b3becf1 --- /dev/null +++ b/tests/integration/cases/000_greeting/proto/greeting.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package greeting; + +message GreetRequest { + string name = 1; +} + +message GreetResponse { + string text = 1; +} + +service Greeter { + rpc Greet(GreetRequest) returns (GreetResponse) {} +} diff --git a/tests/integration/cases/001_types/expected_gen/types_pb2.pyi b/tests/integration/cases/001_types/expected_gen/types_pb2.pyi index d791380..aa29168 100644 --- a/tests/integration/cases/001_types/expected_gen/types_pb2.pyi +++ b/tests/integration/cases/001_types/expected_gen/types_pb2.pyi @@ -2,9 +2,9 @@ import builtins import google.protobuf.message import typing -class STD(google.protobuf.message.Message): +class Container(google.protobuf.message.Message): - def __init__(self, *, b1: builtins.bool, i32: builtins.int, f: builtins.float, str: builtins.str, int_seq: typing.Sequence[builtins.int], str_seq: typing.Sequence[builtins.str], opt_str: typing.Optional[builtins.str]=None, i64_to_float: typing.Mapping[builtins.int, builtins.float], str_to_bool: typing.Mapping[builtins.str, builtins.bool]) -> None:... + def __init__(self, *, b1: builtins.bool, i32: builtins.int, f: builtins.float, str: builtins.str, int_seq: typing.Sequence[builtins.int], str_seq: typing.Sequence[builtins.str], opt_str: typing.Optional[builtins.str]=None, i64_to_float: typing.Mapping[builtins.int, builtins.float], str_to_bool: typing.Mapping[builtins.str, builtins.bool], nested: Nested, nested_seq: typing.Sequence[Nested], str_to_nested: typing.Mapping[builtins.str, Nested]) -> None:... @builtins.property def b1(self) -> builtins.bool:... @@ -33,6 +33,32 @@ class STD(google.protobuf.message.Message): @builtins.property def str_to_bool(self) -> typing.Mapping[builtins.str, builtins.bool]:... + @builtins.property + def nested(self) -> Nested:... + + @builtins.property + def nested_seq(self) -> typing.Sequence[Nested]:... + + @builtins.property + def str_to_nested(self) -> typing.Mapping[builtins.str, Nested]:... + def HasField(self, field_name: typing.Literal['opt_str']) -> builtins.bool:... - def WhichOneof(self, oneof_group: typing.NoReturn) -> typing.NoReturn:... \ No newline at end of file + def WhichOneof(self, oneof_group: typing.NoReturn) -> typing.NoReturn:... + +class Nested(google.protobuf.message.Message): + + def __init__(self, *, b: typing.Optional[builtins.bool]=None, i: typing.Optional[builtins.int]=None, s: typing.Optional[builtins.str]=None) -> None:... + + @builtins.property + def b(self) -> builtins.bool:... + + @builtins.property + def i(self) -> builtins.int:... + + @builtins.property + def s(self) -> builtins.str:... + + def HasField(self, field_name: typing.NoReturn) -> typing.NoReturn:... + + def WhichOneof(self, oneof_group: typing.Literal['value']) -> typing.Optional[typing.Literal['b', 'i', 's']]:... \ No newline at end of file diff --git a/tests/integration/cases/001_types/proto/types.proto b/tests/integration/cases/001_types/proto/types.proto index e2656fa..de7e2fd 100644 --- a/tests/integration/cases/001_types/proto/types.proto +++ b/tests/integration/cases/001_types/proto/types.proto @@ -2,7 +2,7 @@ syntax = "proto3"; package types; -message STD { +message Container { bool b1 = 1; int32 i32 = 2; float f = 3; @@ -12,4 +12,15 @@ message STD { optional string opt_str = 7; map i64_to_float = 8; map str_to_bool = 9; + Nested nested = 10; + repeated Nested nested_seq = 11; + map str_to_nested = 12; +} + +message Nested { + oneof value { + bool b = 1; + int32 i = 2; + string s = 3; + } } diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 748f8f1..973d2aa 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -7,7 +7,6 @@ import pytest from _pytest.fixtures import SubRequest from google.protobuf.compiler.plugin_pb2 import CodeGeneratorRequest, CodeGeneratorResponse - from pyprotostuben.codegen.abc import ProtocPlugin from pyprotostuben.codegen.mypy.plugin import MypyStubProtocPlugin @@ -46,7 +45,7 @@ def case(request: SubRequest, tmp_path: Path) -> Case: def _read_request(proto_dir: Path, tmp_path: Path) -> CodeGeneratorRequest: echo_result = subprocess.run( - [ + [ # noqa: S603,S607 "protoc", f"-I{proto_dir}", f"--echo_out={tmp_path}", @@ -54,15 +53,14 @@ def _read_request(proto_dir: Path, tmp_path: Path) -> CodeGeneratorRequest: ], stderr=subprocess.PIPE, encoding="utf-8", + check=False, ) if echo_result.returncode != 0: pytest.fail(echo_result.stderr) with (tmp_path / "request.json").open("r") as echo_out: - request = CodeGeneratorRequest(**json.load(echo_out)) - - return request + return CodeGeneratorRequest(**json.load(echo_out)) def _load_content(path: Path) -> str: