Skip to content

Commit d25d680

Browse files
Use type variable bound when it appears as actual during inference (#16178)
This should help with re-enabling the use of `ParamSpec` in `functools.wraps` (as it looks like some of the new errors in AlexWaygood#11 are caused by not handling this). --------- Co-authored-by: hauntsaninja <hauntsaninja@gmail.com> Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
1 parent 5f6961b commit d25d680

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

mypy/constraints.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,18 @@ def _infer_constraints(
328328
if isinstance(template, TypeVarType):
329329
return [Constraint(template, direction, actual)]
330330

331+
if (
332+
isinstance(actual, TypeVarType)
333+
and not actual.id.is_meta_var()
334+
and direction == SUPERTYPE_OF
335+
):
336+
# Unless template is also a type variable (or a union that contains one), using the upper
337+
# bound for inference will usually give better result for actual that is a type variable.
338+
if not isinstance(template, UnionType) or not any(
339+
isinstance(t, TypeVarType) for t in template.items
340+
):
341+
actual = get_proper_type(actual.upper_bound)
342+
331343
# Now handle the case of either template or actual being a Union.
332344
# For a Union to be a subtype of another type, every item of the Union
333345
# must be a subtype of that type, so concatenate the constraints.

test-data/unit/check-inference.test

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3695,6 +3695,36 @@ reveal_type(f(g)) # N: Revealed type is "Tuple[Never, Never]" \
36953695
# E: Argument 1 to "f" has incompatible type "Callable[[VarArg(str)], None]"; expected "Call[Never]"
36963696
[builtins fixtures/list.pyi]
36973697

3698+
[case testInferenceAgainstTypeVarActualBound]
3699+
from typing import Callable, TypeVar
3700+
3701+
T = TypeVar("T")
3702+
S = TypeVar("S")
3703+
def test(f: Callable[[T], S]) -> Callable[[T], S]: ...
3704+
3705+
F = TypeVar("F", bound=Callable[..., object])
3706+
def dec(f: F) -> F:
3707+
reveal_type(test(f)) # N: Revealed type is "def (Any) -> builtins.object"
3708+
return f
3709+
3710+
[case testInferenceAgainstTypeVarActualUnionBound]
3711+
from typing import Protocol, TypeVar, Union
3712+
3713+
T_co = TypeVar("T_co", covariant=True)
3714+
class SupportsFoo(Protocol[T_co]):
3715+
def foo(self) -> T_co: ...
3716+
3717+
class A:
3718+
def foo(self) -> A: ...
3719+
class B:
3720+
def foo(self) -> B: ...
3721+
3722+
def foo(f: SupportsFoo[T_co]) -> T_co: ...
3723+
3724+
ABT = TypeVar("ABT", bound=Union[A, B])
3725+
def simpler(k: ABT):
3726+
foo(k)
3727+
36983728
[case testInferenceWorksWithEmptyCollectionsNested]
36993729
from typing import List, TypeVar, NoReturn
37003730
T = TypeVar('T')

0 commit comments

Comments
 (0)