Skip to content

Commit a14acc7

Browse files
committed
Issue #43
update to 1.33
1 parent 0741083 commit a14acc7

File tree

11 files changed

+302
-97
lines changed

11 files changed

+302
-97
lines changed

coverage.svg

Lines changed: 2 additions & 2 deletions
Loading

coverage.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ typedpy\__init__.py 6 0 0 0 100%
44
typedpy\extfields.py 77 0 19 0 100%
55
typedpy\fields.py 521 10 190 4 98% 265, 465, 475-477, 565, 811, 1275, 1278, 1290, 398->exit, 460->465, 564->565, 810->811
66
typedpy\json_schema_mapping.py 175 1 63 0 99% 346
7-
typedpy\serialization.py 227 7 146 6 96% 244, 338, 354, 386-387, 520, 528, 243->244, 320->338, 353->354, 384->386, 519->520, 527->528
7+
typedpy\serialization.py 224 1 140 1 99% 561, 560->561
88
typedpy\serialization_wrappers.py 37 0 20 2 96% 141->exit, 143->exit
99
typedpy\structures.py 228 7 118 5 96% 307-310, 316, 318, 320, 338, 345, 315->316, 317->318, 319->320, 337->338, 344->345
1010
-------------------------------------------------------------------------------
11-
TOTAL 1271 25 556 17 97%
11+
TOTAL 1268 19 550 12 98%

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@
6060
# built documents.
6161
#
6262
# The short X.Y version.
63-
version = '1.32'
63+
version = '1.33'
6464
# The full version, including alpha/beta/rc tags.
65-
release = '1.32'
65+
release = '1.33'
6666

6767
# The language for content autogenerated by Sphinx. Refer to documentation
6868
# for a list of supported languages.

docs/serialization.rst

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ or a :class:`FunctionCall` (a function and a list of keys in the source dict tha
177177

178178
* When the mapping is using a function call, the provided function is expected to return the value that will be used as the input of the deserialization.
179179

180+
* To determine mapping for an embedded field/structure, use the notation: "<field name>._mapper". See example below.
181+
180182
An example can demonstrate the usage:
181183

182184

@@ -205,6 +207,53 @@ An example can demonstrate the usage:
205207
# keep_undefined=False ensures it does not also create attributes a, name, j in the deserialized instance
206208
assert foo == Foo(i=7, m={'x': 1, 'y': 2}, s='the string is Joe')
207209
210+
An example of mapping of a field item within a list:
211+
212+
.. code-block:: python
213+
214+
class Foo(Structure):
215+
a = String
216+
i = Integer
217+
218+
class Bar(Structure):
219+
wrapped = Array[Foo]
220+
221+
mapper = {'wrapped._mapper': {'a': 'aaa', 'i': 'iii'}, 'wrapped': 'other'}
222+
deserializer = Deserializer(target_class=Bar, mapper=mapper)
223+
deserialized = deserializer.deserialize(
224+
{
225+
'other': [
226+
{'aaa': 'string1', 'iii': 1},
227+
{'aaa': 'string2', 'iii': 2}
228+
]
229+
},
230+
keep_undefined=False)
231+
232+
assert deserialized == Bar(wrapped=[Foo(a='string1', i=1), Foo(a='string2', i=2)])
233+
234+
An example of nested mapping:
235+
236+
.. code-block:: python
237+
238+
class Foo(Structure):
239+
a = String
240+
i = Integer
241+
s = StructureReference(st=String, arr=Array)
242+
243+
mapper = {
244+
'a': 'aaa',
245+
'i': 'iii',
246+
's._mapper': {"arr": FunctionCall(func=lambda x: x * 3, args=['xxx'])}
247+
}
248+
deserializer = Deserializer(target_class=Foo, mapper=mapper)
249+
deserialized = deserializer.deserialize({
250+
'aaa': 'string',
251+
'iii': 1,
252+
's': {'st': 'string', 'xxx': [1, 2, 3]}},
253+
keep_undefined=False)
254+
255+
assert deserialized == Foo(a='string', i=1, s={'st': 'string', 'arr': [1, 2, 3, 1, 2, 3, 1, 2, 3]})
256+
208257
209258
Custom mapping in the serialization
210259
===================================

pylint.svg

Lines changed: 9 additions & 9 deletions
Loading

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
url="http://github.com/loyada/typedpy",
3131
download_url ="https://github.com/loyada/typedpy/archive/v1.30.tar.gz",
3232
keywords=['testing', 'type-safe', 'strict', 'schema', 'validation'],
33-
version='1.32'
33+
version='1.33'
3434
)
3535

3636
# coverage run --source=typedpy/ setup.py test

tests/test_deserialization.py

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ class Foo(Structure):
266266

267267
with raises(ValueError) as excinfo:
268268
deserialize_structure(Foo, {'bar': 1})
269-
assert "could not deserialize bar: value 1 did not match <Array>. reason: bar: must be an iterable; got 1" in str(
269+
assert "could not deserialize bar: value 1 did not match <Array>. reason: bar: must be list, set, or tuple; got 1" in str(
270270
excinfo.value)
271271

272272

@@ -328,7 +328,7 @@ def test_invalid_type_for_array_err():
328328
}
329329
with raises(ValueError) as excinfo:
330330
deserialize_structure(Example, data)
331-
assert "array: must be an iterable" in str(excinfo.value)
331+
assert "array: must be list, set, or tuple" in str(excinfo.value)
332332

333333

334334
def test_array_has_simple_item_in_definition():
@@ -403,7 +403,7 @@ class Foo(Structure):
403403

404404
def test_multifield_with_diffrerent_types_no_match():
405405
class Foo(Structure):
406-
any = AnyOf[Map, Set, String]
406+
any = AnyOf[Map, Set[String], String]
407407

408408
with raises(ValueError) as excinfo:
409409
deserialize_structure(Foo, {'any': [1, 2, 3]})
@@ -538,7 +538,7 @@ class Foo(Structure):
538538
serialized = {'a': 3, 't': 4}
539539
with raises(ValueError) as excinfo:
540540
deserialize_structure(Foo, serialized)
541-
assert "t: must be an iterable" in str(excinfo.value)
541+
assert "t: must be list, set, or tuple" in str(excinfo.value)
542542

543543

544544
def test_deserialize_set_err2():
@@ -684,7 +684,7 @@ class Foo(Structure):
684684
assert foo == Foo(i=6, m={'x': 1, 'y': 2}, s='the string is Joe')
685685

686686

687-
def test_mapper_error():
687+
def test_mapper_error1():
688688
class Foo(Structure):
689689
m = Map
690690
s = String
@@ -708,6 +708,24 @@ class Foo(Structure):
708708
keep_undefined=False)
709709
assert "s: Got {'first': 'Joe', 'last': 'smith'}; Expected a string" in str(excinfo.value)
710710

711+
def test_mapper_error1():
712+
class Foo(Structure):
713+
m = Map
714+
s = String
715+
716+
mapper = ['m']
717+
718+
with raises(TypeError) as excinfo:
719+
deserialize_structure(Foo,
720+
{
721+
'a': {'b': {'x': 1, 'y': 2}},
722+
'name': {'first': 'Joe', 'last': 'smith'},
723+
'i': 3,
724+
'j': 4
725+
},
726+
mapper=mapper)
727+
assert "Mapper must be a mapping" in str(excinfo.value)
728+
711729

712730
def test_bad_path_in_mapper():
713731
class Foo(Structure):
@@ -785,6 +803,78 @@ class Foo(Structure):
785803
assert foo == Foo(i=7, m={'x': 1, 'y': 2}, s='the string is Joe')
786804

787805

