Skip to content

Commit 44d5fd6

Browse files
jsignellkeewisdcherian
authored
Fix ds.merge to prevent altering original object depending on join value (#10596)
Co-authored-by: Justus Magin <[email protected]> Co-authored-by: Deepak Cherian <[email protected]>
1 parent 9b1d570 commit 44d5fd6

File tree

4 files changed

+15
-11
lines changed

4 files changed

+15
-11
lines changed

doc/whats-new.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ Bug fixes
6161
By `Deepak Cherian <https://github.com/dcherian>`_.
6262
- Fix detection of the ``h5netcdf`` backend. Xarray now selects ``h5netcdf`` if the default ``netCDF4`` engine is not available (:issue:`10401`, :pull:`10557`).
6363
By `Scott Staniewicz <https://github.com/scottstanie>`_.
64+
- Fix :py:func:`merge` to prevent altering original object depending on join value (:pull:`10596`)
65+
By `Julia Signell <https://github.com/jsignell>`_.
6466
- Ensure ``unlimited_dims`` passed to :py:meth:`xarray.DataArray.to_netcdf`, :py:meth:`xarray.Dataset.to_netcdf` or :py:meth:`xarray.DataTree.to_netcdf` only contains dimensions present in the object; raise ``ValueError`` otherwise (:issue:`10549`, :pull:`10608`).
6567
By `Kai Mühlbauer <https://github.com/kmuehlbauer>`_.
6668

xarray/core/variable.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,6 @@ def _replace(
971971
data = copy.copy(self.data)
972972
if attrs is _default:
973973
attrs = copy.copy(self._attrs)
974-
975974
if encoding is _default:
976975
encoding = copy.copy(self._encoding)
977976
return type(self)(dims, data, attrs, encoding, fastpath=True)

xarray/structure/merge.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ def merge_collected(
276276
if index is not None:
277277
merged_indexes[name] = index
278278
else:
279+
attrs: dict[Any, Any] = {}
279280
indexed_elements = [
280281
(variable, index)
281282
for variable, index in elements_list
@@ -306,13 +307,7 @@ def merge_collected(
306307
[var.attrs for var, _ in indexed_elements],
307308
combine_attrs=combine_attrs,
308309
)
309-
if variable.attrs or attrs:
310-
# Make a shallow copy to so that assigning merged_vars[name].attrs
311-
# does not affect the original input variable.
312-
merged_vars[name] = variable.copy(deep=False)
313-
merged_vars[name].attrs = attrs
314-
else:
315-
merged_vars[name] = variable
310+
merged_vars[name] = variable
316311
merged_indexes[name] = index
317312
else:
318313
variables = [variable for variable, _ in elements_list]
@@ -342,10 +337,15 @@ def merge_collected(
342337
raise
343338

344339
if name in merged_vars:
345-
merged_vars[name].attrs = merge_attrs(
340+
attrs = merge_attrs(
346341
[var.attrs for var in variables], combine_attrs=combine_attrs
347342
)
348343

344+
if name in merged_vars and (merged_vars[name].attrs or attrs):
345+
# Ensure that assigning attrs does not affect the original input variable.
346+
merged_vars[name] = merged_vars[name].copy(deep=False)
347+
merged_vars[name].attrs = attrs
348+
349349
return merged_vars, merged_indexes
350350

351351

xarray/tests/test_merge.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,16 @@ def test_merge(self):
365365
with pytest.raises(ValueError, match=r"should be coordinates or not"):
366366
data.merge(data.reset_coords())
367367

368-
def test_merge_drop_attrs(self):
368+
@pytest.mark.parametrize(
369+
"join", ["outer", "inner", "left", "right", "exact", "override"]
370+
)
371+
def test_merge_drop_attrs(self, join):
369372
data = create_test_data()
370373
ds1 = data[["var1"]]
371374
ds2 = data[["var3"]]
372375
ds1.coords["dim2"].attrs["keep me"] = "example"
373376
ds2.coords["numbers"].attrs["foo"] = "bar"
374-
actual = ds1.merge(ds2, combine_attrs="drop")
377+
actual = ds1.merge(ds2, combine_attrs="drop", join=join)
375378
assert actual.coords["dim2"].attrs == {}
376379
assert actual.coords["numbers"].attrs == {}
377380
assert ds1.coords["dim2"].attrs["keep me"] == "example"

0 commit comments

Comments
 (0)