Repository: yukinarit/pyserde
Pull request #18 (2020-04-23 09:20:27)

fix: astuple incorrectly deserialize dict


Patch:
diff --git a/serde/se.py b/serde/se.py
index f9edfc1..f0e5e81 100644
--- a/serde/se.py
+++ b/serde/se.py
@@ -115,6 +115,38 @@ def is_serializable(instance_or_class: Any) -> bool:
     return hasattr(instance_or_class, '__serde_serialize__')
 
 
+def astuple(v):
+    """
+    Convert `serialize` class to `tuple`.
+    """
+    if is_serializable(v):
+        return getattr(v, TO_ITER)()
+    elif is_dataclass(v):
+        return _astuple(v)
+    elif isinstance(v, Dict):
+        return {astuple(k): astuple(v) for k, v in v.items()}
+    elif isinstance(v, (Tuple, List)):
+        return tuple(astuple(e) for e in v)
+    else:
+        return v
+
+
+def asdict(v):
+    """
+    Convert `serialize` class to `dict`.
+    """
+    if is_serializable(v):
+        return getattr(v, TO_DICT)()
+    elif is_dataclass(v):
+        return _asdict(v)
+    elif isinstance(v, Dict):
+        return {asdict(k): asdict(v) for k, v in v.items()}
+    elif isinstance(v, (Tuple, List)):
+        return tuple(asdict(e) for e in v)
+    else:
+        return v
+
+
 def to_dict(o) -> Dict:
     """
     Convert object into dictionary.
@@ -152,38 +184,6 @@ def to_dict(o) -> Dict:
     return v
 
 
-def astuple(v):
-    """
-    Convert `serialize` class to `tuple`.
-    """
-    if is_serializable(v):
-        return getattr(v, TO_ITER)()
-    elif is_dataclass(v):
-        return _astuple(v)
-    elif isinstance(v, Dict):
-        return tuple(astuple(e) for e in v.values())
-    elif isinstance(v, (Tuple, List)):
-        return tuple(astuple(e) for e in v)
-    else:
-        return v
-
-
-def asdict(v):
-    """
-    Convert `serialize` class to `dict`.
-    """
-    if is_serializable(v):
-        return getattr(v, TO_DICT)()
-    elif is_dataclass(v):
-        return _asdict(v)
-    elif isinstance(v, Dict):
-        return {asdict(k): asdict(v) for k, v in v.items()}
-    elif isinstance(v, (Tuple, List)):
-        return tuple(asdict(e) for e in v)
-    else:
-        return v
-
-
 @dataclass
 class SeField(Field):
     parent: Optional['Field'] = None


Test patch:
diff --git a/tests/test_basics.py b/tests/test_basics.py
index 94572b7..06f9ec8 100644
--- a/tests/test_basics.py
+++ b/tests/test_basics.py
@@ -2,7 +2,7 @@ import enum
 import json
 import logging
 from dataclasses import dataclass, field, fields
-from typing import Dict, List
+from typing import Dict, List, Tuple
 
 import pytest
 
@@ -257,12 +257,49 @@ def test_msgpack_named():
     assert p == from_msgpack(Pri, d, named=False)
 
 
+def test_from_dict():
+    p = Pri(10, 'foo', 100.0, True)
+    d = {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}
+    assert d == asdict(p)
+    assert p == from_dict(Pri, d)
+
+    p = {'p': Pri(10, 'foo', 100.0, True)}
+    d = {'p': {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}}
+    assert d == asdict(p)
+    assert p == from_dict(Dict[str, Pri], d)
+
+    p = [Pri(10, 'foo', 100.0, True)]
+    d = ({'i': 10, 's': 'foo', 'f': 100.0, 'b': True},)
+    assert d == asdict(p)
+    assert p == from_dict(List[Pri], d)
+
+    p = (Pri(10, 'foo', 100.0, True),)
+    d = ({'i': 10, 's': 'foo', 'f': 100.0, 'b': True},)
+    assert d == asdict(p)
+    assert p == from_dict(Tuple[Pri], d)
+
+
 def test_from_tuple():
     p = Pri(10, 'foo', 100.0, True)
     d = (10, 'foo', 100.0, True)
     assert d == astuple(p)
     assert p == from_tuple(Pri, d)
 
+    p = {'p': Pri(10, 'foo', 100.0, True)}
+    d = {'p': (10, 'foo', 100.0, True)}
+    assert d == astuple(p)
+    assert p == from_tuple(Dict[str, Pri], d)
+
+    p = [Pri(10, 'foo', 100.0, True)]
+    d = ((10, 'foo', 100.0, True),)
+    assert d == astuple(p)
+    assert p == from_tuple(List[Pri], d)
+
+    p = (Pri(10, 'foo', 100.0, True),)
+    d = ((10, 'foo', 100.0, True),)
+    assert d == astuple(p)
+    assert p == from_tuple(Tuple[Pri], d)
+
 
 @pytest.mark.parametrize('se,de', all_formats)
 def test_rename(se, de):