806+
def test_mapper_in_list():
807+
class Foo(Structure):
808+
a = String
809+
i = Integer
810+
811+
class Bar(Structure):
812+
wrapped = Array[Foo]
813+
814+
mapper = {'wrapped._mapper': {'a': 'aaa', 'i': 'iii'}, 'wrapped': 'other'}
815+
deserializer = Deserializer(target_class=Bar, mapper=mapper)
816+
deserialized = deserializer.deserialize(
817+
{
818+
'other': [
819+
{'aaa': 'string1', 'iii': 1},
820+
{'aaa': 'string2', 'iii': 2}
821+
]
822+
},
823+
keep_undefined=False)
824+
825+
assert deserialized == Bar(wrapped=[Foo(a='string1', i=1), Foo(a='string2', i=2)])
826+
827+
828+
def test_mapper_in_embedded_structure():
829+
class Foo(Structure):
830+
a = String
831+
i = Integer
832+
s = StructureReference(st=String, arr=Array)
833+
834+
mapper = {'a': 'aaa', 'i': 'iii', 's._mapper':
835+
{"arr": FunctionCall(func=lambda x: x * 2, args=['xxx'])}}
836+
deserializer = Deserializer(target_class=Foo, mapper=mapper)
837+
deserialized = deserializer.deserialize(
838+
{
839+
'aaa': 'string',
840+
'iii': 1,
841+
's': {'st': 'string', 'xxx': [1, 2, 3]}},
842+
keep_undefined=False)
843+
844+
assert deserialized == Foo(a='string', i=1, s={'st': 'string', 'arr': [1, 2, 3, 1, 2, 3]})
845+
846+
847+
def test_deserialize_with_deep_mapper():
848+
class Foo(Structure):
849+
a = String
850+
i = Integer
851+
852+
class Bar(Structure):
853+
foo = Foo
854+
array = Array
855+
856+
class Example(Structure):
857+
bar = Bar
858+
number = Integer
859+
860+
mapper = {'bar._mapper': {'foo._mapper': {"i": FunctionCall(func=lambda x: x * 2)}}}
861+
deserializer = Deserializer(target_class=Example, mapper=mapper)
862+
deserialized = deserializer.deserialize(
863+
{
864+
"number": 1,
865+
"bar":
866+
{
867+
"foo": {
868+
"a": "string",
869+
"i": 10
870+
},
871+
"array": [1, 2]
872+
}
873+
},
874+
keep_undefined=False)
875+
assert deserialized == Example(number=1, bar=Bar(foo=Foo(a="string", i=20), array=[1, 2]))
876+
877+
788878
def test_deserializer_no_mapper():
789879
class Foo(Structure):
790880
m = Map
@@ -840,6 +930,6 @@ class Foo(Structure):
840930
a = Integer
841931
blah = Blah
842932

843-
input_dict = {'a': 3, 'blah': {'x': 3, 'y':4, 'z': 5}}
933+
input_dict = {'a': 3, 'blah': {'x': 3, 'y': 4, 'z': 5}}
844934
bar = deserialize_structure(Foo, input_dict)
845935
assert bar.blah.z == 5

tests/test_serialization.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,16 @@ class Foo(Structure):
233233
assert serialize(foo.m) == original
234234

235235

236+
def test_serialize_with_anything_field():
237+
class Foo(Structure):
238+
m = Map[String, Anything]
239+
240+
original = {'a': [1, 2, 3], 'b': 1}
241+
242+
foo = Foo(m=original)
243+
assert serialize(foo.m) == original
244+
245+
236246
def test_serialize_with_number(example, serialized_source):
237247
assert serialize(example.i) == serialized_source['i']
238248

@@ -358,6 +368,15 @@ class Foo(Structure):
358368
assert serialize(foo, mapper=mapper) == {'f': [999], 'i': '5.5'}
359369

360370

371+
def test_serialize_invalid_mapper_type():
372+
class Foo(Structure):
373+
i = Integer
374+
375+
with raises(TypeError) as excinfo:
376+
serialize(Foo(i=1), mapper=[1,2])
377+
assert 'Mapper must be a mapping' in str(excinfo.value)
378+
379+
361380
def test_serialize_with_mapper_error():
362381
def my_func(): pass
363382

@@ -478,3 +497,17 @@ class Foo(Structure):
478497
assert Serializer(source=foo.bar1).serialize() == {'x': 3, 'y': 4, 'z': 5}
479498
s = Serializer(source=foo.bar2)
480499
assert s.serialize() == None
500+
501+
502+
def test_serialize_enum_field_directly():
503+
class Values(enum.Enum):
504+
ABC = enum.auto()
505+
DEF = enum.auto()
506+
GHI = enum.auto()
507+
508+
509+
class Foo(Structure):
510+
arr = Array[Enum[Values]]
511+
512+
foo = Foo(arr=[Values.ABC, Values.DEF])
513+
assert serialize(foo.arr[0]) == 'ABC'

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ deps =
2626
commands =
2727
coverage erase
2828
coverage run --source {toxinidir}/typedpy/ -m pytest {toxinidir}/tests/
29-
coverage report --m > coverage.txt
29+
coverage report -m > coverage.txt
3030
coverage html
31-
coverage-badge -o coverage.svg
31+
# coverage-badge -o coverage.svg
3232
deps =
3333
coverage-badge
3434
pytest-cov

0 commit comments

Comments
 (0)