Skip to content
This repository was archived by the owner on Oct 24, 2024. It is now read-only.

Commit dffd32c

Browse files
Add path to error message in map_over_subtree (#264)
* test * implementation * formatting * add version check, if not using 3.11 then you just won't get the extra info in the error message * whatsnew * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use better helper function * xfail test, because this does actually work... --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 5330a3c commit dffd32c

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

datatree/mapping.py

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import functools
4+
import sys
45
from itertools import repeat
56
from textwrap import dedent
67
from typing import TYPE_CHECKING, Callable, Tuple
@@ -202,10 +203,15 @@ def _map_over_subtree(*args, **kwargs) -> DataTree | Tuple[DataTree, ...]:
202203
],
203204
)
204205
)
206+
func_with_error_context = _handle_errors_with_path_context(
207+
node_of_first_tree.path
208+
)(func)
205209

206210
# Now we can call func on the data in this particular set of corresponding nodes
207211
results = (
208-
func(*node_args_as_datasets, **node_kwargs_as_datasets)
212+
func_with_error_context(
213+
*node_args_as_datasets, **node_kwargs_as_datasets
214+
)
209215
if node_of_first_tree.has_data
210216
else None
211217
)
@@ -251,6 +257,34 @@ def _map_over_subtree(*args, **kwargs) -> DataTree | Tuple[DataTree, ...]:
251257
return _map_over_subtree
252258

253259

260+
def _handle_errors_with_path_context(path):
261+
"""Wraps given function so that if it fails it also raises path to node on which it failed."""
262+
263+
def decorator(func):
264+
def wrapper(*args, **kwargs):
265+
try:
266+
return func(*args, **kwargs)
267+
except Exception as e:
268+
if sys.version_info >= (3, 11):
269+
# Add the context information to the error message
270+
e.add_note(
271+
f"Raised whilst mapping function over node with path {path}"
272+
)
273+
raise
274+
275+
return wrapper
276+
277+
return decorator
278+
279+
280+
def add_note(err: BaseException, msg: str) -> None:
281+
# TODO: remove once python 3.10 can be dropped
282+
if sys.version_info < (3, 11):
283+
err.__notes__ = getattr(err, "__notes__", []) + [msg]
284+
else:
285+
err.add_note(msg)
286+
287+
254288
def _check_single_set_return_values(path_to_node, obj):
255289
"""Check types returned from single evaluation of func, and return number of return values received from func."""
256290
if isinstance(obj, (Dataset, DataArray)):

datatree/tests/test_mapping.py

+17
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,23 @@ def check_for_data(ds):
264264

265265
dt.map_over_subtree(check_for_data)
266266

267+
@pytest.mark.xfail(
268+
reason="probably some bug in pytests handling of exception notes"
269+
)
270+
def test_error_contains_path_of_offending_node(self, create_test_datatree):
271+
dt = create_test_datatree()
272+
dt["set1"]["bad_var"] = 0
273+
print(dt)
274+
275+
def fail_on_specific_node(ds):
276+
if "bad_var" in ds:
277+
raise ValueError("Failed because 'bar_var' present in dataset")
278+
279+
with pytest.raises(
280+
ValueError, match="Raised whilst mapping function over node /set1"
281+
):
282+
dt.map_over_subtree(fail_on_specific_node)
283+
267284

268285
class TestMutableOperations:
269286
def test_construct_using_type(self):

docs/source/whats-new.rst

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ v0.0.13 (unreleased)
2323
New Features
2424
~~~~~~~~~~~~
2525

26+
- Indicate which node caused the problem if error encountered while applying user function using :py:func:`map_over_subtree`
27+
(:issue:`190`, :pull:`264`). Only works when using python 3.11 or later.
28+
By `Tom Nicholas <https://github.com/TomNicholas>`_.
29+
2630
Breaking changes
2731
~~~~~~~~~~~~~~~~
2832

0 commit comments

Comments
 (0)