I want to typehint an overload function. For that I use the overload
decorator from typing
. I want to set multiple possible callees based on a parameter's value. This parameter is color
.
I have this code:
from typing import Literal, overload
from enum import Enum
class Color(Enum):
RED = 0
BLUE = 1
GREEN = 2
@overload
def func(
*,
title: str,
color: Literal[Color.RED, Color.BLUE],
description: str,
) -> None:
...
@overload
def func(
*,
title: str,
color: Literal[Color.GREEN],
) -> None:
...
def func(
*,
title: str = None,
color: Color = None,
description: str = None,
) -> None:
print(title, color, description)
func(title="hello", color=Color.GREEN, description="hello")
I want to get a warning when I try to set the description
, even the color
is set to Color.GREEN
, but I don't get a warning:
When I do the same just with strings, it works. I replaced the Literals with Literal["red", "blue"]
and Literal["green"]
and changed the type of color
to str
:
Accordingly, there is no error, when I don't try to set description
, which I expect:
Python Version: 3.8
IDE: PyCharm
You discovered a pycharm
bug, here's the corresponding issue. There is no workaround suggested, and I doubt there can be one.
mypy
handles such overload case properly, also pointing out missing | None
in the implementation signature (title: str = None
is usually a bad thing to write, explicit is better than implicit, and mypy
has hidden the decision to infer such type implicitly under a configuration flag for this reason - such thing was enabled by default earlier). Here's a playground link to check.
To fix the implicit-optional issue mentioned above, you could do the following (Optional[X]
is equivalent to Union[X, None]
, where X
is some valid type):
from typing import Optional
...
# Overloads here
def func(
*,
title: Optional[str] = None,
color: Optional[Color] = None,
description: Optional[str] = None,
) -> None:
print(title, color, description)
or, on python 3.10 and higher or with annotations
future-import,
...
# Overloads here
def func(
*,
title: str | None = None,
color: Color | None = None,
description: str | None = None,
) -> None:
print(title, color, description)
Finally, as long as you always require title
and color
in both overloads, why not make them required in implementation (note that first two args don't have to be Optional any more)?
from typing import Optional
...
# Overloads here
def func(
*,
title: str,
color: Color,
description: Optional[str] = None,
) -> None:
print(title, color, description)
Your usage is absolutely valid, and it's just a bug in your IDE.