Description
Bug Report
Replace a type variable with another one (by specializing and then another generic). Is this type safe?
To Reproduce
Here's some pretty minimal code:
import typing
class A:
pass
class B:
pass
T = typing.TypeVar("T", bound=A)
V = typing.TypeVar("V", bound=B)
_WhateverT = typing.TypeVar("_WhateverT", bound="Whatever[typing.Any]")
_WhateverPartTwoT = typing.TypeVar("_WhateverPartTwoT", bound="WhateverPartTwo[typing.Any]")
class Whatever(typing.Generic[T]):
def something(self: _WhateverT) -> _WhateverT:
return self
def foo(self) -> T:
pass
# here we specialize `Whatever` (only allow `A` to be passed in), and then generalize once again.
class WhateverPartTwo(Whatever[A], typing.Generic[V]):
def something(self: _WhateverPartTwoT) -> _WhateverPartTwoT:
return self
def blah(self) -> V:
pass
Expected Behavior
I'm not sure, honestly. My gut instinct is that mypy is right here and LSP is being violated... but I can't think of any sequence of functions to run to get type unsafety without resorting to typing.Any
which brings its own unsafety. Additionally, pyright does not mark this as an error (though that just may be me failing to use pyright correctly...).
Actual Behavior
main.py:20: error: Return type "WhateverPartTwo[V]" of "something" incompatible with return type "WhateverPartTwo[A]" in supertype "Whatever"
Found 1 error in 1 file (checked 1 source file)
Your Environment
- Mypy version used: latest (0.910) and master
- Mypy command-line flags: none (mypy-playground)
- Mypy configuration options from
mypy.ini
(and other config files): none (mypy-playground) - Python version used: 3.10
- Operating system and version: windows and whatever mypy-playground is using
in simpler (?) terms
I have Thing<A: upper bound 1>
, specialize it to be SpecializedThing
, then generic something in it to get CoolSpecializedThing<B: upper bound 2>
. Is that OK, does CoolSpecializedThing
work?
But confounding the question more, I have a something<T: Thing<A>>(self: T) -> T
and I reimplement that on CoolSpecializedThing
to get something<T: CoolSpecializedThing<B>>(self: T) -> T
.
Normally with subclassing, this is fine:
import typing
AT = typing.TypeVar("AT", bound="A")
BT = typing.TypeVar("BT", bound="B")
class A:
def whatever(self: AT) -> AT: ...
class B:
def whatever(self: BT) -> BT: ...
And since CoolSpecializedThing
is a subclass of Thing
this should work right??
And yet the type variables have different upper bounds so like..... arghhhh. (I can't replace Thing<whatever>
with CoolSpecializedThing<whatever>
as the upper bounds of the type variables are different)
(I'll be honest, I'm mainly opening this issue here because I'm curious about this, I'm certain I can work around this one way or another).