Search code examples
pythonpython-typingmypy

Why do I get mypy no overload variant of "zip" matches argument error?


I have a class with a method:

class TimeUtilitiesTestCase():
    def test_date_and_delta(self) -> None:
        """Tests date_and_delta utility method."""
        now = datetime.datetime.now()
        tdelta = datetime.timedelta
        int_tests = (3, 29, 86399, 86400, 86401 * 30)
        date_tests = [now - tdelta(seconds=x) for x in int_tests]
        td_tests = [tdelta(seconds=x) for x in int_tests]
        results = [(now - tdelta(seconds=x), tdelta(seconds=x)) for x in int_tests]
        for test in (int_tests, date_tests, td_tests):
            for arg, result in zip(test, results):
                dtime, delta = humanizer_portugues.time.date_and_delta(arg)
                self.assertEqualDatetime(dtime, result[0])
                self.assertEqualTimedelta(delta, result[1])
        self.assertEqual(humanizer_portugues.time.date_and_delta("NaN"), (None, "NaN"))

I am running strict mypy checks (configs here) and I get the error:

error: No overload variant of "zip" matches argument
types "object", "List[Tuple[datetime, timedelta]]"  [call-overload]
                for arg, result in zip(test, results):
                                   ^
note: Possible overload variants:
note:     def [_T1, _T2] zip(*iterables: Tuple[_T1, _T2]) -> Tuple[Tuple[_T
1, ...], Tuple[_T2, ...]]
note:     def [_T1, _T2, _T3] zip(*iterables: Tuple[_T1, _T2, _T3]) -> Tupl
e[Tuple[_T1, ...], Tuple[_T2, ...], Tuple[_T3, ...]]
note:     <3 more similar overloads not shown, out of 10 total overloads>

If you want to fully reproduce the error you can clone branch strict-mypy with: git clone -b strict-mypy https://github.com/staticdev/humanizer-portugues.git

I tried to change results from list to also being a tuple but this error doesn't go away.

How is my typing incorrect here?


Editor's note:
This issue can be reproduced with the following minimal example:

def bug_repr() -> None:
    ints = [1, 2]
    floats = [3.14, 2.72]
    strings = ['a', 'b']
    for numeric_list in [ints, floats]:
        for number, string in zip(numeric_list, strings):  # <-- error here
            pass

This gives the same error:

main.py:6: error: No overload variant of "zip" matches argument types "object", "List[str]"
main.py:6: note: Possible overload variants:
main.py:6: note:     def [_T1, _T2] zip(*iterables: Tuple[_T1, _T2]) -> Tuple[Tuple[_T1, ...], Tuple[_T2, ...]]
main.py:6: note:     def [_T1, _T2, _T3] zip(*iterables: Tuple[_T1, _T2, _T3]) -> Tuple[Tuple[_T1, ...], Tuple[_T2, ...], Tuple[_T3, ...]]
main.py:6: note:     <3 more similar overloads not shown, out of 10 total overloads>
Found 1 error in 1 file (checked 1 source file)

You can reproduce this on mypy Playground.


Solution

  • Using your minimum example:

    The problem is, that MyPy infers a type that is too narrow, namely List[int] and List[float] where it then can't infer the type List of something for numeric_list. In general, there are problems with containers and static type safety when you put in different things. E.g., you put in a float and an integer, so how would the static analysis know what you are taking out? See Docs on Variance

    But you can declare a more generic type, using Union types, to declare that it may contain several types.

    from typing import Union, List
    
    def bug_repr() -> None:
        ints: List[Union[int, float]] = [1, 2]
        floats: List[Union[int, float]] = [3.14, 2.72]
        strings = ['a', 'b']
        for numeric_list in [ints, floats]:
            for number, string in zip(numeric_list, strings):
                pass
    

    Alternatively you can just declare it as a List (or Sequence) without specifying what's inside:

    from typing import List
    
    def bug_repr() -> None:
        ints: List = [1, 2]
        floats: List = [3.14, 2.72]
        strings = ['a', 'b']
        for numeric_list in [ints, floats]:
            for number, string in zip(numeric_list, strings):
                pass
    

    But I'm not sure what type that would infer for number in the inner loop.

    (Note, you can also use Sequence as a more generic type instead of List)