Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions mypy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,11 @@ def handle_recursive_union(template: UnionType, actual: Type, direction: int) ->
# the union in two parts, and try inferring sequentially.
non_type_var_items = [t for t in template.items if not isinstance(t, TypeVarType)]
type_var_items = [t for t in template.items if isinstance(t, TypeVarType)]
return infer_constraints(
UnionType.make_union(non_type_var_items), actual, direction
) or infer_constraints(UnionType.make_union(type_var_items), actual, direction)
ret = infer_constraints(UnionType.make_union(non_type_var_items), actual, direction)
if ret or any(mypy.subtypes.is_subtype(t, actual) for t in non_type_var_items):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using is_subtype() independently of direction is suspicious.

return ret
ret = infer_constraints(UnionType.make_union(type_var_items), actual, direction)
return ret


def any_constraints(options: list[list[Constraint] | None], *, eager: bool) -> list[Constraint]:
Expand Down
9 changes: 9 additions & 0 deletions test-data/unit/check-recursive-types.test
Original file line number Diff line number Diff line change
Expand Up @@ -1040,3 +1040,12 @@ def f(obj: OneClass) -> None:
else:
reveal_type(obj) # N: Revealed type is "builtins.list[...]"
[builtins fixtures/isinstancelist.pyi]

[case testRecursiveDictOr]
from typing_extensions import TypeAlias
A: TypeAlias = "dict[str, A | int]"

def f(d1: A, d2: A) -> None:
d3: A = {**d1, **d2}
d4: A = d1 | d2
[builtins fixtures/dict-full.pyi]
12 changes: 3 additions & 9 deletions test-data/unit/fixtures/dict-full.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ from typing import (
)

T = TypeVar('T')
T1 = TypeVar('T1')
T2 = TypeVar('T2')
KT = TypeVar('KT')
VT = TypeVar('VT')
Expand Down Expand Up @@ -39,15 +40,8 @@ class dict(Mapping[KT, VT]):
def get(self, key: KT, default: T, /) -> Union[VT, T]: pass
def __len__(self) -> int: ...

# This was actually added in 3.9:
@overload
def __or__(self, __value: dict[KT, VT]) -> dict[KT, VT]: ...
@overload
def __or__(self, __value: dict[T, T2]) -> dict[Union[KT, T], Union[VT, T2]]: ...
@overload
def __ror__(self, __value: dict[KT, VT]) -> dict[KT, VT]: ...
@overload
def __ror__(self, __value: dict[T, T2]) -> dict[Union[KT, T], Union[VT, T2]]: ...
def __or__(self, value: dict[T1, T2], /) -> dict[KT | T1, VT | T2]: ...
def __ror__(self, value: dict[T1, T2], /) -> dict[KT | T1, VT | T2]: ...
# dict.__ior__ should be kept roughly in line with MutableMapping.update()
@overload # type: ignore[misc]
def __ior__(self, __value: _typeshed.SupportsKeysAndGetItem[KT, VT]) -> Self: ...
Expand Down
Loading