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 breaks with named tuple and some overloaded functions #10470

Open
terencehonles opened this issue May 14, 2021 · 1 comment
Open

@overload breaks with named tuple and some overloaded functions #10470

terencehonles opened this issue May 14, 2021 · 1 comment
Labels
bug mypy got something wrong topic-overloads

Comments

@terencehonles
Copy link
Contributor

Bug Report

Mypy incorrectly reports Any as the resolved type (quite possibly due this python/typing#253 (comment) )

To Reproduce

from collections import namedtuple
from typing import Any, Callable, cast, Dict, List, NamedTuple, overload, TypeVar, Union

F = TypeVar("F", bound=Callable[..., Any])
Arg: NamedTuple = namedtuple("Arg", ["arg"])


class Test:
    @overload
    def __call__(self, fn: F) -> F:
        ...

    @overload
    def __call__(self, *args: Any, **kwargs: Any) -> "Test":
        ...

    def __call__(self, *args: Any, **kwargs: Any) -> Union[F, "Test"]:
        if len(args) == 1:
            return cast(F, args[0])

        return self

reveal_type(Test()(Test))



def broken(arg: Arg) -> Arg:
    return arg

reveal_type(broken)
reveal_type(Test()(broken))



def working(arg: int) -> int:
    return arg

reveal_type(Test()(working))



@overload
def overloaded_broken(arg: F) -> F:
    ...

@overload
def overloaded_broken(*args: Any, **kwargs: Any) -> Dict[str, int]:
    ...

def overloaded_broken(*args: Any, **kwargs: Any) -> Union[F, Dict[str, int]]:
    if len(args) == 1:
        return cast(F, args[0])

    return dict(args=len(args), kwargs=len(kwargs))


reveal_type(overloaded_broken)
reveal_type(Test()(overloaded_broken))


@overload
def overloaded_working(arg: str) -> str:
    ...

@overload
def overloaded_working(arg: bytes) -> bytes:
    ...

def overloaded_working(arg: Union[bytes, str]) -> Union[bytes, str]:
    return arg


reveal_type(Test()(overloaded_working))

Expected Behavior

All revealed types are printed as non Any

Actual Behavior

# a class is OK
test.py:23:13: note: Revealed type is 'def () -> test.Test'

# this is what is expected when using a named tuple, but it resolves as any
test.py:30:13: note: Revealed type is 'def (arg: Tuple[Any, fallback=test.Arg]) -> Tuple[Any, fallback=test.Arg]'
test.py:31:13: note: Revealed type is 'Any'

# this is not using a named tuple and it works as expected
test.py:38:13: note: Revealed type is 'def (arg: builtins.int) -> builtins.int'

# this is what is expected for a complex overloaded function, but it resolves as any
test.py:57:13: note: Revealed type is 'Overload(def [F <: def (*Any, **Any) -> Any] (arg: F`-1) -> F`-1, def (*args: Any, **kwargs: Any) -> builtins.dict[builtins.str, builtins.int])'
test.py:58:13: note: Revealed type is 'Any'

# passing a simpler overloaded function
test.py:73:13: note: Revealed type is 'Overload(def (arg: builtins.str) -> builtins.str, def (arg: builtins.bytes) -> builtins.bytes)'

Your Environment

mypy --version: mypy 0.812 (default config)
python --version: Python 3.8.9

@terencehonles
Copy link
Contributor Author

I thought this is an issue with pytest, but it might not be (although I did try to tighten their definitions pytest-dev/pytest#8674). I had noticed #9787, but I don't believe it's related even though the fact that it's dealing with named tuples makes me curious (the repro does look a little buggy though so it may have just been a mistake)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-overloads
Projects
None yet
Development

No branches or pull requests

2 participants