From 378e67ee63099033902f0d14fb2593da6be20c4b Mon Sep 17 00:00:00 2001 From: mohansree14 Date: Fri, 16 Oct 2026 00:28:23 +0100 Subject: [PATCH 1/3] Make groupby and resample args keyword-only, start groupby_bins deprecation Passing keyword-only args of groupby and resample positionally has warned since v2024.07.0 (#9236) and now raises a TypeError. groupby_bins had the decorator but no `*`, so it never warned; add the `*` so the deprecation actually starts. Part of #11721. Co-authored-by: Claude --- doc/whats-new.rst | 10 ++++++++++ xarray/core/dataarray.py | 5 ++--- xarray/core/dataset.py | 5 ++--- xarray/tests/test_groupby.py | 30 +++++++++++++++++++++++++----- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 060730f571f..5760867c4ba 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -55,6 +55,11 @@ New Features Breaking Changes ~~~~~~~~~~~~~~~~ +- Passing keyword-only arguments of :py:meth:`Dataset.groupby`, + :py:meth:`DataArray.groupby`, :py:meth:`Dataset.resample` and + :py:meth:`DataArray.resample` positionally, deprecated since v2024.07.0, now + raises a ``TypeError`` (:issue:`11721`). + By `Mohansree Vijayakumar `_. - The ``Variable.to_variable``, ``Variable.to_coord`` and ``IndexVariable.to_coord`` aliases, deprecated since v0.9.0, have been removed. Use :py:meth:`Variable.to_base_variable` and @@ -113,6 +118,11 @@ Breaking Changes Deprecations ~~~~~~~~~~~~ +- Passing any argument after ``bins`` positionally to + :py:meth:`Dataset.groupby_bins` and :py:meth:`DataArray.groupby_bins` is + deprecated. It was meant to be deprecated in v2024.07.0, but the warning never + fired (:issue:`11721`). + By `Mohansree Vijayakumar `_. - ``reset_encoding`` of :py:class:`Dataset`, :py:class:`DataArray` and :py:class:`Variable` now emits a ``FutureWarning`` instead of a ``UserWarning``, like the other deprecations, and gives the right version (2023.10). diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index e8662f2b1a8..36f1b209982 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -7146,7 +7146,6 @@ def interp_calendar( """ return interp_calendar(self, target, dim=dim) - @_deprecate_positional_args("v2024.07.0") def groupby( self, group: GroupInput = None, @@ -7290,11 +7289,12 @@ def groupby( ) return DataArrayGroupBy(self, rgroupers, restore_coord_dims=restore_coord_dims) - @_deprecate_positional_args("v2024.07.0") + @_deprecate_positional_args("v2026.09.1") def groupby_bins( self, group: Hashable | DataArray | IndexVariable, bins: Bins, + *, right: bool = True, labels: ArrayLike | Literal[False] | None = None, precision: int = 3, @@ -7718,7 +7718,6 @@ def coarsen( coord_func=coord_func, ) - @_deprecate_positional_args("v2024.07.0") def resample( self, indexer: Mapping[Hashable, ResampleCompatible | Resampler] | None = None, diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index ec85bbb38b7..6535e5b15ad 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -10324,7 +10324,6 @@ def interp_calendar( """ return interp_calendar(self, target, dim=dim) - @_deprecate_positional_args("v2024.07.0") def groupby( self, group: GroupInput = None, @@ -10437,11 +10436,12 @@ def groupby( return DatasetGroupBy(self, rgroupers, restore_coord_dims=restore_coord_dims) - @_deprecate_positional_args("v2024.07.0") + @_deprecate_positional_args("v2026.09.1") def groupby_bins( self, group: Hashable | DataArray | IndexVariable, bins: Bins, + *, right: bool = True, labels: ArrayLike | None = None, precision: int = 3, @@ -10710,7 +10710,6 @@ def coarsen( coord_func=coord_func, ) - @_deprecate_positional_args("v2024.07.0") def resample( self, indexer: Mapping[Any, ResampleCompatible | Resampler] | None = None, diff --git a/xarray/tests/test_groupby.py b/xarray/tests/test_groupby.py index 6485367ffe6..28974547989 100644 --- a/xarray/tests/test_groupby.py +++ b/xarray/tests/test_groupby.py @@ -3069,17 +3069,37 @@ def test_multiple_groupers_string(as_dataset) -> None: actual = obj.groupby(("labels1", "labels2")).mean() assert_identical(expected, actual) - # Passes `"labels2"` to squeeze; will raise an error around kwargs rather than the - # warning & type error in the future - with pytest.warns(FutureWarning): - with pytest.raises(TypeError): - obj.groupby("labels1", "labels2") # type: ignore[arg-type, call-arg] + with pytest.raises(TypeError, match=r"positional argument"): + obj.groupby("labels1", "labels2") # type: ignore[arg-type, call-arg] with pytest.raises(ValueError): obj.groupby("labels1", foo="bar") # type: ignore[arg-type] with pytest.raises(ValueError): obj.groupby("labels1", foo=UniqueGrouper()) +@pytest.mark.parametrize("as_dataset", [True, False]) +def test_keyword_only_args(as_dataset: bool) -> None: + obj: DataArray | Dataset = DataArray( + [1, 2, 3], + dims="time", + coords={"time": pd.date_range("2000-01-01", periods=3)}, + name="foo", + ) + if as_dataset: + obj = obj.to_dataset() + + with pytest.raises(TypeError, match=r"positional argument"): + obj.groupby("time", False) # type: ignore[call-arg] + with pytest.raises(TypeError, match=r"positional argument"): + obj.resample({"time": "D"}, True) # type: ignore[call-arg] + + obj = obj.assign_coords(x=("time", [1, 2, 3])) + expected = obj.groupby_bins("x", [0, 2, 4], right=False).sum() + with pytest.warns(FutureWarning, match=r"Passing 'right' as positional"): + actual = obj.groupby_bins("x", [0, 2, 4], False).sum() # type: ignore[call-arg] + assert_identical(expected, actual) + + @pytest.mark.parametrize("shuffle", [True, False]) @pytest.mark.parametrize("use_flox", [True, False]) def test_multiple_groupers(use_flox: bool, shuffle: bool) -> None: From 983ea8cd79c512b4d2edb8f71f876c420b8d8bed Mon Sep 17 00:00:00 2001 From: mohansree14 Date: Fri, 16 Oct 2026 00:28:34 +0100 Subject: [PATCH 2/3] Add PR number to whats-new entries Co-authored-by: Claude --- doc/whats-new.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 5760867c4ba..7a2d9a14eea 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -58,7 +58,7 @@ Breaking Changes - Passing keyword-only arguments of :py:meth:`Dataset.groupby`, :py:meth:`DataArray.groupby`, :py:meth:`Dataset.resample` and :py:meth:`DataArray.resample` positionally, deprecated since v2024.07.0, now - raises a ``TypeError`` (:issue:`11721`). + raises a ``TypeError`` (:issue:`11721`, :pull:`11744`). By `Mohansree Vijayakumar `_. - The ``Variable.to_variable``, ``Variable.to_coord`` and ``IndexVariable.to_coord`` aliases, deprecated since v0.9.0, have been removed. @@ -121,7 +121,7 @@ Deprecations - Passing any argument after ``bins`` positionally to :py:meth:`Dataset.groupby_bins` and :py:meth:`DataArray.groupby_bins` is deprecated. It was meant to be deprecated in v2024.07.0, but the warning never - fired (:issue:`11721`). + fired (:issue:`11721`, :pull:`11744`). By `Mohansree Vijayakumar `_. - ``reset_encoding`` of :py:class:`Dataset`, :py:class:`DataArray` and :py:class:`Variable` now emits a ``FutureWarning`` instead of a ``UserWarning``, From 29876a9a8df61de9af47ce2d351f8d958d82b64d Mon Sep 17 00:00:00 2001 From: Michael Niklas Date: Mon, 19 Oct 2026 08:51:12 +0200 Subject: [PATCH 3/3] Start the groupby_bins deprecation in v2026.10.0 Co-authored-by: Claude --- xarray/core/dataarray.py | 2 +- xarray/core/dataset.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/core/dataarray.py b/xarray/core/dataarray.py index 6af559bac0d..081df7e2d99 100644 --- a/xarray/core/dataarray.py +++ b/xarray/core/dataarray.py @@ -7283,7 +7283,7 @@ def groupby( ) return DataArrayGroupBy(self, rgroupers, restore_coord_dims=restore_coord_dims) - @_deprecate_positional_args("v2026.09.1") + @_deprecate_positional_args("v2026.10.0") def groupby_bins( self, group: Hashable | DataArray | IndexVariable, diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 6ab069595db..759e57dc76e 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -10431,7 +10431,7 @@ def groupby( return DatasetGroupBy(self, rgroupers, restore_coord_dims=restore_coord_dims) - @_deprecate_positional_args("v2026.09.1") + @_deprecate_positional_args("v2026.10.0") def groupby_bins( self, group: Hashable | DataArray | IndexVariable,