Skip to content

Commit d2fbc8f

Browse files
committed
Validate more deeply required attribute
1 parent efc04da commit d2fbc8f

File tree

4 files changed

+64
-6
lines changed

4 files changed

+64
-6
lines changed

CHANGELOG.txt

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
=== 2.20.0 (2024-06-15)
2+
3+
* Added validations at compile time:
4+
* `required` must have unique items
5+
* checking if schema is not allowing `required` items (item is required, but is not listed in properties)
6+
17
=== 2.19.1 (2023-12-28)
28

39
* Fixed date format to accept only two digit months and days

fastjsonschema/draft04.py

+12
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,18 @@ def generate_required(self):
460460
with self.l('if {variable}_is_dict:'):
461461
if not isinstance(self._definition['required'], (list, tuple)):
462462
raise JsonSchemaDefinitionException('required must be an array')
463+
if len(self._definition['required']) != len(set(self._definition['required'])):
464+
raise JsonSchemaDefinitionException('required must contain unique elements')
465+
if not self._definition.get('additionalProperties', True):
466+
not_possible = [
467+
prop
468+
for prop in self._definition['required']
469+
if
470+
prop not in self._definition.get('properties', {})
471+
and not any(re.search(regex, prop) for regex in self._definition.get('patternProperties', {}))
472+
]
473+
if not_possible:
474+
raise JsonSchemaDefinitionException('{}: items {} are required but not allowed'.format(self._variable, not_possible))
463475
self.l('{variable}__missing_keys = set({required}) - {variable}.keys()')
464476
with self.l('if {variable}__missing_keys:'):
465477
dynamic = 'str(sorted({variable}__missing_keys)) + " properties"'

fastjsonschema/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = '2.19.1'
1+
VERSION = '2.20.0'

tests/test_object.py

+45-5
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,53 @@ def test_properties(asserter, value, expected):
7474
}, value, expected)
7575

7676

77-
def test_invalid_properties(asserter):
77+
@pytest.mark.parametrize('definition', (
78+
{
79+
'properties': {
80+
'item': ['wrong'],
81+
},
82+
},
83+
{
84+
'properties': {
85+
'x': {
86+
'type': 'number',
87+
},
88+
},
89+
'required': ['x', 'y'],
90+
'additionalProperties': False,
91+
},
92+
{
93+
'properties': {
94+
'x': {
95+
'type': 'number',
96+
},
97+
},
98+
'required': ['x', 'x'],
99+
}
100+
))
101+
def test_invalid_properties(asserter, definition):
78102
with pytest.raises(JsonSchemaDefinitionException):
79-
fastjsonschema.compile({
80-
'properties': {
81-
'item': ['wrong'],
103+
fastjsonschema.compile(definition)
104+
105+
106+
@pytest.mark.parametrize('definition', (
107+
{
108+
'properties': {
109+
'x': {
110+
'type': 'number',
111+
},
112+
},
113+
'patternProperties': {
114+
'^y': {
115+
'type': 'number',
82116
},
83-
})
117+
},
118+
'required': ['x', 'y'],
119+
'additionalProperties': False,
120+
},
121+
))
122+
def test_valid_properties(asserter, definition):
123+
asserter(definition, {'x': 1, 'y': 2}, {'x': 1, 'y': 2})
84124

85125

86126
@pytest.mark.parametrize('value, expected', [

0 commit comments

Comments
 (0)