Skip to content
Open
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 @@ -29,12 +29,17 @@ Bug Fixes
- Ensure that ``keep_attrs='drop'`` and ``keep_attrs=False`` remove attrs from result, even when there is
only one xarray object given to ``apply_ufunc`` (:issue:`10982` :pull:`10997`).
By `Julia Signell <https://github.com/jsignell>`_.
- Forbid slashes in ``DataTree`` coordinate names (:issue:`#9485` :pull:`11015`).
By `Ewan Short <https://github.com/eshort0401>`_.

Documentation
~~~~~~~~~~~~~

- Better description of ``keep_attrs`` option on ``xarray.where`` docstring (:issue:`10982` :pull:`10997`).
By `Julia Signell <https://github.com/jsignell>`_.
- Clarify ``DataTree.coords`` and ``DataTree.data_vars`` docstrings to indicate
they refer to the coordinates and variables of that node only (:issue:`9485` :pull:`11015`).
By `Ewan Short <https://github.com/eshort0401>`_.

Internal Changes
~~~~~~~~~~~~~~~~
Expand Down
18 changes: 18 additions & 0 deletions xarray/core/coordinates.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,11 @@ def _update_coords(
) -> None:
from xarray.core.datatree import check_alignment

# For now, ensure coordinate keys do not contain '/' character. See
# https://github.com/pydata/xarray/issues/9485
# https://github.com/pydata/xarray/pull/9492
_validate_coordinate_names(coords.keys())

# create updated node (`.to_dataset` makes a copy so this doesn't modify in-place)
node_ds = self._data.to_dataset(inherit=False)
node_ds.coords._update_coords(coords, indexes)
Expand Down Expand Up @@ -1060,6 +1065,19 @@ def _ipython_key_completions_(self):
]


def _validate_coordinate_names(coordinates: Iterable[Hashable]) -> None:
offending_coordinate_names = [
name for name in coordinates if isinstance(name, str) and "/" in name
]
if len(offending_coordinate_names) > 0:
raise ValueError(
"Given coordinate names contain the '/' character: "
f"{offending_coordinate_names}. Accessing the coordinates of other nodes "
"in the tree is not yet supported here. Retrieve the other node first, "
"then access its coordinates."
)


class DataArrayCoordinates(Coordinates, Generic[T_DataArray]):
"""Dictionary like container for DataArray coordinates (variables + indexes).

Expand Down
5 changes: 3 additions & 2 deletions xarray/core/datatree.py
Original file line number Diff line number Diff line change
Expand Up @@ -1467,13 +1467,14 @@ def xindexes(self) -> Indexes[Index]:
@property
def coords(self) -> DataTreeCoordinates:
"""Dictionary of xarray.DataArray objects corresponding to coordinate
variables
variables at this node.
"""
return DataTreeCoordinates(self)

@property
def data_vars(self) -> DataVariables:
"""Dictionary of DataArray objects corresponding to data variables"""
"""Dictionary of DataArray objects corresponding to data variables at this
node."""
return DataVariables(self.to_dataset())

def isomorphic(self, other: DataTree) -> bool:
Expand Down
17 changes: 17 additions & 0 deletions xarray/tests/test_datatree.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,23 @@ def test_inherited(self) -> None:
# expected = child.assign_coords({"c": 11})
# assert_identical(expected, actual)

def test_forbid_access_other_node_coords(self) -> None:
ds = Dataset(coords={"x": 0})
tree = DataTree(ds, children={"child": DataTree()})
expected = DataTree(ds, children={"child": DataTree(Dataset(coords={"y": 2}))})
with pytest.raises(
ValueError,
match=re.escape(
"Given coordinate names contain the '/' character: ['/child/y']. "
"Accessing the coordinates of other nodes in the tree is not yet "
"supported here. Retrieve the other node first, then access its "
"coordinates."
),
):
tree.coords["/child/y"] = 2
tree["child"].coords["y"] = DataArray(2)
assert_equal(tree, expected)


def test_delitem() -> None:
ds = Dataset({"a": 0}, coords={"x": ("x", [1, 2]), "z": "a"})
Expand Down
Loading