Search code examples
pythonmypypython-typing

Mypy complains about incompatible type when variable type is subset of expected type


Trying to build an xarray Dataset, where we build the input to the arguments "coords" and "attrs" before passing it to the constructor:

coordinates = {"time": ("time", time_coordinates)}
attributes = {"some_flag": False}
...
ds = xr.Dataset(data_vars=variables, coords=coordinates, attrs=attributes)

What puzzles me is the output of mypy run against this code:

error: Argument "coords" to "Dataset" has incompatible type "Dict[str, Tuple[str, Any]]"; expected "Optional[Mapping[Hashable, Any]]"
error: Argument "attrs" to "Dataset" has incompatible type "Dict[str, bool]"; expected "Optional[Mapping[Hashable, Any]]"

Isn't a dict a Mapping? And isn't str also Hashable? And aren't Tuples and bools of type Any, in any case? What do I not understand about mypy and/or Python type hints here?


Solution

  • Using the information from Selcuk, I found this somewhat verbose solution, as detailed in the mypy docs: As the keys of Mappings are invariant, one needs to hint explicitly that the str there is of type Hashable. (While strings are a subtype of Hashable, Mappings keys are not covariant, disallowing subtypes.). Or, as Selcuk puts it in his comment:

    str is a Hashable, but since dict is a mutable data type you must be passing the exact same type for the keys, not a subtype. There is a possibility that the called function might add another Hashable key to the passed argument, breaking the source.

    coordinates: Dict[Hashable, Tuple[str, Any]] = {
        "time": ("time", time_coordinates)
    }
    attributes: Dict[Hashable, Any] = {"some_flag": False}