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

@overload cannot be used with conditional "if TYPE_CHECKING" #9744

Closed
blueyed opened this issue Nov 22, 2020 · 9 comments · Fixed by #10712
Closed

@overload cannot be used with conditional "if TYPE_CHECKING" #9744

blueyed opened this issue Nov 22, 2020 · 9 comments · Fixed by #10712
Labels
bug mypy got something wrong

Comments

@blueyed
Copy link
Contributor

blueyed commented Nov 22, 2020

Bug Report

The handling of @overload decorators happens in (code) blocks only, which makes it not find the implementation when using:

from typing import overload
from typing import TYPE_CHECKING
from typing import Union


class K:
    if TYPE_CHECKING:
        @overload
        def foo(self, arg: int) -> int:
            ...

        @overload
        def foo(self, arg: str) -> str:
            ...

    def foo(self, arg: Union[int, str]) -> Union[int, str]:
        ...
t-overload.py:8: error: An overloaded function outside a stub file must have an implementation  [misc]
t-overload.py:16: error: Name 'foo' already defined on line 8  [no-redef]
Found 2 errors in 1 file (checked 1 source file)

I think it would be nice / feasible to merge if TYPE_CHECKING blocks maybe, so that the parser would handle it together?

mypy 0.800+dev.6e99a2db100d794b1bf900746470527cd03fce16

@blueyed blueyed added the bug mypy got something wrong label Nov 22, 2020
@dnaaun
Copy link

dnaaun commented Nov 22, 2020

I'm curious, why would you like an if TYPE_CHECKING block shielding overloads? They are ignored by the runtime anyways.

@blueyed
Copy link
Contributor Author

blueyed commented Nov 23, 2020

I'm curious, why would you like an if TYPE_CHECKING block shielding overloads? They are ignored by the runtime anyways.

While it's just a dummy decorator I'd like to keep typing separate from runtime altogether.
Apart from that it just confused me initially until I've looked into how mypy parses/handles it, and was surprised to see that if blocks are considered to be separate in this regard, i.e. it looked like something that could/should get improved/fixed in mypy anyway.

@altendky
Copy link

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Jan 23, 2021

For your use case you could just put the whole stub in an if TYPE_CHECKING and add a dummy implementation (def whatever(*args, **kwargs): ...) to each sys.platform branch. And then put the real implementation in the else of the if TYPE_CHECKING

@altendky
Copy link

Thanks for the workaround. I'll keep subscribed here in case there's a full fix. :]

@cdce8p
Copy link
Collaborator

cdce8p commented Aug 2, 2021

I opened a PR a while ago that would also fix this issue. Still waiting for a review though. #10712
Maybe you would be interested to take a look, @hauntsaninja?

@blueyed
Copy link
Contributor Author

blueyed commented Sep 14, 2021

@cdce8p
Thank you for fixing it!
I can confirm that it both fixes my example, and the actual use case I have at hand.

@cdce8p
Copy link
Collaborator

cdce8p commented Sep 14, 2021

@blueyed Great to hear that! Unfortunately I'm still waiting for a review on the PR so no idea when it will get merged. I would be able to remove 300 lines in one file alone just from overloads I need to duplicate at the moment.

@cdce8p
Copy link
Collaborator

cdce8p commented Sep 14, 2021

Sorry to bother you again @hauntsaninja. Do you have an idea who might be able to review it?

JelleZijlstra pushed a commit that referenced this issue Mar 3, 2022
### Description

This PR allows users to define overloads conditionally, e.g., based on the Python version. At the moment this is only possible if all overloads are contained in the same block which requires duplications.

```py
from typing import overload, Any
import sys

class A: ...
class B: ...

if sys.version_info >= (3, 9):
    class C: ...


@overload
def func(g: int) -> A: ...

@overload
def func(g: bytes) -> B: ...

if sys.version_info >= (3, 9):
    @overload
    def func(g: str) -> C: ...

def func(g: Any) -> Any: ...
```

Closes #9744

## Test Plan

Unit tests have been added.

## Limitations
Only `if` is supported. Support for `elif` and `else` might be added in the future. However, I believe that the single if as shown in the example is also the most common use case.

The change itself is fully backwards compatible, i.e. the current workaround (see below) will continue to function as expected.

~~**Update**: Seems like support for `elif` and `else` is required for the tests to pass.~~

**Update**: Added support for `elif` and `else`.

## Current workaround

```py
from typing import overload, Any
import sys

class A: ...
class B: ...

if sys.version_info >= (3, 9):
    class C: ...


if sys.version_info >= (3, 9):
    @overload
    def func(g: int) -> A: ...

    @overload
    def func(g: bytes) -> B: ...

    @overload
    def func(g: str) -> C: ...

    def func(g: Any) -> Any: ...

else:
    @overload
    def func(g: int) -> A: ...

    @overload
    def func(g: bytes) -> B: ...

    def func(g: Any) -> Any: ...
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants