From 5506eff70076bc562615be34530d03f2b18fdc16 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 13 Jul 2019 17:23:27 +0100 Subject: [PATCH 1/9] Let mypy discover xarray typing hints --- doc/whats-new.rst | 7 +++++++ setup.py | 2 +- xarray/py.typed | 0 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 xarray/py.typed diff --git a/doc/whats-new.rst b/doc/whats-new.rst index a1b3e5416ca..400815d8fb4 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -35,6 +35,13 @@ v0.12.3 (10 July 2019) 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:: + + [mypy-xarray] + ignore_missing_imports = True + - New methods :py:meth:`Dataset.to_stacked_array` and :py:meth:`DataArray.to_unstacked_dataset` for reshaping Datasets of variables with different dimensions diff --git a/setup.py b/setup.py index f24392db1f3..db81a729885 100644 --- a/setup.py +++ b/setup.py @@ -104,4 +104,4 @@ tests_require=TESTS_REQUIRE, url=URL, packages=find_packages(), - package_data={'xarray': ['tests/data/*']}) + package_data={'xarray': ['py.typed', 'tests/data/*']}) diff --git a/xarray/py.typed b/xarray/py.typed new file mode 100644 index 00000000000..e69de29bb2d From a537fbbfee639a312d621bc326685d90f97b14cc Mon Sep 17 00:00:00 2001 From: Guido Imperiale Date: Sat, 13 Jul 2019 17:30:10 +0100 Subject: [PATCH 2/9] Docs tweak --- doc/whats-new.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 400815d8fb4..23b633b2bd0 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -35,13 +35,6 @@ v0.12.3 (10 July 2019) 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:: - - [mypy-xarray] - ignore_missing_imports = True - - New methods :py:meth:`Dataset.to_stacked_array` and :py:meth:`DataArray.to_unstacked_dataset` for reshaping Datasets of variables with different dimensions @@ -55,6 +48,15 @@ New functions/methods (:issue:`3026`). By `Julia Kent `_. +- 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:: + + [mypy-xarray] + ignore_missing_imports = True + + By `Guido Imperiale `_ + Enhancements ~~~~~~~~~~~~ From 3b13dcbbb53d313ef4ee638f67e8c1b4db25852d Mon Sep 17 00:00:00 2001 From: Guido Imperiale Date: Sat, 13 Jul 2019 20:05:24 +0100 Subject: [PATCH 3/9] More annotations in Dataset --- xarray/core/alignment.py | 33 +++++-- xarray/core/dataarray.py | 9 +- xarray/core/dataset.py | 207 ++++++++++++++++++++++++++------------- 3 files changed, 173 insertions(+), 76 deletions(-) diff --git a/xarray/core/alignment.py b/xarray/core/alignment.py index 412560d914a..d57335d6ba7 100644 --- a/xarray/core/alignment.py +++ b/xarray/core/alignment.py @@ -3,7 +3,16 @@ import warnings from collections import OrderedDict, defaultdict from contextlib import suppress -from typing import Any, Mapping, Optional, Tuple +from typing import ( + Any, + Dict, + Hashable, + Mapping, + Optional, + Tuple, + Union, + TYPE_CHECKING, +) import numpy as np import pandas as pd @@ -13,6 +22,10 @@ from .utils import is_dict_like, is_full_slice from .variable import IndexVariable, Variable +if TYPE_CHECKING: + from .dataarray import DataArray + from .dataset import Dataset + def _get_joiner(join): if join == 'outer': @@ -169,8 +182,8 @@ def deep_align(objects, join='inner', copy=True, indexes=None, This function is not public API. """ - from .dataarray import DataArray - from .dataset import Dataset + from .dataarray import DataArray # noqa: F811 + from .dataset import Dataset # noqa: F811 if indexes is None: indexes = {} @@ -222,7 +235,10 @@ def is_alignable(obj): return out -def reindex_like_indexers(target, other): +def reindex_like_indexers( + target: Union['DataArray', 'Dataset'], + other: Union['DataArray', 'Dataset'], +) -> Dict[Hashable, pd.Index]: """Extract indexers to align target with other. Not public API. @@ -236,7 +252,8 @@ def reindex_like_indexers(target, other): Returns ------- - Dict[Any, pandas.Index] providing indexes for reindex keyword arguments. + Dict[Hashable, pandas.Index] providing indexes for reindex keyword + arguments. Raises ------ @@ -310,7 +327,7 @@ def reindex_variables( new_indexes : OrderedDict Dict of indexes associated with the reindexed variables. """ - from .dataarray import DataArray + from .dataarray import DataArray # noqa: F811 # create variables for the new dataset reindexed = OrderedDict() # type: OrderedDict[Any, Variable] @@ -463,8 +480,8 @@ def broadcast(*args, exclude=None): a (x, y) int64 1 1 2 2 3 3 b (x, y) int64 5 6 5 6 5 6 """ - from .dataarray import DataArray - from .dataset import Dataset + from .dataarray import DataArray # noqa: F811 + from .dataset import Dataset # noqa: F811 if exclude is None: exclude = set() diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index beaf148df6b..b1c3d46394f 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -1042,8 +1042,13 @@ def reindex_like(self, other: Union['DataArray', Dataset], align """ indexers = reindex_like_indexers(self, other) - return self.reindex(method=method, tolerance=tolerance, copy=copy, - fill_value=fill_value, **indexers) + return self.reindex( + indexers=indexers, + method=method, + tolerance=tolerance, + copy=copy, + fill_value=fill_value, + ) def reindex(self, indexers: Optional[Mapping[Hashable, Any]] = None, method: Optional[str] = None, tolerance=None, diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 060c80b6722..e4e229f5d49 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -1682,7 +1682,12 @@ def _get_indexers_coords_and_indexes(self, indexers): ) return attached_coords, attached_indexes - def isel(self, indexers=None, drop=False, **indexers_kwargs): + def isel( + self, + indexers: Mapping[Hashable, Any] = None, + drop: bool = False, + **indexers_kwargs: Any + ) -> 'Dataset': """Returns a new dataset with each array indexed along the specified dimension(s). @@ -1727,8 +1732,9 @@ def isel(self, indexers=None, drop=False, **indexers_kwargs): indexers_list = self._validate_indexers(indexers) - variables = OrderedDict() - indexes = OrderedDict() + variables = OrderedDict() # type: OrderedDict[Hashable, Variable] + indexes = OrderedDict() # type: OrderedDict[Hashable, pd.Index] + for name, var in self.variables.items(): var_indexers = {k: v for k, v in indexers_list if k in var.dims} if drop and name in var_indexers: @@ -1759,8 +1765,14 @@ def isel(self, indexers=None, drop=False, **indexers_kwargs): return self._replace_with_new_dims( variables, coord_names, indexes=indexes) - def sel(self, indexers=None, method=None, tolerance=None, drop=False, - **indexers_kwargs): + def sel( + self, + indexers: Mapping[Hashable, Any] = None, + method: Optional[str] = None, + tolerance: Optional[Number] = None, + drop: bool = False, + **indexers_kwargs: Any + ) -> 'Dataset': """Returns a new dataset with each array indexed by tick labels along the specified dimension(s). @@ -1829,8 +1841,7 @@ def sel(self, indexers=None, method=None, tolerance=None, drop=False, result = self.isel(indexers=pos_indexers, drop=drop) return result._overwrite_indexes(new_indexes) - def isel_points(self, dim='points', **indexers): - # type: (...) -> Dataset + def isel_points(self, dim: Any = 'points', **indexers: Any) -> 'Dataset': """Returns a new dataset with each array indexed pointwise along the specified dimension(s). @@ -1840,9 +1851,10 @@ def isel_points(self, dim='points', **indexers): Parameters ---------- - dim : str or DataArray or pandas.Index or other list-like object, optional + dim : hashable or DataArray or pandas.Index or other list-like object, + optional Name of the dimension to concatenate along. If dim is provided as a - string, it must be a new dimension name, in which case it is added + hashable, it must be a new dimension name, in which case it is added along axis=0. If dim is provided as a DataArray or Index or list-like object, its name, which must not be present in the dataset, is used as the dimension to concatenate along and the @@ -1967,8 +1979,9 @@ def relevant_keys(mapping): dset.coords[dim_name] = dim_coord return dset - def sel_points(self, dim='points', method=None, tolerance=None, - **indexers): + def sel_points(self, dim: Any = 'points', method: Optional[str] = None, + tolerance: Optional[Number] = None, + **indexers: Any): """Returns a new dataset with each array indexed pointwise by tick labels along the specified dimension(s). @@ -1980,9 +1993,10 @@ def sel_points(self, dim='points', method=None, tolerance=None, Parameters ---------- - dim : str or DataArray or pandas.Index or other list-like object, optional + dim : hashable or DataArray or pandas.Index or other list-like object, + optional Name of the dimension to concatenate along. If dim is provided as a - string, it must be a new dimension name, in which case it is added + hashable, it must be a new dimension name, in which case it is added along axis=0. If dim is provided as a DataArray or Index or list-like object, its name, which must not be present in the dataset, is used as the dimension to concatenate along and the @@ -2027,8 +2041,14 @@ def sel_points(self, dim='points', method=None, tolerance=None, ) return self.isel_points(dim=dim, **pos_indexers) - def reindex_like(self, other, method=None, tolerance=None, copy=True, - fill_value=dtypes.NA): + def reindex_like( + self, + other: Union['Dataset', 'DataArray'], + method: Optional[str] = None, + tolerance: Optional[Number] = None, + copy: bool = True, + fill_value: Any = dtypes.NA + ) -> 'Dataset': """Conform this object onto the indexes of another object, filling in missing values with ``fill_value``. The default fill value is NaN. @@ -2077,8 +2097,15 @@ def reindex_like(self, other, method=None, tolerance=None, copy=True, return self.reindex(indexers=indexers, method=method, copy=copy, fill_value=fill_value, tolerance=tolerance) - def reindex(self, indexers=None, method=None, tolerance=None, copy=True, - fill_value=dtypes.NA, **indexers_kwargs): + def reindex( + self, + indexers: Mapping[Hashable, Any] = None, + method: Optional[str] = None, + tolerance: Optional[Number] = None, + copy: bool = True, + fill_value: Any = dtypes.NA, + **indexers_kwargs: Any + ) -> 'Dataset': """Conform this object onto a new set of indexes, filling in missing values with ``fill_value``. The default fill value is NaN. @@ -2140,8 +2167,14 @@ def reindex(self, indexers=None, method=None, tolerance=None, copy=True, return self._replace_with_new_dims( variables, coord_names, indexes=indexes) - def interp(self, coords=None, method='linear', assume_sorted=False, - kwargs=None, **coords_kwargs): + def interp( + self, + coords: Mapping[Hashable, Any] = None, + method: str = 'linear', + assume_sorted: bool = False, + kwargs: Mapping[str, Any] = None, + **coords_kwargs: Any + ) -> 'Dataset': """ Multidimensional interpolation of Dataset. Parameters @@ -2210,7 +2243,7 @@ def _validate_interp_indexer(x, new_x): else: return (x, new_x) - variables = OrderedDict() + variables = OrderedDict() # type: OrderedDict[Hashable, Variable] for name, var in obj._variables.items(): if name not in indexers: if var.dtype.kind in 'uifc': @@ -2233,9 +2266,10 @@ def _validate_interp_indexer(x, new_x): # attach indexer as coordinate variables.update(indexers) - indexes.update( - (k, v.to_index()) for k, v in indexers.items() if v.dims == (k,) - ) + for k, v in indexers.items(): + assert isinstance(v, Variable) + if v.dims == (k,): + indexes[k] = v.to_index() # Extract coordinates from indexers coord_vars, new_indexes = ( @@ -2249,8 +2283,13 @@ def _validate_interp_indexer(x, new_x): return self._replace_with_new_dims( variables, coord_names, indexes=indexes) - def interp_like(self, other, method='linear', assume_sorted=False, - kwargs=None): + def interp_like( + self, + other: Union['Dataset', 'DataArray'], + method: str = 'linear', + assume_sorted: bool = False, + kwargs: Mapping[str, Any] = None + ) -> 'Dataset': """Interpolate this object onto the coordinates of another object, filling the out of range values with NaN. @@ -2293,8 +2332,8 @@ def interp_like(self, other, method='linear', assume_sorted=False, kwargs = {} coords = alignment.reindex_like_indexers(self, other) - numeric_coords = OrderedDict() - object_coords = OrderedDict() + numeric_coords = OrderedDict() # type: OrderedDict[Hashable, pd.Index] + object_coords = OrderedDict() # type: OrderedDict[Hashable, pd.Index] for k, v in coords.items(): if v.dtype.kind in 'uifcMm': numeric_coords[k] = v @@ -2347,7 +2386,12 @@ def _rename_all(self, name_dict, dims_dict): indexes = self._rename_indexes(name_dict) return variables, coord_names, dims, indexes - def rename(self, name_dict=None, inplace=None, **names): + def rename( + self, + name_dict: Mapping[Hashable, Hashable] = None, + inplace: bool = None, + **names: Hashable + ) -> 'Dataset': """Returns a new object with renamed variables and dimensions. Parameters @@ -2386,7 +2430,11 @@ def rename(self, name_dict=None, inplace=None, **names): return self._replace(variables, coord_names, dims=dims, indexes=indexes, inplace=inplace) - def rename_dims(self, dims_dict=None, **dims): + def rename_dims( + self, + dims_dict: Mapping[Hashable, Hashable] = None, + **dims: Hashable + ) -> 'Dataset': """Returns a new object with renamed dimensions only. Parameters @@ -2416,12 +2464,16 @@ def rename_dims(self, dims_dict=None, **dims): raise ValueError("cannot rename %r because it is not a " "dimension in this dataset" % k) - variables, coord_names, dims, indexes = self._rename_all( + variables, coord_names, sizes, indexes = self._rename_all( name_dict={}, dims_dict=dims_dict) - return self._replace(variables, coord_names, dims=dims, - indexes=indexes) + return self._replace( + variables, coord_names, dims=sizes, indexes=indexes) - def rename_vars(self, name_dict=None, **names): + def rename_vars( + self, + name_dict: Mapping[Hashable, Hashable] = None, + **names: Hashable + ) -> 'Dataset': """Returns a new object with renamed variables including coordinates Parameters @@ -2455,7 +2507,11 @@ def rename_vars(self, name_dict=None, **names): return self._replace(variables, coord_names, dims=dims, indexes=indexes) - def swap_dims(self, dims_dict, inplace=None): + def swap_dims( + self, + dims_dict: Mapping[Hashable, Hashable], + inplace: bool = None + ) -> 'Dataset': """Returns a new object with swapped dimensions. Parameters @@ -2496,8 +2552,8 @@ def swap_dims(self, dims_dict, inplace=None): coord_names = self._coord_names.copy() coord_names.update(dims_dict.values()) - variables = OrderedDict() - indexes = OrderedDict() + variables = OrderedDict() # type: OrderedDict[Hashable, Variable] + indexes = OrderedDict() # type: OrderedDict[Hashable, pd.Index] for k, v in self.variables.items(): dims = tuple(dims_dict.get(dim, dim) for dim in v.dims) if k in result_dims: @@ -2514,7 +2570,13 @@ def swap_dims(self, dims_dict, inplace=None): return self._replace_with_new_dims(variables, coord_names, indexes=indexes, inplace=inplace) - def expand_dims(self, dim=None, axis=None, **dim_kwargs): + def expand_dims( + self, + dim: Union[None, Hashable, Sequence[Hashable], + Mapping[Hashable, Any]] = None, + axis: Union[None, int, Sequence[int]] = None, + **dim_kwargs: Any + ) -> 'Dataset': """Return a new object with an additional axis (or axes) inserted at the corresponding position in the array shape. @@ -2523,16 +2585,21 @@ def expand_dims(self, dim=None, axis=None, **dim_kwargs): Parameters ---------- - dim : str, sequence of str, dict, or None - Dimensions to include on the new variable. - If provided as str or sequence of str, then dimensions are inserted - with length 1. If provided as a dict, then the keys are the new - dimensions and the values are either integers (giving the length of - the new dimensions) or sequence/ndarray (giving the coordinates of - the new dimensions). **WARNING** for python 3.5, if ``dim`` is - dict-like, then it must be an ``OrderedDict``. This is to ensure - that the order in which the dims are given is maintained. - axis : integer, list (or tuple) of integers, or None + dim : hashable, sequence of hashable, mapping, or None + Dimensions to include on the new variable. If provided as hashable + or sequence of hashable, then dimensions are inserted with length + 1. If provided as a mapping, then the keys are the new dimensions + and the values are either integers (giving the length of the new + dimensions) or array-like (giving the coordinates of the new + dimensions). + + .. note:: + + For Python 3.5, if ``dim`` is a mapping, then it must be an + ``OrderedDict``. This is to ensure that the order in which the + dims are given is maintained. + + axis : integer, sequence of integers, or None Axis position(s) where new axis is to be inserted (position(s) on the result array). If a list (or tuple) of integers is passed, multiple axes are inserted. In this case, dim arguments should be @@ -2542,39 +2609,49 @@ def expand_dims(self, dim=None, axis=None, **dim_kwargs): The keywords are arbitrary dimensions being inserted and the values are either the lengths of the new dims (if int is given), or their coordinates. Note, this is an alternative to passing a dict to the - dim kwarg and will only be used if dim is None. **WARNING** for - python 3.5 ``dim_kwargs`` is not available. + dim kwarg and will only be used if dim is None. + + .. note:: + + For Python 3.5, ``dim_kwargs`` is not available. Returns ------- expanded : same type as caller This object, but with an additional dimension(s). """ - if isinstance(dim, int): - raise TypeError('dim should be str or sequence of strs or dict') - elif isinstance(dim, str): + if dim is None: + pass + elif isinstance(dim, Mapping): + # We're later going to modify dim in place; don't tamper with + # the input + dim = OrderedDict(dim) + elif isinstance(dim, int): + raise TypeError( + "dim should be hashable or sequence of hashables or mapping" + ) + elif isinstance(dim, str) or not isinstance(dim, Sequence): dim = OrderedDict(((dim, 1),)) - elif isinstance(dim, (list, tuple)): + elif isinstance(dim, Sequence): if len(dim) != len(set(dim)): raise ValueError('dims should not contain duplicate values.') dim = OrderedDict(((d, 1) for d in dim)) # TODO: get rid of the below code block when python 3.5 is no longer # supported. - python36_plus = sys.version_info[0] == 3 and sys.version_info[1] > 5 - not_ordereddict = dim is not None and not isinstance(dim, OrderedDict) - if not python36_plus and not_ordereddict: - raise TypeError("dim must be an OrderedDict for python <3.6") - elif not python36_plus and dim_kwargs: - raise ValueError("dim_kwargs isn't available for python <3.6") + if sys.version < '3.6': + if dim is not None and not isinstance(dim, OrderedDict): + raise TypeError("dim must be an OrderedDict for python <3.6") + if dim_kwargs: + raise ValueError("dim_kwargs isn't available for python <3.6") dim = either_dict_or_kwargs(dim, dim_kwargs, 'expand_dims') - - if axis is not None and not isinstance(axis, (list, tuple)): - axis = [axis] + assert isinstance(dim, MutableMapping) if axis is None: axis = list(range(len(dim))) + elif not isinstance(axis, Sequence): + axis = [axis] if len(dim) != len(axis): raise ValueError('lengths of dim and axis should be identical.') @@ -2588,7 +2665,7 @@ def expand_dims(self, dim=None, axis=None, **dim_kwargs): '{dim} already exists as coordinate or' ' variable name.'.format(dim=d)) - variables = OrderedDict() + variables = OrderedDict() # type: OrderedDict[Hashable, Variable] coord_names = self._coord_names.copy() # If dim is a dict, then ensure that the values are either integers # or iterables. @@ -2632,9 +2709,7 @@ def expand_dims(self, dim=None, axis=None, **dim_kwargs): all_dims = list(zip(v.dims, v.shape)) for d, c in zip_axis_dim: all_dims.insert(d, c) - all_dims = OrderedDict(all_dims) - - variables[k] = v.set_dims(all_dims) + variables[k] = v.set_dims(OrderedDict(all_dims)) else: # If dims includes a label of a non-dimension coordinate, # it will be promoted to a 1D coordinate with a single value. From 29abedfb8824d114bfdad86e127ad12479ace41e Mon Sep 17 00:00:00 2001 From: Guido Imperiale Date: Sun, 14 Jul 2019 01:15:47 +0100 Subject: [PATCH 4/9] Fix Python 3.5-specific check for OrderedDict --- xarray/core/dataset.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index a478ce0eac2..9e8a931d217 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -2609,6 +2609,14 @@ def expand_dims( expanded : same type as caller This object, but with an additional dimension(s). """ + # TODO: get rid of the below code block when python 3.5 is no longer + # supported. + if sys.version < '3.6': + if isinstance(dim, Mapping) and not isinstance(dim, OrderedDict): + raise TypeError("dim must be an OrderedDict for python <3.6") + if dim_kwargs: + raise ValueError("dim_kwargs isn't available for python <3.6") + if dim is None: pass elif isinstance(dim, Mapping): @@ -2626,14 +2634,6 @@ def expand_dims( raise ValueError('dims should not contain duplicate values.') dim = OrderedDict(((d, 1) for d in dim)) - # TODO: get rid of the below code block when python 3.5 is no longer - # supported. - if sys.version < '3.6': - if dim is not None and not isinstance(dim, OrderedDict): - raise TypeError("dim must be an OrderedDict for python <3.6") - if dim_kwargs: - raise ValueError("dim_kwargs isn't available for python <3.6") - dim = either_dict_or_kwargs(dim, dim_kwargs, 'expand_dims') assert isinstance(dim, MutableMapping) From 44cfb8e039cc70a2366fb7b1cfa1e07e8eee5bab Mon Sep 17 00:00:00 2001 From: Guido Imperiale Date: Sun, 14 Jul 2019 20:43:55 +0100 Subject: [PATCH 5/9] typing in merge --- xarray/core/dataarray.py | 39 +++++++++------ xarray/core/dataset.py | 104 +++++++++++++++++++++++++-------------- xarray/core/merge.py | 80 +++++++++++++++++++++--------- 3 files changed, 148 insertions(+), 75 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 1a79785b6c4..7f83993a19d 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -1334,9 +1334,13 @@ def expand_dims(self, dim: Union[None, Hashable, Sequence[Hashable], ds = self._to_temp_dataset().expand_dims(dim, axis) return self._from_temp_dataset(ds) - def set_index(self, indexes: Optional[Mapping[Hashable, Any]] = None, - append: bool = False, inplace: Optional[bool] = None, - **indexes_kwargs: Any) -> Optional['DataArray']: + def set_index( + self, + indexes: Mapping[Hashable, Union[Hashable, Sequence[Hashable]]] = None, + append: bool = False, + inplace: bool = None, + **indexes_kwargs: Union[Hashable, Sequence[Hashable]] + ) -> Optional['DataArray']: """Set DataArray (multi-)indexes using one or more existing coordinates. @@ -1375,9 +1379,12 @@ def set_index(self, indexes: Optional[Mapping[Hashable, Any]] = None, else: return self._replace(coords=coords) - def reset_index(self, dims_or_levels: Union[Hashable, Sequence[Hashable]], - drop: bool = False, inplace: Optional[bool] = None - ) -> Optional['DataArray']: + def reset_index( + self, + dims_or_levels: Union[Hashable, Sequence[Hashable]], + drop: bool = False, + inplace: bool = None + ) -> Optional['DataArray']: """Reset the specified index(es) or multi-index level(s). Parameters @@ -1411,12 +1418,12 @@ def reset_index(self, dims_or_levels: Union[Hashable, Sequence[Hashable]], else: return self._replace(coords=coords) - def reorder_levels(self, - dim_order: Optional[ - Mapping[Hashable, Sequence[int]]] = None, - inplace: Optional[bool] = None, - **dim_order_kwargs: Sequence[int] - ) -> Optional['DataArray']: + def reorder_levels( + self, + dim_order: Mapping[Hashable, Sequence[int]] = None, + inplace: bool = None, + **dim_order_kwargs: Sequence[int] + ) -> Optional['DataArray']: """Rearrange index levels using input order. Parameters @@ -1457,9 +1464,11 @@ def reorder_levels(self, else: return self._replace(coords=coords) - def stack(self, dimensions: Optional[ - Mapping[Hashable, Sequence[Hashable]]] = None, - **dimensions_kwargs: Sequence[Hashable]) -> 'DataArray': + def stack( + self, + dimensions: Mapping[Hashable, Sequence[Hashable]] = None, + **dimensions_kwargs: Sequence[Hashable] + ) -> 'DataArray': """ Stack any number of existing dimensions into a single new dimension. diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 9e8a931d217..6830db515fa 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -39,6 +39,7 @@ if TYPE_CHECKING: from ..backends import AbstractDataStore, ZarrStore from .dataarray import DataArray + from .merge import DatasetLike try: from dask.delayed import Delayed except ImportError: @@ -2710,8 +2711,13 @@ def expand_dims( return self._replace_vars_and_dims( variables, dims=new_dims, coord_names=coord_names) - def set_index(self, indexes=None, append=False, inplace=None, - **indexes_kwargs): + def set_index( + self, + indexes: Mapping[Hashable, Union[Hashable, Sequence[Hashable]]] = None, + append: bool = False, + inplace: bool = None, + **indexes_kwargs: Union[Hashable, Sequence[Hashable]] + ) -> 'Dataset': """Set Dataset (multi-)indexes using one or more existing coordinates or variables. @@ -2749,7 +2755,12 @@ def set_index(self, indexes=None, append=False, inplace=None, return self._replace_vars_and_dims(variables, coord_names=coord_names, inplace=inplace) - def reset_index(self, dims_or_levels, drop=False, inplace=None): + def reset_index( + self, + dims_or_levels: Union[Hashable, Sequence[Hashable]], + drop: bool = False, + inplace: bool = None, + ) -> 'Dataset': """Reset the specified index(es) or multi-index level(s). Parameters @@ -2774,14 +2785,22 @@ def reset_index(self, dims_or_levels, drop=False, inplace=None): Dataset.set_index """ inplace = _check_inplace(inplace) - variables, coord_names = split_indexes(dims_or_levels, self._variables, - self._coord_names, - self._level_coords, drop=drop) + variables, coord_names = split_indexes( + dims_or_levels, + self._variables, + self._coord_names, + cast(Mapping[Hashable, Hashable], self._level_coords), + drop=drop, + ) return self._replace_vars_and_dims(variables, coord_names=coord_names, inplace=inplace) - def reorder_levels(self, dim_order=None, inplace=None, - **dim_order_kwargs): + def reorder_levels( + self, + dim_order: Mapping[Hashable, Sequence[int]] = None, + inplace: bool = None, + **dim_order_kwargs: Sequence[int] + ) -> 'Dataset': """Rearrange index levels using input order. Parameters @@ -2854,7 +2873,11 @@ def _stack_once(self, dims, new_dim): return self._replace_with_new_dims( variables, coord_names=coord_names, indexes=indexes) - def stack(self, dimensions=None, **dimensions_kwargs): + def stack( + self, + dimensions: Mapping[Hashable, Sequence[Hashable]] = None, + **dimensions_kwargs: Sequence[Hashable] + ) -> 'Dataset': """ Stack any number of existing dimensions into a single new dimension. @@ -2886,8 +2909,13 @@ def stack(self, dimensions=None, **dimensions_kwargs): result = result._stack_once(dims, new_dim) return result - def to_stacked_array(self, new_dim, sample_dims, variable_dim='variable', - name=None): + def to_stacked_array( + self, + new_dim: Hashable, + sample_dims: Sequence[Hashable], + variable_dim: str = 'variable', + name: Hashable = None + ) -> 'DataArray': """Combine variables of differing dimensionality into a DataArray without broadcasting. @@ -2896,9 +2924,9 @@ def to_stacked_array(self, new_dim, sample_dims, variable_dim='variable', Parameters ---------- - new_dim : str + new_dim : Hashable Name of the new stacked coordinate - sample_dims : Sequence[str] + sample_dims : Sequence[Hashable] Dimensions that **will not** be stacked. Each array in the dataset must share these dimensions. For machine learning applications, these define the dimensions over which samples are drawn. @@ -2999,7 +3027,7 @@ def ensure_stackable(val): return data_array - def _unstack_once(self, dim): + def _unstack_once(self, dim: Hashable) -> 'Dataset': index = self.get_index(dim) # GH2619. For MultiIndex, we need to call remove_unused. if LooseVersion(pd.__version__) >= "0.20": @@ -3018,7 +3046,7 @@ def _unstack_once(self, dim): new_dim_names = index.names new_dim_sizes = [lev.size for lev in index.levels] - variables = OrderedDict() + variables = OrderedDict() # type: OrderedDict[Hashable, Variable] indexes = OrderedDict( (k, v) for k, v in self.indexes.items() if k != dim) @@ -3039,7 +3067,10 @@ def _unstack_once(self, dim): return self._replace_with_new_dims( variables, coord_names=coord_names, indexes=indexes) - def unstack(self, dim=None): + def unstack( + self, + dim: Union[Hashable, Iterable[Hashable]] = None + ) -> 'Dataset': """ Unstack existing dimensions corresponding to MultiIndexes into multiple new dimensions. @@ -3048,7 +3079,7 @@ def unstack(self, dim=None): Parameters ---------- - dim : str or sequence of str, optional + dim : Hashable or iterable of Hashable, optional Dimension(s) over which to unstack. By default unstacks all MultiIndexes. @@ -3061,12 +3092,16 @@ def unstack(self, dim=None): -------- Dataset.stack """ - if dim is None: - dims = [d for d in self.dims if isinstance(self.get_index(d), - pd.MultiIndex)] + dims = [ + d for d in self.dims + if isinstance(self.get_index(d), pd.MultiIndex) + ] else: - dims = [dim] if isinstance(dim, str) else dim + if isinstance(dim, str) or not isinstance(dim, Iterable): + dims = [dim] + else: + dims = list(dim) missing_dims = [d for d in dims if d not in self.dims] if missing_dims: @@ -3084,19 +3119,7 @@ def unstack(self, dim=None): result = result._unstack_once(dim) return result - def update( - self, - other: Union[ - 'Dataset', - Mapping[Hashable, Union[ - 'DataArray', - Variable, - Tuple[Hashable, Any], - Tuple[Sequence[Hashable], Any], - ]], - ], - inplace: bool = None - ) -> 'Dataset': + def update(self, other: 'DatasetLike', inplace: bool = None) -> 'Dataset': """Update this dataset's variables with those from another dataset. Parameters @@ -3131,8 +3154,15 @@ def update( return self._replace_vars_and_dims(variables, coord_names, dims, inplace=inplace) - def merge(self, other, inplace=None, overwrite_vars=frozenset(), - compat='no_conflicts', join='outer', fill_value=dtypes.NA): + def merge( + self, + other: 'DatasetLike', + inplace: bool = None, + overwrite_vars: Union[Hashable, Iterable[Hashable]] = frozenset(), + compat: str = 'no_conflicts', + join: str = 'outer', + fill_value: Any = dtypes.NA + ) -> 'Dataset': """Merge the arrays of two datasets into a single dataset. This method generally does not allow for overriding data, with the @@ -3147,7 +3177,7 @@ def merge(self, other, inplace=None, overwrite_vars=frozenset(), inplace : bool, optional If True, merge the other dataset into this dataset in-place. Otherwise, return a new dataset object. - overwrite_vars : str or sequence, optional + overwrite_vars : Hashable or iterable of Hashable, optional If provided, update variables of these name(s) without checking for conflicts in this dataset. compat : {'broadcast_equals', 'equals', 'identical', diff --git a/xarray/core/merge.py b/xarray/core/merge.py index e71cf16a41d..83869c996a6 100644 --- a/xarray/core/merge.py +++ b/xarray/core/merge.py @@ -1,6 +1,19 @@ from collections import OrderedDict -from typing import (Any, Dict, Hashable, List, Mapping, Optional, Sequence, - Set, Tuple, Union, TYPE_CHECKING) +from typing import ( + Any, + Dict, + Hashable, + Iterable, + List, + Mapping, + MutableMapping, + Optional, + Sequence, + Set, + Tuple, + Union, + TYPE_CHECKING +) import pandas as pd @@ -14,6 +27,21 @@ from .dataarray import DataArray from .dataset import Dataset + DatasetLikeValue = Union[ + DataArray, + Variable, + Tuple[Hashable, Any], + Tuple[Sequence[Hashable], Any], + ] + DatasetLike = Union[Dataset, Mapping[Hashable, DatasetLikeValue]] + """Any object type that can be used on the rhs of Dataset.update, + Dataset.merge, etc. + """ + MutableDatasetLike = Union[ + Dataset, + MutableMapping[Hashable, DatasetLikeValue], + ] + PANDAS_TYPES = (pd.Series, pd.DataFrame, pdcompat.Panel) @@ -228,8 +256,9 @@ def expand_variable_dicts( return var_dicts -def determine_coords(list_of_variable_dicts): - # type: (List[Dict]) -> Tuple[Set, Set] +def determine_coords( + list_of_variable_dicts: Iterable['DatasetLike'] +) -> Tuple[Set[Hashable], Set[Hashable]]: """Given a list of dicts with xarray object values, identify coordinates. Parameters @@ -265,7 +294,9 @@ def determine_coords(list_of_variable_dicts): return coord_names, noncoord_names -def coerce_pandas_values(objects): +def coerce_pandas_values( + objects: Iterable['DatasetLike'] +) -> List['DatasetLike']: """Convert pandas values found in a list of labeled objects. Parameters @@ -285,7 +316,7 @@ def coerce_pandas_values(objects): out = [] for obj in objects: if isinstance(obj, Dataset): - variables = obj + variables = obj # type: DatasetLike else: variables = OrderedDict() if isinstance(obj, PANDAS_TYPES): @@ -555,17 +586,28 @@ def merge(objects, compat='no_conflicts', join='outer', fill_value=dtypes.NA): return merged -def dataset_merge_method(dataset, other, overwrite_vars, compat, join, - fill_value=dtypes.NA): - """Guts of the Dataset.merge method.""" +def dataset_merge_method( + dataset: 'Dataset', + other: 'DatasetLike', + overwrite_vars: Union[Hashable, Iterable[Hashable]], + compat: str, + join: str, + fill_value: Any +) -> Tuple['OrderedDict[Hashable, Variable]', + Set[Hashable], + Dict[Hashable, int]]: + """Guts of the Dataset.merge method. + """ # we are locked into supporting overwrite_vars for the Dataset.merge # method due for backwards compatibility # TODO: consider deprecating it? - if isinstance(overwrite_vars, str): - overwrite_vars = set([overwrite_vars]) - overwrite_vars = set(overwrite_vars) + if isinstance(overwrite_vars, Iterable) and not isinstance( + overwrite_vars, str): + overwrite_vars = set(overwrite_vars) + else: + overwrite_vars = {overwrite_vars} if not overwrite_vars: objs = [dataset, other] @@ -574,8 +616,8 @@ def dataset_merge_method(dataset, other, overwrite_vars, compat, join, objs = [dataset, other] priority_arg = 1 else: - other_overwrite = OrderedDict() - other_no_overwrite = OrderedDict() + other_overwrite = OrderedDict() # type: MutableDatasetLike + other_no_overwrite = OrderedDict() # type: MutableDatasetLike for k, v in other.items(): if k in overwrite_vars: other_overwrite[k] = v @@ -590,15 +632,7 @@ def dataset_merge_method(dataset, other, overwrite_vars, compat, join, def dataset_update_method( dataset: 'Dataset', - other: Union[ - 'Dataset', - Mapping[Hashable, Union[ - 'DataArray', - Variable, - Tuple[Hashable, Any], - Tuple[Sequence[Hashable], Any], - ]], - ], + other: 'DatasetLike', ) -> Tuple['OrderedDict[Hashable, Variable]', Set[Hashable], Dict[Hashable, int]]: From 9f71e0cc8156c13f5a9714183c6fdfdf141eb881 Mon Sep 17 00:00:00 2001 From: Guido Imperiale Date: Mon, 15 Jul 2019 19:03:20 +0100 Subject: [PATCH 6/9] broadcast_like typing tweaks --- xarray/core/dataarray.py | 7 ++++--- xarray/core/dataset.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index dd95edbcae4..1be5f007f8c 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -997,7 +997,7 @@ def sel_points(self, dim='points', method=None, tolerance=None, def broadcast_like(self, other: Union['DataArray', Dataset], - exclude=None) -> 'DataArray': + exclude: Iterable[Hashable] = None) -> 'DataArray': """Broadcast this DataArray against another Dataset or DataArray. This is equivalent to xr.broadcast(other, self)[1] @@ -1005,12 +1005,13 @@ def broadcast_like(self, ---------- other : Dataset or DataArray Object against which to broadcast this array. - exclude : sequence of str, optional + exclude : iterable of hashable, optional Dimensions that must not be broadcasted """ - if exclude is None: exclude = set() + else: + exclude = set(exclude) args = align(other, self, join='outer', copy=False, exclude=exclude) dims_map, common_coords = _get_broadcast_dims_map_common_coords( diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index b781f9736ee..42d3e1304ef 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -2034,7 +2034,7 @@ def sel_points(self, dim: Any = 'points', method: Optional[str] = None, def broadcast_like(self, other: Union['Dataset', 'DataArray'], - exclude=None) -> 'Dataset': + exclude: Iterable[Hashable] = None) -> 'Dataset': """Broadcast this DataArray against another Dataset or DataArray. This is equivalent to xr.broadcast(other, self)[1] @@ -2042,13 +2042,14 @@ def broadcast_like(self, ---------- other : Dataset or DataArray Object against which to broadcast this array. - exclude : sequence of str, optional + exclude : iterable of hashable, optional Dimensions that must not be broadcasted """ - if exclude is None: exclude = set() + else: + exclude = set(exclude) args = align(other, self, join='outer', copy=False, exclude=exclude) dims_map, common_coords = _get_broadcast_dims_map_common_coords( From 829f5ffb66d84addab6e039c5dd5f7a1b53f9f61 Mon Sep 17 00:00:00 2001 From: Guido Imperiale Date: Mon, 15 Jul 2019 19:21:34 +0100 Subject: [PATCH 7/9] Changed redundant x: Optional[MyType] = None to x: MyType = None --- xarray/core/common.py | 34 +++++++++---------- xarray/core/computation.py | 6 ++-- xarray/core/dataarray.py | 68 +++++++++++++++++++------------------- xarray/core/dataset.py | 44 +++++++++++++----------- xarray/core/merge.py | 10 +++--- xarray/core/utils.py | 6 ++-- 6 files changed, 86 insertions(+), 82 deletions(-) diff --git a/xarray/core/common.py b/xarray/core/common.py index c4b13b1a5e7..bae3b6cd73d 100644 --- a/xarray/core/common.py +++ b/xarray/core/common.py @@ -3,7 +3,7 @@ from textwrap import dedent from typing import ( Any, Callable, Hashable, Iterable, Iterator, List, Mapping, MutableMapping, - Optional, Tuple, TypeVar, Union) + Tuple, TypeVar, Union) import numpy as np import pandas as pd @@ -101,7 +101,7 @@ def __int__(self: Any) -> int: def __complex__(self: Any) -> complex: return complex(self.values) - def __array__(self: Any, dtype: Optional[DTypeLike] = None) -> np.ndarray: + def __array__(self: Any, dtype: DTypeLike = None) -> np.ndarray: return np.asarray(self.values, dtype=dtype) def __repr__(self) -> str: @@ -448,7 +448,7 @@ def pipe(self, func: Union[Callable[..., T], Tuple[Callable[..., T], str]], return func(self, *args, **kwargs) def groupby(self, group, squeeze: bool = True, - restore_coord_dims: Optional[bool] = None): + restore_coord_dims: bool = None): """Returns a GroupBy object for performing grouped operations. Parameters @@ -501,7 +501,7 @@ def groupby(self, group, squeeze: bool = True, def groupby_bins(self, group, bins, right: bool = True, labels=None, precision: int = 3, include_lowest: bool = False, squeeze: bool = True, - restore_coord_dims: Optional[bool] = None): + restore_coord_dims: bool = None): """Returns a GroupBy object for performing grouped operations. Rather than using all unique values of `group`, the values are discretized @@ -557,8 +557,8 @@ def groupby_bins(self, group, bins, right: bool = True, labels=None, 'include_lowest': include_lowest}) - def rolling(self, dim: Optional[Mapping[Hashable, int]] = None, - min_periods: Optional[int] = None, center: bool = False, + def rolling(self, dim: Mapping[Hashable, int] = None, + min_periods: int = None, center: bool = False, **window_kwargs: int): """ Rolling window object. @@ -621,7 +621,7 @@ def rolling(self, dim: Optional[Mapping[Hashable, int]] = None, def rolling_exp( self, - window: Optional[Mapping[Hashable, int]] = None, + window: Mapping[Hashable, int] = None, window_type: str = 'span', **window_kwargs ): @@ -658,7 +658,7 @@ def rolling_exp( return self._rolling_exp_cls(self, window, window_type) - def coarsen(self, dim: Optional[Mapping[Hashable, int]] = None, + def coarsen(self, dim: Mapping[Hashable, int] = None, boundary: str = 'exact', side: Union[str, Mapping[Hashable, str]] = 'left', coord_func: str = 'mean', @@ -721,11 +721,11 @@ def coarsen(self, dim: Optional[Mapping[Hashable, int]] = None, self, dim, boundary=boundary, side=side, coord_func=coord_func) - def resample(self, indexer: Optional[Mapping[Hashable, str]] = None, - skipna=None, closed: Optional[str] = None, - label: Optional[str] = None, - base: int = 0, keep_attrs: Optional[bool] = None, - loffset=None, restore_coord_dims: Optional[bool] = None, + def resample(self, indexer: Mapping[Hashable, str] = None, + skipna=None, closed: str = None, + label: str = None, + base: int = 0, keep_attrs: bool = None, + loffset=None, restore_coord_dims: bool = None, **indexer_kwargs: str): """Returns a Resample object for performing resampling operations. @@ -1003,7 +1003,7 @@ def __getitem__(self, value): raise NotImplementedError -def full_like(other, fill_value, dtype: Optional[DTypeLike] = None): +def full_like(other, fill_value, dtype: DTypeLike = None): """Return a new object with the same shape and type as a given object. Parameters @@ -1044,7 +1044,7 @@ def full_like(other, fill_value, dtype: Optional[DTypeLike] = None): def _full_like_variable(other, fill_value, - dtype: Optional[DTypeLike] = None): + dtype: DTypeLike = None): """Inner function of full_like, where other must be a variable """ from .variable import Variable @@ -1061,13 +1061,13 @@ def _full_like_variable(other, fill_value, return Variable(dims=other.dims, data=data, attrs=other.attrs) -def zeros_like(other, dtype: Optional[DTypeLike] = None): +def zeros_like(other, dtype: DTypeLike = None): """Shorthand for full_like(other, 0, dtype) """ return full_like(other, 0, dtype) -def ones_like(other, dtype: Optional[DTypeLike] = None): +def ones_like(other, dtype: DTypeLike = None): """Shorthand for full_like(other, 1, dtype) """ return full_like(other, 1, dtype) diff --git a/xarray/core/computation.py b/xarray/core/computation.py index ef8a3e621c3..7ccfeae2219 100644 --- a/xarray/core/computation.py +++ b/xarray/core/computation.py @@ -683,7 +683,7 @@ def apply_array_ufunc(func, *args, dask='forbidden'): def apply_ufunc( func: Callable, *args: Any, - input_core_dims: Optional[Sequence[Sequence]] = None, + input_core_dims: Sequence[Sequence] = None, output_core_dims: Optional[Sequence[Sequence]] = ((),), exclude_dims: AbstractSet = frozenset(), vectorize: bool = False, @@ -693,8 +693,8 @@ def apply_ufunc( keep_attrs: bool = False, kwargs: Mapping = None, dask: str = 'forbidden', - output_dtypes: Optional[Sequence] = None, - output_sizes: Optional[Mapping[Any, int]] = None + output_dtypes: Sequence = None, + output_sizes: Mapping[Any, int] = None ) -> Any: """Apply a vectorized function for unlabeled arrays on xarray objects. diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 1be5f007f8c..edb5a20f1e9 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -197,8 +197,8 @@ def __init__( None, ] = None, dims: Union[Hashable, Sequence[Hashable], None] = None, - name: Optional[Hashable] = None, - attrs: Optional[Mapping] = None, + name: Hashable = None, + attrs: Mapping = None, # deprecated parameters encoding=None, # internal parameters @@ -298,7 +298,7 @@ def __init__( def _replace( self, - variable: Optional[Variable] = None, + variable: Variable = None, coords=None, name: Union[Hashable, None, ReprObject] = __default, ) -> 'DataArray': @@ -379,7 +379,7 @@ def subset(dim, label): def _to_dataset_whole( self, - name: Optional[Hashable] = None, + name: Hashable = None, shallow_copy: bool = True ) -> Dataset: if name is None: @@ -403,8 +403,8 @@ def _to_dataset_whole( def to_dataset( self, - dim: Optional[Hashable] = None, - name: Optional[Hashable] = None, + dim: Hashable = None, + name: Hashable = None, ) -> Dataset: """Convert a DataArray to a Dataset. @@ -636,7 +636,7 @@ def coords(self) -> DataArrayCoordinates: def reset_coords(self, names: Union[Iterable[Hashable], Hashable, None] = None, - drop: bool = False, inplace: Optional[bool] = None + drop: bool = False, inplace: bool = None ) -> Union[None, 'DataArray', Dataset]: """Given names of coordinates, reset them to become variables. @@ -777,7 +777,7 @@ def persist(self, **kwargs) -> 'DataArray': def copy( self, deep: bool = True, - data: Optional[Any] = None, + data: Any = None, ) -> 'DataArray': """Returns a copy of this array. @@ -882,7 +882,7 @@ def chunk( Mapping[Hashable, Union[None, Number, Tuple[Number, ...]]], ] = None, name_prefix: str = 'xarray-', - token: Optional[str] = None, + token: str = None, lock: bool = False ) -> 'DataArray': """Coerce this array's data into a dask arrays with the given chunks. @@ -921,7 +921,7 @@ def chunk( def isel( self, - indexers: Optional[Mapping[Hashable, Any]] = None, + indexers: Mapping[Hashable, Any] = None, drop: bool = False, **indexers_kwargs: Any ) -> 'DataArray': @@ -939,8 +939,8 @@ def isel( def sel( self, - indexers: Optional[Mapping[Hashable, Any]] = None, - method: Optional[str] = None, + indexers: Mapping[Hashable, Any] = None, + method: str = None, tolerance=None, drop: bool = False, **indexers_kwargs: Any @@ -1020,7 +1020,7 @@ def broadcast_like(self, return _broadcast_helper(self, exclude, dims_map, common_coords) def reindex_like(self, other: Union['DataArray', Dataset], - method: Optional[str] = None, tolerance=None, + method: str = None, tolerance=None, copy: bool = True, fill_value=dtypes.NA) -> 'DataArray': """Conform this object onto the indexes of another object, filling in missing values with ``fill_value``. The default fill value is NaN. @@ -1075,8 +1075,8 @@ def reindex_like(self, other: Union['DataArray', Dataset], fill_value=fill_value, ) - def reindex(self, indexers: Optional[Mapping[Hashable, Any]] = None, - method: Optional[str] = None, tolerance=None, + def reindex(self, indexers: Mapping[Hashable, Any] = None, + method: str = None, tolerance=None, copy: bool = True, fill_value=dtypes.NA, **indexers_kwargs: Any ) -> 'DataArray': """Conform this object onto the indexes of another object, filling in @@ -1131,9 +1131,9 @@ def reindex(self, indexers: Optional[Mapping[Hashable, Any]] = None, fill_value=fill_value) return self._from_temp_dataset(ds) - def interp(self, coords: Optional[Mapping[Hashable, Any]] = None, + def interp(self, coords: Mapping[Hashable, Any] = None, method: str = 'linear', assume_sorted: bool = False, - kwargs: Optional[Mapping[str, Any]] = None, + kwargs: Mapping[str, Any] = None, **coords_kwargs: Any) -> 'DataArray': """ Multidimensional interpolation of variables. @@ -1188,7 +1188,7 @@ def interp(self, coords: Optional[Mapping[Hashable, Any]] = None, def interp_like(self, other: Union['DataArray', Dataset], method: str = 'linear', assume_sorted: bool = False, - kwargs: Optional[Mapping[str, Any]] = None) -> 'DataArray': + kwargs: Mapping[str, Any] = None) -> 'DataArray': """Interpolate this object onto the coordinates of another object, filling out of range values with NaN. @@ -1237,7 +1237,7 @@ def interp_like(self, other: Union['DataArray', Dataset], def rename( self, new_name_or_name_dict: - Optional[Union[Hashable, Mapping[Hashable, Hashable]]] = None, + Union[Hashable, Mapping[Hashable, Hashable]] = None, **names: Hashable ) -> 'DataArray': """Returns a new DataArray with renamed coordinates or a new name. @@ -1657,7 +1657,7 @@ def to_unstacked_dataset(self, dim, level=0): def transpose(self, *dims: Hashable, - transpose_coords: Optional[bool] = None) -> 'DataArray': + transpose_coords: bool = None) -> 'DataArray': """Return a new DataArray object with transposed dimensions. Parameters @@ -1713,7 +1713,7 @@ def T(self) -> 'DataArray': def drop(self, labels: Union[Hashable, Sequence[Hashable]], - dim: Optional[Hashable] = None, + dim: Hashable = None, *, errors: str = 'raise') -> 'DataArray': """Drop coordinates or index labels from this DataArray. @@ -1740,7 +1740,7 @@ def drop(self, return self._from_temp_dataset(ds) def dropna(self, dim: Hashable, how: str = 'any', - thresh: Optional[int] = None) -> 'DataArray': + thresh: int = None) -> 'DataArray': """Returns a new array with dropped labels for missing values along the provided dimension. @@ -1788,7 +1788,7 @@ def fillna(self, value: Any) -> 'DataArray': return out def interpolate_na(self, dim=None, method: str = 'linear', - limit: Optional[int] = None, + limit: int = None, use_coordinate: Union[bool, str] = True, **kwargs: Any) -> 'DataArray': """Interpolate values according to different methods. @@ -1833,7 +1833,7 @@ def interpolate_na(self, dim=None, method: str = 'linear', return interp_na(self, dim=dim, method=method, limit=limit, use_coordinate=use_coordinate, **kwargs) - def ffill(self, dim: Hashable, limit: Optional[int] = None) -> 'DataArray': + def ffill(self, dim: Hashable, limit: int = None) -> 'DataArray': """Fill NaN values by propogating values forward *Requires bottleneck.* @@ -1856,7 +1856,7 @@ def ffill(self, dim: Hashable, limit: Optional[int] = None) -> 'DataArray': from .missing import ffill return ffill(self, dim, limit=limit) - def bfill(self, dim: Hashable, limit: Optional[int] = None) -> 'DataArray': + def bfill(self, dim: Hashable, limit: int = None) -> 'DataArray': """Fill NaN values by propogating values backward *Requires bottleneck.* @@ -1900,7 +1900,7 @@ def combine_first(self, other: 'DataArray') -> 'DataArray': def reduce(self, func: Callable[..., Any], dim: Union[None, Hashable, Sequence[Hashable]] = None, axis: Union[None, int, Sequence[int]] = None, - keep_attrs: Optional[bool] = None, + keep_attrs: bool = None, keepdims: bool = False, **kwargs: Any) -> 'DataArray': """Reduce this array by applying `func` along some dimension(s). @@ -1971,7 +1971,7 @@ def to_pandas(self) -> Union['DataArray', pd.Series, pd.DataFrame]: def to_dataframe( self, - name: Optional[Hashable] = None, + name: Hashable = None, ) -> pd.DataFrame: """Convert this array and its coordinates into a tidy pandas.DataFrame. @@ -2269,7 +2269,7 @@ def func(self, *args, **kwargs): @staticmethod def _binary_op(f: Callable[..., Any], reflexive: bool = False, - join: Optional[str] = None, # see xarray.align + join: str = None, # see xarray.align **ignored_kwargs ) -> Callable[..., 'DataArray']: @functools.wraps(f) @@ -2403,7 +2403,7 @@ def diff(self, dim: Hashable, n: int = 1, label: Hashable = 'upper' ds = self._to_temp_dataset().diff(n=n, dim=dim, label=label) return self._from_temp_dataset(ds) - def shift(self, shifts: Optional[Mapping[Hashable, int]] = None, + def shift(self, shifts: Mapping[Hashable, int] = None, fill_value: Any = dtypes.NA, **shifts_kwargs: int ) -> 'DataArray': """Shift this array by an offset along one or more dimensions. @@ -2448,8 +2448,8 @@ def shift(self, shifts: Optional[Mapping[Hashable, int]] = None, shifts=shifts, fill_value=fill_value, **shifts_kwargs) return self._replace(variable=variable) - def roll(self, shifts: Optional[Mapping[Hashable, int]] = None, - roll_coords: Optional[bool] = None, + def roll(self, shifts: Mapping[Hashable, int] = None, + roll_coords: bool = None, **shifts_kwargs: int) -> 'DataArray': """Roll this array by an offset along one or more dimensions. @@ -2609,7 +2609,7 @@ def sortby(self, variables: Union[Hashable, 'DataArray', def quantile(self, q: Any, dim: Union[Hashable, Sequence[Hashable], None] = None, interpolation: str = 'linear', - keep_attrs: Optional[bool] = None) -> 'DataArray': + keep_attrs: bool = None) -> 'DataArray': """Compute the qth quantile of the data along the specified dimension. Returns the qth quantiles(s) of the array elements. @@ -2697,7 +2697,7 @@ def rank(self, dim: Hashable, pct: bool = False, keep_attrs: bool = None return self._from_temp_dataset(ds) def differentiate(self, coord: Hashable, edge_order: int = 1, - datetime_unit: Optional[str] = None) -> 'DataArray': + datetime_unit: str = None) -> 'DataArray': """ Differentiate the array with the second order accurate central differences. @@ -2753,7 +2753,7 @@ def differentiate(self, coord: Hashable, edge_order: int = 1, return self._from_temp_dataset(ds) def integrate(self, dim: Union[Hashable, Sequence[Hashable]], - datetime_unit: Optional[str] = None) -> 'DataArray': + datetime_unit: str = None) -> 'DataArray': """ integrate the array with the trapezoidal rule. .. note:: diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 42d3e1304ef..4af84d79f5b 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -55,8 +55,8 @@ def _get_virtual_variable(variables, key: Hashable, - level_vars: Optional[Mapping] = None, - dim_sizes: Optional[Mapping] = None, + level_vars: Mapping = None, + dim_sizes: Mapping = None, ) -> Tuple[Hashable, Hashable, Variable]: """Get a virtual variable (e.g., 'time.year' or a MultiIndex level) from a dict of xarray.Variable objects (if possible) @@ -343,14 +343,14 @@ def __init__( self, # could make a VariableArgs to use more generally, and refine these # categories - data_vars: Optional[Mapping[Hashable, Union[ + data_vars: Mapping[Hashable, Union[ 'DataArray', Variable, Tuple[Hashable, Any], Tuple[Sequence[Hashable], Any], - ]]] = None, - coords: Optional[Mapping[Hashable, Any]] = None, - attrs: Optional[Mapping] = None, + ]] = None, + coords: Mapping[Hashable, Any] = None, + attrs: Mapping = None, compat=None, ): """To load data from a file or file-like object, use the `open_dataset` @@ -742,7 +742,7 @@ def _from_vars_and_coord_names(cls, variables, coord_names, attrs=None): def _replace( # type: ignore self, variables: 'OrderedDict[Any, Variable]' = None, - coord_names: Optional[Set[Hashable]] = None, + coord_names: Set[Hashable] = None, dims: Dict[Any, int] = None, attrs: 'Optional[OrderedDict]' = __default, indexes: 'Optional[OrderedDict[Any, pd.Index]]' = __default, @@ -792,8 +792,8 @@ def _replace_with_new_dims( # type: ignore self, variables: 'OrderedDict[Any, Variable]', coord_names: set = None, - attrs: 'Optional[OrderedDict]' = __default, - indexes: 'Optional[OrderedDict[Any, pd.Index]]' = __default, + attrs: 'OrderedDict' = __default, + indexes: 'OrderedDict[Any, pd.Index]' = __default, inplace: bool = False, ) -> 'Dataset': """Replace variables with recalculated dimensions.""" @@ -805,8 +805,8 @@ def _replace_vars_and_dims( # type: ignore self, variables: 'OrderedDict[Any, Variable]', coord_names: set = None, - dims: 'Optional[Dict[Any, int]]' = None, - attrs: 'Optional[OrderedDict]' = __default, + dims: Dict[Any, int] = None, + attrs: 'OrderedDict' = __default, inplace: bool = False, ) -> 'Dataset': """Deprecated version of _replace_with_new_dims(). @@ -1759,8 +1759,8 @@ def isel( def sel( self, indexers: Mapping[Hashable, Any] = None, - method: Optional[str] = None, - tolerance: Optional[Number] = None, + method: str = None, + tolerance: Number = None, drop: bool = False, **indexers_kwargs: Any ) -> 'Dataset': @@ -1970,8 +1970,8 @@ def relevant_keys(mapping): dset.coords[dim_name] = dim_coord return dset - def sel_points(self, dim: Any = 'points', method: Optional[str] = None, - tolerance: Optional[Number] = None, + def sel_points(self, dim: Any = 'points', method: str = None, + tolerance: Number = None, **indexers: Any): """Returns a new dataset with each array indexed pointwise by tick labels along the specified dimension(s). @@ -2057,8 +2057,14 @@ def broadcast_like(self, return _broadcast_helper(self, exclude, dims_map, common_coords) - def reindex_like(self, other, method=None, tolerance=None, copy=True, - fill_value=dtypes.NA): + def reindex_like( + self, + other: Union['Dataset', 'DataArray'], + method: str = None, + tolerance: Number = None, + copy: bool = True, + fill_value: Any = dtypes.NA + ) -> 'Dataset': """Conform this object onto the indexes of another object, filling in missing values with ``fill_value``. The default fill value is NaN. @@ -2110,8 +2116,8 @@ def reindex_like(self, other, method=None, tolerance=None, copy=True, def reindex( self, indexers: Mapping[Hashable, Any] = None, - method: Optional[str] = None, - tolerance: Optional[Number] = None, + method: str = None, + tolerance: Number = None, copy: bool = True, fill_value: Any = dtypes.NA, **indexers_kwargs: Any diff --git a/xarray/core/merge.py b/xarray/core/merge.py index 83869c996a6..9c909aa197c 100644 --- a/xarray/core/merge.py +++ b/xarray/core/merge.py @@ -7,7 +7,6 @@ List, Mapping, MutableMapping, - Optional, Sequence, Set, Tuple, @@ -144,11 +143,10 @@ def __missing__(self, key): def merge_variables( - list_of_variables_dicts, # type: List[Mapping[Any, Variable]] - priority_vars=None, # type: Optional[Mapping[Any, Variable]] - compat='minimal', # type: str -): - # type: (...) -> OrderedDict[Any, Variable] + list_of_variables_dicts: List[Mapping[Any, Variable]], + priority_vars: Mapping[Any, Variable] = None, + compat: str = 'minimal', +) -> 'OrderedDict[Any, Variable]': """Merge dicts of variables, while resolving conflicts appropriately. Parameters diff --git a/xarray/core/utils.py b/xarray/core/utils.py index 811d71b79a9..8ce30a3c517 100644 --- a/xarray/core/utils.py +++ b/xarray/core/utils.py @@ -88,7 +88,7 @@ def safe_cast_to_index(array: Any) -> pd.Index: def multiindex_from_product_levels(levels: Sequence[pd.Index], - names: Optional[Sequence[str]] = None + names: Sequence[str] = None ) -> pd.MultiIndex: """Creating a MultiIndex from a product without refactorizing levels. @@ -351,7 +351,7 @@ class SortedKeysDict(MutableMapping[K, V]): """ __slots__ = ['mapping'] - def __init__(self, mapping: Optional[MutableMapping[K, V]] = None): + def __init__(self, mapping: MutableMapping[K, V] = None): self.mapping = {} if mapping is None else mapping def __getitem__(self, key: K) -> V: @@ -382,7 +382,7 @@ class OrderedSet(MutableSet[T]): The API matches the builtin set, but it preserves insertion order of elements, like an OrderedDict. """ - def __init__(self, values: Optional[AbstractSet[T]] = None): + def __init__(self, values: AbstractSet[T] = None): self._ordered_dict = OrderedDict() # type: MutableMapping[T, None] if values is not None: # Disable type checking - both mypy and PyCharm believes that From 298f88b08084bb01a0d2015bb6f7838c22ed5a99 Mon Sep 17 00:00:00 2001 From: Guido Imperiale Date: Mon, 15 Jul 2019 19:24:34 +0100 Subject: [PATCH 8/9] flake8 --- xarray/core/alignment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/core/alignment.py b/xarray/core/alignment.py index 7db2b2aa025..711634a95ca 100644 --- a/xarray/core/alignment.py +++ b/xarray/core/alignment.py @@ -424,8 +424,8 @@ def _get_broadcast_dims_map_common_coords(args, exclude): def _broadcast_helper(arg, exclude, dims_map, common_coords): - from .dataarray import DataArray - from .dataset import Dataset + from .dataarray import DataArray # noqa: F811 + from .dataset import Dataset # noqa: F811 def _set_dims(var): # Add excluded dims to a copy of dims_map From 7a36ff55bd7af922bad9578044cb1fd54e555722 Mon Sep 17 00:00:00 2001 From: Guido Imperiale Date: Wed, 24 Jul 2019 23:54:31 +0100 Subject: [PATCH 9/9] What's New --- doc/whats-new.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 00381519918..e77e0edd8e6 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -28,6 +28,15 @@ New functions/methods By `Deepak Cherian `_ and `David Mertz `_. +- 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:: + + [mypy-xarray] + ignore_missing_imports = True + + By `Guido Imperiale `_ + Enhancements ~~~~~~~~~~~~ @@ -63,15 +72,6 @@ New functions/methods (:issue:`3026`). By `Julia Kent `_. -- 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:: - - [mypy-xarray] - ignore_missing_imports = True - - By `Guido Imperiale `_ - Enhancements ~~~~~~~~~~~~