Skip to content

Commit 07b1756

Browse files
authored
Slightly improve DataTree repr (#9064)
* Improve DataTree repr * Adjust DataTree repr to include full path * More tweaks * Use "Group:" in repr instead of "DataTree:" * Fix errors in new repr tests * Fix repr on windows
1 parent b518074 commit 07b1756

File tree

6 files changed

+94
-37
lines changed

6 files changed

+94
-37
lines changed

xarray/core/datatree.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,11 +1314,12 @@ def match(self, pattern: str) -> DataTree:
13141314
... }
13151315
... )
13161316
>>> dt.match("*/B")
1317-
DataTree('None', parent=None)
1318-
├── DataTree('a')
1319-
│ └── DataTree('B')
1320-
└── DataTree('b')
1321-
└── DataTree('B')
1317+
<xarray.DataTree>
1318+
Group: /
1319+
├── Group: /a
1320+
│ └── Group: /a/B
1321+
└── Group: /b
1322+
└── Group: /b/B
13221323
"""
13231324
matching_nodes = {
13241325
node.path: node.ds

xarray/core/datatree_render.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,12 @@ def __init__(self):
5757
>>> s0a = DataTree(name="sub0A", parent=s0)
5858
>>> s1 = DataTree(name="sub1", parent=root)
5959
>>> print(RenderDataTree(root))
60-
DataTree('root', parent=None)
61-
├── DataTree('sub0')
62-
│ ├── DataTree('sub0B')
63-
│ └── DataTree('sub0A')
64-
└── DataTree('sub1')
60+
<xarray.DataTree 'root'>
61+
Group: /
62+
├── Group: /sub0
63+
│ ├── Group: /sub0/sub0B
64+
│ └── Group: /sub0/sub0A
65+
└── Group: /sub1
6566
"""
6667
super().__init__("\u2502 ", "\u251c\u2500\u2500 ", "\u2514\u2500\u2500 ")
6768

xarray/core/formatting.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,20 +1023,21 @@ def diff_datatree_repr(a: DataTree, b: DataTree, compat):
10231023

10241024
def _single_node_repr(node: DataTree) -> str:
10251025
"""Information about this node, not including its relationships to other nodes."""
1026-
node_info = f"DataTree('{node.name}')"
1027-
10281026
if node.has_data or node.has_attrs:
10291027
ds_info = "\n" + repr(node.ds)
10301028
else:
10311029
ds_info = ""
1032-
return node_info + ds_info
1030+
return f"Group: {node.path}{ds_info}"
10331031

10341032

10351033
def datatree_repr(dt: DataTree):
10361034
"""A printable representation of the structure of this entire tree."""
10371035
renderer = RenderDataTree(dt)
10381036

1039-
lines = []
1037+
name_info = "" if dt.name is None else f" {dt.name!r}"
1038+
header = f"<xarray.DataTree{name_info}>"
1039+
1040+
lines = [header]
10401041
for pre, fill, node in renderer:
10411042
node_repr = _single_node_repr(node)
10421043

@@ -1051,12 +1052,6 @@ def datatree_repr(dt: DataTree):
10511052
else:
10521053
lines.append(f"{fill}{' ' * len(renderer.style.vertical)}{line}")
10531054

1054-
# Tack on info about whether or not root node has a parent at the start
1055-
first_line = lines[0]
1056-
parent = f'"{dt.parent.name}"' if dt.parent is not None else "None"
1057-
first_line_with_parent = first_line[:-1] + f", parent={parent})"
1058-
lines[0] = first_line_with_parent
1059-
10601055
return "\n".join(lines)
10611056

10621057

xarray/core/iterators.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,16 @@ class LevelOrderIter(Iterator):
3939
>>> i = DataTree(name="i", parent=g)
4040
>>> h = DataTree(name="h", parent=i)
4141
>>> print(f)
42-
DataTree('f', parent=None)
43-
├── DataTree('b')
44-
│ ├── DataTree('a')
45-
│ └── DataTree('d')
46-
│ ├── DataTree('c')
47-
│ └── DataTree('e')
48-
└── DataTree('g')
49-
└── DataTree('i')
50-
└── DataTree('h')
42+
<xarray.DataTree 'f'>
43+
Group: /
44+
├── Group: /b
45+
│ ├── Group: /b/a
46+
│ └── Group: /b/d
47+
│ ├── Group: /b/d/c
48+
│ └── Group: /b/d/e
49+
└── Group: /g
50+
└── Group: /g/i
51+
└── Group: /g/i/h
5152
>>> [node.name for node in LevelOrderIter(f)]
5253
['f', 'b', 'g', 'a', 'd', 'i', 'c', 'e', 'h']
5354
>>> [node.name for node in LevelOrderIter(f, maxlevel=3)]

xarray/tests/test_datatree.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,63 @@ def test_operation_with_attrs_but_no_data(self):
623623
dt.sel(dim_0=0)
624624

625625

626+
class TestRepr:
627+
def test_repr(self):
628+
dt: DataTree = DataTree.from_dict(
629+
{
630+
"/": xr.Dataset(
631+
{"e": (("x",), [1.0, 2.0])},
632+
coords={"x": [2.0, 3.0]},
633+
),
634+
"/b": xr.Dataset({"f": (("y",), [3.0])}),
635+
"/b/c": xr.Dataset(),
636+
"/b/d": xr.Dataset({"g": 4.0}),
637+
}
638+
)
639+
640+
result = repr(dt)
641+
expected = dedent(
642+
"""
643+
<xarray.DataTree>
644+
Group: /
645+
│ Dimensions: (x: 2)
646+
│ Coordinates:
647+
│ * x (x) float64 16B 2.0 3.0
648+
│ Data variables:
649+
│ e (x) float64 16B 1.0 2.0
650+
└── Group: /b
651+
│ Dimensions: (y: 1)
652+
│ Dimensions without coordinates: y
653+
│ Data variables:
654+
│ f (y) float64 8B 3.0
655+
├── Group: /b/c
656+
└── Group: /b/d
657+
Dimensions: ()
658+
Data variables:
659+
g float64 8B 4.0
660+
"""
661+
).strip()
662+
assert result == expected
663+
664+
result = repr(dt.b)
665+
expected = dedent(
666+
"""
667+
<xarray.DataTree 'b'>
668+
Group: /b
669+
│ Dimensions: (y: 1)
670+
│ Dimensions without coordinates: y
671+
│ Data variables:
672+
│ f (y) float64 8B 3.0
673+
├── Group: /b/c
674+
└── Group: /b/d
675+
Dimensions: ()
676+
Data variables:
677+
g float64 8B 4.0
678+
"""
679+
).strip()
680+
assert result == expected
681+
682+
626683
class TestRestructuring:
627684
def test_drop_nodes(self):
628685
sue = DataTree.from_dict({"Mary": None, "Kate": None, "Ashley": None})

xarray/tests/test_formatting.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -555,16 +555,17 @@ def test_array_scalar_format(self) -> None:
555555

556556
def test_datatree_print_empty_node(self):
557557
dt: DataTree = DataTree(name="root")
558-
printout = dt.__str__()
559-
assert printout == "DataTree('root', parent=None)"
558+
printout = str(dt)
559+
assert printout == "<xarray.DataTree 'root'>\nGroup: /"
560560

561561
def test_datatree_print_empty_node_with_attrs(self):
562562
dat = xr.Dataset(attrs={"note": "has attrs"})
563563
dt: DataTree = DataTree(name="root", data=dat)
564-
printout = dt.__str__()
564+
printout = str(dt)
565565
assert printout == dedent(
566566
"""\
567-
DataTree('root', parent=None)
567+
<xarray.DataTree 'root'>
568+
Group: /
568569
Dimensions: ()
569570
Data variables:
570571
*empty*
@@ -575,9 +576,10 @@ def test_datatree_print_empty_node_with_attrs(self):
575576
def test_datatree_print_node_with_data(self):
576577
dat = xr.Dataset({"a": [0, 2]})
577578
dt: DataTree = DataTree(name="root", data=dat)
578-
printout = dt.__str__()
579+
printout = str(dt)
579580
expected = [
580-
"DataTree('root', parent=None)",
581+
"<xarray.DataTree 'root'>",
582+
"Group: /",
581583
"Dimensions",
582584
"Coordinates",
583585
"a",
@@ -591,8 +593,8 @@ def test_datatree_printout_nested_node(self):
591593
dat = xr.Dataset({"a": [0, 2]})
592594
root: DataTree = DataTree(name="root")
593595
DataTree(name="results", data=dat, parent=root)
594-
printout = root.__str__()
595-
assert printout.splitlines()[2].startswith(" ")
596+
printout = str(root)
597+
assert printout.splitlines()[3].startswith(" ")
596598

597599
def test_datatree_repr_of_node_with_data(self):
598600
dat = xr.Dataset({"a": [0, 2]})

0 commit comments

Comments
 (0)