Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does replacing a type variable make a subclass incompatible? #11605

Closed
A5rocks opened this issue Nov 23, 2021 · 3 comments
Closed

Does replacing a type variable make a subclass incompatible? #11605

A5rocks opened this issue Nov 23, 2021 · 3 comments
Labels
bug mypy got something wrong topic-inheritance Inheritance and incompatible overrides topic-type-variables

Comments

@A5rocks
Copy link
Contributor

A5rocks commented Nov 23, 2021

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).

@A5rocks A5rocks added the bug mypy got something wrong label Nov 23, 2021
@erictraut
Copy link

This looks like a bug in mypy. I can simplify the example to the following:

T = typing.TypeVar("T", bound=A)
V = typing.TypeVar("V", bound=B)
S = typing.TypeVar("S")

class Whatever(typing.Generic[T]):
    def something(self: S) -> S:
        return self

class WhateverPartTwo(Whatever[A], typing.Generic[V]):
    def something(self: S) -> S:
        return self

This still results in an error.

If I then delete the typing.Generic[V] from the WhateverPartTwo class, the error disappears. That might provide a clue about the source of the bug.

@A5rocks A5rocks changed the title Is replacing a type variable a violation of LSP? Does replacing a type variable make a subclass incompatible? Nov 23, 2021
@erictraut
Copy link

This appears to be fixed in the latest published version of mypy (0.950).

@AlexWaygood
Copy link
Member

This appears to be fixed in the latest published version of mypy (0.950).

Yup, and a test case was added in #12623, so there's nothing more to do here. Thanks!

(Feel free to ping me when you come across issues like this @erictraut, btw 🙂)

@AlexWaygood AlexWaygood added topic-type-variables topic-inheritance Inheritance and incompatible overrides labels May 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-inheritance Inheritance and incompatible overrides topic-type-variables
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants