|
9 | 9 |
|
10 | 10 | from .. import BaseConverter, SimpleStructureHook
|
11 | 11 | from ..dispatch import UnstructureHook
|
| 12 | +from ..errors import ( |
| 13 | + AttributeValidationNote, |
| 14 | + ClassValidationError, |
| 15 | + IterableValidationError, |
| 16 | + IterableValidationNote, |
| 17 | +) |
12 | 18 | from ..fns import identity
|
13 | 19 | from ..gen.typeddicts import is_typeddict
|
14 | 20 |
|
@@ -48,14 +54,84 @@ def configure_list_from_dict(
|
48 | 54 | if isinstance(field, Attribute):
|
49 | 55 | field = field.name
|
50 | 56 |
|
51 |
| - def structure_hook( |
52 |
| - value: Mapping, |
53 |
| - _: Any = seq_type, |
54 |
| - _arg_type=arg_type, |
55 |
| - _arg_hook=arg_structure_hook, |
56 |
| - _field=field, |
57 |
| - ) -> list[T]: |
58 |
| - return [_arg_hook(v | {_field: k}, _arg_type) for k, v in value.items()] |
| 57 | + if converter.detailed_validation: |
| 58 | + |
| 59 | + def structure_hook( |
| 60 | + value: Mapping, |
| 61 | + _: Any = seq_type, |
| 62 | + _arg_type=arg_type, |
| 63 | + _arg_hook=arg_structure_hook, |
| 64 | + _field=field, |
| 65 | + ) -> list[T]: |
| 66 | + res = [] |
| 67 | + errors = [] |
| 68 | + for k, v in value.items(): |
| 69 | + try: |
| 70 | + res.append(_arg_hook(v | {_field: k}, _arg_type)) |
| 71 | + except ClassValidationError as exc: |
| 72 | + # Rewrite the notes of any errors relating to `_field` |
| 73 | + non_key_exceptions = [] |
| 74 | + key_exceptions = [] |
| 75 | + for inner_exc in exc.exceptions: |
| 76 | + if not (existing := getattr(inner_exc, "__notes__", [])): |
| 77 | + non_key_exceptions.append(inner_exc) |
| 78 | + continue |
| 79 | + for note in existing: |
| 80 | + if not isinstance(note, AttributeValidationNote): |
| 81 | + continue |
| 82 | + if note.name == _field: |
| 83 | + inner_exc.__notes__.remove(note) |
| 84 | + inner_exc.__notes__.append( |
| 85 | + IterableValidationNote( |
| 86 | + f"Structuring mapping key @ key {k!r}", |
| 87 | + note.name, |
| 88 | + note.type, |
| 89 | + ) |
| 90 | + ) |
| 91 | + key_exceptions.append(inner_exc) |
| 92 | + break |
| 93 | + else: |
| 94 | + non_key_exceptions.append(inner_exc) |
| 95 | + |
| 96 | + if non_key_exceptions != exc.exceptions: |
| 97 | + if non_key_exceptions: |
| 98 | + errors.append( |
| 99 | + new_exc := ClassValidationError( |
| 100 | + exc.message, non_key_exceptions, exc.cl |
| 101 | + ) |
| 102 | + ) |
| 103 | + new_exc.__notes__ = [ |
| 104 | + *getattr(exc, "__notes__", []), |
| 105 | + IterableValidationNote( |
| 106 | + "Structuring mapping value @ key {k!r}", |
| 107 | + k, |
| 108 | + _arg_type, |
| 109 | + ), |
| 110 | + ] |
| 111 | + else: |
| 112 | + exc.__notes__ = [ |
| 113 | + *getattr(exc, "__notes__", []), |
| 114 | + IterableValidationNote( |
| 115 | + "Structuring mapping value @ key {k!r}", k, _arg_type |
| 116 | + ), |
| 117 | + ] |
| 118 | + errors.append(exc) |
| 119 | + if key_exceptions: |
| 120 | + errors.extend(key_exceptions) |
| 121 | + if errors: |
| 122 | + raise IterableValidationError("While structuring", errors, dict) |
| 123 | + return res |
| 124 | + |
| 125 | + else: |
| 126 | + |
| 127 | + def structure_hook( |
| 128 | + value: Mapping, |
| 129 | + _: Any = seq_type, |
| 130 | + _arg_type=arg_type, |
| 131 | + _arg_hook=arg_structure_hook, |
| 132 | + _field=field, |
| 133 | + ) -> list[T]: |
| 134 | + return [_arg_hook(v | {_field: k}, _arg_type) for k, v in value.items()] |
59 | 135 |
|
60 | 136 | arg_unstructure_hook = converter.get_unstructure_hook(arg_type, cache_result=False)
|
61 | 137 |
|
|
0 commit comments