Closed
Description
Bug Report
Mypy will return the incorrect type for access descriptors on unions of types. It returns the types of the attributes on the instance of the type rather than the type itself.
See sqlalchemy/sqlalchemy#9419 for a concrete example with sqlalchemy.
To Reproduce
# Ideally, a small sample program that demonstrates the problem.
# Or even better, a reproducible playground link https://mypy-play.net/ (use the "Gist" button)
from typing import overload, Generic, Any, TypeVar, List, Optional, Union, Type
_T_co = TypeVar("_T_co", bound=Any, covariant=True)
class Mapped(Generic[_T_co]):
def __init__(self, value: _T_co):
self.value = value
@overload
def __get__(
self, instance: None, owner: Any
) -> List[_T_co]:
...
@overload
def __get__(self, instance: object, owner: Any) -> _T_co:
...
def __get__(
self, instance: Optional[object], owner: Any
) -> Union[List[_T_co], _T_co]:
return self.value
class A:
field_1: Mapped[int] = Mapped(1)
field_2: Mapped[str] = Mapped('1')
class B:
field_1: Mapped[int] = Mapped(2)
field_2: Mapped[str] = Mapped('2')
Expected Behavior
mix: Union[Type[A], Type[B]] = A
reveal_type(mix) # N: Revealed type is "Union[Type[__main__.A], Type[__main__.B]]"
reveal_type(mix.field_1) # N: Revealed type is "builtins.list[builtins.int]"
reveal_type(mix().field_1) # N: Revealed type is "builtins.int"
Actual Behavior
mix: Union[Type[A], Type[B]] = A
reveal_type(mix) # N: Revealed type is "Union[Type[__main__.A], Type[__main__.B]]"
reveal_type(mix.field_1) # N: Revealed type is "builtins.int"
reveal_type(mix().field_1) # N: Revealed type is "builtins.int"
reveal_type(mix.field_1)
is incorrectly builtins.int
, should be builtins.list[builtins.int]
.
Your Environment
- Mypy version used: master (1.7)
- Mypy command-line flags: None
- Mypy configuration options from
mypy.ini
(and other config files): None - Python version used: 3.11