Skip to content

Commit 5e136f5

Browse files
committed
For forbid_extra_keys raise custom ForbiddenExtraKeyError instead of generic Exception.
1 parent 3ee4b40 commit 5e136f5

File tree

7 files changed

+17
-10
lines changed

7 files changed

+17
-10
lines changed

HISTORY.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ History
1111
(`#218 <https://github.com/python-attrs/cattrs/issues/218>`_)
1212
* Fix structuring bare ``typing.Tuple`` on Pythons lower than 3.9.
1313
(`#218 <https://github.com/python-attrs/cattrs/issues/218>`_)
14-
1514
* Fix a wrong ``AttributeError`` of an missing ``__parameters__`` attribute. This could happen
1615
when inheriting certain generic classes – for example ``typing.*`` classes are affected.
1716
(`#217 <https://github.com/python-attrs/cattrs/issues/217>`_)
17+
* For ``forbid_extra_keys`` raise custom ``ForbiddenExtraKeyError`` instead of generic ``Exception``.
1818

1919
1.10.0 (2022-01-04)
2020
-------------------

docs/customizing.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ creating structure hooks with ``make_dict_structure_fn``.
108108
>>> c.structure({"nummber": 2}, TestClass)
109109
Traceback (most recent call last):
110110
...
111-
Exception: Extra fields in constructor for TestClass: nummber
111+
ForbiddenExtraKeyError: Extra fields in constructor for TestClass: nummber
112112
>>> hook = make_dict_structure_fn(TestClass, c, _cattrs_forbid_extra_keys=False)
113113
>>> c.register_structure_hook(TestClass, hook)
114114
>>> c.structure({"nummber": 2}, TestClass)

docs/structuring.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ Here's a small example showing how to use factory hooks to apply the `forbid_ext
473473
>>> c.structure({"an_int": 1, "else": 2}, E)
474474
Traceback (most recent call last):
475475
...
476-
Exception: Extra fields in constructor for E: else
476+
ForbiddenExtraKeyError: Extra fields in constructor for E: else
477477
478478
479479
A complex use case for hook factories is described over at :ref:`Using factory hooks`.

src/cattr/errors.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ class StructureHandlerNotFoundError(Exception):
77
def __init__(self, message: str, type_: Type) -> None:
88
super().__init__(message)
99
self.type_ = type_
10+
11+
12+
class ForbiddenExtraKeyError(Exception):
13+
"""Raised when `forbid_extra_keys` is activated and such an extra key is detected during structuring."""

src/cattrs/errors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from cattr.errors import StructureHandlerNotFoundError
1+
from cattr.errors import ForbiddenExtraKeyError, StructureHandlerNotFoundError
22

3-
__all__ = ["StructureHandlerNotFoundError"]
3+
__all__ = ["ForbiddenExtraKeyError", "StructureHandlerNotFoundError"]

src/cattrs/gen.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
is_generic,
2727
)
2828
from cattr._generics import deep_copy_with
29+
from cattr.errors import ForbiddenExtraKeyError
2930

3031
if TYPE_CHECKING: # pragma: no cover
3132
from cattr.converters import Converter
@@ -383,10 +384,11 @@ def make_dict_structure_fn(
383384

384385
if _cattrs_forbid_extra_keys:
385386
globs["__c_a"] = allowed_fields
387+
globs["ForbiddenExtraKeyError"] = ForbiddenExtraKeyError
386388
post_lines += [
387389
" unknown_fields = set(o.keys()) - __c_a",
388390
" if unknown_fields:",
389-
" raise Exception(",
391+
" raise ForbiddenExtraKeyError(",
390392
f" 'Extra fields in constructor for {cl_name}: ' + ', '.join(unknown_fields)"
391393
" )",
392394
]

tests/metadata/test_genconverter.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from cattr import GenConverter as Converter
2020
from cattr import UnstructureStrategy
2121
from cattr._compat import is_py39_plus, is_py310_plus
22+
from cattr.errors import ForbiddenExtraKeyError
2223
from cattr.gen import make_dict_structure_fn, override
2324

2425
from . import (
@@ -85,7 +86,7 @@ def test_forbid_extra_keys(cls_and_vals):
8586
while bad_key in unstructured:
8687
bad_key += "A"
8788
unstructured[bad_key] = 1
88-
with pytest.raises(Exception):
89+
with pytest.raises(ForbiddenExtraKeyError):
8990
converter.structure(unstructured, cl)
9091

9192

@@ -100,7 +101,7 @@ def test_forbid_extra_keys_defaults(attr_and_vals):
100101
inst = cl()
101102
unstructured = converter.unstructure(inst)
102103
unstructured["aa"] = unstructured.pop("a")
103-
with pytest.raises(Exception):
104+
with pytest.raises(ForbiddenExtraKeyError):
104105
converter.structure(unstructured, cl)
105106

106107

@@ -120,7 +121,7 @@ class A:
120121
converter.structure(unstructured, A)
121122
# if we break it in the subclass, we need it to raise
122123
unstructured["c"]["aa"] = 5
123-
with pytest.raises(Exception):
124+
with pytest.raises(ForbiddenExtraKeyError):
124125
converter.structure(unstructured, A)
125126
# we can "fix" that by disabling forbid_extra_keys on the subclass
126127
hook = make_dict_structure_fn(
@@ -130,7 +131,7 @@ class A:
130131
converter.structure(unstructured, A)
131132
# but we should still raise at the top level
132133
unstructured["b"] = 6
133-
with pytest.raises(Exception):
134+
with pytest.raises(ForbiddenExtraKeyError):
134135
converter.structure(unstructured, A)
135136

136137

0 commit comments

Comments
 (0)