Coverage for src/bioimageio/spec/_pretty_validation_errors.py: 45%

49 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-18 09:17 +0000

1from __future__ import annotations 

2 

3import warnings 

4from pprint import pformat 

5from types import TracebackType 

6from typing import Any 

7 

8from pydantic import ValidationError 

9 

10from .summary import format_loc 

11 

12try: 

13 from IPython.core.getipython import get_ipython 

14 from IPython.core.interactiveshell import InteractiveShell 

15 

16 class PrettyValidationError(ValueError): 

17 """Wrap a pydantic.ValidationError to custumize formatting.""" 

18 

19 def __init__(self, validation_error: ValidationError): 

20 super().__init__() 

21 self.error = validation_error 

22 

23 def __str__(self): 

24 errors: list[str] = [] 

25 for e in self.error.errors(include_url=False): 

26 ipt_lines = pformat( 

27 e["input"], sort_dicts=False, depth=1, compact=True, width=30 

28 ).split("\n") 

29 if len(ipt_lines) > 2: 

30 ipt_lines[1:-1] = ["..."] 

31 

32 ipt = " ".join([il.strip() for il in ipt_lines]) 

33 

34 errors.append( 

35 f"\n{format_loc(e['loc'], 'plain')}\n {e['msg']} [input={ipt}]" 

36 ) 

37 

38 return ( 

39 f"{self.error.error_count()} validation errors for" 

40 f" {self.error.title}:{''.join(errors)}" 

41 ) 

42 

43 def _custom_exception_handler( 

44 self: InteractiveShell, 

45 etype: type[ValidationError], 

46 evalue: ValidationError, 

47 tb: TracebackType, 

48 tb_offset: Any = None, 

49 ): 

50 assert issubclass(etype, ValidationError), type(etype) 

51 assert isinstance(evalue, ValidationError), type(etype) 

52 

53 stb = self.InteractiveTB.structured_traceback( # pyright: ignore 

54 etype, PrettyValidationError(evalue), tb, tb_offset=tb_offset 

55 ) 

56 

57 if isinstance(stb, list): 

58 stb_clean = [] 

59 for line in stb: # pyright: ignore[reportUnknownVariableType] 

60 if ( 

61 isinstance(line, str) 

62 and "pydantic" in line 

63 and "__tracebackhide__" in line 

64 ): 

65 # ignore pydantic internal frame in traceback 

66 continue 

67 stb_clean.append(line) 

68 

69 stb = stb_clean 

70 

71 self._showtraceback(etype, PrettyValidationError(evalue), stb) # type: ignore 

72 

73 def _enable_pretty_validation_errors_in_ipynb(): 

74 """A modestly hacky way to display prettified validaiton error messages and traceback 

75 in interactive Python notebooks""" 

76 ipy = get_ipython() 

77 if ipy is not None: 

78 ipy.set_custom_exc((ValidationError,), _custom_exception_handler) 

79 

80except ImportError: 

81 _enabled = False 

82else: 

83 try: 

84 _enable_pretty_validation_errors_in_ipynb() 

85 except Exception as e: 

86 _enabled = False 

87 warnings.warn( 

88 "Failed to enable pretty validation errors in ipython: " + str(e), 

89 stacklevel=2, 

90 ) 

91 else: 

92 _enabled = True 

93 

94PRETTY_VALIDATION_ERRORS_IN_IPYNB_ENABLED = _enabled 

95"""Whether pretty validation errors in IPython notebooks were successfully enabled during import."""