Skip to content

Overloads with default arguments do not work properly #5486

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
padix-key opened this issue Aug 15, 2018 · 8 comments
Closed

Overloads with default arguments do not work properly #5486

padix-key opened this issue Aug 15, 2018 · 8 comments

Comments

@padix-key
Copy link

I have the follwoing stub file (test.pyi):

from typing import overload

@overload
def func(
    param: None = ...
) -> int: ...

@overload
def func(
    param: int = ...
) -> str: ...

When I run mypy test.pyi, mypy shows the following error:
test.pyi:4: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
The parameter param clearly has two different types. Hence, the functions should not overlap to my understanding.

mypy 0.620 has been used.

@srittau
Copy link
Contributor

srittau commented Aug 15, 2018

The problem here is that both overloads match a call of func(). Removing the default from the second overload should fix your problem:

  • func() matches (only) the first overload
  • func(None) matches the first overload
  • func(42) matches the second overload

@padix-key
Copy link
Author

Unfortunately, I cannot simply remove the default value from the second overload, because in my actual problem there are default parameters before this parameter, like

from typing import overload

@overload
def func(
    param1: str = "foo",
    param2: None = ...
) -> int: ...

@overload
def func(
    param1: str = "foo",
    param2: int = ...
) -> str: ...

Is there a way to fix this problem?

@ilevkivskyi
Copy link
Member

This is not a bug, mypy shows an actual error. As @srittau mentioned, what is the type of func() (no args)? Is it int or str, because it matches both overloads. You should remove the default value (i.e. ...) from at least one of the overloads. Also please read the docs https://mypy.readthedocs.io/en/latest/more_types.html#function-overloading

@ilevkivskyi
Copy link
Member

@padix-key you can just add more overloads that match runtime semantics of your function.

@JelleZijlstra
Copy link
Member

Specifically, in overload 2 you can mark param2 as a required keyword-only argument by doing *, param2: str.

@henribru
Copy link
Contributor

henribru commented Jun 6, 2020

Specifically, in overload 2 you can mark param2 as a required keyword-only argument by doing *, param2: str.

@JelleZijlstra But then Mypy no longer allows you to call the function as func("foo", 3), you have to do func("foo", param2=3) even though passing them as positional arguments are fine at runtime.

@henribru
Copy link
Contributor

henribru commented Feb 6, 2021

Okay, after reading https://mail.python.org/archives/list/[email protected]/thread/3P36ZCFAMOKFJMHFULTPGRNRSGTHTOLX/ I realize this workaround actually requires three overloads to avoid the problem I mentioned:

from typing import overload

@overload
def func(
    param1: str = "foo",
    param2: None = ...
) -> int: ...

@overload
def func(
    param1: str = "foo",
    *,
    param2: int
) -> str: ...

@overload
def func(
    param1: str,
    param2: int
) -> str: ...

It feels a bit esoteric and unintuitive. Would a PR to better document the interaction between overloads and default values be welcome?

@bashtage
Copy link

It feels a bit esoteric and unintuitive. Would a PR to better document the interaction between overloads and default values be welcome?

I think a tutorial/extended help page on complex overloading would be a great addition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants