mypy-protobuf icon indicating copy to clipboard operation
mypy-protobuf copied to clipboard

Add metaclass=GeneratedProtocolMessageType to protobuf message class

Open summylight opened this issue 11 months ago • 3 comments

I'm working on our codebase to apply mypy-protobuf to generate pyi code. However, we have codes like:

from google.protobuf import json_format
from google.protobuf.reflection import GeneratedProtocolMessageType

def convert_model_to_pb(
        config: Config,
        pb_class: GeneratedProtocolMessageType,
        ignore_unknown_fields: bool = False) -> Message:
    return json_format.ParseDict(
        config.to_json(use_str_for_datetime=True),
        pb_class()
    )

This func accepts protobuf message class as parameters, and parse proto into json. We add GeneratedProtocolMessageType as its type. and mypy would report this error since GeneratedProtocolMessageType does not exist in generated pyi files:

error: Argument 2 to "convert_model_to_pb" has incompatible type "Type[Config]"; expected "GeneratedProtocolMessageType"  [arg-type]

I add metaclass=metaclass=GeneratedProtocolMessageType to pyi files and pass mypy check:

Class Config(_message.Message, metaclass=GeneratedProtocolMessageType)

Can mypy-protobuf support this?

summylight avatar Apr 01 '24 08:04 summylight

Well, I realize it's hard to determine the type of return value in this func. I decide to write typing like this:

from typing import TypeVar, Type

T = TypeVar('T')
def convert_model_to_pb(
        config: Config,
        pb_class: Type['T'],
        ignore_unknown_fields: bool = False) -> T:


summylight avatar Apr 01 '24 09:04 summylight

Well, I realize it's hard to determine the type of return value in this func. I decide to write typing like this:

from typing import TypeVar, Type

T = TypeVar('T')
def convert_model_to_pb(
        config: Config,
        pb_class: Type['T'],
        ignore_unknown_fields: bool = False) -> T:

This seems like the way to go. You may be able to restrict the TypeVar to a Message to really make sure it's a pb message being passed in. Otherwise, seems like you've figured it out.

nipunn1313 avatar Apr 01 '24 20:04 nipunn1313

Well, I realize it's hard to determine the type of return value in this func. I decide to write typing like this:

from typing import TypeVar, Type

T = TypeVar('T')
def convert_model_to_pb(
        config: Config,
        pb_class: Type['T'],
        ignore_unknown_fields: bool = False) -> T:

This seems like the way to go. You may be able to restrict the TypeVar to a Message to really make sure it's a pb message being passed in. Otherwise, seems like you've figured it out.

you are right. I added bound=Message to it:

T = TypeVar('T', bound=Message)

But I still think it's okay to add metaclass typing to proto message classes. Sorry if my idea is stupid.

summylight avatar Apr 02 '24 07:04 summylight