Skip to content

Incorrect types are used to describe google.protobuf enums #2521

Closed
@vmagamedov

Description

@vmagamedov

Let's take google.protobuf.descriptor_pb2.FieldDescriptorProto as an example.

In Python FieldDescriptorProto.TYPE_DOUBLE is instance of int
In mypy FieldDescriptorProto.TYPE_DOUBLE is instance of FieldDescriptorProto.Type

In Python FieldDescriptorProto.Type is instance of EnumTypeWrapper
In mypy FieldDescriptorProto.Type is subclass of int with additional methods

In Python FieldDescriptorProto.Type.keys() is of type List[str]
In mypy FieldDescriptorProto.Type.keys() is of type List[bytes]

In Python EnumTypeWrapper.items is an instancemethod
In mypy EnumTypeWrapper.items is a classmethod

In mypy this code works:

from google.protobuf.descriptor_pb2 import FieldDescriptorProto
print(FieldDescriptorProto.TYPE_DOUBLE.values())

In Python it fails:

Traceback (most recent call last):
  File "sandbox.py", line 5, in <module>
    print(FieldDescriptorProto.TYPE_DOUBLE.values())
AttributeError: 'int' object has no attribute 'values'

Currently this stub looks like this:

class FieldDescriptorProto(Message):
    class Type(int):
        @classmethod
        def Name(cls, number: int) -> bytes: ...

        @classmethod
        def Value(cls, name: bytes) -> FieldDescriptorProto.Type: ...

        @classmethod
        def keys(cls) -> List[bytes]: ...

        @classmethod
        def values(cls) -> List[FieldDescriptorProto.Type]: ...

        @classmethod
        def items(cls) -> List[Tuple[bytes, FieldDescriptorProto.Type]]: ...
    TYPE_DOUBLE: Type
    ...

I think it is possible to change it into this:

class FieldDescriptorProto(Message):
    FieldDescriptorProto_Type = typing.NewType('FieldDescriptorProto_Type', int)
    Type: EnumTypeWrapper[FieldDescriptorProto_Type]
    TYPE_DOUBLE: FieldDescriptorProto_Type
    ...

where google.protobuf.internal.enum_type_wrapper.EnumTypeWrapper looks like this:

class EnumTypeWrapper(Generic[T]):
    def Name(self, number: T) -> str: ...
    def Value(self, name: str) -> T: ...
    def keys(self) -> List[str]: ...
    def values(self) -> List[T]: ...
    def items(self) -> List[Tuple[str, T]]: ...

The only problem I see is how to generate unique type name for enum values, in the example above I'm using concatenation of message name and enum name: FieldDescriptorProto_Type.

Is it ok to create PR with this fix?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions