Skip to content

Commit ae07a38

Browse files
authored
fix: better RNTuple support (#211)
* fix: better RNTuple support Signed-off-by: Henry Schreiner <[email protected]> * fix: bump minimum versions Signed-off-by: Henry Schreiner <[email protected]> --------- Signed-off-by: Henry Schreiner <[email protected]>
1 parent f163bdf commit ae07a38

File tree

6 files changed

+82
-28
lines changed

6 files changed

+82
-28
lines changed

β€Žnoxfile.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
nox.options.default_venv_backend = "uv|virtualenv"
77

88

9-
def dep_group(group: str) -> list[str]:
10-
return nox.project.load_toml("pyproject.toml")["dependency-groups"][group] # type: ignore[no-any-return]
11-
12-
139
@nox.session(reuse_venv=True)
1410
def lint(session: nox.Session) -> None:
1511
"""
@@ -34,7 +30,7 @@ def tests(session: nox.Session) -> None:
3430
"""
3531
Run the unit and regular tests.
3632
"""
37-
session.install("-e.", *dep_group("test"))
33+
session.install("-e.", "--group=test")
3834
session.run("pytest", *session.posargs)
3935

4036

@@ -44,7 +40,7 @@ def minimums(session: nox.Session) -> None:
4440
Run the unit and regular tests.
4541
"""
4642
session.install(
47-
"-e.", *dep_group("test"), "--resolution=lowest-direct", "--only-binary=:all:"
43+
"-e.", "--group=test", "--resolution=lowest-direct", "--only-binary=:all:"
4844
)
4945
session.run("pytest", *session.posargs)
5046

β€Žpyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ dependencies = [
4545
'plotext >=5.2.8',
4646
'rich >=13.3.3',
4747
'textual >=0.86.0',
48-
'uproot >=5',
48+
'uproot >=5.6.1',
4949
]
5050

5151
[project.optional-dependencies]
@@ -55,7 +55,7 @@ iterm = [
5555
"mplhep",
5656
]
5757
testdata = [
58-
"scikit-hep-testdata >=0.4.10",
58+
"scikit-hep-testdata >=0.5.0",
5959
]
6060

6161
[project.urls]
@@ -69,7 +69,7 @@ uproot-browser = "uproot_browser.__main__:main"
6969
test = [
7070
"pytest >=8",
7171
"pytest-asyncio >=0.24",
72-
"scikit-hep-testdata >=0.4.10",
72+
"scikit-hep-testdata >=0.5.0",
7373
]
7474
dev = [
7575
"ipython >=6",

β€Žsrc/uproot_browser/plot.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ def plot_branch(tree: uproot.TBranch | uproot.models.RNTuple.RField) -> None:
7070
plt.title(make_hist_title(tree, histogram))
7171

7272

73-
if hasattr(uproot.models, "RNTuple") and hasattr(uproot.models.RNTuple, "RField"):
74-
plot.register(uproot.models.RNTuple.RField)(plot_branch) # type: ignore[no-untyped-call]
73+
plot.register(uproot.models.RNTuple.RField)(plot_branch) # type: ignore[no-untyped-call]
7574

7675

7776
@plot.register

β€Žsrc/uproot_browser/tree.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,9 @@ def _(item: uproot.behaviors.TBranch.HasBranches) -> bool:
5757
return len(item.branches) > 0
5858

5959

60-
if hasattr(uproot.behaviors, "RNTuple") and hasattr(
61-
uproot.behaviors.RNTuple, "HasFields"
62-
):
63-
64-
@is_dir.register
65-
def _(item: uproot.behaviors.RNTuple.HasFields) -> bool:
66-
return len(item.keys()) > 0
60+
@is_dir.register
61+
def _(item: uproot.behaviors.RNTuple.HasFields) -> bool:
62+
return len(item.keys()) > 0
6763

6864

6965
def get_children(item: Mapping[str, Any]) -> set[str]:
@@ -161,11 +157,8 @@ def _process_item_tfile(
161157
)
162158

163159

164-
# Python 3.11 can just use `|` directly for register
165-
@process_item.register(uproot.TTree)
166-
def _process_item_ttree(
167-
uproot_object: uproot.TTree | uproot.behaviors.RNTuple.RNTuple,
168-
) -> MetaDict:
160+
@process_item.register
161+
def _process_item_ttree(uproot_object: uproot.TTree) -> MetaDict:
169162
"""
170163
Given an tree, return a rich.tree.Tree output.
171164
"""
@@ -181,10 +174,23 @@ def _process_item_ttree(
181174
)
182175

183176

184-
if hasattr(uproot.behaviors, "RNTuple") and hasattr(
185-
uproot.behaviors.RNTuple, "HasFields"
186-
):
187-
process_item.register(uproot.behaviors.RNTuple.RNTuple)(_process_item_ttree) # type: ignore[no-untyped-call]
177+
@process_item.register
178+
def _process_item_rntuple(
179+
uproot_object: uproot.behaviors.RNTuple.RNTuple,
180+
) -> MetaDict:
181+
"""
182+
Given an tree, return a rich.tree.Tree output.
183+
"""
184+
label_text = Text.assemble(
185+
(f"{uproot_object.name} ", "bold"),
186+
f"({uproot_object.num_entries:g})",
187+
)
188+
189+
return MetaDict(
190+
label_icon="🌳 ",
191+
label_text=label_text,
192+
guide_style="bold bright_green",
193+
)
188194

189195

190196
@process_item.register
@@ -213,6 +219,26 @@ def _process_item_tbranch(uproot_object: uproot.TBranch) -> MetaDict:
213219
)
214220

215221

222+
@process_item.register
223+
def _process_item_rbranch(uproot_object: uproot.models.RNTuple.RField) -> MetaDict:
224+
"""
225+
Given an branch, return a rich.tree.Tree output.
226+
"""
227+
228+
icon = "🍁 "
229+
230+
label_text = Text.assemble(
231+
(f"{uproot_object.name} ", "bold"),
232+
(f"{uproot_object.typename}", "italic"),
233+
)
234+
235+
return MetaDict(
236+
label_icon=icon,
237+
label_text=label_text,
238+
guide_style="bold bright_green",
239+
)
240+
241+
216242
@process_item.register
217243
def _process_item_th(uproot_object: uproot.behaviors.TH1.Histogram) -> MetaDict:
218244
"""

β€Žsrc/uproot_browser/tui/plot.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ class Plotext:
4646
def __rich_console__(
4747
self, console: rich.console.Console, options: rich.console.ConsoleOptions
4848
) -> rich.console.RenderResult:
49-
*_, item = apply_selection(self.upfile, self.selection.split(":"))
49+
*_, item = apply_selection(
50+
self.upfile, [s for s in self.selection.split("/") if s]
51+
)
5052

5153
if item is None:
5254
self.app.post_message(EmptyMessage())

β€Žtests/test_printouts.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,34 @@ def test_tree(capsys):
9090

9191
assert not err
9292
assert out == OUT1
93+
94+
95+
OUT2 = """\
96+
πŸ“ ntpl001_staff_rntuple_v1-0-0-0.root
97+
┗━━ 🌳 Staff (3354)
98+
┣━━ 🍁 Age std::int32_t
99+
┣━━ 🍁 Category std::int32_t
100+
┣━━ 🍁 Children std::int32_t
101+
┣━━ 🍁 Cost std::int32_t
102+
┣━━ 🍁 Division std::string
103+
┣━━ 🍁 Flag std::uint32_t
104+
┣━━ 🍁 Grade std::int32_t
105+
┣━━ 🍁 Hrweek std::int32_t
106+
┣━━ 🍁 Nation std::string
107+
┣━━ 🍁 Service std::int32_t
108+
┗━━ 🍁 Step std::int32_t
109+
"""
110+
111+
112+
@pytest.mark.xfail(
113+
sys.platform.startswith("win"),
114+
reason="Unicode is different on Windows, for some reason?",
115+
)
116+
def test_tree_rntuple(capsys):
117+
filename = data_path("ntpl001_staff_rntuple_v1-0-0-0.root")
118+
console = rich.console.Console(width=120)
119+
120+
print_tree(filename, console=console)
121+
out, err = capsys.readouterr()
122+
assert not err
123+
assert out == OUT2

0 commit comments

Comments
Β (0)