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


Discussion:
github-actions[bot]: Please consider formatting your code according to the standards described here: https://github.com/yukinarit/pyserde/blob/master/CONTRIBUTING.md
codecov[bot]: # [Codecov](https://codecov.io/gh/yukinarit/pyserde/pull/208?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit) Report
> Merging [#208](https://codecov.io/gh/yukinarit/pyserde/pull/208?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit) (bfbfe59) into [master](https://codecov.io/gh/yukinarit/pyserde/commit/17419d2bc41d58c8cc7fee326ccb75b22f84adbc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit) (17419d2) will **increase** coverage by `0.04%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master     #208      +/-   ##
==========================================
+ Coverage   92.56%   92.60%   +0.04%     
==========================================
  Files          10       10              
  Lines        1250     1257       +7     
  Branches      272      275       +3     
==========================================
+ Hits         1157     1164       +7     
  Misses         59       59              
  Partials       34       34              
```


| [Impacted Files](https://codecov.io/gh/yukinarit/pyserde/pull/208?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit) | Coverage Δ | |
|---|---|---|
| [serde/compat.py](https://codecov.io/gh/yukinarit/pyserde/pull/208/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit#diff-c2VyZGUvY29tcGF0LnB5) | `87.00% <100.00%> (+0.31%)` | :arrow_up: |
| [serde/de.py](https://codecov.io/gh/yukinarit/pyserde/pull/208/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit#diff-c2VyZGUvZGUucHk=) | `97.43% <100.00%> (+<0.01%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/yukinarit/pyserde/pull/208?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/yukinarit/pyserde/pull/208?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit). Last update [2ab1173...bfbfe59](https://codecov.io/gh/yukinarit/pyserde/pull/208?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yukinarit).


Patch:
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(_, __):