From c98bdd2a1c46e2da8f8053778ba887a4c1390176 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Thu, 8 Jan 2026 11:39:57 -0500 Subject: [PATCH 1/9] Flip default on `use_new_combine_kwarg_defaults` from False to True --- xarray/core/options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xarray/core/options.py b/xarray/core/options.py index 451070ce7b4..54a6fd163af 100644 --- a/xarray/core/options.py +++ b/xarray/core/options.py @@ -93,7 +93,7 @@ class T_Options(TypedDict): "warn_for_unclosed_files": False, "use_bottleneck": True, "use_flox": True, - "use_new_combine_kwarg_defaults": False, + "use_new_combine_kwarg_defaults": True, "use_numbagg": True, "use_opt_einsum": True, } @@ -271,7 +271,7 @@ class set_options: use_flox : bool, default: True Whether to use ``numpy_groupies`` and `flox`` to accelerate groupby and resampling reductions. - use_new_combine_kwarg_defaults : bool, default False + use_new_combine_kwarg_defaults : bool, default True Whether to use new kwarg default values for combine functions: :py:func:`~xarray.concat`, :py:func:`~xarray.merge`, :py:func:`~xarray.open_mfdataset`. New values are: From 14823ef827d0df429cdaffcbea216304bad1bc68 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Thu, 8 Jan 2026 11:40:56 -0500 Subject: [PATCH 2/9] Min test changes to pass with new defaults --- xarray/tests/test_backends.py | 34 +++++++++------------------------- xarray/tests/test_combine.py | 11 ----------- xarray/tests/test_concat.py | 9 +++------ xarray/tests/test_dask.py | 8 ++++++++ xarray/tests/test_dataarray.py | 12 ++++-------- xarray/tests/test_merge.py | 12 ++++-------- xarray/tests/test_units.py | 27 --------------------------- 7 files changed, 28 insertions(+), 85 deletions(-) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index ed86979c73e..1772038a940 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -5415,7 +5415,7 @@ class TestOpenMFDatasetWithDataVarsAndCoordsKw: var_name = "v1" @contextlib.contextmanager - def setup_files_and_datasets(self, *, fuzz=0, new_combine_kwargs: bool = False): + def setup_files_and_datasets(self, *, fuzz=0): ds1, ds2 = self.gen_datasets_with_common_coord_and_time() # to test join='exact' @@ -5427,8 +5427,7 @@ def setup_files_and_datasets(self, *, fuzz=0, new_combine_kwargs: bool = False): ds1.to_netcdf(tmpfile1) ds2.to_netcdf(tmpfile2) - with set_options(use_new_combine_kwarg_defaults=new_combine_kwargs): - yield [tmpfile1, tmpfile2], [ds1, ds2] + yield [tmpfile1, tmpfile2], [ds1, ds2] def gen_datasets_with_common_coord_and_time(self): # create coordinate data @@ -5512,7 +5511,7 @@ def test_open_mfdataset_dataset_combine_attrs( expected, expect_error, ): - with self.setup_files_and_datasets() as (files, [_ds1, _ds2]): + with self.setup_files_and_datasets() as (files, _): # Give the files an inconsistent attribute for i, f in enumerate(files): ds = open_dataset(f).load() @@ -5547,7 +5546,7 @@ def test_open_mfdataset_dataset_attr_by_coords(self) -> None: """ Case when an attribute differs across the multiple files """ - with self.setup_files_and_datasets() as (files, [_ds1, _ds2]): + with self.setup_files_and_datasets() as (files, _): # Give the files an inconsistent attribute for i, f in enumerate(files): ds = open_dataset(f).load() @@ -5563,10 +5562,7 @@ def test_open_mfdataset_dataarray_attr_by_coords(self) -> None: """ Case when an attribute of a member DataArray differs across the multiple files """ - with self.setup_files_and_datasets(new_combine_kwargs=True) as ( - files, - [_ds1, _ds2], - ): + with self.setup_files_and_datasets() as (files, _): # Give the files an inconsistent attribute for i, f in enumerate(files): ds = open_dataset(f).load() @@ -5604,10 +5600,7 @@ def test_open_mfdataset_dataarray_attr_by_coords(self) -> None: def test_open_mfdataset_exact_join_raises_error( self, combine, concat_dim, kwargs ) -> None: - with self.setup_files_and_datasets(fuzz=0.1, new_combine_kwargs=True) as ( - files, - _, - ): + with self.setup_files_and_datasets(fuzz=0.1) as (files, _): if combine == "by_coords": files.reverse() @@ -5625,10 +5618,7 @@ def test_open_mfdataset_exact_join_raises_error( def test_open_mfdataset_defaults_with_exact_join_warns_as_well_as_raising( self, ) -> None: - with self.setup_files_and_datasets(fuzz=0.1, new_combine_kwargs=True) as ( - files, - _, - ): + with self.setup_files_and_datasets(fuzz=0.1) as (files, _): files.reverse() with pytest.raises( ValueError, match="cannot align objects with join='exact'" @@ -5656,10 +5646,7 @@ def test_common_coord_when_datavars_all(self) -> None: def test_common_coord_when_datavars_minimal(self) -> None: opt: Final = "minimal" - with self.setup_files_and_datasets(new_combine_kwargs=True) as ( - files, - [ds1, ds2], - ): + with self.setup_files_and_datasets() as (files, [ds1, ds2]): # open the files using data_vars option with open_mfdataset( files, data_vars=opt, combine="nested", concat_dim="t" @@ -5694,10 +5681,7 @@ def test_invalid_data_vars_value_should_fail(self) -> None: def test_open_mfdataset_warns_when_kwargs_set_to_different( self, combine, concat_dim, kwargs ) -> None: - with self.setup_files_and_datasets(new_combine_kwargs=True) as ( - files, - [ds1, ds2], - ): + with self.setup_files_and_datasets() as (files, [ds1, ds2]): if combine == "by_coords": files.reverse() with pytest.raises( diff --git a/xarray/tests/test_combine.py b/xarray/tests/test_combine.py index 2883187e096..228fd74318d 100644 --- a/xarray/tests/test_combine.py +++ b/xarray/tests/test_combine.py @@ -477,12 +477,6 @@ def test_nested_merge_with_overlapping_values(self): ds1 = Dataset({"a": ("x", [1, 2]), "x": [0, 1]}) ds2 = Dataset({"a": ("x", [2, 3]), "x": [1, 2]}) expected = Dataset({"a": ("x", [1, 2, 3]), "x": [0, 1, 2]}) - with pytest.warns( - FutureWarning, - match="will change from compat='no_conflicts' to compat='override'", - ): - actual = combine_nested([ds1, ds2], join="outer", concat_dim=None) - assert_identical(expected, actual) actual = combine_nested( [ds1, ds2], join="outer", compat="no_conflicts", concat_dim=None ) @@ -497,11 +491,6 @@ def test_nested_merge_with_nan_no_conflicts(self): tmp2 = Dataset({"x": np.nan}) actual = combine_nested([tmp1, tmp2], compat="no_conflicts", concat_dim=None) assert_identical(tmp1, actual) - with pytest.warns( - FutureWarning, - match="will change from compat='no_conflicts' to compat='override'", - ): - combine_nested([tmp1, tmp2], concat_dim=None) actual = combine_nested([tmp1, tmp2], compat="no_conflicts", concat_dim=[None]) assert_identical(tmp1, actual) diff --git a/xarray/tests/test_concat.py b/xarray/tests/test_concat.py index 5207ee3316e..51e09fa4534 100644 --- a/xarray/tests/test_concat.py +++ b/xarray/tests/test_concat.py @@ -980,7 +980,7 @@ def test_concat_do_not_promote(self) -> None: Dataset({"y": ("t", [2])}, {"x": 2, "t": [0]}), ] with pytest.raises(ValueError): - concat(objs, "t", coords="minimal") + concat(objs, "t", compat="equals") def test_concat_dim_is_variable(self) -> None: objs = [Dataset({"x": 0}), Dataset({"x": 1})] @@ -1658,12 +1658,9 @@ def test_concat_datatree_along_existing_dim_defaults(self): dt1 = DataTree.from_dict(data={"/a": ("x", [1]), "/b": 3}, coords={"/x": [0]}) dt2 = DataTree.from_dict(data={"/a": ("x", [2]), "/b": 3}, coords={"/x": [1]}) expected = DataTree.from_dict( - data={"/a": ("x", [1, 2]), "/b": ("x", [3, 3])}, coords={"/x": [0, 1]} + data={"/a": ("x", [1, 2]), "/b": 3}, coords={"/x": [0, 1]} ) - with pytest.warns( - FutureWarning, match="will change from data_vars='all' to data_vars=None" - ): - actual = concat([dt1, dt2], dim="x") + actual = concat([dt1, dt2], dim="x") assert actual.identical(expected) def test_concat_datatree_isomorphic_error(self): diff --git a/xarray/tests/test_dask.py b/xarray/tests/test_dask.py index 2d103994410..f19caf7238b 100644 --- a/xarray/tests/test_dask.py +++ b/xarray/tests/test_dask.py @@ -472,6 +472,14 @@ def test_concat_loads_variables(self): assert isinstance(out["c"].data, dask.array.Array) out = xr.concat([ds1, ds2, ds3], dim="n", data_vars=[], coords=[]) + # no extra kernel calls + assert kernel_call_count == 6 + assert isinstance(out["d"].data, dask.array.Array) + assert isinstance(out["c"].data, dask.array.Array) + + out = xr.concat( + [ds1, ds2, ds3], dim="n", data_vars=[], coords=[], compat="equals" + ) # variables are loaded once as we are validating that they're identical assert kernel_call_count == 12 assert isinstance(out["d"].data, np.ndarray) diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index d1aacba6aaa..0ed582197a7 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -1458,17 +1458,13 @@ def test_selection_multiindex_from_level(self) -> None: expected = data.isel(xy=[0, 1]).unstack("xy").squeeze("y") assert_equal(actual, expected) - def test_concat_with_default_coords_warns(self) -> None: + def test_concat_with_default_coords(self) -> None: da = DataArray([0, 1], dims=["x"], coords={"x": [0, 1], "y": "a"}) db = DataArray([2, 3], dims=["x"], coords={"x": [0, 1], "y": "b"}) - with pytest.warns(FutureWarning): - original = xr.concat([da, db], dim="x") - assert original.y.size == 4 - with set_options(use_new_combine_kwarg_defaults=True): - # default compat="override" will pick the first one - new = xr.concat([da, db], dim="x") - assert new.y.size == 1 + # default compat="override" will pick the first one + new = xr.concat([da, db], dim="x") + assert new.y.size == 1 def test_virtual_default_coords(self) -> None: array = DataArray(np.zeros((5,)), dims="x") diff --git a/xarray/tests/test_merge.py b/xarray/tests/test_merge.py index 68db0babb04..df597692bf9 100644 --- a/xarray/tests/test_merge.py +++ b/xarray/tests/test_merge.py @@ -201,11 +201,7 @@ def test_merge_arrays_attrs_variables( if expect_exception: with pytest.raises(MergeError, match="combine_attrs"): - with pytest.warns( - FutureWarning, - match="will change from compat='no_conflicts' to compat='override'", - ): - actual = xr.merge([data1, data2], combine_attrs=combine_attrs) + actual = xr.merge([data1, data2], combine_attrs=combine_attrs) else: actual = xr.merge( [data1, data2], compat="no_conflicts", combine_attrs=combine_attrs @@ -528,7 +524,7 @@ def test_merge_coordinates(self): def test_merge_error(self): ds = xr.Dataset({"x": 0}) with pytest.raises(xr.MergeError): - xr.merge([ds, ds + 1]) + xr.merge([ds, ds + 1], compat="no_conflicts") def test_merge_alignment_error(self): ds = xr.Dataset(coords={"x": [1, 2]}) @@ -624,7 +620,7 @@ def test_merge(self): assert_identical(data, actual) with pytest.raises(ValueError, match="conflicting values for variable"): - ds1.merge(ds2.rename({"var3": "var1"})) + ds1.merge(ds2.rename({"var3": "var1"}), compat="no_conflicts") with pytest.raises(ValueError, match=r"should be coordinates or not"): data.reset_coords().merge(data) with pytest.raises(ValueError, match=r"should be coordinates or not"): @@ -948,7 +944,7 @@ def test_merge_error_includes_path(self) -> None: "Raised whilst mapping function over node(s) with path 'a'" ), ): - xr.merge([tree1, tree2], join="exact") + xr.merge([tree1, tree2], compat="no_conflicts") def test_fill_value_errors(self) -> None: trees = [xr.DataTree(), xr.DataTree()] diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 56b3d9ad22b..1258997e0c7 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -745,9 +745,6 @@ def test_broadcast_dataset(dtype): "coords", ), ) -@pytest.mark.filterwarnings( - "ignore:.*the default value for coords will change:FutureWarning" -) def test_combine_by_coords(variant, unit, error, dtype): original_unit = unit_registry.m @@ -825,12 +822,6 @@ def test_combine_by_coords(variant, unit, error, dtype): "coords", ), ) -@pytest.mark.filterwarnings( - "ignore:.*the default value for join will change:FutureWarning" -) -@pytest.mark.filterwarnings( - "ignore:.*the default value for compat will change:FutureWarning" -) def test_combine_nested(variant, unit, error, dtype): original_unit = unit_registry.m @@ -1071,12 +1062,6 @@ def test_concat_dataset(variant, unit, error, dtype): "coords", ), ) -@pytest.mark.filterwarnings( - "ignore:.*the default value for join will change:FutureWarning" -) -@pytest.mark.filterwarnings( - "ignore:.*the default value for compat will change:FutureWarning" -) def test_merge_dataarray(variant, unit, error, dtype): original_unit = unit_registry.m @@ -1181,12 +1166,6 @@ def test_merge_dataarray(variant, unit, error, dtype): "coords", ), ) -@pytest.mark.filterwarnings( - "ignore:.*the default value for join will change:FutureWarning" -) -@pytest.mark.filterwarnings( - "ignore:.*the default value for compat will change:FutureWarning" -) def test_merge_dataset(variant, unit, error, dtype): original_unit = unit_registry.m @@ -5607,12 +5586,6 @@ def test_content_manipulation(self, func, variant, dtype): "coords", ), ) - @pytest.mark.filterwarnings( - "ignore:.*the default value for join will change:FutureWarning" - ) - @pytest.mark.filterwarnings( - "ignore:.*the default value for compat will change:FutureWarning" - ) def test_merge(self, variant, unit, error, dtype): left_variants = { "data": (unit_registry.m, 1, 1), From 068003a9cfd3c66e626fa0ff30782145f4934c33 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Thu, 8 Jan 2026 13:06:21 -0500 Subject: [PATCH 3/9] Update docstrings --- xarray/backends/api.py | 22 ++++++++------ xarray/core/dataset.py | 8 ++--- xarray/structure/combine.py | 58 +++++++++++++++++++++++-------------- xarray/structure/concat.py | 23 ++++++++------- xarray/structure/merge.py | 11 +++---- 5 files changed, 71 insertions(+), 51 deletions(-) diff --git a/xarray/backends/api.py b/xarray/backends/api.py index 5cb879620cb..9e75f1d67d1 100644 --- a/xarray/backends/api.py +++ b/xarray/backends/api.py @@ -1437,7 +1437,7 @@ def open_mfdataset( Whether ``xarray.combine_by_coords`` or ``xarray.combine_nested`` is used to combine all the data. Default is to use ``xarray.combine_by_coords``. compat : {"identical", "equals", "broadcast_equals", \ - "no_conflicts", "override"}, default: "no_conflicts" + "no_conflicts", "override"}, default: "override" String indicating how to compare variables of the same name for potential conflicts when merging: @@ -1449,7 +1449,7 @@ def open_mfdataset( * "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - * "override": skip comparing and pick variable from first dataset + * "override" (default): skip comparing and pick variable from first dataset preprocess : callable, optional If provided, call this function on each dataset prior to concatenation. @@ -1463,7 +1463,8 @@ def open_mfdataset( "netcdf4" over "h5netcdf" over "scipy" (customizable via ``netcdf_engine_order`` in ``xarray.set_options()``). A custom backend class (a subclass of ``BackendEntrypoint``) can also be used. - data_vars : {"minimal", "different", "all"} or list of str, default: "all" + data_vars : {"minimal", "different", "all", None} or list of str, \ + default: None These data variables will be concatenated together: * "minimal": Only data variables in which the dimension already appears are included. @@ -1473,12 +1474,15 @@ class (a subclass of ``BackendEntrypoint``) can also be used. load the data payload of data variables into memory if they are not already loaded. * "all": All data variables will be concatenated. + * None (default): Means ``"all"`` if ``concat_dim`` is not present + in any of the ``objs``, and ``"minimal"`` if ``concat_dim`` is + present in any of ``objs``. * list of str: The listed data variables will be concatenated, in addition to the "minimal" data variables. - coords : {"minimal", "different", "all"} or list of str, optional + coords : {"minimal", "different", "all"} or list of str, default: "minimal" These coordinate variables will be concatenated together: - * "minimal": Only coordinates in which the dimension already appears - are included. + * "minimal" (default): Only coordinates in which the dimension already + appears are included. * "different": Coordinates which are not equal (ignoring attributes) across all datasets are also concatenated (as well as all for which dimension already appears). Beware: this option may load the data @@ -1491,7 +1495,7 @@ class (a subclass of ``BackendEntrypoint``) can also be used. parallel : bool, default: False If True, the open and preprocess steps of this function will be performed in parallel using ``dask.delayed``. Default is False. - join : {"outer", "inner", "left", "right", "exact", "override"}, default: "outer" + join : {"outer", "inner", "left", "right", "exact", "override"}, default: "exact" String indicating how to combine differing indexes (excluding concat_dim) in objects @@ -1499,8 +1503,8 @@ class (a subclass of ``BackendEntrypoint``) can also be used. - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact": instead of aligning, raise `ValueError` when indexes to be - aligned are not equal + - "exact" (default): instead of aligning, raise `ValueError` when + indexes to be aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index 84a67d95412..d90a7f58f87 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -5659,7 +5659,7 @@ def merge( If provided, update variables of these name(s) without checking for conflicts in this dataset. compat : {"identical", "equals", "broadcast_equals", \ - "no_conflicts", "override", "minimal"}, default: "no_conflicts" + "no_conflicts", "override", "minimal"}, default: "override" String indicating how to compare variables of the same name for potential conflicts: @@ -5671,18 +5671,18 @@ def merge( - 'no_conflicts': only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - 'override': skip comparing and pick variable from first dataset + - 'override' (default): skip comparing and pick variable from first dataset - 'minimal': drop conflicting coordinates join : {"outer", "inner", "left", "right", "exact", "override"}, \ - default: "outer" + default: "exact" Method for joining ``self`` and ``other`` along shared dimensions: - 'outer': use the union of the indexes - 'inner': use the intersection of the indexes - 'left': use indexes from ``self`` - 'right': use indexes from ``other`` - - 'exact': error instead of aligning non-equal indexes + - 'exact' (default): error instead of aligning non-equal indexes - 'override': use indexes from ``self`` that are the same size as those of ``other`` in that dimension diff --git a/xarray/structure/combine.py b/xarray/structure/combine.py index b5546e6daf0..c45fd42cdef 100644 --- a/xarray/structure/combine.py +++ b/xarray/structure/combine.py @@ -509,7 +509,7 @@ def combine_nested( Must be the same length as the depth of the list passed to ``datasets``. compat : {"identical", "equals", "broadcast_equals", \ - "no_conflicts", "override"}, optional + "no_conflicts", "override"}, default: "override" String indicating how to compare variables of the same name for potential merge conflicts: @@ -521,8 +521,8 @@ def combine_nested( - "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - "override": skip comparing and pick variable from first dataset - data_vars : {"minimal", "different", "all" or list of str}, optional + - "override" (default): skip comparing and pick variable from first dataset + data_vars : {"minimal", "different", "all", None} or list of str, default: None These data variables will be concatenated together: * "minimal": Only data variables in which the dimension already appears are included. @@ -532,15 +532,16 @@ def combine_nested( load the data payload of data variables into memory if they are not already loaded. * "all": All data variables will be concatenated. - * None: Means ``"all"`` if ``dim`` is not present in any of the ``objs``, - and ``"minimal"`` if ``dim`` is present in any of ``objs``. + * None (default): Means ``"all"`` if ``concat_dim`` is not present in + any of the ``objs``,and ``"minimal"`` if ``concat_dim`` is present + in any of ``objs``. * list of dims: The listed data variables will be concatenated, in addition to the "minimal" data variables. - coords : {"minimal", "different", "all" or list of str}, optional + coords : {"minimal", "different", "all"} or list of str, default: "minimal" These coordinate variables will be concatenated together: - * "minimal": Only coordinates in which the dimension already appears - are included. If concatenating over a dimension _not_ + * "minimal" (default): Only coordinates in which the dimension already + appears are included. If concatenating over a dimension _not_ present in any of the objects, then all data variables will be concatenated along that new dimension. * "different": Coordinates which are not equal (ignoring attributes) @@ -557,7 +558,7 @@ def combine_nested( Value to use for newly missing values. If a dict-like, maps variable names to fill values. Use a data array's name to refer to its values. - join : {"outer", "inner", "left", "right", "exact"}, optional + join : {"outer", "inner", "left", "right", "exact"}, default: "exact" String indicating how to combine differing indexes (excluding concat_dim) in objects @@ -565,8 +566,8 @@ def combine_nested( - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact": instead of aligning, raise `ValueError` when indexes to be - aligned are not equal + - "exact" (default): instead of aligning, raise `ValueError` when + indexes to be aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. @@ -836,7 +837,8 @@ def combine_by_coords( data_objects : Iterable of Datasets or DataArrays Data objects to combine. - compat : {"identical", "equals", "broadcast_equals", "no_conflicts", "override"}, optional + compat : {"identical", "equals", "broadcast_equals", "no_conflicts", "override"}, \ + default: "override" String indicating how to compare variables of the same name for potential conflicts: @@ -848,11 +850,10 @@ def combine_by_coords( - "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - "override": skip comparing and pick variable from first dataset + - "override" (default): skip comparing and pick variable from first dataset - data_vars : {"minimal", "different", "all" or list of str}, optional + data_vars : {"minimal", "different", "all", None} or list of str, default: None These data variables will be concatenated together: - - "minimal": Only data variables in which the dimension already appears are included. - "different": Data variables which are not equal (ignoring @@ -861,26 +862,39 @@ def combine_by_coords( load the data payload of data variables into memory if they are not already loaded. - "all": All data variables will be concatenated. + - None (default): Means ``"all"`` if ``concat_dim`` is not present in any of the + ``objs``, and ``"minimal"`` if ``concat_dim`` is present in any of ``objs``. - list of str: The listed data variables will be concatenated, in addition to the "minimal" data variables. - - If objects are DataArrays, `data_vars` must be "all". - coords : {"minimal", "different", "all"} or list of str, optional - As per the "data_vars" kwarg, but for coordinate variables. + coords : {"minimal", "different", "all"} or list of str, default: "minimal" + These coordinate variables will be concatenated together: + - "minimal" (default): Only coordinates in which the dimension already + appears are included. If concatenating over a dimension _not_ + present in any of the objects, then all data variables will + be concatenated along that new dimension. + - "different": Coordinates which are not equal (ignoring attributes) + across all datasets are also concatenated (as well as all for which + dimension already appears). Beware: this option may load the data + payload of coordinate variables into memory if they are not already + loaded. + - "all": All coordinate variables will be concatenated, except + those corresponding to other dimensions. + - list of Hashable: The listed coordinate variables will be concatenated, + in addition to the "minimal" coordinates. fill_value : scalar or dict-like, optional Value to use for newly missing values. If a dict-like, maps variable names to fill values. Use a data array's name to refer to its values. If None, raises a ValueError if the passed Datasets do not create a complete hypercube. - join : {"outer", "inner", "left", "right", "exact"}, optional + join : {"outer", "inner", "left", "right", "exact"}, default: "exact" String indicating how to combine differing indexes in objects - "outer": use the union of object indexes - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact": instead of aligning, raise `ValueError` when indexes to be - aligned are not equal + - "exact" (default): instead of aligning, raise `ValueError` when + indexes to be aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. diff --git a/xarray/structure/concat.py b/xarray/structure/concat.py index 69b05880e3d..ac1ecb059c8 100644 --- a/xarray/structure/concat.py +++ b/xarray/structure/concat.py @@ -114,7 +114,7 @@ def concat( unchanged. If dimension is provided as a Variable, DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate. - data_vars : {"minimal", "different", "all", None} or list of Hashable, optional + data_vars : {"minimal", "different", "all", None} or list of Hashable, default: None These data variables will be concatenated together: * "minimal": Only data variables in which the dimension already appears are included. @@ -124,15 +124,15 @@ def concat( load the data payload of data variables into memory if they are not already loaded. * "all": All data variables will be concatenated. - * None: Means ``"all"`` if ``dim`` is not present in any of the ``objs``, - and ``"minimal"`` if ``dim`` is present in any of ``objs``. + * None (default): Means ``"all"`` if ``dim`` is not present in any of the + ``objs``, and ``"minimal"`` if ``dim`` is present in any of ``objs``. * list of dims: The listed data variables will be concatenated, in addition to the "minimal" data variables. - If objects are DataArrays, data_vars must be "all". - coords : {"minimal", "different", "all"} or list of Hashable, optional + If objects are DataArrays, data_vars must be "all" or None. + coords : {"minimal", "different", "all"} or list of Hashable, default: "minimal" These coordinate variables will be concatenated together: - * "minimal": Only coordinates in which the dimension already appears + * "minimal" (default): Only coordinates in which the dimension already appears are included. * "different": Coordinates which are not equal (ignoring attributes) across all datasets are also concatenated (as well as all for which @@ -143,7 +143,8 @@ def concat( those corresponding to other dimensions. * list of Hashable: The listed coordinate variables will be concatenated, in addition to the "minimal" coordinates. - compat : {"identical", "equals", "broadcast_equals", "no_conflicts", "override"}, optional + compat : {"identical", "equals", "broadcast_equals", "no_conflicts", "override"}, \ + default: "override" String indicating how to compare non-concatenated variables of the same name for potential conflicts. This is passed down to merge. @@ -155,7 +156,7 @@ def concat( - "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - "override": skip comparing and pick variable from first dataset + - "override" (default): skip comparing and pick variable from first dataset positions : None or list of integer arrays, optional List of integer arrays which specifies the integer positions to which to assign each dataset along the concatenated dimension. If not @@ -164,7 +165,7 @@ def concat( Value to use for newly missing values. If a dict-like, maps variable names to fill values. Use a data array's name to refer to its values. - join : {"outer", "inner", "left", "right", "exact"}, optional + join : {"outer", "inner", "left", "right", "exact"}, default: "exact" String indicating how to combine differing indexes (excluding dim) in objects @@ -172,8 +173,8 @@ def concat( - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact": instead of aligning, raise `ValueError` when indexes to be - aligned are not equal + - "exact" (default): instead of aligning, raise `ValueError` when indexes + to be aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. diff --git a/xarray/structure/merge.py b/xarray/structure/merge.py index d398a37c8ef..45592f7c672 100644 --- a/xarray/structure/merge.py +++ b/xarray/structure/merge.py @@ -885,7 +885,7 @@ def merge( Merge together all variables from these objects. If any of them are DataArray objects, they must have a name. compat : {"identical", "equals", "broadcast_equals", "no_conflicts", \ - "override", "minimal"}, default: "no_conflicts" + "override", "minimal"}, default: "override" String indicating how to compare variables of the same name for potential conflicts: @@ -897,18 +897,19 @@ def merge( - "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - "override": skip comparing and pick variable from first dataset + - "override" (default): skip comparing and pick variable from first dataset - "minimal": drop conflicting coordinates - join : {"outer", "inner", "left", "right", "exact", "override"}, default: "outer" + join : {"outer", "inner", "left", "right", "exact", "override"}, \ + default: "exact" String indicating how to combine differing indexes in objects. - "outer": use the union of object indexes - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact": instead of aligning, raise `ValueError` when indexes to be - aligned are not equal + - "exact" (default): instead of aligning, raise `ValueError` when indexes + to be aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. From d7fe147fefa05171881657fa9e0291c75032f16c Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Thu, 8 Jan 2026 13:31:03 -0500 Subject: [PATCH 4/9] Update warning messages --- xarray/tests/test_backends.py | 6 +++--- xarray/tests/test_combine.py | 12 ++++++------ xarray/tests/test_concat.py | 6 +++--- xarray/tests/test_merge.py | 6 +++--- xarray/util/deprecation_helpers.py | 10 +++++----- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index 1772038a940..fef74f1fc18 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -5697,7 +5697,7 @@ def test_open_mfdataset_warns_when_kwargs_set_to_different( expectation: contextlib.AbstractContextManager = ( pytest.warns( FutureWarning, - match="will change from data_vars='all'", + match="changed from data_vars='all'", ) if "data_vars" not in kwargs else contextlib.nullcontext() @@ -5705,12 +5705,12 @@ def test_open_mfdataset_warns_when_kwargs_set_to_different( with pytest.warns( FutureWarning, - match="will change from compat='equals'", + match="changed from compat='equals'", ): with expectation: ds_expect = xr.concat([ds1, ds2], dim="t", **kwargs) with pytest.warns( - FutureWarning, match="will change from compat='no_conflicts'" + FutureWarning, match="changed from compat='no_conflicts'" ): with expectation: with open_mfdataset( diff --git a/xarray/tests/test_combine.py b/xarray/tests/test_combine.py index 228fd74318d..a397fc86769 100644 --- a/xarray/tests/test_combine.py +++ b/xarray/tests/test_combine.py @@ -1253,11 +1253,11 @@ def test_nested_merge_with_overlapping_values(self): expected = Dataset({"a": ("x", [1, 2, 3]), "x": [0, 1, 2]}) with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( - FutureWarning, match="will change from join='outer' to join='exact'" + FutureWarning, match="changed from join='outer' to join='exact'" ): with pytest.warns( FutureWarning, - match="will change from compat='no_conflicts' to compat='override'", + match="changed from compat='no_conflicts' to compat='override'", ): old = combine_nested([ds1, ds2], concat_dim=None) with set_options(use_new_combine_kwarg_defaults=True): @@ -1272,7 +1272,7 @@ def test_nested_merge_with_nan_order_matters(self): with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="will change from compat='no_conflicts' to compat='override'", + match="changed from compat='no_conflicts' to compat='override'", ): old = combine_nested([ds1, ds2], concat_dim=None) with set_options(use_new_combine_kwarg_defaults=True): @@ -1284,7 +1284,7 @@ def test_nested_merge_with_nan_order_matters(self): with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="will change from compat='no_conflicts' to compat='override'", + match="changed from compat='no_conflicts' to compat='override'", ): old = combine_nested([ds2, ds1], concat_dim=None) with set_options(use_new_combine_kwarg_defaults=True): @@ -1320,7 +1320,7 @@ def test_combine_nested_missing_data_new_dim(self): ) with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( - FutureWarning, match="will change from join='outer' to join='exact'" + FutureWarning, match="changed from join='outer' to join='exact'" ): old = combine_nested(datasets, concat_dim="t") with set_options(use_new_combine_kwarg_defaults=True): @@ -1337,7 +1337,7 @@ def test_combine_by_coords_multiple_variables(self): with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( - FutureWarning, match="will change from join='outer' to join='exact'" + FutureWarning, match="changed from join='outer' to join='exact'" ): old = combine_by_coords(objs) with set_options(use_new_combine_kwarg_defaults=True): diff --git a/xarray/tests/test_concat.py b/xarray/tests/test_concat.py index 51e09fa4534..a23b6f951a6 100644 --- a/xarray/tests/test_concat.py +++ b/xarray/tests/test_concat.py @@ -1498,7 +1498,7 @@ def test_concat_second_empty_with_scalar_data_var_only_on_first(self) -> None: with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="will change from compat='equals' to compat='override'", + match="changed from compat='equals' to compat='override'", ): actual = concat( [ds1, ds2], dim="y", coords="different", data_vars="different" @@ -1545,7 +1545,7 @@ def test_concat_coords_kwarg( expectation: AbstractContextManager = ( pytest.warns( FutureWarning, - match="will change from compat='equals' to compat='override'", + match="changed from compat='equals' to compat='override'", ) if coords == "different" else nullcontext() @@ -1570,7 +1570,7 @@ def test_concat_promote_shape_for_scalars_with_mixed_lengths_along_concat_dim( with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="will change from coords='different' to coords='minimal'", + match="changed from coords='different' to coords='minimal'", ): old = concat(objs, "x") assert_identical(old, expected) diff --git a/xarray/tests/test_merge.py b/xarray/tests/test_merge.py index df597692bf9..4850bc36ac2 100644 --- a/xarray/tests/test_merge.py +++ b/xarray/tests/test_merge.py @@ -834,7 +834,7 @@ def test_merge_broadcast_equals(self): with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="will change from compat='no_conflicts' to compat='override'", + match="changed from compat='no_conflicts' to compat='override'", ): old = ds1.merge(ds2) @@ -853,11 +853,11 @@ def test_merge_auto_align(self): ) with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( - FutureWarning, match="will change from join='outer' to join='exact'" + FutureWarning, match="changed from join='outer' to join='exact'" ): assert expected.identical(ds1.merge(ds2)) with pytest.warns( - FutureWarning, match="will change from join='outer' to join='exact'" + FutureWarning, match="changed from join='outer' to join='exact'" ): assert expected.identical(ds2.merge(ds1)) diff --git a/xarray/util/deprecation_helpers.py b/xarray/util/deprecation_helpers.py index 7a8ab6ce960..7501e565591 100644 --- a/xarray/util/deprecation_helpers.py +++ b/xarray/util/deprecation_helpers.py @@ -186,10 +186,10 @@ def __dask_tokenize__(self) -> object: return normalize_token((type(self), self._value)) def warning_message(self, message: str, recommend_set_options: bool = True) -> str: - if recommend_set_options: + if not recommend_set_options: recommendation = ( " To opt in to new defaults and get rid of these warnings now " - "use `set_options(use_new_combine_kwarg_defaults=True) or " + "remove `set_options(use_new_combine_kwarg_defaults=False) or " f"set {self._name} explicitly." ) else: @@ -198,8 +198,8 @@ def warning_message(self, message: str, recommend_set_options: bool = True) -> s ) return ( - f"In a future version of xarray the default value for {self._name} will " - f"change from {self._name}={self._old!r} to {self._name}={self._new!r}. " + f"In xarray v2026.02.1 the default value for {self._name} " + f"changed from {self._name}={self._old!r} to {self._name}={self._new!r}. " + message + recommendation ) @@ -208,7 +208,7 @@ def error_message(self) -> str: return ( f" Error might be related to new default (`{self._name}={self._new!r}`). " f"Previously the default was `{self._name}={self._old!r}`. " - f"The recommendation is to set {self._name!r} explicitly for this case." + f"The recommendation is to set {self._name} explicitly for this case." ) From 5989f63291a0a1206357a66a050e587f5a5a554b Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Thu, 8 Jan 2026 14:06:27 -0500 Subject: [PATCH 5/9] Remove testing of `use_new_combine_kwarg_defaults` outside of explicit classes --- xarray/tests/test_backends.py | 109 ++++++++++------------------------ xarray/tests/test_combine.py | 3 +- xarray/tests/test_concat.py | 13 ++-- xarray/tests/test_merge.py | 16 +++-- 4 files changed, 44 insertions(+), 97 deletions(-) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index fef74f1fc18..cced85b46f2 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -5476,7 +5476,6 @@ def test_open_mfdataset_does_same_as_concat( ) assert_identical(ds, ds_expect) - @pytest.mark.parametrize("use_new_combine_kwarg_defaults", [True, False]) @pytest.mark.parametrize( ["combine_attrs", "attrs", "expected", "expect_error"], ( @@ -5505,7 +5504,6 @@ def test_open_mfdataset_does_same_as_concat( ) def test_open_mfdataset_dataset_combine_attrs( self, - use_new_combine_kwarg_defaults, combine_attrs, attrs, expected, @@ -5519,28 +5517,19 @@ def test_open_mfdataset_dataset_combine_attrs( ds.close() ds.to_netcdf(f) - with set_options( - use_new_combine_kwarg_defaults=use_new_combine_kwarg_defaults - ): - warning: contextlib.AbstractContextManager = ( - pytest.warns(FutureWarning) - if not use_new_combine_kwarg_defaults - else contextlib.nullcontext() - ) - error: contextlib.AbstractContextManager = ( - pytest.raises(xr.MergeError) - if expect_error - else contextlib.nullcontext() - ) - with warning: - with error: - with xr.open_mfdataset( - files, - combine="nested", - concat_dim="t", - combine_attrs=combine_attrs, - ) as ds: - assert ds.attrs == expected + error: contextlib.AbstractContextManager = ( + pytest.raises(xr.MergeError) + if expect_error + else contextlib.nullcontext() + ) + with error: + with xr.open_mfdataset( + files, + combine="nested", + concat_dim="t", + combine_attrs=combine_attrs, + ) as ds: + assert ds.attrs == expected def test_open_mfdataset_dataset_attr_by_coords(self) -> None: """ @@ -5554,9 +5543,8 @@ def test_open_mfdataset_dataset_attr_by_coords(self) -> None: ds.close() ds.to_netcdf(f) - with set_options(use_new_combine_kwarg_defaults=True): - with xr.open_mfdataset(files, combine="nested", concat_dim="t") as ds: - assert ds.test_dataset_attr == 10 + with xr.open_mfdataset(files, combine="nested", concat_dim="t") as ds: + assert ds.test_dataset_attr == 10 def test_open_mfdataset_dataarray_attr_by_coords(self) -> None: """ @@ -5693,31 +5681,6 @@ def test_open_mfdataset_warns_when_kwargs_set_to_different( ): xr.concat([ds1, ds2], dim="t", **kwargs) - with set_options(use_new_combine_kwarg_defaults=False): - expectation: contextlib.AbstractContextManager = ( - pytest.warns( - FutureWarning, - match="changed from data_vars='all'", - ) - if "data_vars" not in kwargs - else contextlib.nullcontext() - ) - - with pytest.warns( - FutureWarning, - match="changed from compat='equals'", - ): - with expectation: - ds_expect = xr.concat([ds1, ds2], dim="t", **kwargs) - with pytest.warns( - FutureWarning, match="changed from compat='no_conflicts'" - ): - with expectation: - with open_mfdataset( - files, combine=combine, concat_dim=concat_dim, **kwargs - ) as ds: - assert_identical(ds, ds_expect) - @requires_dask @requires_scipy @@ -6060,22 +6023,17 @@ def test_encoding_mfdataset_new_defaults(self) -> None: ds1.to_netcdf(tmp1) ds2.to_netcdf(tmp2) - for setting in [True, False]: - with set_options(use_new_combine_kwarg_defaults=setting): - with open_mfdataset( - [tmp1, tmp2], combine="nested", concat_dim="t" - ) as old: - assert ( - old.t.encoding["units"] == original.t.encoding["units"] - ) - assert old.t.encoding["units"] == ds1.t.encoding["units"] - assert old.t.encoding["units"] != ds2.t.encoding["units"] + with open_mfdataset( + [tmp1, tmp2], combine="nested", concat_dim="t" + ) as old: + assert old.t.encoding["units"] == original.t.encoding["units"] + assert old.t.encoding["units"] == ds1.t.encoding["units"] + assert old.t.encoding["units"] != ds2.t.encoding["units"] - with set_options(use_new_combine_kwarg_defaults=True): - with pytest.raises( - AlignmentError, match="If you are intending to concatenate" - ): - open_mfdataset([tmp1, tmp2], combine="nested") + with pytest.raises( + AlignmentError, match="If you are intending to concatenate" + ): + open_mfdataset([tmp1, tmp2], combine="nested") def test_preprocess_mfdataset(self) -> None: original = Dataset({"foo": ("x", np.random.randn(10))}) @@ -6164,16 +6122,13 @@ def test_open_and_do_math(self) -> None: [pytest.param({"concat_dim": None}, id="none"), pytest.param({}, id="default")], ) def test_open_mfdataset_concat_dim(self, kwargs) -> None: - with set_options(use_new_combine_kwarg_defaults=True): - with create_tmp_file() as tmp1: - with create_tmp_file() as tmp2: - data = Dataset({"x": 0}) - data.to_netcdf(tmp1) - Dataset({"x": np.nan}).to_netcdf(tmp2) - with open_mfdataset( - [tmp1, tmp2], **kwargs, combine="nested" - ) as actual: - assert_identical(data, actual) + with create_tmp_file() as tmp1: + with create_tmp_file() as tmp2: + data = Dataset({"x": 0}) + data.to_netcdf(tmp1) + Dataset({"x": np.nan}).to_netcdf(tmp2) + with open_mfdataset([tmp1, tmp2], **kwargs, combine="nested") as actual: + assert_identical(data, actual) def test_open_dataset(self) -> None: original = Dataset({"foo": ("x", np.random.randn(10))}) diff --git a/xarray/tests/test_combine.py b/xarray/tests/test_combine.py index a397fc86769..cd8afb95f5e 100644 --- a/xarray/tests/test_combine.py +++ b/xarray/tests/test_combine.py @@ -1044,8 +1044,7 @@ def test_infer_order_from_coords(self): expected = data assert expected.broadcast_equals(actual) # type: ignore[arg-type] - with set_options(use_new_combine_kwarg_defaults=True): - actual = combine_by_coords(objs) + actual = combine_by_coords(objs) assert_identical(actual, expected) def test_combine_leaving_bystander_dimensions(self): diff --git a/xarray/tests/test_concat.py b/xarray/tests/test_concat.py index a23b6f951a6..111e81cbd3d 100644 --- a/xarray/tests/test_concat.py +++ b/xarray/tests/test_concat.py @@ -649,15 +649,10 @@ def test_concat_constant_index_minimal(self) -> None: ds1 = Dataset({"foo": 1.5}, {"y": 1}) ds2 = Dataset({"foo": 2.5}, {"y": 1}) - with set_options(use_new_combine_kwarg_defaults=False): - with pytest.raises(merge.MergeError, match="conflicting values"): - concat([ds1, ds2], dim="new_dim", data_vars="minimal") - - with set_options(use_new_combine_kwarg_defaults=True): - with pytest.raises( - ValueError, match="data_vars='minimal' and coords='minimal'" - ): - concat([ds1, ds2], dim="new_dim", data_vars="minimal") + with pytest.raises( + ValueError, match="data_vars='minimal' and coords='minimal'" + ): + concat([ds1, ds2], dim="new_dim", data_vars="minimal") def test_concat_size0(self) -> None: data = create_test_data() diff --git a/xarray/tests/test_merge.py b/xarray/tests/test_merge.py index 4850bc36ac2..6e68a570139 100644 --- a/xarray/tests/test_merge.py +++ b/xarray/tests/test_merge.py @@ -42,17 +42,15 @@ def test_merge_arrays(self): expected = data[["var1", "var2"]] assert_identical(actual, expected) - @pytest.mark.parametrize("use_new_combine_kwarg_defaults", [True, False]) - def test_merge_datasets(self, use_new_combine_kwarg_defaults): - with set_options(use_new_combine_kwarg_defaults=use_new_combine_kwarg_defaults): - data = create_test_data(add_attrs=False, use_extension_array=True) + def test_merge_datasets(self): + data = create_test_data(add_attrs=False, use_extension_array=True) - actual = xr.merge([data[["var1"]], data[["var2"]]]) - expected = data[["var1", "var2"]] - assert_identical(actual, expected) + actual = xr.merge([data[["var1"]], data[["var2"]]]) + expected = data[["var1", "var2"]] + assert_identical(actual, expected) - actual = xr.merge([data, data]) - assert_identical(actual, data) + actual = xr.merge([data, data]) + assert_identical(actual, data) def test_merge_dataarray_unnamed(self): data = xr.DataArray([1, 2], dims="x") From 99aff1fa88512e43172ff158738d9fa2789dc8ff Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Thu, 8 Jan 2026 15:11:38 -0500 Subject: [PATCH 6/9] Fix pint tests --- xarray/tests/test_units.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index 1258997e0c7..a3bfa521f66 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -782,7 +782,7 @@ def test_combine_by_coords(variant, unit, error, dtype): if error is not None: with pytest.raises(error): - xr.combine_by_coords([ds, other]) + xr.combine_by_coords([ds, other], coords="different", compat="no_conflicts") return @@ -881,7 +881,7 @@ def test_combine_nested(variant, unit, error, dtype): }, ) - func = function(xr.combine_nested, concat_dim=["x", "y"]) + func = function(xr.combine_nested, concat_dim=["x", "y"], join="outer") if error is not None: with pytest.raises(error): func([[ds1, ds2], [ds3, ds4]]) @@ -1113,9 +1113,10 @@ def test_merge_dataarray(variant, unit, error, dtype): dims=("y", "z"), ) + func = function(xr.merge, compat="no_conflicts", join="outer") if error is not None: with pytest.raises(error): - xr.merge([arr1, arr2, arr3]) + func([arr1, arr2, arr3]) return @@ -1131,13 +1132,13 @@ def test_merge_dataarray(variant, unit, error, dtype): convert_and_strip = lambda arr: strip_units(convert_units(arr, units)) expected = attach_units( - xr.merge( + func( [convert_and_strip(arr1), convert_and_strip(arr2), convert_and_strip(arr3)] ), units, ) - actual = xr.merge([arr1, arr2, arr3]) + actual = func([arr1, arr2, arr3]) assert_units_equal(expected, actual) assert_allclose(expected, actual) @@ -1214,7 +1215,7 @@ def test_merge_dataset(variant, unit, error, dtype): }, ) - func = function(xr.merge) + func = function(xr.merge, compat="no_conflicts", join="outer") if error is not None: with pytest.raises(error): func([ds1, ds2, ds3]) @@ -5624,13 +5625,15 @@ def test_merge(self, variant, unit, error, dtype): if error is not None: with pytest.raises(error): - left.merge(right) + left.merge(right, compat="no_conflicts", join="outer") return converted = convert_units(right, units) - expected = attach_units(strip_units(left).merge(strip_units(converted)), units) - actual = left.merge(right) + expected = attach_units( + strip_units(left).merge(strip_units(converted), join="outer"), units + ) + actual = left.merge(right, join="outer") assert_units_equal(expected, actual) assert_equal(expected, actual) From 740f3a56e9a00be65ef38390b1ba05d35e7df307 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 9 Jan 2026 15:41:46 -0500 Subject: [PATCH 7/9] Revert flipping the option --- xarray/core/options.py | 4 ++-- xarray/util/deprecation_helpers.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/xarray/core/options.py b/xarray/core/options.py index 54a6fd163af..451070ce7b4 100644 --- a/xarray/core/options.py +++ b/xarray/core/options.py @@ -93,7 +93,7 @@ class T_Options(TypedDict): "warn_for_unclosed_files": False, "use_bottleneck": True, "use_flox": True, - "use_new_combine_kwarg_defaults": True, + "use_new_combine_kwarg_defaults": False, "use_numbagg": True, "use_opt_einsum": True, } @@ -271,7 +271,7 @@ class set_options: use_flox : bool, default: True Whether to use ``numpy_groupies`` and `flox`` to accelerate groupby and resampling reductions. - use_new_combine_kwarg_defaults : bool, default True + use_new_combine_kwarg_defaults : bool, default False Whether to use new kwarg default values for combine functions: :py:func:`~xarray.concat`, :py:func:`~xarray.merge`, :py:func:`~xarray.open_mfdataset`. New values are: diff --git a/xarray/util/deprecation_helpers.py b/xarray/util/deprecation_helpers.py index 7501e565591..7a8ab6ce960 100644 --- a/xarray/util/deprecation_helpers.py +++ b/xarray/util/deprecation_helpers.py @@ -186,10 +186,10 @@ def __dask_tokenize__(self) -> object: return normalize_token((type(self), self._value)) def warning_message(self, message: str, recommend_set_options: bool = True) -> str: - if not recommend_set_options: + if recommend_set_options: recommendation = ( " To opt in to new defaults and get rid of these warnings now " - "remove `set_options(use_new_combine_kwarg_defaults=False) or " + "use `set_options(use_new_combine_kwarg_defaults=True) or " f"set {self._name} explicitly." ) else: @@ -198,8 +198,8 @@ def warning_message(self, message: str, recommend_set_options: bool = True) -> s ) return ( - f"In xarray v2026.02.1 the default value for {self._name} " - f"changed from {self._name}={self._old!r} to {self._name}={self._new!r}. " + f"In a future version of xarray the default value for {self._name} will " + f"change from {self._name}={self._old!r} to {self._name}={self._new!r}. " + message + recommendation ) @@ -208,7 +208,7 @@ def error_message(self) -> str: return ( f" Error might be related to new default (`{self._name}={self._new!r}`). " f"Previously the default was `{self._name}={self._old!r}`. " - f"The recommendation is to set {self._name} explicitly for this case." + f"The recommendation is to set {self._name!r} explicitly for this case." ) From c421eff6aa21ac6195d585fa3ad34d94e15fc9d2 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 9 Jan 2026 16:30:18 -0500 Subject: [PATCH 8/9] Revert docstring default changes --- xarray/backends/api.py | 25 +++++++++++----------- xarray/core/dataset.py | 8 ++++---- xarray/structure/combine.py | 41 +++++++++++++++++++------------------ xarray/structure/concat.py | 20 +++++++++--------- xarray/structure/merge.py | 11 +++++----- 5 files changed, 52 insertions(+), 53 deletions(-) diff --git a/xarray/backends/api.py b/xarray/backends/api.py index 9e75f1d67d1..85da88f5339 100644 --- a/xarray/backends/api.py +++ b/xarray/backends/api.py @@ -1437,7 +1437,7 @@ def open_mfdataset( Whether ``xarray.combine_by_coords`` or ``xarray.combine_nested`` is used to combine all the data. Default is to use ``xarray.combine_by_coords``. compat : {"identical", "equals", "broadcast_equals", \ - "no_conflicts", "override"}, default: "override" + "no_conflicts", "override"}, default: "no_conflicts" String indicating how to compare variables of the same name for potential conflicts when merging: @@ -1449,7 +1449,7 @@ def open_mfdataset( * "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - * "override" (default): skip comparing and pick variable from first dataset + * "override": skip comparing and pick variable from first dataset preprocess : callable, optional If provided, call this function on each dataset prior to concatenation. @@ -1463,8 +1463,7 @@ def open_mfdataset( "netcdf4" over "h5netcdf" over "scipy" (customizable via ``netcdf_engine_order`` in ``xarray.set_options()``). A custom backend class (a subclass of ``BackendEntrypoint``) can also be used. - data_vars : {"minimal", "different", "all", None} or list of str, \ - default: None + data_vars : {"minimal", "different", "all", None} or list of str, default: "all" These data variables will be concatenated together: * "minimal": Only data variables in which the dimension already appears are included. @@ -1474,15 +1473,15 @@ class (a subclass of ``BackendEntrypoint``) can also be used. load the data payload of data variables into memory if they are not already loaded. * "all": All data variables will be concatenated. - * None (default): Means ``"all"`` if ``concat_dim`` is not present - in any of the ``objs``, and ``"minimal"`` if ``concat_dim`` is - present in any of ``objs``. + * None: Means ``"all"`` if ``concat_dim`` is not present in any of + the ``objs``, and ``"minimal"`` if ``concat_dim`` is present + in any of ``objs``. * list of str: The listed data variables will be concatenated, in addition to the "minimal" data variables. - coords : {"minimal", "different", "all"} or list of str, default: "minimal" + coords : {"minimal", "different", "all"} or list of str, default: "different" These coordinate variables will be concatenated together: - * "minimal" (default): Only coordinates in which the dimension already - appears are included. + * "minimal": Only coordinates in which the dimension already appears + are included. * "different": Coordinates which are not equal (ignoring attributes) across all datasets are also concatenated (as well as all for which dimension already appears). Beware: this option may load the data @@ -1495,7 +1494,7 @@ class (a subclass of ``BackendEntrypoint``) can also be used. parallel : bool, default: False If True, the open and preprocess steps of this function will be performed in parallel using ``dask.delayed``. Default is False. - join : {"outer", "inner", "left", "right", "exact", "override"}, default: "exact" + join : {"outer", "inner", "left", "right", "exact", "override"}, default: "outer" String indicating how to combine differing indexes (excluding concat_dim) in objects @@ -1503,8 +1502,8 @@ class (a subclass of ``BackendEntrypoint``) can also be used. - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact" (default): instead of aligning, raise `ValueError` when - indexes to be aligned are not equal + - "exact": instead of aligning, raise `ValueError` when indexes to be + aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. diff --git a/xarray/core/dataset.py b/xarray/core/dataset.py index d90a7f58f87..84a67d95412 100644 --- a/xarray/core/dataset.py +++ b/xarray/core/dataset.py @@ -5659,7 +5659,7 @@ def merge( If provided, update variables of these name(s) without checking for conflicts in this dataset. compat : {"identical", "equals", "broadcast_equals", \ - "no_conflicts", "override", "minimal"}, default: "override" + "no_conflicts", "override", "minimal"}, default: "no_conflicts" String indicating how to compare variables of the same name for potential conflicts: @@ -5671,18 +5671,18 @@ def merge( - 'no_conflicts': only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - 'override' (default): skip comparing and pick variable from first dataset + - 'override': skip comparing and pick variable from first dataset - 'minimal': drop conflicting coordinates join : {"outer", "inner", "left", "right", "exact", "override"}, \ - default: "exact" + default: "outer" Method for joining ``self`` and ``other`` along shared dimensions: - 'outer': use the union of the indexes - 'inner': use the intersection of the indexes - 'left': use indexes from ``self`` - 'right': use indexes from ``other`` - - 'exact' (default): error instead of aligning non-equal indexes + - 'exact': error instead of aligning non-equal indexes - 'override': use indexes from ``self`` that are the same size as those of ``other`` in that dimension diff --git a/xarray/structure/combine.py b/xarray/structure/combine.py index c45fd42cdef..c29e6aa1dbf 100644 --- a/xarray/structure/combine.py +++ b/xarray/structure/combine.py @@ -509,7 +509,7 @@ def combine_nested( Must be the same length as the depth of the list passed to ``datasets``. compat : {"identical", "equals", "broadcast_equals", \ - "no_conflicts", "override"}, default: "override" + "no_conflicts", "override"}, default: "no_conflicts" String indicating how to compare variables of the same name for potential merge conflicts: @@ -521,8 +521,8 @@ def combine_nested( - "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - "override" (default): skip comparing and pick variable from first dataset - data_vars : {"minimal", "different", "all", None} or list of str, default: None + - "override": skip comparing and pick variable from first dataset + data_vars : {"minimal", "different", "all", None} or list of str, default: "all" These data variables will be concatenated together: * "minimal": Only data variables in which the dimension already appears are included. @@ -532,15 +532,15 @@ def combine_nested( load the data payload of data variables into memory if they are not already loaded. * "all": All data variables will be concatenated. - * None (default): Means ``"all"`` if ``concat_dim`` is not present in - any of the ``objs``,and ``"minimal"`` if ``concat_dim`` is present + * None: Means ``"all"`` if ``concat_dim`` is not present in any of + the ``objs``, and ``"minimal"`` if ``concat_dim`` is present in any of ``objs``. * list of dims: The listed data variables will be concatenated, in addition to the "minimal" data variables. - coords : {"minimal", "different", "all"} or list of str, default: "minimal" + coords : {"minimal", "different", "all"} or list of str, default: "different" These coordinate variables will be concatenated together: - * "minimal" (default): Only coordinates in which the dimension already + * "minimal": Only coordinates in which the dimension already appears are included. If concatenating over a dimension _not_ present in any of the objects, then all data variables will be concatenated along that new dimension. @@ -558,7 +558,7 @@ def combine_nested( Value to use for newly missing values. If a dict-like, maps variable names to fill values. Use a data array's name to refer to its values. - join : {"outer", "inner", "left", "right", "exact"}, default: "exact" + join : {"outer", "inner", "left", "right", "exact"}, default: "outer" String indicating how to combine differing indexes (excluding concat_dim) in objects @@ -566,8 +566,8 @@ def combine_nested( - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact" (default): instead of aligning, raise `ValueError` when - indexes to be aligned are not equal + - "exact": instead of aligning, raise `ValueError` when indexes to be + aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. @@ -838,7 +838,7 @@ def combine_by_coords( Data objects to combine. compat : {"identical", "equals", "broadcast_equals", "no_conflicts", "override"}, \ - default: "override" + default: "no_conflicts" String indicating how to compare variables of the same name for potential conflicts: @@ -850,9 +850,9 @@ def combine_by_coords( - "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - "override" (default): skip comparing and pick variable from first dataset + - "override": skip comparing and pick variable from first dataset - data_vars : {"minimal", "different", "all", None} or list of str, default: None + data_vars : {"minimal", "different", "all", None} or list of str, default: "all" These data variables will be concatenated together: - "minimal": Only data variables in which the dimension already appears are included. @@ -862,13 +862,14 @@ def combine_by_coords( load the data payload of data variables into memory if they are not already loaded. - "all": All data variables will be concatenated. - - None (default): Means ``"all"`` if ``concat_dim`` is not present in any of the - ``objs``, and ``"minimal"`` if ``concat_dim`` is present in any of ``objs``. + - None: Means ``"all"`` if ``concat_dim`` is not present in any of + the ``objs``, and ``"minimal"`` if ``concat_dim`` is present + in any of ``objs``. - list of str: The listed data variables will be concatenated, in addition to the "minimal" data variables. - coords : {"minimal", "different", "all"} or list of str, default: "minimal" + coords : {"minimal", "different", "all"} or list of str, default: "different" These coordinate variables will be concatenated together: - - "minimal" (default): Only coordinates in which the dimension already + - "minimal": Only coordinates in which the dimension already appears are included. If concatenating over a dimension _not_ present in any of the objects, then all data variables will be concatenated along that new dimension. @@ -886,15 +887,15 @@ def combine_by_coords( variable names to fill values. Use a data array's name to refer to its values. If None, raises a ValueError if the passed Datasets do not create a complete hypercube. - join : {"outer", "inner", "left", "right", "exact"}, default: "exact" + join : {"outer", "inner", "left", "right", "exact"}, default: "outer" String indicating how to combine differing indexes in objects - "outer": use the union of object indexes - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact" (default): instead of aligning, raise `ValueError` when - indexes to be aligned are not equal + - "exact": instead of aligning, raise `ValueError` when indexes to be + aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. diff --git a/xarray/structure/concat.py b/xarray/structure/concat.py index ac1ecb059c8..774993efefa 100644 --- a/xarray/structure/concat.py +++ b/xarray/structure/concat.py @@ -114,7 +114,7 @@ def concat( unchanged. If dimension is provided as a Variable, DataArray or Index, its name is used as the dimension to concatenate along and the values are added as a coordinate. - data_vars : {"minimal", "different", "all", None} or list of Hashable, default: None + data_vars : {"minimal", "different", "all", None} or list of Hashable, default: "all" These data variables will be concatenated together: * "minimal": Only data variables in which the dimension already appears are included. @@ -124,15 +124,15 @@ def concat( load the data payload of data variables into memory if they are not already loaded. * "all": All data variables will be concatenated. - * None (default): Means ``"all"`` if ``dim`` is not present in any of the - ``objs``, and ``"minimal"`` if ``dim`` is present in any of ``objs``. + * None: Means ``"all"`` if ``dim`` is not present in any of the ``objs``, + and ``"minimal"`` if ``dim`` is present in any of ``objs``. * list of dims: The listed data variables will be concatenated, in addition to the "minimal" data variables. If objects are DataArrays, data_vars must be "all" or None. - coords : {"minimal", "different", "all"} or list of Hashable, default: "minimal" + coords : {"minimal", "different", "all"} or list of Hashable, default: "different" These coordinate variables will be concatenated together: - * "minimal" (default): Only coordinates in which the dimension already appears + * "minimal": Only coordinates in which the dimension already appears are included. * "different": Coordinates which are not equal (ignoring attributes) across all datasets are also concatenated (as well as all for which @@ -144,7 +144,7 @@ def concat( * list of Hashable: The listed coordinate variables will be concatenated, in addition to the "minimal" coordinates. compat : {"identical", "equals", "broadcast_equals", "no_conflicts", "override"}, \ - default: "override" + default: "equals" String indicating how to compare non-concatenated variables of the same name for potential conflicts. This is passed down to merge. @@ -156,7 +156,7 @@ def concat( - "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - "override" (default): skip comparing and pick variable from first dataset + - "override": skip comparing and pick variable from first dataset positions : None or list of integer arrays, optional List of integer arrays which specifies the integer positions to which to assign each dataset along the concatenated dimension. If not @@ -165,7 +165,7 @@ def concat( Value to use for newly missing values. If a dict-like, maps variable names to fill values. Use a data array's name to refer to its values. - join : {"outer", "inner", "left", "right", "exact"}, default: "exact" + join : {"outer", "inner", "left", "right", "exact"}, default: "outer" String indicating how to combine differing indexes (excluding dim) in objects @@ -173,8 +173,8 @@ def concat( - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact" (default): instead of aligning, raise `ValueError` when indexes - to be aligned are not equal + - "exact": instead of aligning, raise `ValueError` when indexes to be + aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. diff --git a/xarray/structure/merge.py b/xarray/structure/merge.py index 45592f7c672..d398a37c8ef 100644 --- a/xarray/structure/merge.py +++ b/xarray/structure/merge.py @@ -885,7 +885,7 @@ def merge( Merge together all variables from these objects. If any of them are DataArray objects, they must have a name. compat : {"identical", "equals", "broadcast_equals", "no_conflicts", \ - "override", "minimal"}, default: "override" + "override", "minimal"}, default: "no_conflicts" String indicating how to compare variables of the same name for potential conflicts: @@ -897,19 +897,18 @@ def merge( - "no_conflicts": only values which are not null in both datasets must be equal. The returned dataset then contains the combination of all non-null values. - - "override" (default): skip comparing and pick variable from first dataset + - "override": skip comparing and pick variable from first dataset - "minimal": drop conflicting coordinates - join : {"outer", "inner", "left", "right", "exact", "override"}, \ - default: "exact" + join : {"outer", "inner", "left", "right", "exact", "override"}, default: "outer" String indicating how to combine differing indexes in objects. - "outer": use the union of object indexes - "inner": use the intersection of object indexes - "left": use indexes from the first object with each dimension - "right": use indexes from the last object with each dimension - - "exact" (default): instead of aligning, raise `ValueError` when indexes - to be aligned are not equal + - "exact": instead of aligning, raise `ValueError` when indexes to be + aligned are not equal - "override": if indexes are of same size, rewrite indexes to be those of the first object with that dimension. Indexes for the same dimension must have the same size in all objects. From d5a8debec4a8c1f6b4a5feeaf973f495dd9372e9 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 9 Jan 2026 16:30:34 -0500 Subject: [PATCH 9/9] Revert test changes --- xarray/tests/test_backends.py | 143 +++++++++++++++++++++++---------- xarray/tests/test_combine.py | 26 ++++-- xarray/tests/test_concat.py | 43 +++++++--- xarray/tests/test_dask.py | 11 +-- xarray/tests/test_dataarray.py | 12 ++- xarray/tests/test_merge.py | 30 ++++--- xarray/tests/test_units.py | 6 ++ 7 files changed, 191 insertions(+), 80 deletions(-) diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index cced85b46f2..ed86979c73e 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -5415,7 +5415,7 @@ class TestOpenMFDatasetWithDataVarsAndCoordsKw: var_name = "v1" @contextlib.contextmanager - def setup_files_and_datasets(self, *, fuzz=0): + def setup_files_and_datasets(self, *, fuzz=0, new_combine_kwargs: bool = False): ds1, ds2 = self.gen_datasets_with_common_coord_and_time() # to test join='exact' @@ -5427,7 +5427,8 @@ def setup_files_and_datasets(self, *, fuzz=0): ds1.to_netcdf(tmpfile1) ds2.to_netcdf(tmpfile2) - yield [tmpfile1, tmpfile2], [ds1, ds2] + with set_options(use_new_combine_kwarg_defaults=new_combine_kwargs): + yield [tmpfile1, tmpfile2], [ds1, ds2] def gen_datasets_with_common_coord_and_time(self): # create coordinate data @@ -5476,6 +5477,7 @@ def test_open_mfdataset_does_same_as_concat( ) assert_identical(ds, ds_expect) + @pytest.mark.parametrize("use_new_combine_kwarg_defaults", [True, False]) @pytest.mark.parametrize( ["combine_attrs", "attrs", "expected", "expect_error"], ( @@ -5504,12 +5506,13 @@ def test_open_mfdataset_does_same_as_concat( ) def test_open_mfdataset_dataset_combine_attrs( self, + use_new_combine_kwarg_defaults, combine_attrs, attrs, expected, expect_error, ): - with self.setup_files_and_datasets() as (files, _): + with self.setup_files_and_datasets() as (files, [_ds1, _ds2]): # Give the files an inconsistent attribute for i, f in enumerate(files): ds = open_dataset(f).load() @@ -5517,25 +5520,34 @@ def test_open_mfdataset_dataset_combine_attrs( ds.close() ds.to_netcdf(f) - error: contextlib.AbstractContextManager = ( - pytest.raises(xr.MergeError) - if expect_error - else contextlib.nullcontext() - ) - with error: - with xr.open_mfdataset( - files, - combine="nested", - concat_dim="t", - combine_attrs=combine_attrs, - ) as ds: - assert ds.attrs == expected + with set_options( + use_new_combine_kwarg_defaults=use_new_combine_kwarg_defaults + ): + warning: contextlib.AbstractContextManager = ( + pytest.warns(FutureWarning) + if not use_new_combine_kwarg_defaults + else contextlib.nullcontext() + ) + error: contextlib.AbstractContextManager = ( + pytest.raises(xr.MergeError) + if expect_error + else contextlib.nullcontext() + ) + with warning: + with error: + with xr.open_mfdataset( + files, + combine="nested", + concat_dim="t", + combine_attrs=combine_attrs, + ) as ds: + assert ds.attrs == expected def test_open_mfdataset_dataset_attr_by_coords(self) -> None: """ Case when an attribute differs across the multiple files """ - with self.setup_files_and_datasets() as (files, _): + with self.setup_files_and_datasets() as (files, [_ds1, _ds2]): # Give the files an inconsistent attribute for i, f in enumerate(files): ds = open_dataset(f).load() @@ -5543,14 +5555,18 @@ def test_open_mfdataset_dataset_attr_by_coords(self) -> None: ds.close() ds.to_netcdf(f) - with xr.open_mfdataset(files, combine="nested", concat_dim="t") as ds: - assert ds.test_dataset_attr == 10 + with set_options(use_new_combine_kwarg_defaults=True): + with xr.open_mfdataset(files, combine="nested", concat_dim="t") as ds: + assert ds.test_dataset_attr == 10 def test_open_mfdataset_dataarray_attr_by_coords(self) -> None: """ Case when an attribute of a member DataArray differs across the multiple files """ - with self.setup_files_and_datasets() as (files, _): + with self.setup_files_and_datasets(new_combine_kwargs=True) as ( + files, + [_ds1, _ds2], + ): # Give the files an inconsistent attribute for i, f in enumerate(files): ds = open_dataset(f).load() @@ -5588,7 +5604,10 @@ def test_open_mfdataset_dataarray_attr_by_coords(self) -> None: def test_open_mfdataset_exact_join_raises_error( self, combine, concat_dim, kwargs ) -> None: - with self.setup_files_and_datasets(fuzz=0.1) as (files, _): + with self.setup_files_and_datasets(fuzz=0.1, new_combine_kwargs=True) as ( + files, + _, + ): if combine == "by_coords": files.reverse() @@ -5606,7 +5625,10 @@ def test_open_mfdataset_exact_join_raises_error( def test_open_mfdataset_defaults_with_exact_join_warns_as_well_as_raising( self, ) -> None: - with self.setup_files_and_datasets(fuzz=0.1) as (files, _): + with self.setup_files_and_datasets(fuzz=0.1, new_combine_kwargs=True) as ( + files, + _, + ): files.reverse() with pytest.raises( ValueError, match="cannot align objects with join='exact'" @@ -5634,7 +5656,10 @@ def test_common_coord_when_datavars_all(self) -> None: def test_common_coord_when_datavars_minimal(self) -> None: opt: Final = "minimal" - with self.setup_files_and_datasets() as (files, [ds1, ds2]): + with self.setup_files_and_datasets(new_combine_kwargs=True) as ( + files, + [ds1, ds2], + ): # open the files using data_vars option with open_mfdataset( files, data_vars=opt, combine="nested", concat_dim="t" @@ -5669,7 +5694,10 @@ def test_invalid_data_vars_value_should_fail(self) -> None: def test_open_mfdataset_warns_when_kwargs_set_to_different( self, combine, concat_dim, kwargs ) -> None: - with self.setup_files_and_datasets() as (files, [ds1, ds2]): + with self.setup_files_and_datasets(new_combine_kwargs=True) as ( + files, + [ds1, ds2], + ): if combine == "by_coords": files.reverse() with pytest.raises( @@ -5681,6 +5709,31 @@ def test_open_mfdataset_warns_when_kwargs_set_to_different( ): xr.concat([ds1, ds2], dim="t", **kwargs) + with set_options(use_new_combine_kwarg_defaults=False): + expectation: contextlib.AbstractContextManager = ( + pytest.warns( + FutureWarning, + match="will change from data_vars='all'", + ) + if "data_vars" not in kwargs + else contextlib.nullcontext() + ) + + with pytest.warns( + FutureWarning, + match="will change from compat='equals'", + ): + with expectation: + ds_expect = xr.concat([ds1, ds2], dim="t", **kwargs) + with pytest.warns( + FutureWarning, match="will change from compat='no_conflicts'" + ): + with expectation: + with open_mfdataset( + files, combine=combine, concat_dim=concat_dim, **kwargs + ) as ds: + assert_identical(ds, ds_expect) + @requires_dask @requires_scipy @@ -6023,17 +6076,22 @@ def test_encoding_mfdataset_new_defaults(self) -> None: ds1.to_netcdf(tmp1) ds2.to_netcdf(tmp2) - with open_mfdataset( - [tmp1, tmp2], combine="nested", concat_dim="t" - ) as old: - assert old.t.encoding["units"] == original.t.encoding["units"] - assert old.t.encoding["units"] == ds1.t.encoding["units"] - assert old.t.encoding["units"] != ds2.t.encoding["units"] + for setting in [True, False]: + with set_options(use_new_combine_kwarg_defaults=setting): + with open_mfdataset( + [tmp1, tmp2], combine="nested", concat_dim="t" + ) as old: + assert ( + old.t.encoding["units"] == original.t.encoding["units"] + ) + assert old.t.encoding["units"] == ds1.t.encoding["units"] + assert old.t.encoding["units"] != ds2.t.encoding["units"] - with pytest.raises( - AlignmentError, match="If you are intending to concatenate" - ): - open_mfdataset([tmp1, tmp2], combine="nested") + with set_options(use_new_combine_kwarg_defaults=True): + with pytest.raises( + AlignmentError, match="If you are intending to concatenate" + ): + open_mfdataset([tmp1, tmp2], combine="nested") def test_preprocess_mfdataset(self) -> None: original = Dataset({"foo": ("x", np.random.randn(10))}) @@ -6122,13 +6180,16 @@ def test_open_and_do_math(self) -> None: [pytest.param({"concat_dim": None}, id="none"), pytest.param({}, id="default")], ) def test_open_mfdataset_concat_dim(self, kwargs) -> None: - with create_tmp_file() as tmp1: - with create_tmp_file() as tmp2: - data = Dataset({"x": 0}) - data.to_netcdf(tmp1) - Dataset({"x": np.nan}).to_netcdf(tmp2) - with open_mfdataset([tmp1, tmp2], **kwargs, combine="nested") as actual: - assert_identical(data, actual) + with set_options(use_new_combine_kwarg_defaults=True): + with create_tmp_file() as tmp1: + with create_tmp_file() as tmp2: + data = Dataset({"x": 0}) + data.to_netcdf(tmp1) + Dataset({"x": np.nan}).to_netcdf(tmp2) + with open_mfdataset( + [tmp1, tmp2], **kwargs, combine="nested" + ) as actual: + assert_identical(data, actual) def test_open_dataset(self) -> None: original = Dataset({"foo": ("x", np.random.randn(10))}) diff --git a/xarray/tests/test_combine.py b/xarray/tests/test_combine.py index cd8afb95f5e..2883187e096 100644 --- a/xarray/tests/test_combine.py +++ b/xarray/tests/test_combine.py @@ -477,6 +477,12 @@ def test_nested_merge_with_overlapping_values(self): ds1 = Dataset({"a": ("x", [1, 2]), "x": [0, 1]}) ds2 = Dataset({"a": ("x", [2, 3]), "x": [1, 2]}) expected = Dataset({"a": ("x", [1, 2, 3]), "x": [0, 1, 2]}) + with pytest.warns( + FutureWarning, + match="will change from compat='no_conflicts' to compat='override'", + ): + actual = combine_nested([ds1, ds2], join="outer", concat_dim=None) + assert_identical(expected, actual) actual = combine_nested( [ds1, ds2], join="outer", compat="no_conflicts", concat_dim=None ) @@ -491,6 +497,11 @@ def test_nested_merge_with_nan_no_conflicts(self): tmp2 = Dataset({"x": np.nan}) actual = combine_nested([tmp1, tmp2], compat="no_conflicts", concat_dim=None) assert_identical(tmp1, actual) + with pytest.warns( + FutureWarning, + match="will change from compat='no_conflicts' to compat='override'", + ): + combine_nested([tmp1, tmp2], concat_dim=None) actual = combine_nested([tmp1, tmp2], compat="no_conflicts", concat_dim=[None]) assert_identical(tmp1, actual) @@ -1044,7 +1055,8 @@ def test_infer_order_from_coords(self): expected = data assert expected.broadcast_equals(actual) # type: ignore[arg-type] - actual = combine_by_coords(objs) + with set_options(use_new_combine_kwarg_defaults=True): + actual = combine_by_coords(objs) assert_identical(actual, expected) def test_combine_leaving_bystander_dimensions(self): @@ -1252,11 +1264,11 @@ def test_nested_merge_with_overlapping_values(self): expected = Dataset({"a": ("x", [1, 2, 3]), "x": [0, 1, 2]}) with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( - FutureWarning, match="changed from join='outer' to join='exact'" + FutureWarning, match="will change from join='outer' to join='exact'" ): with pytest.warns( FutureWarning, - match="changed from compat='no_conflicts' to compat='override'", + match="will change from compat='no_conflicts' to compat='override'", ): old = combine_nested([ds1, ds2], concat_dim=None) with set_options(use_new_combine_kwarg_defaults=True): @@ -1271,7 +1283,7 @@ def test_nested_merge_with_nan_order_matters(self): with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="changed from compat='no_conflicts' to compat='override'", + match="will change from compat='no_conflicts' to compat='override'", ): old = combine_nested([ds1, ds2], concat_dim=None) with set_options(use_new_combine_kwarg_defaults=True): @@ -1283,7 +1295,7 @@ def test_nested_merge_with_nan_order_matters(self): with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="changed from compat='no_conflicts' to compat='override'", + match="will change from compat='no_conflicts' to compat='override'", ): old = combine_nested([ds2, ds1], concat_dim=None) with set_options(use_new_combine_kwarg_defaults=True): @@ -1319,7 +1331,7 @@ def test_combine_nested_missing_data_new_dim(self): ) with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( - FutureWarning, match="changed from join='outer' to join='exact'" + FutureWarning, match="will change from join='outer' to join='exact'" ): old = combine_nested(datasets, concat_dim="t") with set_options(use_new_combine_kwarg_defaults=True): @@ -1336,7 +1348,7 @@ def test_combine_by_coords_multiple_variables(self): with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( - FutureWarning, match="changed from join='outer' to join='exact'" + FutureWarning, match="will change from join='outer' to join='exact'" ): old = combine_by_coords(objs) with set_options(use_new_combine_kwarg_defaults=True): diff --git a/xarray/tests/test_concat.py b/xarray/tests/test_concat.py index 111e81cbd3d..bc98d72d50c 100644 --- a/xarray/tests/test_concat.py +++ b/xarray/tests/test_concat.py @@ -649,10 +649,15 @@ def test_concat_constant_index_minimal(self) -> None: ds1 = Dataset({"foo": 1.5}, {"y": 1}) ds2 = Dataset({"foo": 2.5}, {"y": 1}) - with pytest.raises( - ValueError, match="data_vars='minimal' and coords='minimal'" - ): - concat([ds1, ds2], dim="new_dim", data_vars="minimal") + with set_options(use_new_combine_kwarg_defaults=False): + with pytest.raises(merge.MergeError, match="conflicting values"): + concat([ds1, ds2], dim="new_dim", data_vars="minimal") + + with set_options(use_new_combine_kwarg_defaults=True): + with pytest.raises( + ValueError, match="data_vars='minimal' and coords='minimal'" + ): + concat([ds1, ds2], dim="new_dim", data_vars="minimal") def test_concat_size0(self) -> None: data = create_test_data() @@ -974,8 +979,12 @@ def test_concat_do_not_promote(self) -> None: Dataset({"y": ("t", [1])}, {"x": 1, "t": [0]}), Dataset({"y": ("t", [2])}, {"x": 2, "t": [0]}), ] - with pytest.raises(ValueError): - concat(objs, "t", compat="equals") + with set_options(use_new_combine_kwarg_defaults=False): + with pytest.raises(ValueError): + concat(objs, "t", coords="minimal") + with set_options(use_new_combine_kwarg_defaults=True): + with pytest.raises(ValueError): + concat(objs, "t", compat="equals") def test_concat_dim_is_variable(self) -> None: objs = [Dataset({"x": 0}), Dataset({"x": 1})] @@ -1493,7 +1502,7 @@ def test_concat_second_empty_with_scalar_data_var_only_on_first(self) -> None: with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="changed from compat='equals' to compat='override'", + match="will change from compat='equals' to compat='override'", ): actual = concat( [ds1, ds2], dim="y", coords="different", data_vars="different" @@ -1540,7 +1549,7 @@ def test_concat_coords_kwarg( expectation: AbstractContextManager = ( pytest.warns( FutureWarning, - match="changed from compat='equals' to compat='override'", + match="will change from compat='equals' to compat='override'", ) if coords == "different" else nullcontext() @@ -1565,7 +1574,7 @@ def test_concat_promote_shape_for_scalars_with_mixed_lengths_along_concat_dim( with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="changed from coords='different' to coords='minimal'", + match="will change from coords='different' to coords='minimal'", ): old = concat(objs, "x") assert_identical(old, expected) @@ -1653,11 +1662,23 @@ def test_concat_datatree_along_existing_dim_defaults(self): dt1 = DataTree.from_dict(data={"/a": ("x", [1]), "/b": 3}, coords={"/x": [0]}) dt2 = DataTree.from_dict(data={"/a": ("x", [2]), "/b": 3}, coords={"/x": [1]}) expected = DataTree.from_dict( - data={"/a": ("x", [1, 2]), "/b": 3}, coords={"/x": [0, 1]} + data={"/a": ("x", [1, 2]), "/b": ("x", [3, 3])}, coords={"/x": [0, 1]} ) - actual = concat([dt1, dt2], dim="x") + with pytest.warns( + FutureWarning, match="will change from data_vars='all' to data_vars=None" + ): + actual = concat([dt1, dt2], dim="x") + assert actual.identical(expected) + with set_options(use_new_combine_kwarg_defaults=True): + expected = DataTree.from_dict( + data={"/a": ("x", [1, 2]), "/b": 3}, coords={"/x": [0, 1]} + ) + actual = concat([dt1, dt2], dim="x") + + assert actual.identical(expected) + def test_concat_datatree_isomorphic_error(self): dt1 = DataTree.from_dict(data={"/data": ("x", [1]), "/a": None}) dt2 = DataTree.from_dict(data={"/data": ("x", [2]), "/b": None}) diff --git a/xarray/tests/test_dask.py b/xarray/tests/test_dask.py index f19caf7238b..f00e945d0fe 100644 --- a/xarray/tests/test_dask.py +++ b/xarray/tests/test_dask.py @@ -471,11 +471,12 @@ def test_concat_loads_variables(self): assert isinstance(out["d"].data, dask.array.Array) assert isinstance(out["c"].data, dask.array.Array) - out = xr.concat([ds1, ds2, ds3], dim="n", data_vars=[], coords=[]) - # no extra kernel calls - assert kernel_call_count == 6 - assert isinstance(out["d"].data, dask.array.Array) - assert isinstance(out["c"].data, dask.array.Array) + with xr.set_options(use_new_combine_kwarg_defaults=True): + out = xr.concat([ds1, ds2, ds3], dim="n", data_vars=[], coords=[]) + # no extra kernel calls + assert kernel_call_count == 6 + assert isinstance(out["d"].data, dask.array.Array) + assert isinstance(out["c"].data, dask.array.Array) out = xr.concat( [ds1, ds2, ds3], dim="n", data_vars=[], coords=[], compat="equals" diff --git a/xarray/tests/test_dataarray.py b/xarray/tests/test_dataarray.py index 0ed582197a7..d1aacba6aaa 100644 --- a/xarray/tests/test_dataarray.py +++ b/xarray/tests/test_dataarray.py @@ -1458,13 +1458,17 @@ def test_selection_multiindex_from_level(self) -> None: expected = data.isel(xy=[0, 1]).unstack("xy").squeeze("y") assert_equal(actual, expected) - def test_concat_with_default_coords(self) -> None: + def test_concat_with_default_coords_warns(self) -> None: da = DataArray([0, 1], dims=["x"], coords={"x": [0, 1], "y": "a"}) db = DataArray([2, 3], dims=["x"], coords={"x": [0, 1], "y": "b"}) - # default compat="override" will pick the first one - new = xr.concat([da, db], dim="x") - assert new.y.size == 1 + with pytest.warns(FutureWarning): + original = xr.concat([da, db], dim="x") + assert original.y.size == 4 + with set_options(use_new_combine_kwarg_defaults=True): + # default compat="override" will pick the first one + new = xr.concat([da, db], dim="x") + assert new.y.size == 1 def test_virtual_default_coords(self) -> None: array = DataArray(np.zeros((5,)), dims="x") diff --git a/xarray/tests/test_merge.py b/xarray/tests/test_merge.py index 6e68a570139..de24b378539 100644 --- a/xarray/tests/test_merge.py +++ b/xarray/tests/test_merge.py @@ -42,15 +42,17 @@ def test_merge_arrays(self): expected = data[["var1", "var2"]] assert_identical(actual, expected) - def test_merge_datasets(self): - data = create_test_data(add_attrs=False, use_extension_array=True) + @pytest.mark.parametrize("use_new_combine_kwarg_defaults", [True, False]) + def test_merge_datasets(self, use_new_combine_kwarg_defaults): + with set_options(use_new_combine_kwarg_defaults=use_new_combine_kwarg_defaults): + data = create_test_data(add_attrs=False, use_extension_array=True) - actual = xr.merge([data[["var1"]], data[["var2"]]]) - expected = data[["var1", "var2"]] - assert_identical(actual, expected) + actual = xr.merge([data[["var1"]], data[["var2"]]]) + expected = data[["var1", "var2"]] + assert_identical(actual, expected) - actual = xr.merge([data, data]) - assert_identical(actual, data) + actual = xr.merge([data, data]) + assert_identical(actual, data) def test_merge_dataarray_unnamed(self): data = xr.DataArray([1, 2], dims="x") @@ -199,7 +201,11 @@ def test_merge_arrays_attrs_variables( if expect_exception: with pytest.raises(MergeError, match="combine_attrs"): - actual = xr.merge([data1, data2], combine_attrs=combine_attrs) + with pytest.warns( + FutureWarning, + match="will change from compat='no_conflicts' to compat='override'", + ): + actual = xr.merge([data1, data2], combine_attrs=combine_attrs) else: actual = xr.merge( [data1, data2], compat="no_conflicts", combine_attrs=combine_attrs @@ -832,7 +838,7 @@ def test_merge_broadcast_equals(self): with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( FutureWarning, - match="changed from compat='no_conflicts' to compat='override'", + match="will change from compat='no_conflicts' to compat='override'", ): old = ds1.merge(ds2) @@ -851,11 +857,11 @@ def test_merge_auto_align(self): ) with set_options(use_new_combine_kwarg_defaults=False): with pytest.warns( - FutureWarning, match="changed from join='outer' to join='exact'" + FutureWarning, match="will change from join='outer' to join='exact'" ): assert expected.identical(ds1.merge(ds2)) with pytest.warns( - FutureWarning, match="changed from join='outer' to join='exact'" + FutureWarning, match="will change from join='outer' to join='exact'" ): assert expected.identical(ds2.merge(ds1)) @@ -942,7 +948,7 @@ def test_merge_error_includes_path(self) -> None: "Raised whilst mapping function over node(s) with path 'a'" ), ): - xr.merge([tree1, tree2], compat="no_conflicts") + xr.merge([tree1, tree2], join="exact", compat="no_conflicts") def test_fill_value_errors(self) -> None: trees = [xr.DataTree(), xr.DataTree()] diff --git a/xarray/tests/test_units.py b/xarray/tests/test_units.py index a3bfa521f66..42bf3649202 100644 --- a/xarray/tests/test_units.py +++ b/xarray/tests/test_units.py @@ -745,6 +745,9 @@ def test_broadcast_dataset(dtype): "coords", ), ) +@pytest.mark.filterwarnings( + "ignore:.*the default value for coords will change:FutureWarning" +) def test_combine_by_coords(variant, unit, error, dtype): original_unit = unit_registry.m @@ -5587,6 +5590,9 @@ def test_content_manipulation(self, func, variant, dtype): "coords", ), ) + @pytest.mark.filterwarnings( + "ignore:.*the default value for compat will change:FutureWarning" + ) def test_merge(self, variant, unit, error, dtype): left_variants = { "data": (unit_registry.m, 1, 1),