Skip to content

Commit

Permalink
Improve detailed validation for external validators
Browse files Browse the repository at this point in the history
  • Loading branch information
Tinche committed Mar 24, 2022
1 parent 59edafd commit 5675952
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 4 deletions.
18 changes: 14 additions & 4 deletions src/cattrs/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def make_dict_structure_fn(
_cattrs_use_linecache: bool = True,
_cattrs_prefer_attrib_converters: bool = False,
_cattrs_detailed_validation: bool = True,
**kwargs,
**kwargs: AttributeOverride,
) -> Callable[[Mapping[str, Any], Any], T]:
"""Generate a specialized dict structuring function for an attrs class."""

Expand Down Expand Up @@ -326,6 +326,15 @@ def make_dict_structure_fn(
post_lines.append(
f" if errors: raise __c_cve('While structuring {cl.__name__}', errors, __cl)"
)
instantiation_lines = (
[" try:"]
+ [" return __cl("]
+ [f" {line}" for line in invocation_lines]
+ [" )"]
+ [
f" except Exception as exc: raise __c_cve('While structuring {cl.__name__}', [exc], __cl)"
]
)
else:
non_required = []
# The first loop deals with required args.
Expand Down Expand Up @@ -432,6 +441,9 @@ def make_dict_structure_fn(
)
else:
post_lines.append(f" res['{ian}'] = o['{kn}']")
instantiation_lines = (
[" return __cl("] + [f" {line}" for line in invocation_lines] + [" )"]
)

if _cattrs_forbid_extra_keys:
globs["__c_a"] = allowed_fields
Expand All @@ -452,9 +464,7 @@ def make_dict_structure_fn(
[f"def {fn_name}(o, _, *, {internal_arg_line}):"]
+ lines
+ post_lines
+ [" return __cl("]
+ [f" {line}" for line in invocation_lines]
+ [" )"]
+ instantiation_lines
)

fname = _generate_unique_filename(cl, "structure", reserve=_cattrs_use_linecache)
Expand Down
18 changes: 18 additions & 0 deletions tests/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ class Test:
)


def test_external_class_validation():
"""Proper class validation errors are raised when a classes __init__ raises."""
c = GenConverter(detailed_validation=True)

@define
class Test:
a: int
b: str = field(validator=in_(["a", "b"]))
c: str

with pytest.raises(ClassValidationError) as exc:
c.structure({"a": 1, "b": "c", "c": "1"}, Test)

assert repr(exc.value.exceptions[0]) == repr(
ValueError("'b' must be in ['a', 'b'] (got 'c')")
)


def test_list_validation():
"""Proper validation errors are raised structuring lists."""
c = GenConverter(detailed_validation=True)
Expand Down

0 comments on commit 5675952

Please sign in to comment.