-
Notifications
You must be signed in to change notification settings - Fork 15.9k
Description
Hi - I'm the maintainer of https://github.com/dropbox/mypy-protobuf - a protoc plugin for autogenerating mypy stubs compatible with the built in python generator.
What language does this apply to?
Python
Describe the problem you are trying to solve.
Generated enum values are pure ints (per the spec). We prefer type them with a NewType
wrapper type around int - specific to the enum. See the typeshed stub
https://github.com/python/typeshed/blob/master/third_party/2and3/google/protobuf/internal/enum_type_wrapper.pyi#L9
class _EnumTypeWrapper(Generic[_V]):
DESCRIPTOR: EnumDescriptor
def __init__(self, enum_type: EnumDescriptor) -> None: ...
def Name(self, number: _V) -> str: ...
def Value(self, name: str) -> _V: ...
def keys(self) -> List[str]: ...
def values(self) -> List[_V]: ...
def items(self) -> List[Tuple[str, _V]]: ...
I've been cooking up a strategy inside mypy-protobuf where a proto
enum MyEnum {
FOO = 0;
BAR = 1;
}
Autogenerates pyi stubs
class _MyEnum(google.protobuf.internal.enum_type_wrapper.EnumTypeWrapper[OuterEnum.V], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor = ...
FOO = OuterEnum.V(0)
BAR = OuterEnum.V(1)
class MyEnum(metaclass=_MyEnum):
V = typing.NewType('V', builtins.int)
This allows callsites to type their code as
x: MyEnum.V = MyEnum.Value("FOO")
Unfortunately - because .V
does not exist at runtime (only in the pyi stubs) - this does not work.
Fortunately, you can work around this with:
x: "MyEnum.V" = MyEnum.Value("FOO")
Describe the solution you'd like
I'd like to propose adding V = int
runtime type alias to the EnumTypeWrapper (in enum_type_wrapper.py) - to avoid the need for this workaround.
class EnumTypeWrapper(object):
"""A utility for finding the names of enum values."""
# This is a type alias, which mypy typing stubs can type as
# a genericized parameter constrained to an int, allowing subclasses
# to be typed with more constraint
# Eg.
# def Name(self, number: MyGeneratedEnum.V) -> str
V = int
... [rest of impl omitted]
This would mean that enum variants named "V" would need to jump through a hoop using getattr to be accessed, but this is already the case with "Name", "Value", "keys", etc - so it seems feasible as an extension. I'm open to other naming ideas for this.
Could do something like this for more clarity - though it does add a dependency on typing module for python <= 3.5
from typing import NewType
V = NewType('V', int)
I would also be open to discussing other strategies we could employ across mypy-protobuf, protoc, and typeshed in order to get better mypy typing for enum values (better than just typing as an int) - other than this V
inner type alias.
Describe alternatives you've considered
I've also tried mypy typing V at a sibling scope to MyEnum
(eg MyEnumValue = NewType('MyEnumValue', int)
, but this is no improvement - as you still need to play tricks to import it from real code.
Thanks friends! Happy to open discussion.