Skip to content

@overloads on @abstractmethods shouldn't need an implementation #11488

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

Closed
DetachHead opened this issue Nov 8, 2021 · 7 comments · Fixed by #18882
Closed

@overloads on @abstractmethods shouldn't need an implementation #11488

DetachHead opened this issue Nov 8, 2021 · 7 comments · Fixed by #18882

Comments

@DetachHead
Copy link
Contributor

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

@DetachHead DetachHead added the bug mypy got something wrong label Nov 8, 2021
@sobolevn
Copy link
Member

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! 👍

@DetachHead
Copy link
Contributor Author

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

@AlexWaygood AlexWaygood added topic-overloads feature priority-2-low and removed bug mypy got something wrong labels Apr 1, 2022
@alicederyn
Copy link

alicederyn commented Nov 28, 2022

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

@sterliakov
Copy link
Collaborator

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.

@DetachHead
Copy link
Contributor Author

@sterliakov i would raise a separate issue for that

@TeamSpen210
Copy link
Contributor

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.

@spacether
Copy link

spacether commented Jul 11, 2023

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

erictraut pushed a commit to microsoft/pyright that referenced this issue Aug 25, 2023
…an implementation is not required. This is related to mypy issue python/mypy#11488.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants