Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ Bug Fixes
for zarr writes. Existing zarr stores written with the old ``int8`` encoding
are still read correctly. (:issue:`2937`, :pull:`11318`)
By `Evan Lyall <https://github.com/elyall>`_.
- Raise an informative ``TypeError`` when a :py:class:`~xarray.Coordinates` object is
passed as a coordinate value, e.g. ``ds.assign_coords({"x": coords})``, instead
of silently creating a broken coordinate. Pass the object directly with
``ds.assign_coords(coords)`` (:issue:`10194`).
By `NoiceHex <https://github.com/NoiceHax>`_.


Documentation
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ Claus = "Claus"
Celles = "Celles"
slowy = "slowy"
Commun = "Commun"
Noice = "Noice"

# Tests
Ome = "Ome"
Expand Down
7 changes: 7 additions & 0 deletions xarray/core/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def as_variable(
The newly created variable.

"""
from xarray.core.coordinates import Coordinates
from xarray.core.dataarray import DataArray

# TODO: consider extending this method to automatically handle Iris and
Expand Down Expand Up @@ -160,6 +161,12 @@ def as_variable(
obj = Variable([], obj)
elif isinstance(obj, pd.Index | IndexVariable) and obj.name is not None:
obj = Variable(obj.name, obj)
elif isinstance(obj, Coordinates):
raise TypeError(
f"Variable {name!r}: Using a Coordinates object to construct a variable is "
"ambiguous, please pass the Coordinates object directly instead, e.g., "
"`obj.assign_coords(coords)` instead of `obj.assign_coords({name: coords})`."
)
elif isinstance(obj, set | dict):
raise TypeError(f"variable {name!r} has invalid type {type(obj)!r}")
elif name is not None:
Expand Down
20 changes: 20 additions & 0 deletions xarray/tests/test_coordinates.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,26 @@ def test_rename_vars(self) -> None:
assert set(actual.dims) == {"x", "y"}
assert set(actual.variables) == {"a", "u", "v"}

def test_coordinates_as_coord_value(self) -> None:
# a Coordinates object must be passed directly, not as a value of a
# mapping of coordinate names (GH10194)
ds = Dataset({"foo": ("x", [1, 2, 3])})
coords = Coordinates({"x": [4, 5, 6]})

with pytest.raises(TypeError, match=r"Using a Coordinates object"):
ds.assign_coords({"x": coords})
with pytest.raises(TypeError, match=r"Using a Coordinates object"):
ds.foo.assign_coords({"x": coords})
with pytest.raises(TypeError, match=r"Using a Coordinates object"):
Dataset({"foo": ("x", [1, 2, 3])}, coords={"x": coords})
with pytest.raises(TypeError, match=r"Using a Coordinates object"):
DataArray([1, 2, 3], dims="x", coords={"x": coords})

# passing it directly still works and keeps the index
actual = ds.assign_coords(coords)
assert_identical(actual, Dataset({"foo": ("x", [1, 2, 3])}, coords=coords))
assert "x" in actual.xindexes

def test_operator_merge(self) -> None:
coords1 = Coordinates({"x": ("x", [0, 1, 2])})
coords2 = Coordinates({"y": ("y", [3, 4, 5])})
Expand Down
6 changes: 5 additions & 1 deletion xarray/tests/test_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -5021,7 +5021,11 @@ def test_drop_attrs(self) -> None:
mx = xr.Coordinates.from_pandas_multiindex(
pd.MultiIndex.from_tuples([(1, 2), (3, 4)], names=["d", "e"]), "z"
)
ds = Dataset(dict(var1=var), coords=dict(y=idx, z=mx)).assign_attrs(a=1, b=2)
ds = (
Dataset(dict(var1=var), coords=dict(y=idx))
.assign_coords(mx)
.assign_attrs(a=1, b=2)
)
assert ds.attrs != {}
assert ds["var1"].attrs != {}
assert ds["y"].attrs != {}
Expand Down
13 changes: 12 additions & 1 deletion xarray/tests/test_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@
import pytest
import pytz

from xarray import DataArray, Dataset, IndexVariable, Variable, set_options
from xarray import (
Coordinates,
DataArray,
Dataset,
IndexVariable,
Variable,
set_options,
)
from xarray.core import dtypes, duck_array_ops, indexing
from xarray.core.common import full_like, ones_like, zeros_like
from xarray.core.extension_array import PandasExtensionArray
Expand Down Expand Up @@ -1273,6 +1280,10 @@ def test_as_variable(self):
with pytest.raises(TypeError):
as_variable(("x", DataArray([])))

# GH10194
with pytest.raises(TypeError, match=r"Using a Coordinates object"):
as_variable(Coordinates({"x": [1, 2, 3]}), name="x")

def test_repr(self):
v = Variable(["time", "x"], [[1, 2, 3], [4, 5, 6]], {"foo": "bar"})
v = v.astype(np.uint64)
Expand Down
Loading