Repository navigation
Open
Labels
Description
Bug Report
Currently, mypy allows widening type variables without an error, then errors (sometimes, see below) at usage time. This seems backwards to me. I'm opening this based on a minimization from @sterliakov in #18451.
To Reproduce
There's several interesting snippets:
import typing
IntT = typing.TypeVar("IntT", bound=int)
StrT = typing.TypeVar("StrT", bound=str)
AnyT = typing.TypeVar("AnyT", bound=typing.Any)
class IntC(typing.Generic[IntT]): ...
class StrC(typing.Generic[StrT]): ...
AnyC = IntC[AnyT] | StrC[AnyT]
def run(_: AnyC[StrT]) -> StrT: ... # E: Type argument "StrT" of "IntC" must be a subtype of "int"In this case, I would expect an error on the AnyC type alias definition, and maybe at usage time. What about other methods of widening type variables?
import typing
IntT = typing.TypeVar("IntT", bound=int)
StrT = typing.TypeVar("StrT", bound=str)
AnyT = typing.TypeVar("AnyT", bound=typing.Any)
class IntC(typing.Generic[IntT]): ...
class AnyC(IntC[AnyT]): # ! no error
pass
def f(x: StrT) -> StrT:
y: AnyC[StrT] # !? no error
return xThis version doesn't error at all! What I would expect is the behavior achieved by using object plus a class to widen:
import typing
IntT = typing.TypeVar("IntT", bound=int)
StrT = typing.TypeVar("StrT", bound=str)
AnyT = typing.TypeVar("AnyT", bound=object)
class IntC(typing.Generic[IntT]): ...
class StrC(typing.Generic[StrT]): ...
class AnyC1(IntC[AnyT]): # E: Type argument "AnyT" of "IntC" must be a subtype of "int"
pass
AnyC2 = IntC[AnyT] | StrC[AnyT] # ! no error
def f(x: StrT) -> StrT:
y1: AnyC1[StrT] # no error, this is fine
y2: AnyC2[StrT] # E: Type argument "StrT" of "IntC" must be a subtype of "int"
return xExpected Behavior
Consistency.
Actual Behavior
To summarize the above
| method of widening | bound=object |
bound=Any |
|---|---|---|
| type alias | usage error | usage error |
| class | definition error | no errors (!) |
Your Environment
Everything was double checked in mypy playground.
- Mypy version used: v1.14.1
- Mypy command-line flags: none
- Mypy configuration options from
mypy.ini(and other config files): N/A - Python version used: 3.12
Activity
sterliakov commentedon Jan 13, 2025
Perhaps this question is worth a python/typing discussion? It is rather typechecker-agnostic and concerns the meaning of annotations, not a specific implementation.
A5rocks commentedon Jan 14, 2025
I think a good enough solution is to ensure that classes error at definition time and type aliases at usage time, based on the level of formality described in #18451. I think the most egregious issue here is that
bound=Anytypevar that prevents any errors.However, changing
is_subtypetois_proper_subtypein the relevant place leads to rejecting gradual types. I'm not sure the right approach here... I don't like current behavior but at least across mypy's test cases it's typical -- e.g.Callable[..., Any]instead ofCallable[..., object]in a typevar bound (nooo!! why!). Maybe status quo is indeed the best.