Skip to content

Commit ee0a25c

Browse files
committed
typeddicts: raise proper error on invalid input
1 parent e2bdc84 commit ee0a25c

File tree

2 files changed

+20
-3
lines changed

2 files changed

+20
-3
lines changed

src/cattrs/gen/typeddicts.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,16 @@ def make_dict_structure_fn(
307307
globs["__c_a"] = allowed_fields
308308
globs["__c_feke"] = ForbiddenExtraKeysError
309309

310-
lines.append(" res = o.copy()")
311-
312310
if _cattrs_detailed_validation:
311+
# When running under detailed validation, be extra careful about copying
312+
# so that the correct error is raised if the input isn't a dict.
313+
lines.append(" try:")
314+
lines.append(" res = o.copy()")
315+
lines.append(" except Exception as exc:")
316+
lines.append(
317+
f" raise __c_cve('While structuring ' + {cl.__name__!r}, [exc], __cl)"
318+
)
319+
313320
lines.append(" errors = []")
314321
internal_arg_parts["__c_cve"] = ClassValidationError
315322
internal_arg_parts["__c_avn"] = AttributeValidationNote
@@ -383,6 +390,7 @@ def make_dict_structure_fn(
383390
f" if errors: raise __c_cve('While structuring ' + {cl.__name__!r}, errors, __cl)"
384391
)
385392
else:
393+
lines.append(" res = o.copy()")
386394
non_required = []
387395

388396
# The first loop deals with required args.

tests/test_typeddicts.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from pytest import raises
1111
from typing_extensions import NotRequired, Required
1212

13-
from cattrs import BaseConverter, Converter
13+
from cattrs import BaseConverter, Converter, transform_error
1414
from cattrs._compat import ExtensionsTypedDict, get_notrequired_base, is_generic
1515
from cattrs.errors import (
1616
ClassValidationError,
@@ -509,3 +509,12 @@ class A(ExtensionsTypedDict):
509509

510510
assert converter.unstructure({"a": 10, "b": 10}, A) == {"a": 1, "b": 2}
511511
assert converter.structure({"a": 10, "b": 10}, A) == {"a": 1, "b": 2}
512+
513+
514+
def test_nondict_input():
515+
"""Trying to structure typeddict from a non-dict raises the proper exception."""
516+
converter = Converter(detailed_validation=True)
517+
with raises(ClassValidationError) as exc:
518+
converter.structure(1, TypedDictA)
519+
520+
assert transform_error(exc.value) == ["expected a mapping @ $"]

0 commit comments

Comments
 (0)