mmhl
mmhl

Reputation: 11

Variable arguments with mypy: signature of method incompatible with supertype

I'm not quite sure why mypy is returning the Signature of "foo" is incompatible with supertype "Base" error here. The return types and the argument types seem to match up.

from typing import NoReturn, Union


class Base():
    def foo(self, *args: str) -> Union[NoReturn, str]:
        raise NotImplementedError


class A(Base):
    def foo(self, x: str) -> str:
        return x

Any help is greatly appreciated, thank you!

EDIT I changed up A.foo to this:

def foo(self, *args: str) -> str:
    x = args[0]
    return x

Now the mypy error disappeared. However, with this implementation, wouldn't the same issue occur where if there's a NoReturn in Base, the overridden function can only return str?

Upvotes: 1

Views: 2705

Answers (2)

Jan Rüegg
Jan Rüegg

Reputation: 10075

Without your EDIT, you have the issue described by @Skam: https://stackoverflow.com/a/55071593/369009

With the EDIT, everything is fine. Think about this code:

bs: Base = A() # bs is of type Base
return_value = bs.foo('test') # Return value is of type `Union[NoReturn, str]`

When calling bs.foo, we expet the return value to be either a NoReturn or a str. A always returns a str, but this is fine, since a str is also part of the Union[NoReturn, str], so this is always valid. Anyone calling foo on the base class has to handle both return types, and A just happens to always return only a str and never a NoReturn.

Only if it were the other way around it would be an issue: Assuming A would return the Union and Base would return a str, then callers of Base.foo might suddendly get a NoReturn when they expected only a str.

Upvotes: 1

Skam
Skam

Reputation: 7808

The difference is a little subtle. So I'll explain what each does

def foo(self, *args: str) -> Union[NoReturn, str]:

foo is an instance method of the class Base that takes a variable amount of string arguments and returns either NoReturn or a str.

def foo(self, x: str) -> str:

foo is an instance method of the class A that overrides the definition of foo in the Base class. It takes a single str argument and returns a single str

What this means is I could call the Base version like foo('bar', 'baz') but the same call in A would throw an error.

Similarly, I can have no return in Base while the overridden function call only returns str

The issue with this here is that the function has different behavior and type but the same name, which is misleading.

Upvotes: 1

Related Questions