diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 551047a..ba2fa72 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,6 +35,12 @@ jobs: - run: uv sync - run: uv run poe test + # Ensure tests pass without optional dependencies. Issues would come from eager imports, so we go ahead + # and run the single conformance test only to keep this step lightweight and give high coverage. + - run: | + uv sync --no-dev + uv run --no-dev --group dev-required pytest -k test_conformance + lint: runs-on: ubuntu-latest steps: diff --git a/pyproject.toml b/pyproject.toml index 9af31e2..2ac6226 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "google-re2>=1.1.20251105; python_version == '3.14'", # 1.1 started supporting 3.12. "google-re2>=1.1; python_version == '3.12'", - "protobuf-py>=0.1.1", + "protobuf-py>=0.2.0", ] dynamic = ["version"] @@ -45,7 +45,14 @@ cel-expr = [ ] [dependency-groups] +# We ensure all dependencies are installed by default so IDE works properly for development. dev = [ + { include-group = "dev-optional" }, + { include-group = "dev-required" }, +] + +# All dependencies required for CI and development, including running baseline tests. +dev-required = [ "buf-bin==1.71.0", "fix-protobuf-imports==0.1.7", "google-re2-stubs==0.1.1", @@ -57,11 +64,14 @@ dev = [ "tombi==1.2.0", "ty==0.0.59", "types-protobuf==6.32.1.20260221", +] +# Optional dependencies, used for development of optional features. We run tests without +# these installed to ensure missing optional dependencies does not introduce breakage. +# Unlike most dev dependencies, we don't pin to allow testing old and new +# versions. Floored at 6.31.0 required by the cel-expr-python backend. +dev-optional = [ "cel-expr-python>=0.1.3; python_version >= '3.11'", - - # Unlike most dev dependencies, we don't pin to allow testing old and new - # versions. Floored at 6.31.0 required by the cel-expr-python backend. "protobuf>=6.31.0", ] diff --git a/test/_utils.py b/test/_utils.py new file mode 100644 index 0000000..ec2f091 --- /dev/null +++ b/test/_utils.py @@ -0,0 +1,75 @@ +# Copyright (c) 2023-2026 Buf Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +from typing import TYPE_CHECKING, Protocol + +import pytest + +from protovalidate import CompilationError, Violation + +if TYPE_CHECKING: + from google.protobuf import message as google_message + from protobuf import Message + + +class ValidatorProtocol(Protocol): + def validate( + self, message: Message | google_message.Message, *, fail_fast: bool = False + ) -> None: ... + + def collect_violations( + self, message: Message | google_message.Message, *, fail_fast: bool = False + ) -> list[Violation]: ... + + +def check_valid( + validator: ValidatorProtocol, msg: Message | google_message.Message +) -> None: + # Test validate + validator.validate(msg) + + # Test collect_violations + violations = validator.collect_violations(msg) + assert len(violations) == 0 + + +def check_compilation_errors( + validator: ValidatorProtocol, msg: Message | google_message.Message, expected: str +) -> None: + """A helper function for testing compilation errors when validating. + + The tests are run using validators created via all possible methods and + validation is done via a call to `validate` as well as a call to `collect_violations`. + """ + # Test validate + with pytest.raises(CompilationError) as vce: + validator.validate(msg) + assert str(vce.value) == expected + + # Test collect_violations + with pytest.raises(CompilationError) as cvce: + validator.collect_violations(msg) + assert str(cvce.value) == expected + + +def compare_violations(actual: list[Violation], expected: list[Violation]) -> None: + """Compares two lists of violations. The violations are expected to be in the expected order also.""" + assert len(actual) == len(expected) + for a, e in zip(actual, expected, strict=True): + assert a.proto.message == e.proto.message + assert a.proto.rule_id == e.proto.rule_id + assert a.proto.for_key == e.proto.for_key + assert a.field_value == e.field_value + assert a.rule_value == e.rule_value diff --git a/test/conformance/runner.py b/test/conformance/runner.py index 76cc7c7..08a9eb6 100644 --- a/test/conformance/runner.py +++ b/test/conformance/runner.py @@ -11,20 +11,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - from __future__ import annotations import os import sys +from typing import TYPE_CHECKING import celpy import protobuf -from google.protobuf import ( - descriptor_pb2 as google_descriptor_pb2, - descriptor_pool as google_descriptor_pool, - message as google_message, - message_factory as google_message_factory, -) from protobuf import Oneof, Registry, wkt as pb_wkt import protovalidate @@ -37,6 +31,12 @@ TestResult, ) +if TYPE_CHECKING: + from google.protobuf import ( + descriptor_pool as google_descriptor_pool, + message as google_message, + ) + # Set to test google.protobuf messages instead of protobuf-py _LEGACY = os.environ.get("PROTOVALIDATE_CONFORMANCE_LEGACY") == "1" @@ -47,6 +47,11 @@ def build_google_pool( fdset: pb_wkt.FileDescriptorSet, ) -> google_descriptor_pool.DescriptorPool: + from google.protobuf import ( # noqa: PLC0415 + descriptor_pb2 as google_descriptor_pb2, + descriptor_pool as google_descriptor_pool, + ) + pool = google_descriptor_pool.DescriptorPool() by_name = {file.name: file for file in fdset.file} added: set[str] = set() @@ -121,6 +126,8 @@ def run_any_test_case( ) msg = unpacked else: + from google.protobuf import message_factory as google_message_factory # noqa: PLC0415, I001 + try: google_desc = registry.FindMessageTypeByName(type_name) except KeyError: diff --git a/test/conformance/test_conformance.py b/test/conformance/test_conformance.py index 0c48fda..b25afef 100644 --- a/test/conformance/test_conformance.py +++ b/test/conformance/test_conformance.py @@ -66,6 +66,10 @@ def test_conformance(*, legacy: bool, cel_backend: str) -> None: env = os.environ.copy() if legacy: + pytest.importorskip( + "google.protobuf", reason="optional dependency not installed" + ) + env["PROTOVALIDATE_CONFORMANCE_LEGACY"] = "1" env["PROTOVALIDATE_CONFORMANCE_BACKEND"] = cel_backend diff --git a/test/test_format.py b/test/test_format.py index 13b8685..38dabc8 100644 --- a/test/test_format.py +++ b/test/test_format.py @@ -21,18 +21,20 @@ import celpy import pytest from celpy import celtypes -from google.protobuf import text_format as google_text_format +from protobuf import Oneof +from protobuf.txtpb import message_from_text from protovalidate import _extra_func from protovalidate._cel_field_presence import InterpretedRunner -from .gen.cel.expr.conformance.test import simple_pb2 +from .gen.cel.expr.conformance.test import simple_pb from .versions import CEL_SPEC_VERSION if TYPE_CHECKING: from collections.abc import Iterable, MutableMapping - from .gen.cel.expr import eval_pb2 + from .gen.cel.expr import eval_pb + skipped_tests = [ # cel-python seems to have a bug with ints and booleans in the same map which evaluate to the same value @@ -52,37 +54,35 @@ ] -def load_test_data(file_name: Path) -> simple_pb2.SimpleTestFile: - msg = simple_pb2.SimpleTestFile() - google_text_format.Parse(file_name.read_bytes(), msg) - return msg +def load_test_data(file_name: Path) -> simple_pb.SimpleTestFile: + return message_from_text(simple_pb.SimpleTestFile, file_name.read_text()) -def build_variables( - bindings: MutableMapping[str, eval_pb2.ExprValue], -) -> dict[Any, Any]: +def build_variables(bindings: MutableMapping[str, eval_pb.ExprValue]) -> dict[Any, Any]: binder = {} for key, value in bindings.items(): - if value.HasField("value"): - val = value.value - if val.HasField("string_value"): - binder[key] = celtypes.StringType(val.string_value) + match value.kind: + case Oneof("value", val): + match val.kind: + case Oneof("string_value", s): + binder[key] = celtypes.StringType(s) return binder -def get_expected_result(test: simple_pb2.SimpleTest) -> str | None: - if test.HasField("value"): - val = test.value - if val.HasField("string_value"): - return val.string_value +def get_expected_result(test: simple_pb.SimpleTest) -> str | None: + match test.result_matcher: + case Oneof("value", val): + match val.kind: + case Oneof("string_value", s): + return s return None -def get_eval_error_message(test: simple_pb2.SimpleTest) -> str | None: - if test.HasField("eval_error"): - err_set = test.eval_error - if len(err_set.errors) == 1: - return celtypes.StringType(err_set.errors[0].message) +def get_eval_error_message(test: simple_pb.SimpleTest) -> str | None: + match test.result_matcher: + case Oneof("eval_error", err_set): + if len(err_set.errors) == 1: + return celtypes.StringType(err_set.errors[0].message) return None @@ -101,11 +101,11 @@ def get_eval_error_message(test: simple_pb2.SimpleTest) -> str | None: sections.extend(supplemental_test_data.section) # Find the format tests which test successful formatting -_format_tests: Iterable[simple_pb2.SimpleTest] = chain.from_iterable( +_format_tests: Iterable[simple_pb.SimpleTest] = chain.from_iterable( x.test for x in sections if x.name == "format" ) # Find the format error tests which test errors during formatting -_format_error_tests: Iterable[simple_pb2.SimpleTest] = chain.from_iterable( +_format_error_tests: Iterable[simple_pb.SimpleTest] = chain.from_iterable( x.test for x in sections if x.name == "format_errors" ) diff --git a/test/test_validate.py b/test/test_validate.py index 6c5b8ec..4bbf94f 100644 --- a/test/test_validate.py +++ b/test/test_validate.py @@ -14,37 +14,21 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Protocol - import protobuf import pytest from protobuf import Oneof import protovalidate -from protovalidate import _rules - +from protovalidate import Violation, _rules + +from ._utils import ( + ValidatorProtocol, + check_compilation_errors, + check_valid, + compare_violations, +) from .conftest import backend_validators -from .gen.tests.example.v1 import validations_pb, validations_pb2 - -if TYPE_CHECKING: - from google.protobuf import message as google_message - - -class ValidatorProtocol(Protocol): - def validate( - self, - message: protobuf.Message | google_message.Message, - *, - fail_fast: bool = False, - ) -> None: ... - - def collect_violations( - self, - message: protobuf.Message | google_message.Message, - *, - fail_fast: bool = False, - ) -> list[_rules.Violation]: ... - +from .gen.tests.example.v1 import validations_pb validators: list[ValidatorProtocol] = [ protovalidate, # global module singleton @@ -219,76 +203,15 @@ def test_fail_fast(validator: ValidatorProtocol) -> None: validator.validate(msg, fail_fast=True) e = cm.value assert str(e) == f"invalid {type(msg).desc().name}" - _compare_violations(e.violations, [expected_violation]) # ty: ignore + compare_violations(e.violations, [expected_violation]) # ty: ignore # Test collect_violations violations = validator.collect_violations(msg, fail_fast=True) - _compare_violations(violations, [expected_violation]) - - -@pytest.mark.parametrize("validator", validators) -def test_legacy_message_valid(validator: ValidatorProtocol) -> None: - """A google.protobuf message validates through the legacy conversion path.""" - msg = validations_pb2.DoubleFinite() - msg.val = 1.0 - - check_valid(validator, msg) - - -@pytest.mark.parametrize("validator", validators) -def test_legacy_message_invalid(validator: ValidatorProtocol) -> None: - msg = validations_pb2.DoubleFinite() - msg.val = float("-inf") - - expected_violation = _rules.Violation( - message="must be finite", - rule_id="double.finite", - field_value=msg.val, - rule_value=True, - ) - - with pytest.raises(protovalidate.ValidationError) as exc_info: - validator.validate(msg) - e = exc_info.value - assert str(e) == f"invalid {msg.DESCRIPTOR.name}" - _compare_violations(e.violations, [expected_violation]) # ty: ignore - - violations = validator.collect_violations(msg) - _compare_violations(violations, [expected_violation]) - - -@pytest.mark.parametrize("validator", validators) -def test_legacy_message_map_key(validator: ValidatorProtocol) -> None: - msg = validations_pb2.MapKeys() - msg.val[1] = "a" - - expected_violation = _rules.Violation( - message="must be less than 0", - rule_id="sint64.lt", - for_key=True, - field_value=1, - rule_value=0, - ) - - violations = validator.collect_violations(msg) - _compare_violations(violations, [expected_violation]) - - -def check_valid( - validator: ValidatorProtocol, msg: protobuf.Message | google_message.Message -) -> None: - # Test validate - validator.validate(msg) - - # Test collect_violations - violations = validator.collect_violations(msg) - assert len(violations) == 0 + compare_violations(violations, [expected_violation]) def check_invalid( - validator: ValidatorProtocol, - msg: protobuf.Message | google_message.Message, - expected: list[_rules.Violation], + validator: ValidatorProtocol, msg: protobuf.Message, expected: list[Violation] ) -> None: # Test validate with pytest.raises(protovalidate.ValidationError) as exc_info: @@ -298,42 +221,8 @@ def check_invalid( assert str(e) == f"invalid {type(msg).desc().name}" else: assert str(e) == f"invalid {msg.DESCRIPTOR.name}" - _compare_violations(e.violations, expected) # ty: ignore + compare_violations(e.violations, expected) # ty: ignore # Test collect_violations violations = validator.collect_violations(msg) - _compare_violations(violations, expected) - - -def check_compilation_errors( - validator: ValidatorProtocol, - msg: protobuf.Message | google_message.Message, - expected: str, -) -> None: - """A helper function for testing compilation errors when validating. - - The tests are run using validators created via all possible methods and - validation is done via a call to `validate` as well as a call to `collect_violations`. - """ - # Test validate - with pytest.raises(protovalidate.CompilationError) as vce: - validator.validate(msg) - assert str(vce.value) == expected - - # Test collect_violations - with pytest.raises(protovalidate.CompilationError) as cvce: - validator.collect_violations(msg) - assert str(cvce.value) == expected - - -def _compare_violations( - actual: list[_rules.Violation], expected: list[_rules.Violation] -) -> None: - """Compares two lists of violations. The violations are expected to be in the expected order also.""" - assert len(actual) == len(expected) - for a, e in zip(actual, expected, strict=True): - assert a.proto.message == e.proto.message - assert a.proto.rule_id == e.proto.rule_id - assert a.proto.for_key == e.proto.for_key - assert a.field_value == e.field_value - assert a.rule_value == e.rule_value + compare_violations(violations, expected) diff --git a/test/test_validate_legacy.py b/test/test_validate_legacy.py new file mode 100644 index 0000000..dc12649 --- /dev/null +++ b/test/test_validate_legacy.py @@ -0,0 +1,100 @@ +# Copyright (c) 2023-2026 Buf Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import pytest + +pytest.importorskip("google.protobuf", reason="optional dependency not installed") + + +from typing import TYPE_CHECKING + +import protovalidate +from protovalidate import Violation + +from ._utils import ValidatorProtocol, check_valid, compare_violations +from .conftest import backend_validators +from .gen.tests.example.v1 import validations_pb2 + +if TYPE_CHECKING: + from google.protobuf import message as google_message + +validators: list[ValidatorProtocol] = [ + protovalidate, # global module singleton + *backend_validators(), +] + + +@pytest.mark.parametrize("validator", validators) +def test_legacy_message_valid(validator: ValidatorProtocol) -> None: + """A google.protobuf message validates through the legacy conversion path.""" + msg = validations_pb2.DoubleFinite() + msg.val = 1.0 + + check_valid(validator, msg) + + +@pytest.mark.parametrize("validator", validators) +def test_legacy_message_invalid(validator: ValidatorProtocol) -> None: + msg = validations_pb2.DoubleFinite() + msg.val = float("-inf") + + expected_violation = Violation( + message="must be finite", + rule_id="double.finite", + field_value=msg.val, + rule_value=True, + ) + + with pytest.raises(protovalidate.ValidationError) as exc_info: + validator.validate(msg) + e = exc_info.value + assert str(e) == f"invalid {msg.DESCRIPTOR.name}" + compare_violations(e.violations, [expected_violation]) # ty: ignore + + violations = validator.collect_violations(msg) + compare_violations(violations, [expected_violation]) + + +@pytest.mark.parametrize("validator", validators) +def test_legacy_message_map_key(validator: ValidatorProtocol) -> None: + msg = validations_pb2.MapKeys() + msg.val[1] = "a" + + expected_violation = Violation( + message="must be less than 0", + rule_id="sint64.lt", + for_key=True, + field_value=1, + rule_value=0, + ) + + violations = validator.collect_violations(msg) + compare_violations(violations, [expected_violation]) + + +def check_invalid( + validator: ValidatorProtocol, msg: google_message.Message, expected: list[Violation] +) -> None: + # Test validate + with pytest.raises(protovalidate.ValidationError) as exc_info: + validator.validate(msg) + e = exc_info.value + assert str(e) == f"invalid {msg.DESCRIPTOR.name}" + compare_violations(e.violations, expected) # ty: ignore + + # Test collect_violations + violations = validator.collect_violations(msg) + compare_violations(violations, expected) diff --git a/uv.lock b/uv.lock index 3595a08..c81ca0d 100644 --- a/uv.lock +++ b/uv.lock @@ -96,7 +96,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -355,49 +355,49 @@ wheels = [ [[package]] name = "protobuf-py" -version = "0.1.1" +version = "0.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf-py-ext", marker = "(platform_machine == 'arm64' and platform_python_implementation == 'CPython' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'x86_64' and platform_python_implementation == 'CPython' and sys_platform == 'linux') or (platform_machine == 'AMD64' and platform_python_implementation == 'CPython' and sys_platform == 'win32') or (platform_machine == 'ARM64' and platform_python_implementation == 'CPython' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/ed/02fd902d9c51b7ff53dfc9a745eb11490722edfd30073af889e171f07b8e/protobuf_py-0.1.1.tar.gz", hash = "sha256:6bd08ac4d8f1661965bbe2685429d79043704cdd1ee720a7a89617331742240b", size = 133525, upload-time = "2026-06-24T19:02:15.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/06/56/b93a2ed9588779cd19dff9d62a611a7852987db195a0c9e6ca6d4a66682f/protobuf_py-0.2.0.tar.gz", hash = "sha256:e4f2e08c8c99a0d652f20c7e52d9290e04e14f4f086fc16a83ce728bd509f0ba", size = 148832, upload-time = "2026-07-14T05:15:24.122Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/00/1b3775aca1c70e3007e06ef5996f6bb9b3a32341eb0cce3ffb6effad8dec/protobuf_py-0.1.1-py3-none-any.whl", hash = "sha256:efc4f50f275ed6dae10a1f30bb81ad1a75368557b3ff22a532b7a472050368f1", size = 181656, upload-time = "2026-06-24T19:01:29.556Z" }, + { url = "https://files.pythonhosted.org/packages/74/78/f86aba47416d66fab4f522bca7916920b94e5e5cf73e79ea49c6a5e5c8ab/protobuf_py-0.2.0-py3-none-any.whl", hash = "sha256:6811340f5a803c4e23575d9ee47c00c1385e18d93964b8cbacb0fa86eb3378f3", size = 199903, upload-time = "2026-07-14T05:14:46.744Z" }, ] [[package]] name = "protobuf-py-ext" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/05/6dc9ccff1e8159eb9a144e6d3c4acfd2211cd4fcd20c34fe9155d17d6a7f/protobuf_py_ext-0.1.1.tar.gz", hash = "sha256:e85bfdfdb3ed50634db8ccc7429dd9286520109489c735463971a418707b4fef", size = 31912, upload-time = "2026-06-24T19:02:16.321Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/19/70/2b5d62a60a2e0d88ecde1ae98db3132bcd2672fb39c8d581b82b34ac0bd8/protobuf_py_ext-0.1.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:310039e03cb15181781a0b78017419f6d4ee302e988c3c70b87f1facdf05532d", size = 306008, upload-time = "2026-06-24T19:01:31.399Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d6/73c06bb4cac2e04c0adc154965fe8b6520224bd737fd7e73bba405056321/protobuf_py_ext-0.1.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ec8348d1ba045f79b2fedd14e40cca36f2a41b52f2c4fdf55a60c58add2353", size = 314769, upload-time = "2026-06-24T19:01:32.89Z" }, - { url = "https://files.pythonhosted.org/packages/18/c5/e4e6bc6096b66d1c82639a1b501147f16ee65d32567304b987d002e2c666/protobuf_py_ext-0.1.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:182798e4861aba72d05855bd06febe4926aa7265e6f444a1b8af5252beee4f7e", size = 328122, upload-time = "2026-06-24T19:01:34.394Z" }, - { url = "https://files.pythonhosted.org/packages/b0/4b/1d792a40d0f0a0f914f1dfa8bb5e9573ca0ecd5fe5cecf80d77193212abd/protobuf_py_ext-0.1.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9eb25c3a329c0551cc86b209a5e5d8ecb8d834b9924a3aa019377853a703b6d3", size = 492338, upload-time = "2026-06-24T19:01:36.103Z" }, - { url = "https://files.pythonhosted.org/packages/52/51/5ad329223c905e5c530cae38ae24cde3584d8ab7e09457f2a01afce80e60/protobuf_py_ext-0.1.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:aaecbde82bef10c7c40578cbb61b7a19896bf7fa450972050a3bb302acb7d5d6", size = 541578, upload-time = "2026-06-24T19:01:37.481Z" }, - { url = "https://files.pythonhosted.org/packages/ed/c7/01bd8a8bfe2df4b07aafac12ccfb7b7ebe2303f65296a9e02fcafa28ff3e/protobuf_py_ext-0.1.1-cp310-abi3-win_amd64.whl", hash = "sha256:6b0c615c48e95acc53cf33e9310eeaff8b30d2d7555bf93e7bca8fb4f40e9a5c", size = 251319, upload-time = "2026-06-24T19:01:38.946Z" }, - { url = "https://files.pythonhosted.org/packages/24/dc/914065538b5db54b6a920b5af38c1ef142252ea1eea711820435fa259fac/protobuf_py_ext-0.1.1-cp310-abi3-win_arm64.whl", hash = "sha256:72956cd0af5dee24b41c6f5ba5e42622d17e6d555002b5efc1634e27a1446de2", size = 241635, upload-time = "2026-06-24T19:01:40.301Z" }, - { url = "https://files.pythonhosted.org/packages/13/a5/0b5d73cab815fd50615cb87a02e04e8332f9e27380c890aba1459cf7e919/protobuf_py_ext-0.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f0cd58620f415a3534d195358338f4999a774e12510f91c592b38d13d37388", size = 304903, upload-time = "2026-06-24T19:01:41.796Z" }, - { url = "https://files.pythonhosted.org/packages/37/a9/14c120b9ac36a0e11bb05e482fa6ae322de583a8e1068e4859035c289947/protobuf_py_ext-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21572764f625d829604fc4d83635f533f35775f11c6da142345b3f3c8d64bd09", size = 316148, upload-time = "2026-06-24T19:01:43.247Z" }, - { url = "https://files.pythonhosted.org/packages/35/54/7c00a1a9783c3ba74f6b2f5d2f049f09037bbd489c465c09a34f32fb611b/protobuf_py_ext-0.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7f14f00c2678bfbe7ad46f057b9f9938c1677bcf39e405e163e272c6f9814b8", size = 326458, upload-time = "2026-06-24T19:01:44.655Z" }, - { url = "https://files.pythonhosted.org/packages/0d/0e/81fa50c0d6c4d664e5be6d0a3c4f2c7edfef87ab12d0bac764abca1f2955/protobuf_py_ext-0.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1610865622e2e27568277ca63d8d2d23dcc55eaa767398865c21539ddf4ce24d", size = 493596, upload-time = "2026-06-24T19:01:46.344Z" }, - { url = "https://files.pythonhosted.org/packages/56/19/3183cf6e4de62846c20549654a1d573dae51ca06aaf7fed06accdfab74f6/protobuf_py_ext-0.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8995efba9476e1ea18ef9873306871dba697308994bc2766558fec3387acc3de", size = 539656, upload-time = "2026-06-24T19:01:48.21Z" }, - { url = "https://files.pythonhosted.org/packages/a4/6c/09841f3dbe7c3d4b3f2779f8f2832ac23fb96ac8ab71674e91af732cf9ff/protobuf_py_ext-0.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba61d49dace8f874361583030a7c48139b42eb37c9ffbb1e7e8a227a51576f44", size = 305017, upload-time = "2026-06-24T19:01:49.84Z" }, - { url = "https://files.pythonhosted.org/packages/52/43/e450b5e202f6715274acc9acd9f9769908cbdeab861a53c798fcbd467d35/protobuf_py_ext-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a9c2f026096ca4c595c89a297067ef371e241d3ff8e1f6d7c779aa8419cdca7", size = 316215, upload-time = "2026-06-24T19:01:51.249Z" }, - { url = "https://files.pythonhosted.org/packages/97/5c/23e79b35f1b5755b9bdc0c0c9b8cd8abababaef1a1639608d8a96bb61a9d/protobuf_py_ext-0.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf78707a040b9294e5e1ec4a1875f0046acfe52e92150cea27de4e9fc9db39bb", size = 326419, upload-time = "2026-06-24T19:01:52.93Z" }, - { url = "https://files.pythonhosted.org/packages/e8/de/5493de0ec12920a3b1dd72608ce0c1e8cdb7e0eb9e87accaecc5216df29e/protobuf_py_ext-0.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ae4373845cbb85bdade3ef5368cc2f4b5f80bf173383afc1ab063f95644e5599", size = 493734, upload-time = "2026-06-24T19:01:54.301Z" }, - { url = "https://files.pythonhosted.org/packages/98/89/31da55b414e6332aad11d858e9a86bd36fbb324f52fa3fc6d8f1c57840a9/protobuf_py_ext-0.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2a4cc478eef7a2acc1daaebcde479a3ac2396d47e6bdc7e776ca4c4147ba8b4c", size = 539703, upload-time = "2026-06-24T19:01:55.707Z" }, - { url = "https://files.pythonhosted.org/packages/1c/71/39f231838ef06476d46ac40dd814894277a732a3688c0a0c994850b3b62f/protobuf_py_ext-0.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:359dccdc1c3eafed2a913c570bb082b8df848a1d27d548ce8385f246a7d68be2", size = 302145, upload-time = "2026-06-24T19:01:57.116Z" }, - { url = "https://files.pythonhosted.org/packages/87/f1/01c81ff5f420600366a0e6a613ce2af20834534d2b3d7523f4f3b4ddb54f/protobuf_py_ext-0.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91c38b4a10a306366443273ee03ca554537a0965bf05b7ada8e7dbdee04cc93e", size = 314541, upload-time = "2026-06-24T19:01:58.745Z" }, - { url = "https://files.pythonhosted.org/packages/86/d8/1cbf5a0298ceddc0cdbb28bf1ecaf8bd3cff54ed4ced6a1392d5226172fc/protobuf_py_ext-0.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79eee3bcbb289d6ea114eb8fe3a1469c5b59bf53e207238469f567d9c53ba56f", size = 325092, upload-time = "2026-06-24T19:02:00.382Z" }, - { url = "https://files.pythonhosted.org/packages/af/23/8b1694616044cbfec2d18d4c6fffb03e05089c8bdb5970c6bafd60cc7fd5/protobuf_py_ext-0.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:10992141a8282a71ac3e3530d1a489efb27618d84000b9a47918cf70e5816d9b", size = 491684, upload-time = "2026-06-24T19:02:01.862Z" }, - { url = "https://files.pythonhosted.org/packages/49/d8/99997a7a6cf6989c944d9183c5deb6a045c27460add0316c7b34763891b3/protobuf_py_ext-0.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4411ffe0e06a774b83c5c71c546ce097640a25f596c45f95f53d3e3148e3f22d", size = 538048, upload-time = "2026-06-24T19:02:03.362Z" }, - { url = "https://files.pythonhosted.org/packages/15/3c/9e99ecbb68e1d5b46016d821eb1cbba9a91e6b50e71e75faf0c1e6189f0d/protobuf_py_ext-0.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55a17d80ea419501ff221a6627523f38a431bb33e6aa3de81ae3a7f271c49c75", size = 296337, upload-time = "2026-06-24T19:02:04.787Z" }, - { url = "https://files.pythonhosted.org/packages/af/f2/305338a28225fb54b28d6c3f5948109b9088b329a2b6f77ca610c2bdcd34/protobuf_py_ext-0.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbb518b5638403ceaa08ac4fc7dac626f45ed9b856b3517de3906cf3de4d632", size = 308961, upload-time = "2026-06-24T19:02:06.185Z" }, - { url = "https://files.pythonhosted.org/packages/60/f9/416103c93677ff2ea407704ea64fa6de9e700dc030c48360a182d91ce373/protobuf_py_ext-0.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a4adc65ab6a5e4885c67fc808bcacb83d755d05b566d312a0e10c2a873f2ad4", size = 321895, upload-time = "2026-06-24T19:02:07.714Z" }, - { url = "https://files.pythonhosted.org/packages/5a/83/eb3e81bb2f83834b7deff4cee2d56eeb1adcbc847492a56b7f910ab257db/protobuf_py_ext-0.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e97f45c49676efacfb8e95bfcb1a002bc337e618a6780be1e55df2ba5ebd2f1e", size = 486091, upload-time = "2026-06-24T19:02:09.243Z" }, - { url = "https://files.pythonhosted.org/packages/99/96/bd88fca38556b3105e4dd41d6a176e31dcc583fe979e830aeedd2f8c20ee/protobuf_py_ext-0.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:53a6b3590f6aa7f97b8ed60f62fef9b096babfeae82f7283fd3a4c405827d4f8", size = 534582, upload-time = "2026-06-24T19:02:11.227Z" }, +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/8a/45b769a5475f2c95696907265074c3c8113e091a7afaa27e8b4ca7ce0a95/protobuf_py_ext-0.2.0.tar.gz", hash = "sha256:b4a79dc7c341ac05a47081a58b0692ea5ec4530aa9a08c94da0c9023d988080e", size = 31488, upload-time = "2026-07-14T05:15:25.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/98/197802e24a0b1bc138d9a32f9522998d8465bd02b996ae178af8f948d8e2/protobuf_py_ext-0.2.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:3a6e1b8d653a26a67b1bc17bb0efda237bc8b9f1179efc415de2549d536e1ae2", size = 304882, upload-time = "2026-07-14T05:14:48.378Z" }, + { url = "https://files.pythonhosted.org/packages/12/7e/db5ace17eef81afafdda3a710e9e1eb376cb2984e2e51cb1b56e9cd49440/protobuf_py_ext-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7640e2e7cf53d4d8aaa3063659d2ee2be52687e66fd4087039c700b11ba837bf", size = 312969, upload-time = "2026-07-14T05:14:49.815Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7e/81b63b4de45f3a91b3fa6a6d209d325f9c3c69289dc81c75f3305fadd9ef/protobuf_py_ext-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:959acb10bd84fabfbbe09d10f3c45a8cb59fce803f97a6af0805dc2a93f739d3", size = 327164, upload-time = "2026-07-14T05:14:51.178Z" }, + { url = "https://files.pythonhosted.org/packages/f1/a5/5d9c39d154e6a554c7f6b5a42d370ef4211d369572229681a582e194b6da/protobuf_py_ext-0.2.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2d2ace3421a6c4ac7a413b84de2910086a54af37ca46aafc4899d4935a2bd5b9", size = 490567, upload-time = "2026-07-14T05:14:52.392Z" }, + { url = "https://files.pythonhosted.org/packages/85/7f/65d2083373d5a19a2900d90dfdcc960968f493b6fceb1dd612d5a537336f/protobuf_py_ext-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:722b69b1993effa4c03ea0e90916273388fa7e2f3077faef8863c99d6ca36da2", size = 540066, upload-time = "2026-07-14T05:14:53.925Z" }, + { url = "https://files.pythonhosted.org/packages/f8/eb/92530ff853c5601e8f9ca65f125fd9832266f5c9a4dff6b2a47dfae8a788/protobuf_py_ext-0.2.0-cp310-abi3-win_amd64.whl", hash = "sha256:cba5e1dfba68ecd69cedfaf8761aa7fb408fa4723f5b3e341208b13f3ebdebb1", size = 250251, upload-time = "2026-07-14T05:14:55.175Z" }, + { url = "https://files.pythonhosted.org/packages/fc/18/c9e603abecf90fa8c08f619e12c018707ae8ce8ee34a30102cda1963656f/protobuf_py_ext-0.2.0-cp310-abi3-win_arm64.whl", hash = "sha256:3cafe6fd772e661f6b593223412795026abb5a14e7c645823a27f01d53b3af4b", size = 240701, upload-time = "2026-07-14T05:14:56.442Z" }, + { url = "https://files.pythonhosted.org/packages/63/2d/66603f517b15073d0638be557cb64e12ca3ab4a7e1996de9c4d8afa20a8d/protobuf_py_ext-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e37bb6880e7065c29760fd969414d56aa117c5a707abaa11e98164b7e962452", size = 303802, upload-time = "2026-07-14T05:14:57.565Z" }, + { url = "https://files.pythonhosted.org/packages/01/cc/bfb5b23a94f2447d314fc5cd933dffa981df0e789cfc5c0280fc502190a3/protobuf_py_ext-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f1b91efb7bddea459d70ba86001720e29859a788eaaa8bde2cd8c71ccae1ec4", size = 314159, upload-time = "2026-07-14T05:14:58.735Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ee/9624cc545c6f558ff158732aea9eea6e5f0cb56b919d45a5769582e4dedb/protobuf_py_ext-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69392ef99bfd5b2598466e2cde82d90fc4077924c9a9b24e651aa093fb0f763", size = 325915, upload-time = "2026-07-14T05:15:00.11Z" }, + { url = "https://files.pythonhosted.org/packages/15/55/a962e054435d2cdcb3effa55ec385b8959d588d9d12978ccf53b33d23c6b/protobuf_py_ext-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a1cc3cdab302ed310aeed4a4586f0600b458291430ea95d2eb6d1d05d4c6f592", size = 491459, upload-time = "2026-07-14T05:15:01.252Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3a/7f2701ee6d28c821cfd9a9b123b69d7ecfc6dad067c00a1f6f86e49c9cbe/protobuf_py_ext-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6325c5676a09dd8f129d2d72b5c1f3a97aaf172f5db016abefd5eaf4fc6f53eb", size = 538696, upload-time = "2026-07-14T05:15:02.594Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c4/7e6bf96c5fac84a557036d185c7fbd0f561e89e4459e130f867796333a7b/protobuf_py_ext-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c70440ad22fa38fda6a73987c0fb84ba37252fb23c87ddf2fc8d935bb32a127b", size = 303874, upload-time = "2026-07-14T05:15:03.928Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5c/7acb810f41b5821eee0249b5820ae3a5a6335251b3b70336048d36d0ba1a/protobuf_py_ext-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86a7d7e047c2e3b764cf288cad7578b1be9fac90476a68d22c52112248d94fe6", size = 314215, upload-time = "2026-07-14T05:15:05.039Z" }, + { url = "https://files.pythonhosted.org/packages/03/ea/eb328fd99817e0308cb3d4669c0d553186650c45098ade52aee0520f1293/protobuf_py_ext-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e997e59916d3ab7206c68a858a61eeb2c9bdebb1aad0eccddf5b4c78be2d8cc6", size = 326016, upload-time = "2026-07-14T05:15:06.15Z" }, + { url = "https://files.pythonhosted.org/packages/56/23/a3d58915611515523c0098958e7cd2abb8b9491c37821baf875a9ae901f0/protobuf_py_ext-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12a1ccec44f29060d496d608711825e73f1649bb98ee50d974b769ae2a205d51", size = 491556, upload-time = "2026-07-14T05:15:07.505Z" }, + { url = "https://files.pythonhosted.org/packages/f8/01/3e0f533d540033f61bb088cbc3fad0a245b9d0de93585af6c74fd38996f0/protobuf_py_ext-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f4bc97fd4d50e9029ce9aff57f9af211a4e893a68603180b23cdc20e2f1ffcbd", size = 538726, upload-time = "2026-07-14T05:15:08.75Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f2/47efc784f3753a6b0d726b26b4ebe3c5f3d94ba84a525543432e2aa14fd7/protobuf_py_ext-0.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:35f316c6732f63f4002e6441294da3f730d43624cd179b9cfae3d0ffabbc972a", size = 301017, upload-time = "2026-07-14T05:15:09.935Z" }, + { url = "https://files.pythonhosted.org/packages/2e/fe/ec47d9fde881176cdd729fcd95032233372d05f8061b4727c5c758637a83/protobuf_py_ext-0.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e126dceb0a83a7146e91520abf648e281a8137f9a4ac86baee358e24721e537d", size = 312792, upload-time = "2026-07-14T05:15:11.103Z" }, + { url = "https://files.pythonhosted.org/packages/fc/80/548045a14f3ae298cf2f996c952061d58a1dc5eef231786954ad53d2f578/protobuf_py_ext-0.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d5abee03e1031cc905d9443bb0b9d14d6fa8d36c7f64ff0c555ec883d5afef", size = 324495, upload-time = "2026-07-14T05:15:12.583Z" }, + { url = "https://files.pythonhosted.org/packages/f1/be/92fd8698a1cb033e2851cf45ab69815aff362a6577cd301c27fed00e9388/protobuf_py_ext-0.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:70243a1f803037acdce7022040818cbc55d3348bdc3903ffb1d4b18fecbf18ff", size = 489849, upload-time = "2026-07-14T05:15:14.051Z" }, + { url = "https://files.pythonhosted.org/packages/2a/3b/ee34c785c0cddad7666b6d1d0a1c2524e122c1cc96e1855cb905ceca325b/protobuf_py_ext-0.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f4982b1abf24a30a6414ebbb6001171abcbf8f5b945d4a2ca273d132be879732", size = 536823, upload-time = "2026-07-14T05:15:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/15/5a/032bb471ae761cb4b7021ae54cda3587cdc0fca3fcef396cf27a2f85a8a7/protobuf_py_ext-0.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ba51b6298cda70cdabd88a6cc0f998e5d74856254db5bcf009eb22c5248eb0ef", size = 295335, upload-time = "2026-07-14T05:15:16.592Z" }, + { url = "https://files.pythonhosted.org/packages/d4/12/082fe80c66ba7b87ceb71c31faebe8989015cb0d7feee2006fba00f1c194/protobuf_py_ext-0.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:626d0982819b316529729223b52676049e0912bd4a75696059317037bf241b0c", size = 306898, upload-time = "2026-07-14T05:15:17.929Z" }, + { url = "https://files.pythonhosted.org/packages/c2/41/364b0f19165d8476ec1a5c121ed0cb2579c952f5872c4d4eeb3df534a22c/protobuf_py_ext-0.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd6fef4b16b564396f96e58663ac077c42d686968c4ce5771c2333cf50e6fbb", size = 321085, upload-time = "2026-07-14T05:15:19.189Z" }, + { url = "https://files.pythonhosted.org/packages/ed/60/d445897080aca64f36f212d3725a57da2e46983ce517657d831b2542be29/protobuf_py_ext-0.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ded28f377c32b0dc51b134924a54a553fbcaaeb87eaa68f2a80973169ca06b01", size = 484260, upload-time = "2026-07-14T05:15:20.49Z" }, + { url = "https://files.pythonhosted.org/packages/96/26/f41f884487187cd812687782681c31acdf007b00df85927db6e998b81c97/protobuf_py_ext-0.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d303366fdb084d0532e243909b96bbb35fbfd8bb960728489d9729396794711f", size = 533528, upload-time = "2026-07-14T05:15:21.77Z" }, ] [[package]] @@ -431,6 +431,23 @@ dev = [ { name = "ty" }, { name = "types-protobuf" }, ] +dev-optional = [ + { name = "cel-expr-python", marker = "python_full_version >= '3.11'" }, + { name = "protobuf" }, +] +dev-required = [ + { name = "buf-bin" }, + { name = "fix-protobuf-imports" }, + { name = "google-re2-stubs" }, + { name = "license-header" }, + { name = "poethepoet" }, + { name = "pytest" }, + { name = "pytest-benchmark" }, + { name = "ruff" }, + { name = "tombi" }, + { name = "ty" }, + { name = "types-protobuf" }, +] [package.metadata] requires-dist = [ @@ -441,7 +458,7 @@ requires-dist = [ { name = "google-re2", marker = "python_full_version == '3.13.*'", specifier = ">=1.1.20250722" }, { name = "google-re2", marker = "python_full_version == '3.14.*'", specifier = ">=1.1.20251105" }, { name = "protobuf", marker = "python_full_version >= '3.11' and extra == 'cel-expr'", specifier = ">=6.31.0" }, - { name = "protobuf-py", specifier = ">=0.1.1" }, + { name = "protobuf-py", specifier = ">=0.2.0" }, ] provides-extras = ["cel-expr"] @@ -461,6 +478,23 @@ dev = [ { name = "ty", specifier = "==0.0.59" }, { name = "types-protobuf", specifier = "==6.32.1.20260221" }, ] +dev-optional = [ + { name = "cel-expr-python", marker = "python_full_version >= '3.11'", specifier = ">=0.1.3" }, + { name = "protobuf", specifier = ">=6.31.0" }, +] +dev-required = [ + { name = "buf-bin", specifier = "==1.71.0" }, + { name = "fix-protobuf-imports", specifier = "==0.1.7" }, + { name = "google-re2-stubs", specifier = "==0.1.1" }, + { name = "license-header", specifier = "==0.0.1" }, + { name = "poethepoet", specifier = "==0.48.0" }, + { name = "pytest", specifier = "==9.1.1" }, + { name = "pytest-benchmark", specifier = "==5.2.3" }, + { name = "ruff", specifier = "==0.15.21" }, + { name = "tombi", specifier = "==1.2.0" }, + { name = "ty", specifier = "==0.0.59" }, + { name = "types-protobuf", specifier = "==6.32.1.20260221" }, +] [[package]] name = "py-cpuinfo"