Repository: yukinarit/pyserde

Issue:
ArgumentError for List when using custom deserializer
Hey,

thanks for the fast fix on #190 ! While testing the fix I found the following:

```python
from serde import serde, SerdeSkip
from serde.json import from_json
from dataclasses import dataclass
from typing import Optional


def des(cls, o):
  raise SerdeSkip()

@serde(deserializer=des)
@dataclass
class Foo:
  a: list[str]

print(from_json(Foo, '{"a": []}'))      # -> Foo(a=[])
print(from_json(Foo, '{"a": ["foo"]}')) # -> AttributeError: 'str' object has no attribute 'get'
```

Greetings


Comments:

Patch:
diff --git a/pyproject.toml b/pyproject.toml
index c19a5cf..51ec0ca 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -54,7 +54,8 @@ pytest-cov = "*"
 pytest-watch = "*"
 pytest-flake8 = "*"
 coverage = "==4.5.4"
-pdoc = { version = "~=8" }
+pdoc = "~=11"
+pygments = "<=2.11.2"
 mypy = { version = "==0.931", markers = "platform_python_implementation!='PyPy'" }
 more-itertools = "~=8.6.0"
 pre-commit = "==v2.10.1"
diff --git a/serde/compat.py b/serde/compat.py
index 8c1af36..b5e2143 100644
--- a/serde/compat.py
+++ b/serde/compat.py
@@ -121,6 +121,11 @@ def typename(typ) -> str:
         else:
             return 'Union'
     elif is_list(typ):
+        # Workaround for python 3.7.
+        # get_args for the bare List returns parameter T.
+        if typ is List:
+            return 'List'
+
         args = type_args(typ)
         if args:
             et = typename(args[0])
@@ -128,6 +133,11 @@ def typename(typ) -> str:
         else:
             return 'List'
     elif is_set(typ):
+        # Workaround for python 3.7.
+        # get_args for the bare Set returns parameter T.
+        if typ is Set:
+            return 'Set'
+
         args = type_args(typ)
         if args:
             et = typename(args[0])
@@ -135,6 +145,11 @@ def typename(typ) -> str:
         else:
             return 'Set'
     elif is_dict(typ):
+        # Workaround for python 3.7.
+        # get_args for the bare Dict returns parameter K, V.
+        if typ is Dict:
+            return 'Dict'
+
         args = type_args(typ)
         if args and len(args) == 2:
             kt = typename(args[0])
diff --git a/serde/de.py b/serde/de.py
index e76660b..3416505 100644
--- a/serde/de.py
+++ b/serde/de.py
@@ -562,7 +562,9 @@ class Renderer:
             res = self.default(arg, res)
 
         if self.custom and not arg.deserializer:
-            return self.custom_class_deserializer(arg, res)
+            # Rerender the code for default deserializer.
+            default = Renderer(self.func, self.cls, None).render(arg)
+            return self.custom_class_deserializer(arg, default)
         else:
             return res
 


Test patch:
diff --git a/tests/test_compat.py b/tests/test_compat.py
index f0819ff..8991865 100644
--- a/tests/test_compat.py
+++ b/tests/test_compat.py
@@ -1,5 +1,6 @@
 import sys
 from dataclasses import dataclass
+from datetime import datetime
 from typing import Dict, Generic, List, NewType, Optional, Set, Tuple, TypeVar, Union
 
 import serde
@@ -67,9 +68,18 @@ def test_typename():
     class Foo(Generic[T]):
         nested: Bar[T]
 
-    assert typename(Optional) == "Optional"
     assert typename(Foo[int]) == "Foo"
     assert typename(Foo) == "Foo"
+    assert typename(List[int]) == "List[int]"
+    assert typename(Optional) == "Optional"
+    assert typename(List) == "List"
+    assert typename(List[int]) == "List[int]"
+    assert typename(Tuple) == "Tuple"
+    assert typename(Tuple[int, str]) == "Tuple[int, str]"
+    assert typename(Dict) == "Dict"
+    assert typename(Dict[str, Foo]) == "Dict[str, Foo]"
+    assert typename(Set) == "Set"
+    assert typename(Set[int]) == "Set[int]"
 
 
 def test_iter_types():
@@ -79,6 +89,17 @@ def test_iter_types():
     assert [Tuple, int, str, bool, float] == list(iter_types(Tuple[int, str, bool, float]))
     assert [PriOpt, Optional, int, Optional, str, Optional, float, Optional, bool] == list(iter_types(PriOpt))
 
+    @serde.serde
+    class Foo:
+        a: int
+        b: datetime
+        c: datetime
+        d: Optional[str] = None
+        e: Union[str, int] = 10
+        f: List[int] = serde.field(default_factory=list)
+
+    assert [Foo, int, datetime, datetime, Optional, str, Union, str, int, List, int] == list(iter_types(Foo))
+
 
 def test_iter_unions():
     assert [Union[str, int]] == list(iter_unions(Union[str, int]))
diff --git a/tests/test_custom.py b/tests/test_custom.py
index 060f046..dc75515 100644
--- a/tests/test_custom.py
+++ b/tests/test_custom.py
@@ -2,7 +2,7 @@
 Tests for custom serializer/deserializer.
 """
 from datetime import datetime
-from typing import Optional, Union
+from typing import List, Optional, Union
 
 import pytest
 
@@ -81,14 +81,15 @@ def test_custom_class_serializer():
         c: datetime
         d: Optional[str] = None
         e: Union[str, int] = 10
+        f: List[int] = field(default_factory=list)
 
     dt = datetime(2021, 1, 1, 0, 0, 0)
-    f = Foo(10, dt, dt)
+    f = Foo(10, dt, dt, f=[1, 2, 3])
 
-    assert to_json(f) == '{"a": 10, "b": "01/01/21", "c": "01/01/21", "d": null, "e": 10}'
+    assert to_json(f) == '{"a": 10, "b": "01/01/21", "c": "01/01/21", "d": null, "e": 10, "f": [1, 2, 3]}'
     assert f == from_json(Foo, to_json(f))
 
-    assert to_tuple(f) == (10, '01/01/21', '01/01/21', None, 10)
+    assert to_tuple(f) == (10, '01/01/21', '01/01/21', None, 10, [1, 2, 3])
     assert f == from_tuple(Foo, to_tuple(f))
 
     def fallback(_, __):