10

I'm trying to add type annotations to an existing package, and clearly I'm missing something important. I have an abstract superclass, and subclasses. The superclass should be generic, whereas the subclasses should be for a specific type. Here's a simple example if that I see, and what I'd like to see:

from typing import Generic, TypeVar

T = TypeVar("T")

class A(Generic[T]):
    def method(self, arg: T):
        ...

class B(A[int]):
    def method(self, arg):
        reveal_locals()

Expected (or at least hoped for):

GenericTest.py:11: note: Revealed local types are:
GenericTest.py:11: note:     arg: int
GenericTest.py:11: note:     self: Any

Got:

GenericTest.py:11: note: Revealed local types are:
GenericTest.py:11: note:     arg: Any
GenericTest.py:11: note:     self: Any
brunns
  • 2,689
  • 1
  • 13
  • 24
  • This is a [known limitation](https://github.com/python/mypy/issues/3903) and it does not seem there are plans to implement it – Jackson H Jun 30 '22 at 14:39

2 Answers2

6

You need to add type annotations to the methods in your subclass. So doing:

from typing import Generic, TypeVar

T = TypeVar("T")

class A(Generic[T]):
    def method(self, arg: T) -> None:
        ...

class B(A[int]):
    def method(self, arg: int) -> None:
        reveal_locals()

...results in the expected output of:

test.py:11: note: Revealed local types are:
test.py:11: note:     arg: builtins.int
test.py:11: note:     self: test.B

If a function is not annotated with type hints, mypy will treat that function as being dynamically typed and assume all of its arguments are of type Any.

If you would like mypy to warn you when you forget type hints like this, run mypy with the --disallow-untyped-defs command line flag -- and also maybe --disallow-incomplete-defs for good measure. Or alternatively, run mypy with the --strict flag, which automatically enables the above two flags (and more).

Michael0x2a
  • 58,192
  • 30
  • 175
  • 224
  • 3
    That certainly works, but feels repetitive - I need `int` in two places (and perhaps more in real world cases). Class `A` is defined over a generic type `T`, and I was hoping to be able to define a subclass setting `T` to a concrete type, just the once, but I see that isn't going to work. Thanks for your help, though! – brunns Aug 19 '19 at 13:25
5

reveal_locals() prints the types of the variables inferred by mypy in the scope where the call its placed. And when redefining a method in a subclass, you are also overriding the annotations (Any is used by default when no parameter annotation is given explicitly)

This could be more clear:

class A(Generic[T]):
    def method(self, arg: T):
        ...

class B(A[int]):
    def method(self, arg):
        pass

B().method('')

The above code is fine for mypy but the next gives an error:

class C(A[int]):
    pass

C().method('')
error: Argument 1 to "method" of "A" has incompatible type "str"; expected "int"
Victor Ruiz
  • 1,192
  • 9
  • 9
  • 1
    I my case, I do need to override the method in the subclass, and I was hoping to specify the subclasses concrete type just the once. Looks like I can't do that. Thanks for your help, though! – brunns Aug 19 '19 at 13:27