Mypy: Function + @overload cause "Single overload definition, multiple required"

Created on 15 May 2018  路  3Comments  路  Source: python/mypy

Assume a function like:

def get_item(key: str, default: Optional[Item] = None) -> Optional[Item]:
   # implementation goes here
   ...

Now let's assume we want to enhance typing through an @overload by changing it to:

@overload
def get_item(key: str) -> Item:
   ...

def get_item(key: str, default: Optional[Item] = None) -> Optional[Item]:
   # implementation goes here
   ...

mypy will complain with:

error: Single overload definition, multiple required

This check makes sense for a .pyi file where you would expect multiple @overloads on equal footing, but in this case the original declaration might as well serve as an "overload".

A workaround is obviously to add:

@overload
def get_item(key: str, default: Optional[Item]) -> Optional[Item]:
   ...

which adds needless verbosity.

Most helpful comment

If someone comes over this issue: The intended way of writing two different signatures, is to have them in two @overload signatures. And the actual function header for the implementation should be empty or should include the merge of all signatures with Union:

@overload
def get_item(key: str) -> Item:
   ...

@overload
def get_item(key: str, default: Optional[Item] = None) -> Optional[Item]:
   ...

def get_item(key, default = None):
   # implementation goes here
   ...

That's why there must be at least two @overload signatures and the error error: Single overload definition, multiple required will else arise.

See also #72 , #1136 , and #3360 .

All 3 comments

I'm a little bit confused about your type signature here: if 'default' is set to None, doesn't that mean the first overload alternative will always match? So, the only case where 'get_item' can return 'None' would be if 'default' is a non-None object?

Basically, I think your type signature is equivalent to the following:

@overload
def get_item(key: str, default: None = None) -> Item: ...
@overload
def get_item(key: str, default: Item) -> Optional[Item]: ...
def get_item(key: str, default: Optional[Item] = None) -> Optional[Item]:
    # implementation goes here

In any case, I think you usually want your overload signatures to be "specializations" of the implementation signature. We don't really want to force users to have the implementation signature be a valid overload alternative in that case.

I suspect that the OP intended something like this:

@overload
def get_item(key: str) -> Optional[Item]: ...
@overload
def get_item(key: str, default: None) -> Optional[Item]: ...
@overload
def get_item(key: str, default: Item) -> Item: ...
def get_item(key: str, default: Optional[item]) -> Optional[Item]:
    # implementation goes here

In any case we don't have plans to make it easier to shoot yourself in the foot here.

If someone comes over this issue: The intended way of writing two different signatures, is to have them in two @overload signatures. And the actual function header for the implementation should be empty or should include the merge of all signatures with Union:

@overload
def get_item(key: str) -> Item:
   ...

@overload
def get_item(key: str, default: Optional[Item] = None) -> Optional[Item]:
   ...

def get_item(key, default = None):
   # implementation goes here
   ...

That's why there must be at least two @overload signatures and the error error: Single overload definition, multiple required will else arise.

See also #72 , #1136 , and #3360 .

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JukkaL picture JukkaL  路  4Comments

arquolo picture arquolo  路  3Comments

Stiivi picture Stiivi  路  3Comments

squarewave24 picture squarewave24  路  3Comments

co-dh picture co-dh  路  3Comments