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.
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
)