Skip to content

Commit 454035f

Browse files
committed
rename prevent_additional_properties to reject_unknown_fields
1 parent 4cf3eb0 commit 454035f

7 files changed

Lines changed: 66 additions & 66 deletions

File tree

docs/05-dataclasses.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,28 +297,28 @@ It is also worth noting that you can use the `@validataclass` decorator with opt
297297
being applied to the class.
298298

299299

300-
## Prevent additional properties
300+
## Reject unknown fields
301301

302-
Per default, validataclass just ignores any additional properties in the input dictionary when validating an object.
303-
This makes sense for normal APIs, as additional fields are just filtered out, and it makes validataclass more robust to
304-
changes in the API. There might be situations where one needs to have a strict validation of additional parameters,
305-
for example, to match an OpenAPI validation.
302+
Per default, validataclass just ignores any unknown fields in the input dictionary when validating an object.
303+
This makes sense for normal APIs, as additional fields are just filtered out, and it makes validataclass more robust to
304+
changes in the API. There might be situations where one needs to have a strict validation of additional fields,
305+
for example, to match an OpenAPI validation.
306306

307-
You can prevent additional properties by setting `prevent_additional_properties` at the `@validataclass` to `True`,
307+
You can reject unknown fields by setting `reject_unknown_fields` at the `@validataclass` to `True`,
308308
like this:
309309

310310
```python
311311
from validataclass.dataclasses import validataclass
312312
from validataclass.validators import DataclassValidator, StringValidator
313313

314-
@validataclass(prevent_additional_properties=True)
314+
@validataclass(reject_unknown_fields=True)
315315
class MyModel:
316316
name: str = StringValidator()
317317

318318
my_validator = DataclassValidator(MyModel)
319319

320320
my_validator.validate({'name': 'test'}) # would work fine
321-
my_validator.validate({'name': 'test', 'more': 'stuff'}) # would throw an AdditionalPropertiesError
321+
my_validator.validate({'name': 'test', 'more': 'stuff'}) # would throw an UnknownFieldsError
322322
```
323323

324324

src/validataclass/dataclasses/validataclass.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class ExampleDataclass:
8989

9090
def decorator(_cls: type[_T]) -> type[_T]:
9191
# Pop validataclass-specific options before passing kwargs to @dataclass
92-
prevent_additional_properties = kwargs.pop('prevent_additional_properties', False)
92+
reject_unknown_fields = kwargs.pop('reject_unknown_fields', False)
9393

9494
# Set kw_only=True as the default to allow required and optional fields in any order
9595
kwargs.setdefault('kw_only', True)
@@ -101,7 +101,7 @@ def decorator(_cls: type[_T]) -> type[_T]:
101101
_cls = dataclasses.dataclass(**kwargs)(_cls)
102102

103103
# Store validataclass-specific settings on the class
104-
_cls.__prevent_additional_properties__ = prevent_additional_properties # type: ignore[attr-defined]
104+
_cls.__reject_unknown_fields__ = reject_unknown_fields # type: ignore[attr-defined]
105105

106106
return _cls
107107

