0

Take the following example of code:

from typing import Literal, overload, Tuple

SpecificStrings = Literal['bacon', 'eggs']

@overload
def my_func(foo: SpecificStrings) -> float:
    ...

@overload
def my_func(foo: str) -> bool:
    ...

def my_func(foo):
    if foo in ('bacon', 'eggs'):
        return 101.5
    else:
        return bool(foo)

def main() -> Tuple[float, bool]:
    x = my_func('bacon')  # float
    y = my_func('any other string')  # bool
    return x, y

The basic idea is that if my_func is called with a specific string 'bacon' or 'eggs' the return type should be a float. If any other string is used, the return type should be bool.

Intuitively, I would expect the more specific annotation (foo: SpecificStrings) to be checked first before the less specific : str annotation.

However, when type checking this file with mypy, I receive an error:

t.py:6: error: Overloaded function signatures 1 and 2 overlap with incompatible return types

Additionally, when type checking with pyright, there is no errors found.

It seems this could be a bug in mypy, a bug in pyright, or possibly something else... PEP484 doesn't seem to indicate what the behavior should be in this scenario.

Is there another way to typehint this scenario such that it works (that is, such that x and y in the above example are understood as their respective types float and bool) with mypy?


Edit:

Interesting enough, using Enum instead of Literal seems to be a workaround:

from enum import Enum

class SpecificStrings(Enum):
    bacon = 'bacon'
    eggs = 'eggs'

Using reveal_type() with pyright and mypy show the correct types in this scenario.

# pyright
C:\Users\path\to\t.py
  C:\Users\path\to\t.py:38:13 - information: Type of "x" is "float"
  C:\Users\path\to\t.pyt.py:39:13 - information: Type of "y" is "bool"
0 errors, 0 warnings, 2 informations
# mypy
t.py:38: note: Revealed type is "builtins.float"
t.py:39: note: Revealed type is "builtins.bool"

To me, this begs another question: why does Enum work but not Literal?

Frustratingly, however, PyCharm does not do the same thing:

pycharm bad type

sytech
  • 29,298
  • 3
  • 45
  • 86
  • 1
    This is a bug in `pyright` IMO, since your function can indeed return a float for a non-literal (`str`) input based on its runtime value. You should type the second overload as returning `Union[float, bool]`. – Samwise Jan 27 '22 at 18:22
  • @Samwise I considered the same thought. That might be the case. Although I tried using `Enum` instead of `Literal` and both mypy and pyright seem happy... that would seem to contradict the idea that the `Literal` should be considered an error. Does that make sense? (edited question with those details). – sytech Jan 27 '22 at 18:44
  • 2
    "using `Enum` instead of `Literal` seems to be a workaround" It doesn't, it just "fails" without an error. If you add ``reveal_type((x, y))`` you will see that you actually get a ``Tuple[bool, bool]`` – because none of the strings match the ``float`` case. However, ``bool`` is a valid ``int`` – and ``int`` is a valid ``float`` as per [PEP 484](https://www.python.org/dev/peps/pep-0484/#the-numeric-tower)! Change ``my_func`` to provide e.g. a ``str`` for each ``SpecificStrings`` and the enum case fails loudly as well. – MisterMiyagi Jan 27 '22 at 19:12

0 Answers0