Repository: yukinarit/pyserde (python)

Issue:
Aliases are not checked when a default value is provided
When providing a default value, pyserde doesn't check any aliases.

Example:
```python
from dataclasses import dataclass

from serde import field, serde
from serde.json import from_json


@serde
@dataclass
class Foo:
    a: int = field(default=2, alias=["b", "c", "d"])


def main():
    s = '{"a": 10}'
    print(f"From Json: {from_json(Foo, s)}")
    # Expected: From Json: Foo(a=10)
    # Actual: From Json: Foo(a=10)

    s = '{"b": 20}'
    print(f"From Json: {from_json(Foo, s)}")
    # Expected: From Json: Foo(a=20)
    # Actual: From Json: Foo(a=2)


if __name__ == "__main__":
    main()
```

Pull request:
Fix combination of alias and default
Fixes #353 

Patch:
diff --git a/serde/core.py b/serde/core.py
index cdb7f08..63420b3 100644
--- a/serde/core.py
+++ b/serde/core.py
@@ -335,7 +335,7 @@ def field(
     skip_if_default: Optional[bool] = None,
     serializer=None,
     deserializer=None,
-    flatten: Optional[Union[FlattenOpts, bool]] = None,
+    flatten: Optional[FlattenOpts] = None,
     metadata=None,
     **kwargs,
 ):
@@ -361,9 +361,7 @@ def field(
         metadata["serde_serializer"] = serializer
     if deserializer:
         metadata["serde_deserializer"] = deserializer
-    if flatten is True:
-        metadata["serde_flatten"] = FlattenOpts()
-    elif flatten:
+    if flatten:
         metadata["serde_flatten"] = flatten
 
     return dataclasses.field(*args, metadata=metadata, **kwargs)
diff --git a/serde/de.py b/serde/de.py
index 778ac82..3d1dc7f 100644
--- a/serde/de.py
+++ b/serde/de.py
@@ -123,6 +123,13 @@ def _get_by_aliases(d: Dict[str, str], aliases: List[str]):
         return _get_by_aliases(d, aliases[1:])
 
 
+def _exists_by_aliases(d: Dict[str, str], aliases: List[str]):
+    for alias in aliases:
+        if alias in d:
+            return True
+    return False
+
+
 def _make_deserialize(
     cls_name: str,
     fields,
@@ -262,6 +269,7 @@ def deserialize(
         g['NoCheck'] = NoCheck
         g['coerce'] = coerce
         g['_get_by_aliases'] = _get_by_aliases
+        g['_exists_by_aliases'] = _exists_by_aliases
         if deserialize:
             g['serde_custom_class_deserializer'] = functools.partial(
                 serde_custom_class_deserializer, custom=deserializer
@@ -877,7 +885,11 @@ for k, v in data["f"].items()}'
         return f"serde_scope.funcs['{func_name}'](cls=cls, data={arg.data}, reuse_instances=reuse_instances)"
 
     def default(self, arg: DeField, code: str) -> str:
-        exists = f'"{arg.conv_name()}" in {arg.datavar}'
+        if arg.alias:
+            aliases = map(lambda s: f'"{s}"', [arg.name, *arg.alias])
+            exists = f'_exists_by_aliases({arg.datavar}, [{",".join(aliases)}])'
+        else:
+            exists = f'"{arg.conv_name()}" in {arg.datavar}'
         if has_default(arg):
             return f'({code}) if {exists} else serde_scope.defaults["{arg.name}"]'
         elif has_default_factory(arg):


Test patch:
diff --git a/tests/test_basics.py b/tests/test_basics.py
index 2902b82..277caca 100644
--- a/tests/test_basics.py
+++ b/tests/test_basics.py
@@ -4,7 +4,17 @@ import enum
 import logging
 import pathlib
 import uuid
-from typing import ClassVar, DefaultDict, Dict, FrozenSet, List, Optional, Set, Tuple, Union
+from typing import (
+    ClassVar,
+    DefaultDict,
+    Dict,
+    FrozenSet,
+    List,
+    Optional,
+    Set,
+    Tuple,
+    Union,
+)
 
 import pytest
 
@@ -536,6 +546,45 @@ def test_rename_and_alias():
     assert ff.a == 10
 
 
+def test_default_and_alias():
+    @serde.serde
+    class Foo:
+        a: int = serde.field(default=2, alias=["b", "c", "d"])
+
+    f = Foo(a=1)
+    assert '{"a":1}' == serde.json.to_json(f)
+    ff = serde.json.from_json(Foo, '{"b":10}')
+    assert ff.a == 10
+    ff = serde.json.from_json(Foo, '{"e":10}')
+    assert ff.a == 2
+
+
+def test_default_and_rename():
+    @serde.serde
+    class Foo:
+        a: int = serde.field(default=2, rename="z")
+
+    f = Foo(a=1)
+    assert '{"z":1}' == serde.json.to_json(f)
+    ff = serde.json.from_json(Foo, '{"z":10}')
+    assert ff.a == 10
+    fff = serde.json.from_json(Foo, '{"a":10}')
+    assert fff.a == 2
+
+
+def test_default_rename_and_alias():
+    @serde.serde
+    class Foo:
+        a: int = serde.field(default=2, rename="z", alias=["b", "c", "d"])
+
+    f = Foo(a=1)
+    assert '{"z":1}' == serde.json.to_json(f)
+    ff = serde.json.from_json(Foo, '{"b":10}')
+    assert ff.a == 10
+    ff = serde.json.from_json(Foo, '{"e":10}')
+    assert ff.a == 2
+
+
 @pytest.mark.parametrize(
     'se,de', (format_dict + format_json + format_msgpack + format_yaml + format_toml + format_pickle)
 )