src/validataclass/exceptions/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
InvalidTypeError,
1111
RequiredValueError,
1212
)
13-
from .dataclass_exceptions import AdditionalPropertiesError, DataclassPostValidationError
13+
from .dataclass_exceptions import UnknownFieldsError, DataclassPostValidationError
1414
from .datetime_exceptions import (
1515
DateTimeRangeError,
1616
InvalidDateError,
@@ -50,7 +50,7 @@
5050
from .url_exceptions import InvalidUrlError
5151

5252
__all__ = [
53-
'AdditionalPropertiesError',
53+
'UnknownFieldsError',
5454
'DataclassInvalidPreValidateSignatureException',
5555
'DataclassPostValidationError',
5656
'DataclassValidatorFieldException',

src/validataclass/exceptions/dataclass_exceptions.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,31 @@
99
from .base_exceptions import ValidationError
1010

1111
__all__ = [
12-
'AdditionalPropertiesError',
12+
'UnknownFieldsError',
1313
'DataclassPostValidationError',
1414
]
1515

1616

17-
class AdditionalPropertiesError(ValidationError):
17+
class UnknownFieldsError(ValidationError):
1818
"""
1919
Validation error raised by `DataclassValidator` when the input dictionary contains keys that do not correspond to
20-
any field in the dataclass, and `prevent_additional_properties` is set to `True`.
20+
any field in the dataclass, and `reject_unknown_fields` is set to `True`.
2121
22-
The `additional_properties` attribute contains a sorted list of the extra field names.
22+
The `unknown_fields` attribute contains a sorted list of the extra field names.
2323
2424
Example as dictionary:
2525
2626
```
2727
{
28-
'code': 'additional_properties',
29-
'additional_properties': ['unknown1', 'unknown2'],
28+
'code': 'unknown_fields',
29+
'unknown_fields': ['unknown1', 'unknown2'],
3030
}
3131
```
3232
"""
33-
code = 'additional_properties'
33+
code = 'unknown_fields'
3434

35-
def __init__(self, *, additional_properties: list[str], **kwargs: Any):
36-
super().__init__(additional_properties=sorted(additional_properties), **kwargs)
35+
def __init__(self, *, unknown_fields: list[str], **kwargs: Any):
36+
super().__init__(unknown_fields=sorted(unknown_fields), **kwargs)
3737

3838

3939
class DataclassPostValidationError(ValidationError):

src/validataclass/validators/dataclass_validator.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from validataclass.dataclasses import Default, NoDefault
1313
from validataclass.exceptions import (
14-
AdditionalPropertiesError,
14+
UnknownFieldsError,
1515
DataclassInvalidPreValidateSignatureException,
1616
DataclassPostValidationError,
1717
DataclassValidatorFieldException,
@@ -110,8 +110,8 @@ def __post_validate__(self, *, require_optional_field: bool = False):
110110
# Dataclass type that the validated dictionary will be converted to
111111
dataclass_cls: type[T_Dataclass]
112112

113-
# Whether to prevent additional properties in the input dictionary
114-
prevent_additional_properties: bool
113+
# Whether to reject unknown fields in the input dictionary
114+
reject_unknown_fields: bool
115115

116116
# Field default values
117117
field_defaults: dict[str, Default]
@@ -133,7 +133,7 @@ def __init__(self, dataclass_cls: type[T_Dataclass] | None = None) -> None:
133133
raise InvalidValidatorOptionException('Parameter "dataclass_cls" must be a dataclass type.')
134134

135135
self.dataclass_cls = dataclass_cls
136-
self.prevent_additional_properties = getattr(dataclass_cls, '__prevent_additional_properties__', False)
136+
self.reject_unknown_fields = getattr(dataclass_cls, '__reject_unknown_fields__', False)
137137
self.field_defaults = {}
138138

139139
# Collect field validators and required fields for the DictValidator by examining the dataclass fields
@@ -228,12 +228,12 @@ def _pre_validate(self, input_data: Any, **kwargs: Any) -> dict[str, Any]:
228228
# Filter input dictionary through __pre_validate__()
229229
input_data = pre_validate_func(input_data, **context_kwargs)
230230

231-
# Check for additional properties if not allowed
232-
if self.prevent_additional_properties:
231+
# Check for unknown fields if not allowed
232+
if self.reject_unknown_fields:
233233
self._ensure_type(input_data, dict)
234-
additional = sorted(set(input_data.keys()) - set(self.field_validators.keys()))
235-
if additional:
236-
raise AdditionalPropertiesError(additional_properties=additional)
234+
unknown = sorted(set(input_data.keys()) - set(self.field_validators.keys()))
235+
if unknown:
236+
raise UnknownFieldsError(unknown_fields=unknown)
237237

238238
# Validate raw dictionary using underlying DictValidator
239239
validated_dict = super().validate(input_data, **kwargs)

tests/unit/exceptions/dataclass_exceptions_test.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,48 @@
55
"""
66

77
from validataclass.exceptions import (
8-
AdditionalPropertiesError,
8+
UnknownFieldsError,
99
DataclassPostValidationError,
1010
DictRequiredFieldError,
1111
InvalidTypeError,
1212
ValidationError,
1313
)
1414

1515

16-
class AdditionalPropertiesErrorTest:
16+
class UnknownFieldsErrorTest:
1717
"""
18-
Tests for the AdditionalPropertiesError exception class.
18+
Tests for the UnknownFieldsError exception class.
1919
"""
2020

2121
@staticmethod
22-
def test_additional_properties_error_single_property():
23-
""" Tests AdditionalPropertiesError with a single additional property. """
24-
error = AdditionalPropertiesError(additional_properties=['unknown_field'])
22+
def test_unknown_fields_error_single_property():
23+
""" Tests UnknownFieldsError with a single additional property. """
24+
error = UnknownFieldsError(unknown_fields=['unknown_field'])
2525

2626
assert error.to_dict() == {
27-
'code': 'additional_properties',
28-
'additional_properties': ['unknown_field'],
27+
'code': 'unknown_fields',
28+
'unknown_fields': ['unknown_field'],
2929
}
3030

3131
@staticmethod
32-
def test_additional_properties_error_multiple_properties():
33-
""" Tests AdditionalPropertiesError with multiple additional properties (sorted). """
34-
error = AdditionalPropertiesError(additional_properties=['watermelon', 'apple', 'mango'])
32+
def test_unknown_fields_error_multiple_fields():
33+
""" Tests UnknownFieldsError with multiple additional fields (sorted). """
34+
error = UnknownFieldsError(unknown_fields=['watermelon', 'apple', 'mango'])
3535

3636
assert error.to_dict() == {
37-
'code': 'additional_properties',
38-
'additional_properties': ['apple', 'mango', 'watermelon'],
37+
'code': 'unknown_fields',
38+
'unknown_fields': ['apple', 'mango', 'watermelon'],
3939
}
4040

4141
@staticmethod
42-
def test_additional_properties_error_repr():
43-
""" Tests repr of AdditionalPropertiesError. """
44-
error = AdditionalPropertiesError(additional_properties=['unknown1', 'unknown2'])
42+
def test_unknown_fields_error_repr():
43+
""" Tests repr of UnknownFieldsError. """
44+
error = UnknownFieldsError(unknown_fields=['unknown1', 'unknown2'])
4545

4646
assert (
4747
repr(error)
48-
== "AdditionalPropertiesError(code='additional_properties', "
49-
"additional_properties=['unknown1', 'unknown2'])"
48+
== "UnknownFieldsError(code='unknown_fields', "
49+
"unknown_fields=['unknown1', 'unknown2'])"
5050
)
5151
assert str(error) == repr(error)
5252

tests/unit/validators/dataclass_validator_test.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from tests.test_utils import UnitTestContextValidator
1414
from validataclass.dataclasses import Default, DefaultFactory, DefaultUnset, validataclass, validataclass_field
1515
from validataclass.exceptions import (
16-
AdditionalPropertiesError,
16+
UnknownFieldsError,
1717
DataclassInvalidPreValidateSignatureException,
1818
DataclassPostValidationError,
1919
DataclassValidatorFieldException,
@@ -59,9 +59,9 @@ class UnitTestNestedDataclass:
5959
validataclass_field(DataclassValidator(UnitTestDataclass), default=None)
6060

6161

62-
# Dataclass with prevent_additional_properties=True
62+
# Dataclass with reject_unknown_fields=True
6363

64-
@validataclass(prevent_additional_properties=True)
64+
@validataclass(reject_unknown_fields=True)
6565
class UnitTestStrictDataclass:
6666
"""
6767
Dataclass that does not allow additional properties in the input dictionary.
@@ -1144,7 +1144,7 @@ class IncompatibleDataclass:
11441144

11451145
assert str(exception_info.value) == 'Default specified for dataclass field "foo" is not of type "Default".'
11461146

1147-
# Tests for prevent_additional_properties option
1147+
# Tests for reject_unknown_fields option
11481148

11491149
@staticmethod
11501150
def test_strict_dataclass_valid():
@@ -1173,26 +1173,26 @@ def test_strict_dataclass_valid_with_optional_field_omitted():
11731173

11741174
@staticmethod
11751175
def test_strict_dataclass_with_additional_properties():
1176-
""" Test that a strict dataclass raises AdditionalPropertiesError for unknown keys. """
1176+
""" Test that a strict dataclass raises UnknownFieldsError for unknown keys. """
11771177
validator = DataclassValidator(UnitTestStrictDataclass)
11781178

1179-
with pytest.raises(AdditionalPropertiesError) as exception_info:
1179+
with pytest.raises(UnknownFieldsError) as exception_info:
11801180
validator.validate({
11811181
'name': 'banana',
11821182
'unknown_field': 'unknown_value',
11831183
})
11841184

11851185
assert exception_info.value.to_dict() == {
1186-
'code': 'additional_properties',
1187-
'additional_properties': ['unknown_field'],
1186+
'code': 'unknown_fields',
1187+
'unknown_fields': ['unknown_field'],
11881188
}
11891189

11901190
@staticmethod
1191-
def test_strict_dataclass_with_multiple_additional_properties():
1192-
""" Test that additional properties are sorted in the error. """
1191+
def test_strict_dataclass_with_multiple_additional_fields():
1192+
""" Test that additional fields are sorted in the error. """
11931193
validator = DataclassValidator(UnitTestStrictDataclass)
11941194

1195-
with pytest.raises(AdditionalPropertiesError) as exception_info:
1195+
with pytest.raises(UnknownFieldsError) as exception_info:
11961196
validator.validate({
11971197
'name': 'banana',
11981198
'zebra': 1,
@@ -1201,13 +1201,13 @@ def test_strict_dataclass_with_multiple_additional_properties():
12011201
})
12021202

12031203
assert exception_info.value.to_dict() == {
1204-
'code': 'additional_properties',
1205-
'additional_properties': ['alpha', 'mango', 'zebra'],
1204+
'code': 'unknown_fields',
1205+
'unknown_fields': ['alpha', 'mango', 'zebra'],
12061206
}
12071207

12081208
@staticmethod
1209-
def test_default_allows_additional_properties():
1210-
""" Test that by default (prevent_additional_properties=False), unknown keys are silently ignored. """
1209+
def test_default_allows_additional_fields():
1210+
""" Test that by default (reject_unknown_fields=False), unknown keys are silently ignored. """
12111211
validator = DataclassValidator(UnitTestDataclass)
12121212
validated_data = validator.validate({
12131213
'name': 'banana',
@@ -1221,10 +1221,10 @@ def test_default_allows_additional_properties():
12211221
assert validated_data.name == 'banana'
12221222

12231223
@staticmethod
1224-
def test_explicit_prevent_additional_properties_false():
1225-
""" Test that prevent_additional_properties=False explicitly allows unknown keys. """
1224+
def test_explicit_reject_unknown_fields_false():
1225+
""" Test that reject_unknown_fields=False explicitly allows unknown keys. """
12261226

1227-
@validataclass(prevent_additional_properties=False)
1227+
@validataclass(reject_unknown_fields=False)
12281228
class ExplicitAllowDataclass:
12291229
name: str = StringValidator()
12301230

0 commit comments

Comments
 (0)