Skip to content

Commit 1d8c60f

Browse files
committed
Updated Stone
1 parent 3f109f5 commit 1d8c60f

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

dropbox/stone_serializers.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ def encode_sub(self, validator, value):
106106
# serialization
107107
validate_f = validator.validate
108108
encode_f = self.encode_list
109+
elif isinstance(validator, bv.Map):
110+
# Also validate maps during serialization because they are also mutable
111+
validate_f = validator.validate
112+
encode_f = self.encode_map
109113
elif isinstance(validator, bv.Nullable):
110114
validate_f = validator.validate
111115
encode_f = self.encode_nullable
@@ -139,6 +143,14 @@ def encode_list(self, validator, value):
139143
"""
140144
raise NotImplementedError
141145

146+
def encode_map(self, validator, value):
147+
# type: (bv.Map, typing.Any) -> typing.Any
148+
"""
149+
Callback for serializing a ``stone_validators.Map``. Arguments
150+
have the same semantics as with the ``encode`` method.
151+
"""
152+
raise NotImplementedError
153+
142154
def encode_nullable(self, validator, value):
143155
# type: (bv.Nullable, typing.Any) -> typing.Any
144156
"""
@@ -222,6 +234,15 @@ def encode_list(self, validator, value):
222234
return [self.encode_sub(validator.item_validator, value_item) for value_item in
223235
validated_value]
224236

237+
def encode_map(self, validator, value):
238+
validated_value = validator.validate(value)
239+
240+
return {
241+
self.encode_sub(validator.key_validator, key):
242+
self.encode_sub(validator.value_validator, value) for
243+
key, value in validated_value.items()
244+
}
245+
225246
def encode_nullable(self, validator, value):
226247
if value is None:
227248
return None
@@ -461,6 +482,7 @@ def json_decode(
461482
- Float -> float
462483
- Integer -> long
463484
- List -> list
485+
- Map -> dict
464486
- Nullable -> None or its wrapped type.
465487
- String -> unicode (PY2) or str (PY3)
466488
- Struct -> An instance of its definition attribute.
@@ -522,6 +544,9 @@ def _json_compat_obj_decode_helper(
522544
elif isinstance(data_type, bv.List):
523545
return _decode_list(
524546
data_type, obj, alias_validators, strict, old_style, for_msgpack)
547+
elif isinstance(data_type, bv.Map):
548+
return _decode_map(
549+
data_type, obj, alias_validators, strict, old_style, for_msgpack)
525550
elif isinstance(data_type, bv.Nullable):
526551
return _decode_nullable(
527552
data_type, obj, alias_validators, strict, old_style, for_msgpack)
@@ -656,7 +681,7 @@ def _decode_union_dict(data_type, obj, alias_validators, strict, for_msgpack):
656681
raise bv.ValidationError("unexpected key '%s'" % key)
657682
val = None
658683
elif isinstance(val_data_type,
659-
(bv.Primitive, bv.List, bv.StructTree, bv.Union)):
684+
(bv.Primitive, bv.List, bv.StructTree, bv.Union, bv.Map)):
660685
if tag in obj:
661686
raw_val = obj[tag]
662687
try:
@@ -812,6 +837,26 @@ def _decode_list(
812837
for item in obj]
813838

814839

840+
def _decode_map(
841+
data_type, obj, alias_validators, strict, old_style, for_msgpack):
842+
"""
843+
The data_type argument must be a Map.
844+
See json_compat_obj_decode() for argument descriptions.
845+
"""
846+
if not isinstance(obj, dict):
847+
raise bv.ValidationError(
848+
'expected dict, got %s' % bv.generic_type_name(obj))
849+
return {
850+
_json_compat_obj_decode_helper(
851+
data_type.key_validator, key, alias_validators, strict,
852+
old_style, for_msgpack):
853+
_json_compat_obj_decode_helper(
854+
data_type.value_validator, value, alias_validators, strict,
855+
old_style, for_msgpack)
856+
for key, value in obj.items()
857+
}
858+
859+
815860
def _decode_nullable(
816861
data_type, obj, alias_validators, strict, old_style, for_msgpack):
817862
"""

dropbox/stone_validators.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,26 @@ def validate(self, val):
408408
return [self.item_validator.validate(item) for item in val]
409409

410410

411+
class Map(Composite):
412+
"""Assumes map keys and values are homogeneous with respect to types."""
413+
414+
def __init__(self, key_validator, value_validator):
415+
"""
416+
Every Map key/value pair will be validated with item_validator.
417+
key validators must be a subclass of a String validator
418+
"""
419+
self.key_validator = key_validator
420+
self.value_validator = value_validator
421+
422+
def validate(self, val):
423+
if not isinstance(val, dict):
424+
raise ValidationError('%r is not a valid dict' % val)
425+
return {
426+
self.key_validator.validate(key):
427+
self.value_validator.validate(value) for key, value in val.items()
428+
}
429+
430+
411431
class Struct(Composite):
412432

413433
def __init__(self, definition):

0 commit comments

Comments
 (0)