Skip to content
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

fix: incorrect returned type of access descriptors on unions of types #16604

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def copy_modified(
messages: MessageBuilder | None = None,
self_type: Type | None = None,
is_lvalue: bool | None = None,
original_type: Type | None = None,
) -> MemberContext:
mx = MemberContext(
self.is_lvalue,
Expand All @@ -142,6 +143,8 @@ def copy_modified(
mx.self_type = self_type
if is_lvalue is not None:
mx.is_lvalue = is_lvalue
if original_type is not None:
mx.original_type = original_type
return mx


Expand Down Expand Up @@ -650,6 +653,16 @@ def analyze_descriptor_access(descriptor_type: Type, mx: MemberContext) -> Type:
return make_simplified_union(
[analyze_descriptor_access(typ, mx) for typ in descriptor_type.items]
)
elif isinstance(instance_type, UnionType):
# map over the instance types
return make_simplified_union(
[
analyze_descriptor_access(
descriptor_type, mx.copy_modified(original_type=original_type)
)
for original_type in instance_type.items
]
)
elif not isinstance(descriptor_type, Instance):
return orig_descriptor_type

Expand Down
38 changes: 38 additions & 0 deletions test-data/unit/check-unions.test
Original file line number Diff line number Diff line change
Expand Up @@ -1220,3 +1220,41 @@ nc: Union[Container[str], int]
'x' in nc # E: Unsupported right operand type for in ("Union[Container[str], int]")
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]

[case testDescriptorAccessForUnionOfTypes]
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')

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"
[builtins fixtures/list.pyi]