diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 34b9997ba07..d78dd38dd85 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,5 +2,5 @@ - [ ] Closes #xxxx - [ ] Tests added - - [ ] Passes `black .` & `flake8` + - [ ] Passes `black . && mypy . && flake8` - [ ] Fully documented, including `whats-new.rst` for all changes and `api.rst` for new API diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a94a53778ce..15f2803c1c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,6 +10,10 @@ repos: rev: v2.2.3 hooks: - id: flake8 +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v0.720 + hooks: + - id: mypy # run these occasionally, ref discussion https://github.com/pydata/xarray/pull/3194 # - repo: https://github.com/asottile/pyupgrade # rev: v1.22.1 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 82d3b70704b..55c4ecb05f5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -75,7 +75,7 @@ jobs: - template: ci/azure/install.yml - bash: | source activate xarray-tests - mypy . || exit 0 + mypy . displayName: mypy type checks - job: Docs diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 83f59d9eea4..9e6fb954a8e 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -29,8 +29,8 @@ New functions/methods `_. - The xarray package is now discoverably by mypy (although typing hints - coverage is not complete yet). mypy users can now remove from their setup.cfg - the lines:: + coverage is not complete yet). mypy type checking is now enforced by CI. + Libraries that depend on xarray and use mypy can now remove from their setup.cfg the lines:: [mypy-xarray] ignore_missing_imports = True diff --git a/xarray/core/common.py b/xarray/core/common.py index 15ce8ca9f04..98acf8f1339 100644 --- a/xarray/core/common.py +++ b/xarray/core/common.py @@ -46,12 +46,7 @@ def wrapped_func(self, dim=None, axis=None, skipna=None, **kwargs): else: - def wrapped_func( - self, - dim=None, - axis=None, # type: ignore - **kwargs - ): + def wrapped_func(self, dim=None, axis=None, **kwargs): # type: ignore return self.reduce(func, dim, axis, allow_lazy=True, **kwargs) return wrapped_func diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index a85f015cfa8..8aada5117c1 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -408,17 +408,9 @@ def __init__( self, # could make a VariableArgs to use more generally, and refine these # categories - data_vars: Mapping[ - Hashable, - Union[ - "DataArray", - Variable, - Tuple[Hashable, Any], - Tuple[Sequence[Hashable], Any], - ], - ] = None, + data_vars: Mapping[Hashable, Any] = None, coords: Mapping[Hashable, Any] = None, - attrs: Mapping = None, + attrs: Mapping[Hashable, Any] = None, compat=None, ): """To load data from a file or file-like object, use the `open_dataset` @@ -439,6 +431,8 @@ def __init__( - mapping {var name: Variable} - mapping {var name: (dimension name, array-like)} - mapping {var name: (tuple of dimension names, array-like)} + - mapping {dimension name: array-like} + (it will be automatically moved to coords, see below) Each dimension must have the same length in all variables in which it appears. @@ -460,6 +454,7 @@ def __init__( - mapping {coord name: (dimension name, array-like)} - mapping {coord name: (tuple of dimension names, array-like)} - mapping {dimension name: array-like} + (the dimension name is implicitly set to be the same as the coord name) The last notation implies that the coord name is the same as the dimension name. @@ -2052,17 +2047,13 @@ def relevant_keys(mapping): ] coords = relevant_keys(self.coords) - indexers = [ - (k, np.asarray(v)) # type: ignore - for k, v in indexers.items() - ] - indexers_dict = dict(indexers) + indexers = {k: np.asarray(v) for k, v in indexers.items()} non_indexed_dims = set(self.dims) - indexer_dims non_indexed_coords = set(self.coords) - set(coords) # All the indexers should be iterables # Check that indexers are valid dims, integers, and 1D - for k, v in indexers: + for k, v in indexers.items(): if k not in self.dims: raise ValueError("dimension %s does not exist" % k) if v.dtype.kind != "i": # type: ignore @@ -2071,7 +2062,7 @@ def relevant_keys(mapping): raise ValueError("Indexers must be 1 dimensional") # all the indexers should have the same length - lengths = {len(v) for k, v in indexers} + lengths = {len(v) for k, v in indexers.items()} if len(lengths) > 1: raise ValueError("All indexers must be the same length") @@ -2109,12 +2100,9 @@ def relevant_keys(mapping): variables = OrderedDict() # type: ignore for name, var in reordered.variables.items(): - if name in indexers_dict or any(d in indexer_dims for d in var.dims): + if name in indexers or any(d in indexer_dims for d in var.dims): # slice if var is an indexer or depends on an indexed dim - slc = [ - indexers_dict[k] if k in indexers_dict else slice(None) - for k in var.dims - ] + slc = [indexers.get(k, slice(None)) for k in var.dims] var_dims = [dim_name] + [d for d in var.dims if d in non_indexed_dims] selection = take(var, tuple(slc)) diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py index a0d260c3f33..c01ad013e5b 100644 --- a/xarray/core/groupby.py +++ b/xarray/core/groupby.py @@ -775,12 +775,8 @@ def wrapped_func( else: - def wrapped_func( - self, - dim=DEFAULT_DIMS, - axis=None, # type: ignore - keep_attrs=None, - **kwargs + def wrapped_func( # type: ignore + self, dim=DEFAULT_DIMS, axis=None, keep_attrs=None, **kwargs ): return self.reduce( func, dim, axis, keep_attrs=keep_attrs, allow_lazy=True, **kwargs @@ -912,11 +908,7 @@ def wrapped_func(self, dim=DEFAULT_DIMS, skipna=None, **kwargs): else: - def wrapped_func( - self, - dim=DEFAULT_DIMS, # type: ignore - **kwargs - ): + def wrapped_func(self, dim=DEFAULT_DIMS, **kwargs): # type: ignore return self.reduce( func, dim, numeric_only=numeric_only, allow_lazy=True, **kwargs )