Skip to content

Commit f4ab380

Browse files
authored
Merge pull request #486 from yukinarit/reuse-instances
Fix reuse_instances for non dataclass objects
2 parents a517254 + 43dca1d commit f4ab380

File tree

8 files changed

+59
-20
lines changed

8 files changed

+59
-20
lines changed

serde/core.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,13 @@ def _generate_class(self, cls: Type[Any]) -> Type[Any]:
150150
logger.debug(f"(de)serializing code for {class_name} was generated")
151151
return wrapper
152152

153-
def serialize(self, cls: Type[Any], obj: Any) -> Any:
153+
def serialize(self, cls: Type[Any], obj: Any, **kwargs: Any) -> Any:
154154
"""
155155
Serialize the specified type of object into dict or tuple.
156156
"""
157157
wrapper = self._get_class(cls)
158158
scope: Scope = getattr(wrapper, SERDE_SCOPE)
159-
data = scope.funcs[TO_DICT](wrapper(obj), reuse_instances=False, convert_sets=True)
159+
data = scope.funcs[TO_DICT](wrapper(obj), **kwargs)
160160

161161
logging.debug(f"Intermediate value: {data}")
162162

serde/json.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ def deserialize(cls, data: AnyStr, **opts: Any) -> Any:
5252

5353

5454
def to_json(
55-
obj: Any, cls: Optional[Any] = None, se: Type[Serializer[str]] = JsonSerializer, **opts: Any
55+
obj: Any,
56+
cls: Optional[Any] = None,
57+
se: Type[Serializer[str]] = JsonSerializer,
58+
reuse_instances: bool = False,
59+
convert_sets: bool = True,
60+
**opts: Any,
5661
) -> str:
5762
"""
5863
Serialize the object into JSON str. [orjson](https://github.com/ijl/orjson)
@@ -67,7 +72,9 @@ def to_json(
6772
If you want to use another json package, you can subclass `JsonSerializer` and implement
6873
your own logic.
6974
"""
70-
return se.serialize(to_dict(obj, c=cls, reuse_instances=False, convert_sets=True), **opts)
75+
return se.serialize(
76+
to_dict(obj, c=cls, reuse_instances=reuse_instances, convert_sets=convert_sets), **opts
77+
)
7178

7279

7380
@overload

