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.