Skip to content

Commit 2a91507

Browse files
Hypro999benneyman
authored andcommitted
validators: Add optional keys feature to check_dict and check_dict_only.
along with the `required_keys` argument an additional `optional_keys` argument can be passed to both `check_dict` and `check_dict_only`. Fixes zulip#10892.
1 parent 9a451aa commit 2a91507

File tree

2 files changed

+43
-3
lines changed

2 files changed

+43
-3
lines changed

zerver/lib/validator.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def f(var_name: str, val: object) -> Optional[str]:
140140
return f
141141

142142
def check_dict(required_keys: Iterable[Tuple[str, Validator]]=[],
143+
optional_keys: Iterable[Tuple[str, Validator]]=[],
143144
value_validator: Optional[Validator]=None,
144145
_allow_only_listed_keys: bool=False) -> Validator:
145146
def f(var_name: str, val: object) -> Optional[str]:
@@ -155,6 +156,13 @@ def f(var_name: str, val: object) -> Optional[str]:
155156
if error:
156157
return error
157158

159+
for k, sub_validator in optional_keys:
160+
if k in val:
161+
vname = '%s["%s"]' % (var_name, k)
162+
error = sub_validator(vname, val[k])
163+
if error:
164+
return error
165+
158166
if value_validator:
159167
for key in val:
160168
vname = '%s contains a value that' % (var_name,)
@@ -163,16 +171,19 @@ def f(var_name: str, val: object) -> Optional[str]:
163171
return error
164172

165173
if _allow_only_listed_keys:
166-
delta_keys = set(val.keys()) - set(x[0] for x in required_keys)
174+
required_keys_set = set(x[0] for x in required_keys)
175+
optional_keys_set = set(x[0] for x in optional_keys)
176+
delta_keys = set(val.keys()) - required_keys_set - optional_keys_set
167177
if len(delta_keys) != 0:
168178
return _("Unexpected arguments: %s" % (", ".join(list(delta_keys))))
169179

170180
return None
171181

172182
return f
173183

174-
def check_dict_only(required_keys: Iterable[Tuple[str, Validator]]) -> Validator:
175-
return check_dict(required_keys, _allow_only_listed_keys=True)
184+
def check_dict_only(required_keys: Iterable[Tuple[str, Validator]],
185+
optional_keys: Iterable[Tuple[str, Validator]]=[]) -> Validator:
186+
return check_dict(required_keys, optional_keys, _allow_only_listed_keys=True)
176187

177188
def check_variable_type(allowed_type_funcs: Iterable[Validator]) -> Validator:
178189
"""

zerver/tests/test_decorators.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,35 @@ def test_check_dict(self) -> None:
861861
error = check_dict_only(keys)('x', x)
862862
self.assertEqual(error, 'Unexpected arguments: state')
863863

864+
# Test optional keys
865+
optional_keys = [
866+
('food', check_list(check_string)),
867+
('year', check_int)
868+
]
869+
870+
x = {
871+
'names': ['alice', 'bob'],
872+
'city': 'Boston',
873+
'food': ['Lobster Spaghetti']
874+
}
875+
876+
error = check_dict(keys)('x', x)
877+
self.assertEqual(error, None) # since _allow_only_listed_keys is False
878+
879+
error = check_dict_only(keys)('x', x)
880+
self.assertEqual(error, 'Unexpected arguments: food')
881+
882+
error = check_dict_only(keys, optional_keys)('x', x)
883+
self.assertEqual(error, None)
884+
885+
x = {
886+
'names': ['alice', 'bob'],
887+
'city': 'Boston',
888+
'food': 'Lobster Spaghetti'
889+
}
890+
error = check_dict_only(keys, optional_keys)('x', x)
891+
self.assertEqual(error, 'x["food"] is not a list')
892+
864893
def test_encapsulation(self) -> None:
865894
# There might be situations where we want deep
866895
# validation, but the error message should be customized.

0 commit comments

Comments
 (0)