mypy
mypy copied to clipboard
`@overload`s on `@abstractmethod`s shouldn't need an implementation
from abc import ABC, abstractmethod
from typing import overload
class Foo(ABC):
@overload # error: An overloaded function outside a stub file must have an implementation
@abstractmethod
def foo(self, value: int) -> int:
...
@overload
@abstractmethod
def foo(self, value: str) -> str:
...
# (this fake impl shouldnt be necessary, that's the impl class's responsibility)
# @abstractmethod
# def foo(self, value: str | int) -> str | int:
# ...
class Bar(Foo):
@overload
def foo(self, value: int) -> int:
...
@overload
def foo(self, value: str) -> str:
...
def foo(self, value: str | int) -> str | int:
return 1
https://mypy-play.net/?mypy=latest&python=3.10&gist=9c19741f8c5682b5854d59f3cf5f9ade
the implementation on the abstract class serves no purpose and should not be required imo
Try this:
from abc import ABC, abstractmethod
from typing import overload
class Foo(ABC):
@overload
def foo(self, value: int) -> int:
...
@overload
def foo(self, value: str) -> str:
...
@abstractmethod
def foo(self, value: str | int) -> str | int:
...
class Bar(Foo):
@overload
def foo(self, value: int) -> int:
...
@overload
def foo(self, value: str) -> str:
...
def foo(self, value: str | int) -> str | int:
return 1
Works like a charm! 👍
yeah, but the implementation is useless in the abstract method. i'd prefer if i coulkd just go
from abc import ABC, abstractmethod
from typing import overload
class Foo(ABC):
@abstractmethod
@overload
def foo(self, value: int) -> int:
...
@abstractmethod
@overload
def foo(self, value: str) -> str:
...
class Bar(Foo):
@overload
def foo(self, value: int) -> int:
...
@overload
def foo(self, value: str) -> str:
...
def foo(self, value: str | int) -> str | int:
return 1
I agree the original declaration seems reasonable. FWIW, there seems to be a workaround using Protocols (low confidence in there not being something wrong with it):
from abc import ABC, abstractmethod
from typing import Protocol, overload
class FooMethod(Protocol):
@overload
def __call__(self, value: int) -> int:
...
@overload
def __call__(self, value: str) -> str:
...
class Foo(ABC):
@property
@abstractmethod
def foo(self) -> FooMethod:
...
class Bar(Foo):
@overload
def foo(self, value: int) -> int:
...
@overload
def foo(self, value: str) -> str:
...
def foo(self, value):
return 1
I'm not sure that this is the same issue, but at least very similar. It would be great to allow omitting implementation in if TYPE_CHECKING
block as well:
from __future__ import annotations
from typing import overload, TYPE_CHECKING
if TYPE_CHECKING:
@overload # E: An overloaded function outside a stub file must have an implementation [no-overload-impl]
def f(self, obj: int) -> int: ...
@overload
def f(self, obj: str) -> str: ...
else:
from other.module import f
Here's a playground with this sample. Implementation signature will be discarded anyway, and there is nothing to typecheck.
Discovered in this SO answer.
@sterliakov i would raise a separate issue for that
Some specific logic might be needed for this case. Though you couldn't instantiate Foo
, it would normally be valid to do super().foo()
in Bar
. Mypy would need to show an error if foo
is actually called elsewhere.
Would it be possible to make the base class a generic bases on T input type and U output type. Then in the implementation class add the overloads for each type of T and U?
Hmm nope this does not work:
import typing
import abc
InputTypes = typing.TypeVar('InputTypes')
OutputTypes = typing.TypeVar('OutputTypes')
class Schema(typing.Generic[InputTypes, OutputTypes]):
@classmethod
@abc.abstractmethod
def validate(cls, arg: InputTypes) -> OutputTypes:
pass
class IntOrStrSchema(Schema[typing.Union[int, str], typing.Union[int, str]]):
@typing.overload
@classmethod
def validate(cls, arg: str) -> str: ...
@typing.overload
@classmethod
def validate(cls, arg: int) -> int: ...
@classmethod
def validate(cls, arg: typing.Union[str, int]) -> typing.Union[str, int]:
return arg
a = IntOrStrSchema.validate('a')
b = IntOrStrSchema.validate('1')
Errors:
validate_generic.py:15: error: Signature of "validate" incompatible with supertype "Schema"
validate_generic.py:15: note: Superclass:
validate_generic.py:15: note: @classmethod
validate_generic.py:15: note: def validate(cls, arg: Union[int, str]) -> Union[int, str]
validate_generic.py:15: note: Subclass:
validate_generic.py:15: note: @overload
validate_generic.py:15: note: @classmethod
validate_generic.py:15: note: def validate(cls, arg: str) -> str
validate_generic.py:15: note: @overload
validate_generic.py:15: note: @classmethod
validate_generic.py:15: note: def validate(cls, arg: int) -> int