serde/msgpack.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def to_msgpack(
4545
se: Type[Serializer[bytes]] = MsgPackSerializer,
4646
named: bool = True,
4747
ext_dict: Optional[Dict[Type[Any], int]] = None,
48+
reuse_instances: bool = False,
49+
convert_sets: bool = True,
4850
**opts: Any,
4951
) -> bytes:
5052
"""
@@ -68,7 +70,7 @@ def to_msgpack(
6870
if ext_type_code is None:
6971
raise SerdeError(f"Could not find type code for {obj_type.__name__} in ext_dict")
7072

71-
kwargs: Any = {"c": cls, "reuse_instances": False, "convert_sets": True}
73+
kwargs: Any = {"c": cls, "reuse_instances": reuse_instances, "convert_sets": convert_sets}
7274
dict_or_tuple = to_dict(obj, **kwargs) if named else to_tuple(obj, **kwargs)
7375
return se.serialize(
7476
dict_or_tuple,

serde/pickle.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,16 @@ def deserialize(cls, data: bytes, **opts: Any) -> Any:
2424

2525

2626
def to_pickle(
27-
obj: Any, cls: Optional[Any] = None, se: Type[Serializer[bytes]] = PickleSerializer, **opts: Any
27+
obj: Any,
28+
cls: Optional[Any] = None,
29+
se: Type[Serializer[bytes]] = PickleSerializer,
30+
reuse_instances: bool = False,
31+
convert_sets: bool = True,
32+
**opts: Any,
2833
) -> bytes:
29-
return se.serialize(to_dict(obj, c=cls, reuse_instances=False), **opts)
34+
return se.serialize(
35+
to_dict(obj, c=cls, reuse_instances=reuse_instances, convert_sets=convert_sets), **opts
36+
)
3037

3138

3239
@overload

serde/se.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ def serializable_to_obj(object: Any) -> Any:
368368
if is_dataclass_without_se(o):
369369
serialize(type(o))
370370
return serializable_to_obj(o)
371-
if is_serializable(o):
371+
elif is_serializable(o):
372372
return serializable_to_obj(o)
373373
elif isinstance(o, list):
374374
return [thisfunc(e) for e in o]
@@ -378,10 +378,10 @@ def serializable_to_obj(object: Any) -> Any:
378378
return [thisfunc(e) for e in o]
379379
elif isinstance(o, dict):
380380
return {k: thisfunc(v) for k, v in o.items()}
381-
elif is_str_serializable_instance(o):
382-
return str(o)
383-
elif is_datetime_instance(o):
384-
return o.isoformat()
381+
elif is_str_serializable_instance(o) or is_datetime_instance(o):
382+
return CACHE.serialize(
383+
c or o.__class__, o, reuse_instances=reuse_instances, convert_sets=convert_sets
384+
)
385385

386386
return o
387387

serde/toml.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ def deserialize(cls, data: str, **opts: Any) -> Any:
3434

3535

3636
def to_toml(
37-
obj: Any, cls: Optional[Any] = None, se: Type[Serializer[str]] = TomlSerializer, **opts: Any
37+
obj: Any,
38+
cls: Optional[Any] = None,
39+
se: Type[Serializer[str]] = TomlSerializer,
40+
reuse_instances: bool = False,
41+
convert_sets: bool = True,
42+
**opts: Any,
3843
) -> str:
3944
"""
4045
Serialize the object into TOML.
@@ -45,7 +50,9 @@ def to_toml(
4550
If you want to use the other toml package, you can subclass `TomlSerializer` and implement
4651
your own logic.
4752
"""
48-
return se.serialize(to_dict(obj, c=cls, reuse_instances=False), **opts)
53+
return se.serialize(
54+
to_dict(obj, c=cls, reuse_instances=reuse_instances, convert_sets=convert_sets), **opts
55+
)
4956

5057

5158
@overload

serde/yaml.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ def deserialize(cls, data: str, **opts: Any) -> Any:
2626

2727

2828
def to_yaml(
29-
obj: Any, cls: Optional[Any] = None, se: Type[Serializer[str]] = YamlSerializer, **opts: Any
29+
obj: Any,
30+
cls: Optional[Any] = None,
31+
se: Type[Serializer[str]] = YamlSerializer,
32+
reuse_instances: bool = False,
33+
convert_sets: bool = True,
34+
**opts: Any,
3035
) -> str:
3136
"""
3237
Serialize the object into YAML.
@@ -37,7 +42,9 @@ def to_yaml(
3742
If you want to use the other yaml package, you can subclass `YamlSerializer` and implement
3843
your own logic.
3944
"""
40-
return se.serialize(to_dict(obj, c=cls, reuse_instances=False), **opts)
45+
return se.serialize(
46+
to_dict(obj, c=cls, reuse_instances=reuse_instances, convert_sets=convert_sets), **opts
47+
)
4148

4249

4350
@overload

tests/test_basics.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import enum
33
import logging
44
import uuid
5+
import datetime
56
from beartype.roar import BeartypeCallHintViolation
67
from typing import (
78
ClassVar,
@@ -111,6 +112,16 @@ class C:
111112
assert c == de(C, se(c, reuse_instances=True), reuse_instances=True)
112113

113114

115+
def test_non_dataclass_reuse_instances() -> None:
116+
dt = datetime.datetime.fromisoformat("2020-01-01 00:00:00+00:00")
117+
assert "2020-01-01T00:00:00+00:00" == serde.to_dict(dt, reuse_instances=False) # type: ignore
118+
assert dt == serde.to_dict(dt, reuse_instances=True)
119+
assert dt is serde.to_dict(dt, reuse_instances=True)
120+
assert "2020-01-01T00:00:00+00:00" == serde.to_tuple(dt, reuse_instances=False) # type: ignore
121+
assert dt == serde.to_dict(dt, reuse_instances=True)
122+
assert dt is serde.to_dict(dt, reuse_instances=True)
123+
124+
114125
def test_non_dataclass():
115126
@serde.serde
116127
class Foo:
@@ -480,10 +491,8 @@ class Foo:
480491
class Foo:
481492
v: Set[int]
482493

483-
# TODO: Should raise SerdeError
484-
with pytest.raises(TypeError):
485-
f = Foo({1, 2, 3})
486-
serde.toml.to_toml(f)
494+
f = Foo({1, 2, 3})
495+
serde.toml.to_toml(f)
487496

488497

489498
@pytest.mark.parametrize("se,de", all_formats)

0 commit comments

Comments
 (0)