-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Invalid typing assumption over variable with list(StrEnum) on python 3.11 #14688
Comments
In case it helps, I've just encountered the same issue in a slightly different situation: from enum import StrEnum
class Choices(StrEnum):
A = "a"
def my_func(obj: Choices) -> None:
assert isinstance(obj, Choices)
for element in list(Choices):
my_func(element) Which gives:
In my case I'm not assigning |
Are there any plans to work on this? We've had to silence this error many times in our codebase :( |
I was fiddling around with this issue and I found a, somewhat reasonable, workaround where you don't have to from typing import overload, Self
from enum import StrEnum as _StrEnum
class StrEnum(_StrEnum):
# mimic str.__new__'s overloads
@overload
def __new__(cls, object: object = ...) -> Self: ...
@overload
def __new__(cls, object: object, encoding: str = ..., errors: str = ...) -> Self: ...
def __new__(cls, *values):
# when we import enum, _StrEnum.__new__ gets moved to _new_member_ when
# the "final" _StrEnum class is created via EnumType
return _StrEnum._new_member_(cls, *values)
class Choices(StrEnum):
LOREM = "lorem"
IPSUM = "ipsum"
# this is still ok
def ok_func() -> list[Choices]:
return list(Choices)
# and this no longer produces an error
def error_func() -> list[Choices]:
var = list(Choices)
return var https://mypy-play.net/?mypy=latest&python=3.11&gist=3e91cdf18f5a07b47d6619cb43940b6c This also seems to cover the issue @Apakottur found: You can then use it like so: # file path: project_root/fixes/enum.py
from typing import overload, Self
from enum import StrEnum as _StrEnum
__all__ = ['StrEnum', ]
class StrEnum(_StrEnum):
@overload
def __new__(cls, object: object = ...) -> Self: ...
@overload
def __new__(cls, object: object, encoding: str = ..., errors: str = ...) -> Self: ...
def __new__(cls, *values):
return _StrEnum._new_member_(cls, *values) # file path: project_root/main.py
from fixes.enum import StrEnum
class Choices(StrEnum):
LOREM = "lorem"
IPSUM = "ipsum"
if __name__ == '__main__':
print(Choices.__members__)
# outputs:
# {'LOREM': <Choices.LOREM: 'lorem'>, 'IPSUM': <Choices.IPSUM: 'ipsum'>} I'm not sure how # current enum.pyi
class StrEnum(str, ReprEnum):
def __new__(cls, value: str) -> Self: ...
_value_: str
@_magic_enum_attr
def value(self) -> str: ...
@staticmethod
def _generate_next_value_(name: str, start: int, count: int, last_values: list[str]) -> str: ... Was updated to account for # proposed enum.pyi
class StrEnum(str, ReprEnum):
@overload
def __new__(cls, object: object = ...) -> Self: ...
@overload
def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
_value_: str
@_magic_enum_attr
def value(self) -> str: ...
@staticmethod
def _generate_next_value_(name: str, start: int, count: int, last_values: list[str]) -> str: ... I understand that the documentation for def __new__(cls, *values):
"values must already be of type `str`"
if len(values) > 3:
raise TypeError('too many arguments for str(): %r' % (values, ))
if len(values) == 1:
# it must be a string
if not isinstance(values[0], str):
raise TypeError('%r is not a string' % (values[0], ))
if len(values) >= 2:
# check that encoding argument is a string
if not isinstance(values[1], str):
raise TypeError('encoding must be a string, not %r' % (values[1], ))
if len(values) == 3:
# check that errors argument is a string
if not isinstance(values[2], str):
raise TypeError('errors must be a string, not %r' % (values[2]))
value = str(*values)
member = str.__new__(cls, value)
member._value_ = value
return member EditUpdating the |
@jhenly your fix works, thanks! Although I really don't like having to import a custom StrEnum, we'll probably just keep silencing the errors for now. |
I don't think this has anything to do with assigning from enum import (
StrEnum,
Enum,
)
from typing import reveal_type
class TestEnum(Enum):
ONE = 'one'
class TestStrEnum(StrEnum):
ONE = 'one'
reveal_type(TestEnum.ONE)
reveal_type(TestStrEnum.ONE)
def test_enum(param: TestEnum):
print(param)
def test_str_enum(param: TestStrEnum):
print(param)
test_enum(TestEnum.ONE)
test_str_enum(TestStrEnum.ONE) The output is:
|
@jarmstrong-atlassian how are you performing the test? I get the following output with Python 3.11.1 and Mypy 1.9.0:
|
@kikones34 My bad. I was running |
Another weird case + a workaround that appeases mypy: from collections.abc import Iterable
from enum import StrEnum
def takes_enum(e: StrEnum) -> StrEnum:
return e
def takes_enum_type(type_: type[StrEnum]) -> StrEnum:
return next(iter(type_)) # error: Incompatible return value type (got "str", expected "StrEnum") [return-value]
def takes_enum_type_with_trick(type_: type[StrEnum]) -> StrEnum:
return next(iter(mypy_type_hint_trick(type_))) # no error!
def mypy_type_hint_trick(type_: type[StrEnum]) -> Iterable[StrEnum]:
return type_ |
Bug Report
On python 3.11 using StrEnum, if converting given enum to a list and assigning to a variable, mypy will wrongly assumes that given variable will be
list[str]
, notlist[StrEnum]
.To Reproduce
https://mypy-play.net/?mypy=latest&python=3.11&gist=e73a15c8902092236567da4a4567f372
Expected Behavior
Mypy should assume
list[Choices]
type forvar
.Actual Behavior
Incompatible return value type (got "List[str]", expected "List[Choices]") [return-value]
Your Environment
mypy.ini
(and other config files): noneThe text was updated successfully, but these errors were encountered: