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

generic subclasss and typevar with constraint #7362

Open
ZeeD opened this issue Aug 18, 2019 · 10 comments
Open

generic subclasss and typevar with constraint #7362

ZeeD opened this issue Aug 18, 2019 · 10 comments
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-type-variables

Comments

@ZeeD
Copy link

ZeeD commented Aug 18, 2019

Not sure if this is a "bug" or I didn't understand generics, but

import typing

T = typing.TypeVar('T', str, int)


class Base(typing.Generic[T]):

    def __init__(self, bar: T):
        self.bar: T = bar


class Child(Base[T]):

    def __init__(self, bar: T):
        super().__init__(bar)

mypy 0.720 (on windows) says

> mypy.exe test\__init__.py
test\__init__.py:15: error: Argument 1 to "__init__" of "Base" has incompatible type "str"; expected "T"
test\__init__.py:15: error: Argument 1 to "__init__" of "Base" has incompatible type "int"; expected "T"

but if I change the T definition to T = typing.TypeVar('T') the errors go away

What I want is to specify that

  • the "Base" constructor has a parameter that could be an int or a str
  • the "Child" constructor has the same "signature" of the "Base" one (well, this is a simplified example, I want something more...)

is this a bug? di I misinterpreted the TypeVar usage?

@ilevkivskyi
Copy link
Member

I think this is a bug. Most likely this is a bug during generation of function expansion for __init__(). You can just # type: ignore it for now (this will still record the type, but will silence the error).

@ilevkivskyi ilevkivskyi added bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-type-variables labels Aug 19, 2019
@gantsevdenis
Copy link
Contributor

gantsevdenis commented Aug 21, 2019

I am not an expert here, but is it really a bug?..
I think what @ZeeD you wanted to do is following, correct me if I am wrong:

T = typing.TypeVar('T', str, int)
class Base(typing.Generic[T]):

   def __init__(self, bar: T):
       self.bar: T = bar

class Child(Base, typing.Generic[T]):  # Why would you pass `T` parameter again to the `Base`? 
   def __init__(self, bar: T):
       super().__init__(bar)
       self.bar: T = bar

def f(x: int) -> int:  return x
def g(y: str) -> str: return y


f(Base(5).bar)  # no error here, inferred int correctly
g(Base("").bar)  # no error, inferred str correctly

f(Child(5).bar)  # no error here, inferred int correctly
g(Child("").bar)  # no error, inferred str correctly

# f(Child(1.0).bar)  # error, Value of type variable "T" of "Child" cannot be "float"
# f(Base(1.0).bar)  # error, Value of type variable "T" of "Base" cannot be "float"

You said:

What I want is to specify that

  1. the "Base" constructor has a parameter that could be an int or a str
  2. the "Child" constructor has the same "signature" of the "Base" one

I think my example fulfills both points

On the other hand, running mypy with --strict flag reports an error error: Missing type parameters for generic type "Base".

@ilevkivskyi
Copy link
Member

@gantsevdenis This is just wrong, in your example, e.g., Child[str] is a subtype of Base[int]. This is because Child[<whatever>] maps to Base[Any].

Why would you pass T parameter again to the Base?

Sometimes it helps to just read the docs https://mypy.readthedocs.io/en/latest/generics.html#defining-sub-classes-of-generic-classes

@gantsevdenis
Copy link
Contributor

Oh... well thank you for pointing that out, my bad

@aldanor
Copy link

aldanor commented Oct 29, 2019

@ilevkivskyi Was about to post almost exactly the example pointed out in this issue, apparently this bug's still present in the latest version. Are there any plans on resolving this, or is any help needed?

@JukkaL
Copy link
Collaborator

JukkaL commented Oct 29, 2019

We have no plan to fix this in the near future. Any help would be appreciated, though! If you want to fix this, feel free to ask questions here (or in a draft PR).

@intgr
Copy link
Contributor

intgr commented Mar 16, 2022

I recently hit a similar problem, here's a shorter reproducer. Mypy Playground link

from typing import TypeVar

T = TypeVar('T', str, int)


class BlaList(list[T]):
    def pop_item(self, key: int) -> T:
        return super().pop(key)

# main.py:8: error: Incompatible return value type (got "T", expected "str")
# main.py:8: error: Incompatible return value type (got "T", expected "int")

(In case I am mistaken, let me know, I can open a separate issue)

@intgr
Copy link
Contributor

intgr commented Mar 16, 2022

I found a work-around for my use case, using TypeVar(bound=<union type>). Is there a good reason why mypy treats these differently? Mypy Playground link

from typing import TypeVar

T = TypeVar('T', bound=str | int)


class BlaList(list[T]):
    def pop_item(self, key: int) -> T:
        return super().pop(key)

@JelleZijlstra
Copy link
Member

@intgr that's a very different thing. We're about to add better documentation of the difference to CPython: https://github.com/python/cpython/pull/31712/files (I should probably merge that PR)

@intgr
Copy link
Contributor

intgr commented Mar 17, 2022

Thanks, that documentation clears up the distinction for me.

Your wording "very different thing" is too strong; in my use case it really doesn't matter, so it's a decent work-around for this bug. But I understand now why mypy needs to treat them differently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal topic-type-variables
Projects
None yet
Development

No branches or pull requests

7 participants