diff --git a/betterproto2/src/betterproto2/__init__.py b/betterproto2/src/betterproto2/__init__.py index 57d674c9..e1f101cd 100644 --- a/betterproto2/src/betterproto2/__init__.py +++ b/betterproto2/src/betterproto2/__init__.py @@ -1,6 +1,14 @@ from __future__ import annotations -__all__ = ["__version__", "check_compiler_version", "classproperty", "unwrap", "MessagePool", "validators"] +__all__ = [ + "__version__", + "check_compiler_version", + "classproperty", + "staticproperty", + "unwrap", + "MessagePool", + "validators", +] import dataclasses import enum as builtin_enum @@ -36,7 +44,7 @@ from .casing import camel_case, safe_snake_case, snake_case from .enum_ import Enum as Enum from .grpc.grpclib_client import ServiceStub as ServiceStub -from .utils import classproperty +from .utils import classproperty, staticproperty if TYPE_CHECKING: from _typeshed import SupportsRead, SupportsWrite @@ -1015,7 +1023,7 @@ def parse(cls, data: bytes) -> Self: # For compatibility with other libraries. @classmethod - def FromString(cls: type[T], data: bytes) -> T: + def FromString(cls: type[T], s: bytes) -> T: """ Parse the binary encoded Protobuf into this message instance. This returns the instance itself and is therefore assignable and chainable. @@ -1035,7 +1043,7 @@ def FromString(cls: type[T], data: bytes) -> T: :class:`Message` The initialized message. """ - return cls.parse(data) + return cls.parse(s) def to_dict( self, diff --git a/betterproto2/src/betterproto2/_types.py b/betterproto2/src/betterproto2/_types.py index df7c3b91..fc2e8179 100644 --- a/betterproto2/src/betterproto2/_types.py +++ b/betterproto2/src/betterproto2/_types.py @@ -1,7 +1,4 @@ -from typing import ( - TYPE_CHECKING, - TypeVar, -) +from typing import TYPE_CHECKING, TypeVar if TYPE_CHECKING: from grpclib._typing import IProtoMessage # type: ignore[reportPrivateImportUsage] diff --git a/betterproto2/src/betterproto2/utils.py b/betterproto2/src/betterproto2/utils.py index 423d923b..b25759d2 100644 --- a/betterproto2/src/betterproto2/utils.py +++ b/betterproto2/src/betterproto2/utils.py @@ -18,6 +18,14 @@ def __get__(self, instance: Any, type: TT_co) -> T_co: T = TypeVar("T") +class staticproperty(Generic[T]): # Should be applied after @staticmethod + def __init__(self, fget: Callable[[], T]) -> None: + self.fget = fget + + def __get__(self, instance: Any, owner: type[Any]) -> T: + return self.fget() + + def unwrap(x: T | None) -> T: """ Unwraps an optional value, returning the value if it exists, or raises a ValueError if the value is None. diff --git a/betterproto2_compiler/pyproject.toml b/betterproto2_compiler/pyproject.toml index e530f010..d78aa655 100644 --- a/betterproto2_compiler/pyproject.toml +++ b/betterproto2_compiler/pyproject.toml @@ -46,15 +46,6 @@ test = [ package = true default-groups = "all" -# [tool.hatch.build.targets.sdist] -# include = ["src/betterproto2_compiler"] - -# [tool.hatch.build.targets.wheel] -# include = ["src/betterproto2_compiler"] - -# [tool.hatch.build.targets.wheel.sources] -# "src/betterproto2_compiler" = "betterproto2_compiler" - [build-system] requires = ["hatchling"] build-backend = "hatchling.build" diff --git a/betterproto2_compiler/src/betterproto2_compiler/plugin/parser.py b/betterproto2_compiler/src/betterproto2_compiler/plugin/parser.py index 1d609a74..0ef6fd93 100644 --- a/betterproto2_compiler/src/betterproto2_compiler/plugin/parser.py +++ b/betterproto2_compiler/src/betterproto2_compiler/plugin/parser.py @@ -171,6 +171,8 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse: ) ) + response.file.append(CodeGeneratorResponseFile(name="py.typed", content="")) + if settings.google_protobuf_descriptors: response.file.append( CodeGeneratorResponseFile( diff --git a/betterproto2_compiler/src/betterproto2_compiler/templates/template.py.j2 b/betterproto2_compiler/src/betterproto2_compiler/templates/template.py.j2 index 36d78036..eed0a719 100644 --- a/betterproto2_compiler/src/betterproto2_compiler/templates/template.py.j2 +++ b/betterproto2_compiler/src/betterproto2_compiler/templates/template.py.j2 @@ -8,8 +8,9 @@ class {{ enum.py_name | add_to_all }}(betterproto2.Enum): {% if output_file.settings.google_protobuf_descriptors %} {# Add descriptor class property to be more drop-in compatible with other libraries. #} - @betterproto2.classproperty - def DESCRIPTOR(self) -> EnumDescriptor: + @betterproto2.staticproperty + @staticmethod + def DESCRIPTOR() -> EnumDescriptor: return {{ enum.descriptor_name }}.enum_types_by_name['{{ enum.prefixed_proto_name }}'] {% endif %} @@ -76,8 +77,9 @@ class {{ message.py_name | add_to_all }}(betterproto2.Message): {% if output_file.settings.google_protobuf_descriptors %} {# Add descriptor class property to be more drop-in compatible with other libraries. #} - @betterproto2.classproperty - def DESCRIPTOR(self) -> Descriptor: + @betterproto2.staticproperty + @staticmethod + def DESCRIPTOR() -> Descriptor: return {{ message.descriptor_name }}.message_types_by_name['{{ message.prefixed_proto_name }}'] {% endif %} @@ -179,6 +181,7 @@ class {{ (service.py_name + "Base") | add_to_all }}(ServiceBase): async def __rpc_{{ method.py_name }}(self, stream: "grpclib.server.Stream[{{ method.py_input_message_type }}, {{ method.py_output_message_type }}]") -> None: {% if not method.client_streaming %} request = await stream.recv_message() + assert request is not None {% else %} request = stream.__aiter__() {% endif %}