Description
Sometimes, it is necessary to accumulate data using a mutable structure and then recast the data into an immutable object suitable for use as a dictionary key. If the transformation switching data types while using the same variable name, mypy emits an inscrutable error message that doesn't hint at what the actual problem is.
The solution was to use another variable name for the transmuted data.
Recommendation: Let variables be rebound to different types as needed. Barring that, provide a better error message.
Simplified example:
from typing import List, DefaultDict, Tuple, Dict
from collections import defaultdict
from pprint import pprint
scores = [95, 86, 64, 92, 71, 68, 79, 92]
Decile = int
Score = int
deciles = defaultdict(list) # type: DefaultDict[Decile, List[Score]]
for score in scores:
deciles[score // 10].append(score)
# turn deciles into a plain dict mapping to tuples
deciles = {decile : tuple(group) for decile, group in deciles.items()} # type: Dict[Decile, Tuple[Score]]
pprint(deciles)
The error message is:
tmp.py:10: error: Argument 1 to "defaultdict" has incompatible type List[_T]; expected Callable[[], Tuple[int]]
tmp.py:12: error: "Tuple[int]" has no attribute "append"
tmp.py:15: error: Name 'deciles' already defined
tmp.py:15: error: Value expression in dictionary comprehension has incompatible type Tuple[int, ...]; expected type "Tuple[int]"
Note, the message for line 10 makes no sense until you see the later message for line 15. In my client's code base, these two we very far apart and made it difficult to figure out why line 10 gets an error message even though it is correct code.
The solution was to change the last lines to use new variable names:
# turn deciles into a plain dict mapping to tuples
newdeciles = {decile : tuple(group) for decile, group in deciles.items()} # type: Dict[Decile, Tuple[Score]]
pprint(newdeciles)
It would be nice if the error message applied only to the redefinition line rather than the initial correct declaration. It would be even nicer if mypy supported patterns that allowed a tranformation in-place or re-use of a variable name for cases like converting a list of lists into a list of tuples. The latter example is not uncommon in my client's code base (because the inner lists need to be used as dict keys).