From d70e657d790d289d767b106facbf9ce54d334794 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 23 Mar 2022 10:45:43 +0100 Subject: [PATCH 01/14] make more args kw only (except 'dim') --- xarray/core/dataarray.py | 18 +++++++++++++++--- xarray/core/dataset.py | 12 ++++++++++-- xarray/core/groupby.py | 1 + xarray/core/weighted.py | 6 ++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index df1e096b021..4542e57c921 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2188,6 +2188,7 @@ def stack( def unstack( self, dim: Hashable | Sequence[Hashable] | None = None, + *, fill_value: Any = dtypes.NA, sparse: bool = False, ) -> DataArray: @@ -2462,7 +2463,9 @@ def drop_isel(self, indexers=None, **indexers_kwargs): dataset = dataset.drop_isel(indexers=indexers, **indexers_kwargs) return self._from_temp_dataset(dataset) - def dropna(self, dim: Hashable, how: str = "any", thresh: int = None) -> DataArray: + def dropna( + self, dim: Hashable, *, how: str = "any", thresh: int = None + ) -> DataArray: """Returns a new array with dropped labels for missing values along the provided dimension. @@ -3210,7 +3213,9 @@ def _title_for_slice(self, truncate: int = 50) -> str: return title - def diff(self, dim: Hashable, n: int = 1, label: Hashable = "upper") -> DataArray: + def diff( + self, dim: Hashable, *, n: int = 1, label: Hashable = "upper" + ) -> DataArray: """Calculate the n-th order discrete difference along given axis. Parameters @@ -3483,6 +3488,7 @@ def quantile( self, q: ArrayLike, dim: str | Sequence[Hashable] | None = None, + *, method: QUANTILE_METHODS = "linear", keep_attrs: bool = None, skipna: bool = None, @@ -3599,7 +3605,7 @@ def quantile( return self._from_temp_dataset(ds) def rank( - self, dim: Hashable, pct: bool = False, keep_attrs: bool = None + self, dim: Hashable, *, pct: bool = False, keep_attrs: bool = None ) -> DataArray: """Ranks the data. @@ -4169,6 +4175,7 @@ def pad( def idxmin( self, dim: Hashable = None, + *, skipna: bool = None, fill_value: Any = dtypes.NA, keep_attrs: bool = None, @@ -4265,6 +4272,7 @@ def idxmin( def idxmax( self, dim: Hashable = None, + *, skipna: bool = None, fill_value: Any = dtypes.NA, keep_attrs: bool = None, @@ -4361,6 +4369,7 @@ def idxmax( def argmin( self, dim: Hashable | Sequence[Hashable] = None, + *, axis: int = None, keep_attrs: bool = None, skipna: bool = None, @@ -4464,6 +4473,7 @@ def argmin( def argmax( self, dim: Hashable | Sequence[Hashable] = None, + *, axis: int = None, keep_attrs: bool = None, skipna: bool = None, @@ -4720,6 +4730,7 @@ def curvefit( def drop_duplicates( self, dim: Hashable | Iterable[Hashable] | ..., + *, keep: Literal["first", "last"] | Literal[False] = "first", ): """Returns a new DataArray with duplicate dimension values removed. @@ -4730,6 +4741,7 @@ def drop_duplicates( Pass `...` to drop duplicates along all dimensions. keep : {"first", "last", False}, default: "first" Determines which duplicates (if any) to keep. + - ``"first"`` : Drop duplicates except for the first occurrence. - ``"last"`` : Drop duplicates except for the last occurrence. - False : Drop all duplicates. diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 855718cfe74..1ea2daddcb7 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -3514,6 +3514,7 @@ def swap_dims( def expand_dims( self, dim: None | Hashable | Sequence[Hashable] | Mapping[Any, Any] = None, + *, axis: None | int | Sequence[int] = None, **dim_kwargs: Any, ) -> Dataset: @@ -3789,6 +3790,7 @@ def set_index( def reset_index( self, dims_or_levels: Hashable | Sequence[Hashable], + *, drop: bool = False, ) -> Dataset: """Reset the specified index(es) or multi-index level(s). @@ -4272,6 +4274,7 @@ def _unstack_full_reindex( def unstack( self, dim: Hashable | Iterable[Hashable] = None, + *, fill_value: Any = dtypes.NA, sparse: bool = False, ) -> Dataset: @@ -4818,6 +4821,7 @@ def transpose( def dropna( self, dim: Hashable, + *, how: str = "any", thresh: int = None, subset: Iterable[Hashable] = None, @@ -5995,7 +5999,7 @@ def _copy_attrs_from(self, other): if v in self.variables: self.variables[v].attrs = other.variables[v].attrs - def diff(self, dim, n=1, label="upper"): + def diff(self, dim, *, n=1, label="upper"): """Calculate the n-th order discrete difference along given axis. Parameters @@ -6317,6 +6321,7 @@ def quantile( self, q: ArrayLike, dim: str | Iterable[Hashable] | None = None, + *, method: QUANTILE_METHODS = "linear", numeric_only: bool = False, keep_attrs: bool = None, @@ -6491,7 +6496,7 @@ def quantile( ) return new.assign_coords(quantile=q) - def rank(self, dim, pct=False, keep_attrs=None): + def rank(self, dim, *, pct=False, keep_attrs=None): """Ranks the data. Equal values are assigned a rank that is the average of the ranks that @@ -7392,6 +7397,7 @@ def pad( def idxmin( self, dim: Hashable = None, + *, skipna: bool = None, fill_value: Any = dtypes.NA, keep_attrs: bool = None, @@ -7489,6 +7495,7 @@ def idxmin( def idxmax( self, dim: Hashable = None, + *, skipna: bool = None, fill_value: Any = dtypes.NA, keep_attrs: bool = None, @@ -7972,6 +7979,7 @@ def _wrapper(Y, *coords_, **kwargs): def drop_duplicates( self, dim: Hashable | Iterable[Hashable] | ..., + *, keep: Literal["first", "last"] | Literal[False] = "first", ): """Returns a new Dataset with duplicate dimension values removed. diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py index df78b7789f7..980bc1a6756 100644 --- a/xarray/core/groupby.py +++ b/xarray/core/groupby.py @@ -558,6 +558,7 @@ def quantile( self, q, dim=None, + *, method="linear", keep_attrs=None, skipna=None, diff --git a/xarray/core/weighted.py b/xarray/core/weighted.py index 83ce36bcb35..18eef1c1b16 100644 --- a/xarray/core/weighted.py +++ b/xarray/core/weighted.py @@ -248,6 +248,7 @@ def _implementation(self, func, dim, **kwargs): def sum_of_weights( self, dim: Hashable | Iterable[Hashable] | None = None, + *, keep_attrs: bool | None = None, ) -> T_Xarray: @@ -258,6 +259,7 @@ def sum_of_weights( def sum_of_squares( self, dim: Hashable | Iterable[Hashable] | None = None, + *, skipna: bool | None = None, keep_attrs: bool | None = None, ) -> T_Xarray: @@ -269,6 +271,7 @@ def sum_of_squares( def sum( self, dim: Hashable | Iterable[Hashable] | None = None, + *, skipna: bool | None = None, keep_attrs: bool | None = None, ) -> T_Xarray: @@ -280,6 +283,7 @@ def sum( def mean( self, dim: Hashable | Iterable[Hashable] | None = None, + *, skipna: bool | None = None, keep_attrs: bool | None = None, ) -> T_Xarray: @@ -291,6 +295,7 @@ def mean( def var( self, dim: Hashable | Iterable[Hashable] | None = None, + *, skipna: bool | None = None, keep_attrs: bool | None = None, ) -> T_Xarray: @@ -302,6 +307,7 @@ def var( def std( self, dim: Hashable | Iterable[Hashable] | None = None, + *, skipna: bool | None = None, keep_attrs: bool | None = None, ) -> T_Xarray: From 06ca96068109c267301cc7cefc6c6b6a075292ea Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Mon, 2 Oct 2023 11:24:44 +0200 Subject: [PATCH 02/14] add deprecation --- xarray/core/dataarray.py | 12 ++++++++++++ xarray/core/dataset.py | 11 +++++++++++ xarray/core/groupby.py | 2 ++ xarray/core/weighted.py | 7 +++++++ 4 files changed, 32 insertions(+) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 9dbedd71535..2f19437da66 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -66,6 +66,7 @@ ) from xarray.plot.accessor import DataArrayPlotAccessor from xarray.plot.utils import _get_units_from_attrs +from xarray.util.deprecation_helpers import _deprecate_positional_args if TYPE_CHECKING: from typing import TypeVar, Union @@ -967,9 +968,11 @@ def reset_coords( ) -> Self: ... + @_deprecate_positional_args("v2023.10.0") def reset_coords( self, names: Dims = None, + *, drop: bool = False, ) -> Self | Dataset: """Given names of coordinates, reset them to become variables. @@ -1287,9 +1290,11 @@ def chunksizes(self) -> Mapping[Any, tuple[int, ...]]: all_variables = [self.variable] + [c.variable for c in self.coords.values()] return get_chunksizes(all_variables) + @_deprecate_positional_args("v2023.10.0") def chunk( self, chunks: T_Chunks = {}, # {} even though it's technically unsafe, is being used intentionally here (#4667) + *, name_prefix: str = "xarray-", token: str | None = None, lock: bool = False, @@ -1724,9 +1729,11 @@ def thin( ds = self._to_temp_dataset().thin(indexers, **indexers_kwargs) return self._from_temp_dataset(ds) + @_deprecate_positional_args("v2023.10.0") def broadcast_like( self, other: T_DataArrayOrSet, + *, exclude: Iterable[Hashable] | None = None, ) -> Self: """Broadcast this DataArray against another Dataset or DataArray. @@ -1835,9 +1842,11 @@ def _reindex_callback( return da + @_deprecate_positional_args("v2023.10.0") def reindex_like( self, other: T_DataArrayOrSet, + *, method: ReindexMethodOptions = None, tolerance: int | float | Iterable[int | float] | None = None, copy: bool = True, @@ -2005,9 +2014,11 @@ def reindex_like( fill_value=fill_value, ) + @_deprecate_positional_args("v2023.10.0") def reindex( self, indexers: Mapping[Any, Any] | None = None, + *, method: ReindexMethodOptions = None, tolerance: float | Iterable[float] | None = None, copy: bool = True, @@ -4986,6 +4997,7 @@ def sortby( ds = self._to_temp_dataset().sortby(variables, ascending=ascending) return self._from_temp_dataset(ds) + @_deprecate_positional_args("v2023.10.0") def quantile( self, q: ArrayLike, diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 12cbd302d0f..7cb6bc3ac86 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -123,6 +123,7 @@ calculate_dimensions, ) from xarray.plot.accessor import DatasetPlotAccessor +from xarray.util.deprecation_helpers import _deprecate_positional_args if TYPE_CHECKING: from numpy.typing import ArrayLike @@ -4410,6 +4411,7 @@ def swap_dims( return self._replace_with_new_dims(variables, coord_names, indexes=indexes) + @_deprecate_positional_args("v2023.10.0") def expand_dims( self, dim: None | Hashable | Sequence[Hashable] | Mapping[Any, Any] = None, @@ -4771,6 +4773,7 @@ def set_index( variables, coord_names=coord_names, indexes=indexes_ ) + @_deprecate_positional_args("v2023.10.0") def reset_index( self, dims_or_levels: Hashable | Sequence[Hashable], @@ -5409,6 +5412,7 @@ def _unstack_full_reindex( variables, coord_names=coord_names, indexes=indexes ) + @_deprecate_positional_args("v2023.10.0") def unstack( self, dim: Dims = None, @@ -6153,6 +6157,7 @@ def transpose( ds._variables[name] = var.transpose(*var_dims) return ds + @_deprecate_positional_args("v2023.10.0") def dropna( self, dim: Hashable, @@ -7582,6 +7587,7 @@ def _copy_attrs_from(self, other): if v in self.variables: self.variables[v].attrs = other.variables[v].attrs + @_deprecate_positional_args("v2023.10.0") def diff( self, dim: Hashable, @@ -7913,6 +7919,7 @@ def sortby( indices[key] = order if ascending else order[::-1] return aligned_self.isel(indices) + @_deprecate_positional_args("v2023.10.0") def quantile( self, q: ArrayLike, @@ -8092,6 +8099,7 @@ def quantile( ) return new.assign_coords(quantile=q) + @_deprecate_positional_args("v2023.10.0") def rank( self, dim: Hashable, @@ -9039,6 +9047,7 @@ def pad( attrs = self._attrs if keep_attrs else None return self._replace_with_new_dims(variables, indexes=indexes, attrs=attrs) + @_deprecate_positional_args("v2023.10.0") def idxmin( self, dim: Hashable | None = None, @@ -9137,6 +9146,7 @@ def idxmin( ) ) + @_deprecate_positional_args("v2023.10.0") def idxmax( self, dim: Hashable | None = None, @@ -9761,6 +9771,7 @@ def _wrapper(Y, *args, **kwargs): return result + @_deprecate_positional_args("v2023.10.0") def drop_duplicates( self, dim: Hashable | Iterable[Hashable], diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py index 6b85d7cca1e..8ed7148e2a1 100644 --- a/xarray/core/groupby.py +++ b/xarray/core/groupby.py @@ -43,6 +43,7 @@ peek_at, ) from xarray.core.variable import IndexVariable, Variable +from xarray.util.deprecation_helpers import _deprecate_positional_args if TYPE_CHECKING: from numpy.typing import ArrayLike @@ -1092,6 +1093,7 @@ def fillna(self, value: Any) -> T_Xarray: """ return ops.fillna(self, value) + @_deprecate_positional_args("v2023.10.0") def quantile( self, q: ArrayLike, diff --git a/xarray/core/weighted.py b/xarray/core/weighted.py index ea68e01f757..28740a99020 100644 --- a/xarray/core/weighted.py +++ b/xarray/core/weighted.py @@ -11,6 +11,7 @@ from xarray.core.computation import apply_ufunc, dot from xarray.core.pycompat import is_duck_dask_array from xarray.core.types import Dims, T_Xarray +from xarray.util.deprecation_helpers import _deprecate_positional_args # Weighted quantile methods are a subset of the numpy supported quantile methods. QUANTILE_METHODS = Literal[ @@ -450,6 +451,7 @@ def _weighted_quantile_1d( def _implementation(self, func, dim, **kwargs): raise NotImplementedError("Use `Dataset.weighted` or `DataArray.weighted`") + @_deprecate_positional_args("v2023.10.0") def sum_of_weights( self, dim: Dims = None, @@ -460,6 +462,7 @@ def sum_of_weights( self._sum_of_weights, dim=dim, keep_attrs=keep_attrs ) + @_deprecate_positional_args("v2023.10.0") def sum_of_squares( self, dim: Dims = None, @@ -471,6 +474,7 @@ def sum_of_squares( self._sum_of_squares, dim=dim, skipna=skipna, keep_attrs=keep_attrs ) + @_deprecate_positional_args("v2023.10.0") def sum( self, dim: Dims = None, @@ -482,6 +486,7 @@ def sum( self._weighted_sum, dim=dim, skipna=skipna, keep_attrs=keep_attrs ) + @_deprecate_positional_args("v2023.10.0") def mean( self, dim: Dims = None, @@ -493,6 +498,7 @@ def mean( self._weighted_mean, dim=dim, skipna=skipna, keep_attrs=keep_attrs ) + @_deprecate_positional_args("v2023.10.0") def var( self, dim: Dims = None, @@ -504,6 +510,7 @@ def var( self._weighted_var, dim=dim, skipna=skipna, keep_attrs=keep_attrs ) + @_deprecate_positional_args("v2023.10.0") def std( self, dim: Dims = None, From 8306d1fd3b1ea20af8c5fe26c7ecc322178a909a Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Mon, 2 Oct 2023 11:28:37 +0200 Subject: [PATCH 03/14] add forgotten deprecations --- xarray/core/dataarray.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 2f19437da66..10d749f890a 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2798,6 +2798,7 @@ def stack( ) return self._from_temp_dataset(ds) + @_deprecate_positional_args("v2023.10.0") def unstack( self, dim: Dims = None, @@ -3210,6 +3211,7 @@ def drop_isel( dataset = dataset.drop_isel(indexers=indexers, **indexers_kwargs) return self._from_temp_dataset(dataset) + @_deprecate_positional_args("v2023.10.0") def dropna( self, dim: Hashable, @@ -4707,6 +4709,7 @@ def _title_for_slice(self, truncate: int = 50) -> str: return title + @_deprecate_positional_args("v2023.10.0") def diff( self, dim: Hashable, @@ -5117,6 +5120,7 @@ def quantile( ) return self._from_temp_dataset(ds) + @_deprecate_positional_args("v2023.10.0") def rank( self, dim: Hashable, @@ -5693,6 +5697,7 @@ def pad( ) return self._from_temp_dataset(ds) + @_deprecate_positional_args("v2023.10.0") def idxmin( self, dim: Hashable | None = None, @@ -5790,6 +5795,7 @@ def idxmin( keep_attrs=keep_attrs, ) + @_deprecate_positional_args("v2023.10.0") def idxmax( self, dim: Hashable = None, @@ -5887,6 +5893,7 @@ def idxmax( keep_attrs=keep_attrs, ) + @_deprecate_positional_args("v2023.10.0") def argmin( self, dim: Dims = None, @@ -5988,6 +5995,7 @@ def argmin( else: return self._replace_maybe_drop_dims(result) + @_deprecate_positional_args("v2023.10.0") def argmax( self, dim: Dims = None, @@ -6336,6 +6344,7 @@ def curvefit( kwargs=kwargs, ) + @_deprecate_positional_args("v2023.10.0") def drop_duplicates( self, dim: Hashable | Iterable[Hashable], From d5a5831ee29ec5f9a5bc763c6455d8514de1c2d7 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Mon, 2 Oct 2023 11:41:37 +0200 Subject: [PATCH 04/14] doctest fixes --- xarray/core/dataarray.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 10d749f890a..6ea8ffa7d3c 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2476,9 +2476,11 @@ def swap_dims( ds = self._to_temp_dataset().swap_dims(dims_dict) return self._from_temp_dataset(ds) + @_deprecate_positional_args("v2023.10.0") def expand_dims( self, dim: None | Hashable | Sequence[Hashable] | Mapping[Any, Any] = None, + *, axis: None | int | Sequence[int] = None, **dim_kwargs: Any, ) -> Self: @@ -2567,7 +2569,7 @@ def expand_dims( dim = {dim: 1} dim = either_dict_or_kwargs(dim, dim_kwargs, "expand_dims") - ds = self._to_temp_dataset().expand_dims(dim, axis) + ds = self._to_temp_dataset().expand_dims(dim, axis=axis) return self._from_temp_dataset(ds) def set_index( @@ -2860,7 +2862,7 @@ def unstack( -------- DataArray.stack """ - ds = self._to_temp_dataset().unstack(dim, fill_value, sparse) + ds = self._to_temp_dataset().unstack(dim, fill_value=fill_value, sparse=sparse) return self._from_temp_dataset(ds) def to_unstacked_dataset(self, dim: Hashable, level: int | Hashable = 0) -> Dataset: From 12abe8ee1acdb5c68401c701b1f8b47896b84e56 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Mon, 2 Oct 2023 11:45:34 +0200 Subject: [PATCH 05/14] fix some warnings --- xarray/tests/test_dataset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index ac641c4abc3..7f41cfddc8e 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -3287,7 +3287,7 @@ def test_expand_dims_int(self) -> None: attrs={"key": "entry"}, ) - actual = original.expand_dims(["z"], [1]) + actual = original.expand_dims(["z"], axis=[1]) expected = Dataset( { "x": original["x"].expand_dims("z", 1), @@ -3306,7 +3306,7 @@ def test_expand_dims_int(self) -> None: assert_identical(original, roundtripped) # another test with a negative axis - actual = original.expand_dims(["z"], [-1]) + actual = original.expand_dims(["z"], axis=[-1]) expected = Dataset( { "x": original["x"].expand_dims("z", -1), From 5f124c3c80edac7b71c20280dba20f2ca7c6415e Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Mon, 2 Oct 2023 12:00:07 +0200 Subject: [PATCH 06/14] remove expand_dims again --- xarray/core/dataarray.py | 2 -- xarray/core/dataset.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 6ea8ffa7d3c..9307480a67d 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2476,11 +2476,9 @@ def swap_dims( ds = self._to_temp_dataset().swap_dims(dims_dict) return self._from_temp_dataset(ds) - @_deprecate_positional_args("v2023.10.0") def expand_dims( self, dim: None | Hashable | Sequence[Hashable] | Mapping[Any, Any] = None, - *, axis: None | int | Sequence[int] = None, **dim_kwargs: Any, ) -> Self: diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 7cb6bc3ac86..3bf0a3f5c5f 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -4411,11 +4411,9 @@ def swap_dims( return self._replace_with_new_dims(variables, coord_names, indexes=indexes) - @_deprecate_positional_args("v2023.10.0") def expand_dims( self, dim: None | Hashable | Sequence[Hashable] | Mapping[Any, Any] = None, - *, axis: None | int | Sequence[int] = None, **dim_kwargs: Any, ) -> Self: From 0c6117f94015ec3900d0c8d0dd9e8b10b12ecb40 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Mon, 2 Oct 2023 14:16:51 +0200 Subject: [PATCH 07/14] undo expand_dims, fix mypy --- xarray/core/dataarray.py | 2 +- xarray/tests/test_dataset.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 9307480a67d..2aa2131396a 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -2567,7 +2567,7 @@ def expand_dims( dim = {dim: 1} dim = either_dict_or_kwargs(dim, dim_kwargs, "expand_dims") - ds = self._to_temp_dataset().expand_dims(dim, axis=axis) + ds = self._to_temp_dataset().expand_dims(dim, axis) return self._from_temp_dataset(ds) def set_index( diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index 7f41cfddc8e..0c297e5979d 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -3287,7 +3287,7 @@ def test_expand_dims_int(self) -> None: attrs={"key": "entry"}, ) - actual = original.expand_dims(["z"], axis=[1]) + actual = original.expand_dims(["z"], [1]) expected = Dataset( { "x": original["x"].expand_dims("z", 1), @@ -3306,7 +3306,7 @@ def test_expand_dims_int(self) -> None: assert_identical(original, roundtripped) # another test with a negative axis - actual = original.expand_dims(["z"], axis=[-1]) + actual = original.expand_dims(["z"], [-1]) expected = Dataset( { "x": original["x"].expand_dims("z", -1), @@ -5051,9 +5051,9 @@ def test_dropna(self) -> None: ): ds.dropna("foo") with pytest.raises(ValueError, match=r"invalid how"): - ds.dropna("a", how="somehow") # type: ignore + ds.dropna("a", how="somehow") with pytest.raises(TypeError, match=r"must specify how or thresh"): - ds.dropna("a", how=None) # type: ignore + ds.dropna("a", how=None) def test_fillna(self) -> None: ds = Dataset({"a": ("x", [np.nan, 1, np.nan, 3])}, {"x": [0, 1, 2, 3]}) @@ -5985,7 +5985,7 @@ def test_dataset_diff_exception_n_neg(self) -> None: def test_dataset_diff_exception_label_str(self) -> None: ds = create_test_data(seed=1) with pytest.raises(ValueError, match=r"'label' argument has to"): - ds.diff("dim2", label="raise_me") # type: ignore[arg-type] + ds.diff("dim2", label="raise_me") @pytest.mark.parametrize("fill_value", [dtypes.NA, 2, 2.0, {"foo": -10}]) def test_shift(self, fill_value) -> None: From 57c792cf22205437d869daf04792576e6db18f75 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Tue, 3 Oct 2023 09:43:58 +0200 Subject: [PATCH 08/14] whats-new entry [skip-ci] --- doc/whats-new.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index e485b24bf3e..c149e3501c5 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -30,6 +30,9 @@ New Features Breaking changes ~~~~~~~~~~~~~~~~ +- Made more arguments keyword-only (e.g. ``keep_attrs``, ``skipna``) for many :py:class:`xarray.DataArray` and + :py:class:`xarray.Dataset` methods (:pull:`6403`). By `Mathias Hauser `_. + Deprecations ~~~~~~~~~~~~ From 94a939ef97920e579b45fcc87275c1a98302bf47 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 4 Oct 2023 09:38:26 +0200 Subject: [PATCH 09/14] add typing to _deprecate_positional_args helper --- xarray/util/deprecation_helpers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xarray/util/deprecation_helpers.py b/xarray/util/deprecation_helpers.py index e9681bdf398..8eff5d847d7 100644 --- a/xarray/util/deprecation_helpers.py +++ b/xarray/util/deprecation_helpers.py @@ -34,6 +34,9 @@ import inspect import warnings from functools import wraps +from typing import Callable, TypeVar + +T = TypeVar("T", bound=Callable) POSITIONAL_OR_KEYWORD = inspect.Parameter.POSITIONAL_OR_KEYWORD KEYWORD_ONLY = inspect.Parameter.KEYWORD_ONLY @@ -71,7 +74,7 @@ def func(a, *, b=2): licences/SCIKIT_LEARN_LICENSE """ - def _decorator(func): + def _decorator(func) -> Callable[[T], T]: signature = inspect.signature(func) pos_or_kw_args = [] From dc514ccd4db01d25b99dcdda6f9218ca45b7f801 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 4 Oct 2023 10:50:34 +0200 Subject: [PATCH 10/14] Update xarray/util/deprecation_helpers.py Co-authored-by: Michael Niklas --- xarray/util/deprecation_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xarray/util/deprecation_helpers.py b/xarray/util/deprecation_helpers.py index 8eff5d847d7..b6f07f03e26 100644 --- a/xarray/util/deprecation_helpers.py +++ b/xarray/util/deprecation_helpers.py @@ -74,7 +74,7 @@ def func(a, *, b=2): licences/SCIKIT_LEARN_LICENSE """ - def _decorator(func) -> Callable[[T], T]: + def _decorator(func: T) -> T: signature = inspect.signature(func) pos_or_kw_args = [] From 000c5e50dda6d1d297312e3bbe6f6ba301e9a60d Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 4 Oct 2023 10:59:50 +0200 Subject: [PATCH 11/14] fix kw only for overload --- xarray/core/dataarray.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 2aa2131396a..a95d0ce3d05 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -955,6 +955,7 @@ def coords(self) -> DataArrayCoordinates: def reset_coords( self, names: Dims = None, + *, drop: Literal[False] = False, ) -> Dataset: ... From 752a73146e21365495c23e1804019f54218dfb7a Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 4 Oct 2023 11:00:07 +0200 Subject: [PATCH 12/14] move typing --- xarray/util/deprecation_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/util/deprecation_helpers.py b/xarray/util/deprecation_helpers.py index b6f07f03e26..7b4cf901aa1 100644 --- a/xarray/util/deprecation_helpers.py +++ b/xarray/util/deprecation_helpers.py @@ -44,7 +44,7 @@ EMPTY = inspect.Parameter.empty -def _deprecate_positional_args(version): +def _deprecate_positional_args(version) -> Callable[[T], T]: """Decorator for methods that issues warnings for positional arguments Using the keyword-only argument syntax in pep 3102, arguments after the @@ -74,7 +74,7 @@ def func(a, *, b=2): licences/SCIKIT_LEARN_LICENSE """ - def _decorator(func: T) -> T: + def _decorator(func): signature = inspect.signature(func) pos_or_kw_args = [] From f9472ffc1fcef014682d39da28e5fb64c7e1861d Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 4 Oct 2023 11:01:15 +0200 Subject: [PATCH 13/14] restore # type: ignore --- xarray/tests/test_dataset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index 0c297e5979d..8b229f2af31 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -5051,9 +5051,9 @@ def test_dropna(self) -> None: ): ds.dropna("foo") with pytest.raises(ValueError, match=r"invalid how"): - ds.dropna("a", how="somehow") + ds.dropna("a", how="somehow") # type: ignore[arg-type] with pytest.raises(TypeError, match=r"must specify how or thresh"): - ds.dropna("a", how=None) + ds.dropna("a", how=None) # type: ignore[arg-type] def test_fillna(self) -> None: ds = Dataset({"a": ("x", [np.nan, 1, np.nan, 3])}, {"x": [0, 1, 2, 3]}) @@ -5985,7 +5985,7 @@ def test_dataset_diff_exception_n_neg(self) -> None: def test_dataset_diff_exception_label_str(self) -> None: ds = create_test_data(seed=1) with pytest.raises(ValueError, match=r"'label' argument has to"): - ds.diff("dim2", label="raise_me") + ds.diff("dim2", label="raise_me") # type: ignore[arg-type] @pytest.mark.parametrize("fill_value", [dtypes.NA, 2, 2.0, {"foo": -10}]) def test_shift(self, fill_value) -> None: From 003996da4f2c63e689f2d8f9ece2fdcbff275158 Mon Sep 17 00:00:00 2001 From: Mathias Hauser Date: Wed, 4 Oct 2023 11:05:51 +0200 Subject: [PATCH 14/14] add type ignores to test_deprecation_helpers --- xarray/tests/test_deprecation_helpers.py | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/xarray/tests/test_deprecation_helpers.py b/xarray/tests/test_deprecation_helpers.py index 35128829073..f21c8097060 100644 --- a/xarray/tests/test_deprecation_helpers.py +++ b/xarray/tests/test_deprecation_helpers.py @@ -15,15 +15,15 @@ def f1(a, b, *, c="c", d="d"): assert result == (1, 2, 3, 4) with pytest.warns(FutureWarning, match=r".*v0.1"): - result = f1(1, 2, 3) + result = f1(1, 2, 3) # type: ignore[misc] assert result == (1, 2, 3, "d") with pytest.warns(FutureWarning, match=r"Passing 'c' as positional"): - result = f1(1, 2, 3) + result = f1(1, 2, 3) # type: ignore[misc] assert result == (1, 2, 3, "d") with pytest.warns(FutureWarning, match=r"Passing 'c, d' as positional"): - result = f1(1, 2, 3, 4) + result = f1(1, 2, 3, 4) # type: ignore[misc] assert result == (1, 2, 3, 4) @_deprecate_positional_args("v0.1") @@ -31,7 +31,7 @@ def f2(a="a", *, b="b", c="c", d="d"): return a, b, c, d with pytest.warns(FutureWarning, match=r"Passing 'b' as positional"): - result = f2(1, 2) + result = f2(1, 2) # type: ignore[misc] assert result == (1, 2, "c", "d") @_deprecate_positional_args("v0.1") @@ -39,11 +39,11 @@ def f3(a, *, b="b", **kwargs): return a, b, kwargs with pytest.warns(FutureWarning, match=r"Passing 'b' as positional"): - result = f3(1, 2) + result = f3(1, 2) # type: ignore[misc] assert result == (1, 2, {}) with pytest.warns(FutureWarning, match=r"Passing 'b' as positional"): - result = f3(1, 2, f="f") + result = f3(1, 2, f="f") # type: ignore[misc] assert result == (1, 2, {"f": "f"}) @_deprecate_positional_args("v0.1") @@ -57,7 +57,7 @@ def f4(a, /, *, b="b", **kwargs): assert result == (1, 2, {"f": "f"}) with pytest.warns(FutureWarning, match=r"Passing 'b' as positional"): - result = f4(1, 2, f="f") + result = f4(1, 2, f="f") # type: ignore[misc] assert result == (1, 2, {"f": "f"}) with pytest.raises(TypeError, match=r"Keyword-only param without default"): @@ -80,15 +80,15 @@ def method(self, a, b, *, c="c", d="d"): assert result == (1, 2, 3, 4) with pytest.warns(FutureWarning, match=r".*v0.1"): - result = A1().method(1, 2, 3) + result = A1().method(1, 2, 3) # type: ignore[misc] assert result == (1, 2, 3, "d") with pytest.warns(FutureWarning, match=r"Passing 'c' as positional"): - result = A1().method(1, 2, 3) + result = A1().method(1, 2, 3) # type: ignore[misc] assert result == (1, 2, 3, "d") with pytest.warns(FutureWarning, match=r"Passing 'c, d' as positional"): - result = A1().method(1, 2, 3, 4) + result = A1().method(1, 2, 3, 4) # type: ignore[misc] assert result == (1, 2, 3, 4) class A2: @@ -97,11 +97,11 @@ def method(self, a=1, b=1, *, c="c", d="d"): return a, b, c, d with pytest.warns(FutureWarning, match=r"Passing 'c' as positional"): - result = A2().method(1, 2, 3) + result = A2().method(1, 2, 3) # type: ignore[misc] assert result == (1, 2, 3, "d") with pytest.warns(FutureWarning, match=r"Passing 'c, d' as positional"): - result = A2().method(1, 2, 3, 4) + result = A2().method(1, 2, 3, 4) # type: ignore[misc] assert result == (1, 2, 3, 4) class A3: @@ -110,11 +110,11 @@ def method(self, a, *, b="b", **kwargs): return a, b, kwargs with pytest.warns(FutureWarning, match=r"Passing 'b' as positional"): - result = A3().method(1, 2) + result = A3().method(1, 2) # type: ignore[misc] assert result == (1, 2, {}) with pytest.warns(FutureWarning, match=r"Passing 'b' as positional"): - result = A3().method(1, 2, f="f") + result = A3().method(1, 2, f="f") # type: ignore[misc] assert result == (1, 2, {"f": "f"}) class A4: @@ -129,7 +129,7 @@ def method(self, a, /, *, b="b", **kwargs): assert result == (1, 2, {"f": "f"}) with pytest.warns(FutureWarning, match=r"Passing 'b' as positional"): - result = A4().method(1, 2, f="f") + result = A4().method(1, 2, f="f") # type: ignore[misc] assert result == (1, 2, {"f": "f"}) with pytest.raises(TypeError, match=r"Keyword-only param without default"):