Skip to content

Overloads with default arguments do not work properly #5486

Closed

Description

@padix-key

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.

Activity

  1. srittau commented on Aug 15, 2018

    @srittau
    Contributor

    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
  2. padix-key commented on Aug 15, 2018

    @padix-key
    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?

  3. ilevkivskyi commented on Aug 15, 2018

    @ilevkivskyi
    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

  4. ilevkivskyi commented on Aug 15, 2018

    @ilevkivskyi
    Member

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

  5. JelleZijlstra commented on Aug 15, 2018

    @JelleZijlstra
    Member

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

  6. henribru commented on Jun 6, 2020

    @henribru
    Contributor

    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.

  7. henribru commented on Feb 6, 2021

    @henribru
    Contributor

    Okay, after reading https://mail.python.org/archives/list/typing-sig@python.org/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?

  8. bashtage commented on Feb 24, 2021

    @bashtage

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions