-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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:
passExpected 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).