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

Add path to error message in map_over_subtree #264

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3ee67fe
test
TomNicholas Oct 23, 2023
f367f0f
implementation
TomNicholas Oct 23, 2023
688dc4a
formatting
TomNicholas Oct 23, 2023
333a4e9
add version check, if not using 3.11 then you just won't get the extr…
TomNicholas Oct 24, 2023
b603cd6
whatsnew
TomNicholas Oct 24, 2023
cce673d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 24, 2023
bfb4fbd
Merge branch 'main' into error_context_map_over_subtree
TomNicholas Oct 24, 2023
1adc58a
Merge branch 'main' into error_context_map_over_subtree
TomNicholas Oct 24, 2023
5a1b8b1
use better helper function
TomNicholas Oct 24, 2023
f41cd95
xfail test, because this does actually work...
TomNicholas Oct 24, 2023
b449c9c
Merge branch 'error_context_map_over_subtree' of https://github.com/T…
TomNicholas Oct 24, 2023
6c72798
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 24, 2023
fc59b0e
clean up after bad merge
TomNicholas Oct 24, 2023
eb4a38e
Merge branch 'error_context_map_over_subtree' of https://github.com/T…
TomNicholas Oct 24, 2023
479c74a
Merge branch 'main' into error_context_map_over_subtree
TomNicholas Oct 24, 2023
33efed1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 24, 2023
a50b7f6
re-enable feature after stupid merge
TomNicholas Oct 24, 2023
5da9479
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 24, 2023
7e8aea1
formatting
TomNicholas Oct 24, 2023
2a9b510
Merge branch 'error_context_map_over_subtree' of https://github.com/T…
TomNicholas Oct 24, 2023
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
36 changes: 35 additions & 1 deletion datatree/mapping.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import functools
import sys
from itertools import repeat
from textwrap import dedent
from typing import TYPE_CHECKING, Callable, Tuple
Expand Down Expand Up @@ -202,10 +203,15 @@ def _map_over_subtree(*args, **kwargs) -> DataTree | Tuple[DataTree, ...]:
],
)
)
func_with_error_context = _handle_errors_with_path_context(
node_of_first_tree.path
)(func)

# Now we can call func on the data in this particular set of corresponding nodes
results = (
func(*node_args_as_datasets, **node_kwargs_as_datasets)
func_with_error_context(
*node_args_as_datasets, **node_kwargs_as_datasets
)
if node_of_first_tree.has_data
else None
)
Expand Down Expand Up @@ -251,6 +257,34 @@ def _map_over_subtree(*args, **kwargs) -> DataTree | Tuple[DataTree, ...]:
return _map_over_subtree


def _handle_errors_with_path_context(path):
"""Wraps given function so that if it fails it also raises path to node on which it failed."""

def decorator(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
if sys.version_info >= (3, 11):
# Add the context information to the error message
e.add_note(
f"Raised whilst mapping function over node with path {path}"
)
raise

return wrapper

return decorator


def add_note(err: BaseException, msg: str) -> None:
# TODO: remove once python 3.10 can be dropped
if sys.version_info < (3, 11):
err.__notes__ = getattr(err, "__notes__", []) + [msg]
else:
err.add_note(msg)


def _check_single_set_return_values(path_to_node, obj):
"""Check types returned from single evaluation of func, and return number of return values received from func."""
if isinstance(obj, (Dataset, DataArray)):
Expand Down
17 changes: 17 additions & 0 deletions datatree/tests/test_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,23 @@ def check_for_data(ds):

dt.map_over_subtree(check_for_data)

@pytest.mark.xfail(
reason="probably some bug in pytests handling of exception notes"
)
def test_error_contains_path_of_offending_node(self, create_test_datatree):
dt = create_test_datatree()
dt["set1"]["bad_var"] = 0
print(dt)

def fail_on_specific_node(ds):
if "bad_var" in ds:
raise ValueError("Failed because 'bar_var' present in dataset")

with pytest.raises(
ValueError, match="Raised whilst mapping function over node /set1"
):
dt.map_over_subtree(fail_on_specific_node)


class TestMutableOperations:
def test_construct_using_type(self):
Expand Down
4 changes: 4 additions & 0 deletions docs/source/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ v0.0.13 (unreleased)
New Features
~~~~~~~~~~~~

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

Breaking changes
~~~~~~~~~~~~~~~~

Expand Down
Loading