Skip to content

Commit 94109aa

Browse files
Fix TypeVar upper bounds sometimes not being displayed in pretty callables (#17802)
Fixes #17792. Related to #17791. Currently, [`pretty_callable`](https://github.com/python/mypy/blob/5dfc7d941253553ab77836e9845cb8fdfb9d23a9/mypy/messages.py#L2862) only renders `TypeVar` upper bounds if they are of type `Instance`: https://github.com/python/mypy/blob/5dfc7d941253553ab77836e9845cb8fdfb9d23a9/mypy/messages.py#L2943-L2949 However, there are some types that can appear as `TypeVar` upper bounds which are not represented by `Instance`, such as `UnionType` and `CallableType`. This PR allows such non-`Instance` upper bounds to be rendered as well. ## Effect Consider the below code. Playground link: https://mypy-play.net/?mypy=1.11.2&python=3.12&enable-incomplete-feature=NewGenericSyntax&gist=ba30c820cc3668e0919dadf2f391ff4b ```python from collections.abc import Callable from typing import Any, overload ### No matching overloads @overload def f1[T: int](x: T) -> T: ... @overload def f1[T: Callable[..., None]](x: T) -> T: ... @overload def f1[T: tuple[int]](x: T) -> T: ... @overload def f1[T: None](x: T) -> T: ... @overload def f1[T: type[int]](x: T) -> T: ... @overload def f1[T: bytes | bytearray](x: T) -> T: ... def f1(x): return x f1(1.23) ### Mismatching conditional definitions if input(): def f2[T](x: T) -> T: return x else: def f2[T: Callable[..., None]](x: T) -> T: return x ``` ### Before * In the first error on line 20, all overloads aside from the first one are displayed as `def [T] f1(x: T) -> T` (upper bound missing). Duplicate entries are suppressed. * In the second error on line 28, the second definition is displayed as `def [T] f2(x: T) -> T` (upper bound missing), and is removed as an apparent duplicate of the first. ```none main.py:20: error: No overload variant of "f1" matches argument type "float" [call-overload] main.py:20: note: Possible overload variants: main.py:20: note: def [T: int] f1(x: T) -> T main.py:20: note: def [T] f1(x: T) -> T main.py:28: error: All conditional function variants must have identical signatures [misc] main.py:28: note: Original: main.py:28: note: def [T] f2(x: T) -> T main.py:28: note: Redefinition: Found 2 errors in 1 file (checked 1 source file) ``` ### After * All type var upper bounds are rendered. ```none main.py:20: error: No overload variant of "f1" matches argument type "float" [call-overload] main.py:20: note: Possible overload variants: main.py:20: note: def [T: int] f1(x: T) -> T main.py:20: note: def [T: Callable[..., None]] f1(x: T) -> T main.py:20: note: def [T: tuple[int]] f1(x: T) -> T main.py:20: note: def [T: None] f1(x: T) -> T main.py:20: note: def [T: type[int]] f1(x: T) -> T main.py:20: note: def [T: bytes | bytearray] f1(x: T) -> T main.py:28: error: All conditional function variants must have identical signatures [misc] main.py:28: note: Original: main.py:28: note: def [T] f2(x: T) -> T main.py:28: note: Redefinition: main.py:28: note: def [T: Callable[..., None]] f2(x: T) -> T Found 2 errors in 1 file (checked 1 source file) ```
1 parent 5dfc7d9 commit 94109aa

File tree

2 files changed

+16
-2
lines changed

2 files changed

+16
-2
lines changed

mypy/messages.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2942,9 +2942,9 @@ def [T <: int] f(self, x: int, y: T) -> None
29422942
for tvar in tp.variables:
29432943
if isinstance(tvar, TypeVarType):
29442944
upper_bound = get_proper_type(tvar.upper_bound)
2945-
if (
2945+
if not (
29462946
isinstance(upper_bound, Instance)
2947-
and upper_bound.type.fullname != "builtins.object"
2947+
and upper_bound.type.fullname == "builtins.object"
29482948
):
29492949
tvars.append(f"{tvar.name}: {format_type_bare(upper_bound, options)}")
29502950
elif tvar.values:

test-data/unit/check-functions.test

+14
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,20 @@ else:
14281428
# N: Redefinition: \
14291429
# N: def f(x: int = ...) -> None
14301430

1431+
[case testIncompatibleConditionalFunctionDefinition4]
1432+
from typing import Any, Union, TypeVar
1433+
T1 = TypeVar('T1')
1434+
T2 = TypeVar('T2', bound=Union[int, str])
1435+
x = None # type: Any
1436+
if x:
1437+
def f(x: T1) -> T1: pass
1438+
else:
1439+
def f(x: T2) -> T2: pass # E: All conditional function variants must have identical signatures \
1440+
# N: Original: \
1441+
# N: def [T1] f(x: T1) -> T1 \
1442+
# N: Redefinition: \
1443+
# N: def [T2: Union[int, str]] f(x: T2) -> T2
1444+
14311445
[case testConditionalFunctionDefinitionUsingDecorator1]
14321446
from typing import Callable
14331447

0 commit comments

Comments
 (0)