Skip to content

Commit 809ccba

Browse files
authored
Merge branch 'master' into feature/gh-19392-decorator-selftypes
2 parents 7be5bd3 + a56adc8 commit 809ccba

File tree

10 files changed

+98
-28
lines changed

10 files changed

+98
-28
lines changed

mypy/checkexpr.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,14 +1755,6 @@ def check_callable_call(
17551755
return AnyType(TypeOfAny.from_error), callee
17561756
seen_unpack = True
17571757

1758-
formal_to_actual = map_actuals_to_formals(
1759-
arg_kinds,
1760-
arg_names,
1761-
callee.arg_kinds,
1762-
callee.arg_names,
1763-
lambda i: self.accept(args[i]),
1764-
)
1765-
17661758
# This is tricky: return type may contain its own type variables, like in
17671759
# def [S] (S) -> def [T] (T) -> tuple[S, T], so we need to update their ids
17681760
# to avoid possible id clashes if this call itself appears in a generic
@@ -1773,27 +1765,29 @@ def check_callable_call(
17731765
freeze_all_type_vars(fresh_ret_type)
17741766
callee = callee.copy_modified(ret_type=fresh_ret_type)
17751767

1768+
if callee.is_generic():
1769+
callee = freshen_function_type_vars(callee)
1770+
callee = self.infer_function_type_arguments_using_context(callee, context)
1771+
1772+
formal_to_actual = map_actuals_to_formals(
1773+
arg_kinds,
1774+
arg_names,
1775+
callee.arg_kinds,
1776+
callee.arg_names,
1777+
lambda i: self.accept(args[i]),
1778+
)
1779+
17761780
if callee.is_generic():
17771781
need_refresh = any(
17781782
isinstance(v, (ParamSpecType, TypeVarTupleType)) for v in callee.variables
17791783
)
1780-
callee = freshen_function_type_vars(callee)
1781-
callee = self.infer_function_type_arguments_using_context(callee, context)
1782-
if need_refresh:
1783-
# Argument kinds etc. may have changed due to
1784-
# ParamSpec or TypeVarTuple variables being replaced with an arbitrary
1785-
# number of arguments; recalculate actual-to-formal map
1786-
formal_to_actual = map_actuals_to_formals(
1787-
arg_kinds,
1788-
arg_names,
1789-
callee.arg_kinds,
1790-
callee.arg_names,
1791-
lambda i: self.accept(args[i]),
1792-
)
17931784
callee = self.infer_function_type_arguments(
17941785
callee, args, arg_kinds, arg_names, formal_to_actual, need_refresh, context
17951786
)
17961787
if need_refresh:
1788+
# Argument kinds etc. may have changed due to
1789+
# ParamSpec or TypeVarTuple variables being replaced with an arbitrary
1790+
# number of arguments; recalculate actual-to-formal map
17971791
formal_to_actual = map_actuals_to_formals(
17981792
arg_kinds,
17991793
arg_names,
@@ -2258,6 +2252,11 @@ def infer_function_type_arguments_pass2(
22582252
if isinstance(arg, (NoneType, UninhabitedType)) or has_erased_component(arg):
22592253
inferred_args[i] = None
22602254
callee_type = self.apply_generic_arguments(callee_type, inferred_args, context)
2255+
2256+
if not callee_type.is_generic():
2257+
# Fast path, second pass can't give new information.
2258+
return callee_type, []
2259+
22612260
if need_refresh:
22622261
formal_to_actual = map_actuals_to_formals(
22632262
arg_kinds,

mypy/checkmember.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ def expand_and_bind_callable(
956956
) -> Type:
957957
if not mx.preserve_type_var_ids:
958958
functype = freshen_all_functions_type_vars(functype)
959-
typ = get_proper_type(expand_self_type(var, functype, mx.original_type))
959+
typ = get_proper_type(expand_self_type(var, functype, mx.self_type))
960960
assert isinstance(typ, FunctionLike)
961961
if is_trivial_self:
962962
typ = bind_self_fast(typ, mx.self_type)

mypy/constraints.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,10 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]:
11101110
# like U -> U, should be Callable[..., Any], but if U is a self-type, we can
11111111
# allow it to leak, to be later bound to self. A bunch of existing code
11121112
# depends on this old behaviour.
1113-
and not any(tv.id.is_self() for tv in cactual.variables)
1113+
and not (
1114+
any(tv.id.is_self() for tv in cactual.variables)
1115+
and template.is_ellipsis_args
1116+
)
11141117
):
11151118
# If the actual callable is generic, infer constraints in the opposite
11161119
# direction, and indicate to the solver there are extra type variables

mypy/semanal_shared.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
TypeVarLikeType,
4747
TypeVarTupleType,
4848
UnpackType,
49+
flatten_nested_tuples,
4950
get_proper_type,
5051
)
5152

@@ -290,7 +291,7 @@ def calculate_tuple_fallback(typ: TupleType) -> None:
290291
fallback = typ.partial_fallback
291292
assert fallback.type.fullname == "builtins.tuple"
292293
items = []
293-
for item in typ.items:
294+
for item in flatten_nested_tuples(typ.items):
294295
# TODO: this duplicates some logic in typeops.tuple_fallback().
295296
if isinstance(item, UnpackType):
296297
unpacked_type = get_proper_type(item.type)

mypy/semanal_typeargs.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ def visit_type_alias_type(self, t: TypeAliasType) -> None:
102102
# If there was already an error for the alias itself, there is no point in checking
103103
# the expansion, most likely it will result in the same kind of error.
104104
get_proper_type(t).accept(self)
105+
if t.alias is not None:
106+
t.alias.accept(self)
105107

106108
def visit_tuple_type(self, t: TupleType) -> None:
107109
t.items = flatten_nested_tuples(t.items)
@@ -254,6 +256,10 @@ def visit_unpack_type(self, typ: UnpackType) -> None:
254256
def check_type_var_values(
255257
self, name: str, actuals: list[Type], arg_name: str, valids: list[Type], context: Context
256258
) -> bool:
259+
if self.in_type_alias_expr:
260+
# See testValidTypeAliasValues - we do not enforce typevar compatibility
261+
# at the definition site. We check instantiation validity later.
262+
return False
257263
is_error = False
258264
for actual in get_proper_types(actuals):
259265
# We skip UnboundType here, since they may appear in defn.bases,

mypy/solve.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None:
270270
uppers = new_uppers
271271

272272
# ...unless this is the only information we have, then we just pass it on.
273+
lowers = list(lowers)
273274
if not uppers and not lowers:
274275
candidate = UninhabitedType()
275276
candidate.ambiguous = True
@@ -281,10 +282,11 @@ def solve_one(lowers: Iterable[Type], uppers: Iterable[Type]) -> Type | None:
281282
# Process each bound separately, and calculate the lower and upper
282283
# bounds based on constraints. Note that we assume that the constraint
283284
# targets do not have constraint references.
284-
if type_state.infer_unions:
285+
if type_state.infer_unions and lowers:
285286
# This deviates from the general mypy semantics because
286287
# recursive types are union-heavy in 95% of cases.
287-
bottom = UnionType.make_union(list(lowers))
288+
# Retain `None` when no bottoms were provided to avoid bogus `Never` inference.
289+
bottom = UnionType.make_union(lowers)
288290
else:
289291
# The order of lowers is non-deterministic.
290292
# We attempt to sort lowers because joins are non-associative. For instance:

test-data/unit/check-protocols.test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4646,3 +4646,17 @@ reveal_type(t.bar) # N: Revealed type is "def () -> builtins.int"
46464646
tt: Type[P] = C
46474647
reveal_type(tt.foo) # N: Revealed type is "def (builtins.object) -> builtins.int"
46484648
reveal_type(tt.bar) # N: Revealed type is "def (builtins.object) -> builtins.int"
4649+
4650+
[case testProtocolDecoratedSelfBound]
4651+
from abc import abstractmethod
4652+
from typing import Protocol, Self
4653+
4654+
class Proto(Protocol):
4655+
@abstractmethod
4656+
def foo(self, x: Self) -> None: ...
4657+
4658+
class Impl:
4659+
def foo(self, x: Self) -> None:
4660+
pass
4661+
4662+
x: Proto = Impl()

test-data/unit/check-recursive-types.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ reveal_type(flatten([1, [2, [3]]])) # N: Revealed type is "builtins.list[builti
5454

5555
class Bad: ...
5656
x: Nested[int] = [1, [2, [3]]]
57-
x = [1, [Bad()]] # E: List item 1 has incompatible type "list[Bad]"; expected "Union[int, Nested[int]]"
57+
x = [1, [Bad()]] # E: List item 0 has incompatible type "Bad"; expected "Union[int, Nested[int]]"
5858
[builtins fixtures/isinstancelist.pyi]
5959

6060
[case testRecursiveAliasGenericInferenceNested]
@@ -605,7 +605,7 @@ class NT(NamedTuple, Generic[T]):
605605
class A: ...
606606
class B(A): ...
607607

608-
nti: NT[int] = NT(key=0, value=NT(key=1, value=A())) # E: Argument "value" to "NT" has incompatible type "NT[A]"; expected "Union[int, NT[int]]"
608+
nti: NT[int] = NT(key=0, value=NT(key=1, value=A())) # E: Argument "value" to "NT" has incompatible type "A"; expected "Union[int, NT[int]]"
609609
reveal_type(nti) # N: Revealed type is "tuple[builtins.int, Union[builtins.int, ...], fallback=__main__.NT[builtins.int]]"
610610

611611
nta: NT[A]

test-data/unit/check-typeddict.test

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4271,3 +4271,21 @@ reveal_type(dicts.TF) # N: Revealed type is "def (*, user_id: builtins.int =) -
42714271
reveal_type(dicts.TotalFalse) # N: Revealed type is "def (*, user_id: builtins.int =) -> TypedDict('__main__.Dicts.TF', {'user_id'?: builtins.int})"
42724272
[builtins fixtures/dict.pyi]
42734273
[typing fixtures/typing-typeddict.pyi]
4274+
4275+
[case testRecursiveNestedTypedDictInference]
4276+
from typing import TypedDict, Sequence
4277+
from typing_extensions import NotRequired
4278+
4279+
class Component(TypedDict):
4280+
type: str
4281+
components: NotRequired[Sequence['Component']]
4282+
4283+
inputs: Sequence[Component] = [{
4284+
'type': 'tuple',
4285+
'components': [
4286+
{'type': 'uint256'},
4287+
{'type': 'address'},
4288+
]
4289+
}]
4290+
[builtins fixtures/dict.pyi]
4291+
[typing fixtures/typing-typeddict.pyi]

test-data/unit/check-typevar-tuple.test

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,3 +2666,30 @@ def identity(smth: _FT) -> _FT:
26662666
class S(tuple[Unpack[Ts]], Generic[T, Unpack[Ts]]):
26672667
def f(self, x: T, /) -> T: ...
26682668
[builtins fixtures/tuple.pyi]
2669+
2670+
[case testNoCrashSubclassingTupleWithTrivialUnpack]
2671+
# https://github.com/python/mypy/issues/19105
2672+
from typing import Unpack
2673+
2674+
class A(tuple[Unpack[tuple[int]]]): ...
2675+
class B(tuple[Unpack[tuple[()]]]): ...
2676+
2677+
a: A
2678+
reveal_type(tuple(a)) # N: Revealed type is "builtins.tuple[builtins.int, ...]"
2679+
(x,) = a
2680+
2681+
b: B
2682+
(_,) = b # E: Need more than 0 values to unpack (1 expected)
2683+
[builtins fixtures/tuple.pyi]
2684+
2685+
[case testNoCrashSubclassingTupleWithVariadicUnpack]
2686+
# https://github.com/python/mypy/issues/19105
2687+
from typing import Unpack
2688+
2689+
class A(tuple[Unpack[tuple[int, ...]]]): ...
2690+
2691+
a: A
2692+
tuple(a)
2693+
(x,) = a
2694+
(_,) = a
2695+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)