bioimageio.spec.pretty_validation_errors

 1import warnings
 2from pprint import pformat
 3from types import TracebackType
 4from typing import Any, List, Type, Union
 5
 6from pydantic import ValidationError
 7
 8from .summary import format_loc
 9
10try:
11    from IPython.core.getipython import get_ipython
12    from IPython.core.interactiveshell import InteractiveShell
13
14    class PrettyValidationError(ValueError):
15        """Wrap a pydantic.ValidationError to custumize formatting."""
16
17        def __init__(self, validation_error: ValidationError):
18            super().__init__()
19            self.error = validation_error
20
21        def __str__(self):
22            errors: List[str] = []
23            for e in self.error.errors(include_url=False):
24                ipt_lines = pformat(
25                    e["input"], sort_dicts=False, depth=1, compact=True, width=30
26                ).split("\n")
27                if len(ipt_lines) > 2:
28                    ipt_lines[1:-1] = ["..."]
29
30                ipt = " ".join([il.strip() for il in ipt_lines])
31
32                errors.append(
33                    f"\n{format_loc(e['loc'], 'plain')}\n  {e['msg']} [input={ipt}]"
34                )
35
36            return (
37                f"{self.error.error_count()} validation errors for"
38                f" {self.error.title}:{''.join(errors)}"
39            )
40
41    def _custom_exception_handler(
42        self: InteractiveShell,
43        etype: Type[ValidationError],
44        evalue: ValidationError,
45        tb: TracebackType,
46        tb_offset: Any = None,
47    ):
48        assert issubclass(etype, ValidationError), type(etype)
49        assert isinstance(evalue, ValidationError), type(etype)
50
51        stb: Union[Any, List[Union[str, Any]]]
52        stb = self.InteractiveTB.structured_traceback(
53            etype, PrettyValidationError(evalue), tb, tb_offset=tb_offset
54        )
55
56        if isinstance(stb, list):
57            stb_clean = []
58            for line in stb:  # pyright: ignore[reportUnknownVariableType]
59                if (
60                    isinstance(line, str)
61                    and "pydantic" in line
62                    and "__tracebackhide__" in line
63                ):
64                    # ignore pydantic internal frame in traceback
65                    continue
66                stb_clean.append(line)
67
68            stb = stb_clean
69
70        self._showtraceback(etype, PrettyValidationError(evalue), stb)  # type: ignore
71
72    def _enable_pretty_validation_errors_in_ipynb():
73        """A modestly hacky way to display prettified validaiton error messages and traceback
74        in interactive Python notebooks"""
75        ipy = get_ipython()
76        if ipy is not None:
77            ipy.set_custom_exc((ValidationError,), _custom_exception_handler)
78
79except ImportError:
80    pass
81else:
82    try:
83        _enable_pretty_validation_errors_in_ipynb()
84    except Exception as e:
85        warnings.warn(
86            "Failed to enable pretty validation errors in ipython: " + str(e),
87            stacklevel=2,
88        )
89
90
91def enable_pretty_validation_errors_in_ipynb():
92    """DEPRECATED; this is enabled by default at import time."""
93    warnings.warn(
94        "deprecated, this is enabled by default at import time.",
95        DeprecationWarning,
96        stacklevel=2,
97    )
def enable_pretty_validation_errors_in_ipynb():
92def enable_pretty_validation_errors_in_ipynb():
93    """DEPRECATED; this is enabled by default at import time."""
94    warnings.warn(
95        "deprecated, this is enabled by default at import time.",
96        DeprecationWarning,
97        stacklevel=2,
98    )

DEPRECATED; this is enabled by default at import time.

class PrettyValidationError(builtins.ValueError):
15    class PrettyValidationError(ValueError):
16        """Wrap a pydantic.ValidationError to custumize formatting."""
17
18        def __init__(self, validation_error: ValidationError):
19            super().__init__()
20            self.error = validation_error
21
22        def __str__(self):
23            errors: List[str] = []
24            for e in self.error.errors(include_url=False):
25                ipt_lines = pformat(
26                    e["input"], sort_dicts=False, depth=1, compact=True, width=30
27                ).split("\n")
28                if len(ipt_lines) > 2:
29                    ipt_lines[1:-1] = ["..."]
30
31                ipt = " ".join([il.strip() for il in ipt_lines])
32
33                errors.append(
34                    f"\n{format_loc(e['loc'], 'plain')}\n  {e['msg']} [input={ipt}]"
35                )
36
37            return (
38                f"{self.error.error_count()} validation errors for"
39                f" {self.error.title}:{''.join(errors)}"
40            )

Wrap a pydantic.ValidationError to custumize formatting.

PrettyValidationError(validation_error: pydantic_core._pydantic_core.ValidationError)
18        def __init__(self, validation_error: ValidationError):
19            super().__init__()
20            self.error = validation_error
error