serializable 1.3.4__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {serializable-1.3.4/serializable.egg-info → serializable-1.4.0}/PKG-INFO +1 -1
- {serializable-1.3.4 → serializable-1.4.0}/serializable/dataclass_serializable.py +15 -2
- {serializable-1.3.4 → serializable-1.4.0}/serializable/serializable.py +15 -2
- {serializable-1.3.4 → serializable-1.4.0}/serializable/version.py +1 -1
- {serializable-1.3.4 → serializable-1.4.0/serializable.egg-info}/PKG-INFO +1 -1
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_dataclass_serializable.py +24 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_object.py +39 -0
- {serializable-1.3.4 → serializable-1.4.0}/LICENSE +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/MANIFEST.in +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/README.md +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/pyproject.toml +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable/__init__.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable/helpers.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable/keyword_aliases.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable/primitive_types.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable/py.typed +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable.egg-info/SOURCES.txt +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable.egg-info/dependency_links.txt +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable.egg-info/requires.txt +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/serializable.egg-info/top_level.txt +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/setup.cfg +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/__init__.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/common.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_class.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_default_to_dict_implementation.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_function.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_keyword_aliases.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_namedtuple.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_nesting.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_pickle.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_primitive_values.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_set.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_tuple.py +0 -0
- {serializable-1.3.4 → serializable-1.4.0}/tests/test_typing.py +0 -0
|
@@ -82,8 +82,21 @@ class DataclassSerializable:
|
|
|
82
82
|
|
|
83
83
|
@classmethod
|
|
84
84
|
def from_json(cls: type[_T], json_string: str) -> _T:
|
|
85
|
-
"""Reconstruct an
|
|
86
|
-
|
|
85
|
+
"""Reconstruct an instance of this class, or of a subclass, from a
|
|
86
|
+
string produced by ``to_json``. The class built is the one the JSON
|
|
87
|
+
names.
|
|
88
|
+
|
|
89
|
+
Raises ``ValueError`` if the JSON holds anything else, such as
|
|
90
|
+
another class or a plain dict. Use the module-level
|
|
91
|
+
``serializable.from_json`` to load JSON whose type isn't known in
|
|
92
|
+
advance."""
|
|
93
|
+
obj = from_json(json_string)
|
|
94
|
+
if not isinstance(obj, cls):
|
|
95
|
+
raise ValueError(
|
|
96
|
+
f"{cls.__name__}.from_json: the JSON holds {type(obj).__name__}, "
|
|
97
|
+
f"not {cls.__name__}; use serializable.from_json to load any type"
|
|
98
|
+
)
|
|
99
|
+
return obj
|
|
87
100
|
|
|
88
101
|
def __reduce__(self) -> tuple[Any, ...]:
|
|
89
102
|
"""Pickle via the same to_dict / from_dict path used for JSON so
|
|
@@ -100,9 +100,20 @@ class Serializable:
|
|
|
100
100
|
@classmethod
|
|
101
101
|
def from_json(cls: type[_T], json_string: str) -> _T:
|
|
102
102
|
"""
|
|
103
|
-
Reconstruct an instance from a JSON
|
|
103
|
+
Reconstruct an instance of this class, or of a subclass, from a JSON
|
|
104
|
+
string written by to_json. The class built is the one the JSON names.
|
|
105
|
+
|
|
106
|
+
Raises ValueError if the JSON holds anything else, such as another
|
|
107
|
+
class or a plain dict. Use the module-level serializable.from_json to
|
|
108
|
+
load JSON whose type isn't known in advance.
|
|
104
109
|
"""
|
|
105
|
-
|
|
110
|
+
obj = from_json(json_string)
|
|
111
|
+
if not isinstance(obj, cls):
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f"{cls.__name__}.from_json: the JSON holds {type(obj).__name__}, "
|
|
114
|
+
f"not {cls.__name__}; use serializable.from_json to load any type"
|
|
115
|
+
)
|
|
116
|
+
return obj
|
|
106
117
|
|
|
107
118
|
def write_json_file(self, path: Union[str, os.PathLike[str]]) -> None:
|
|
108
119
|
"""
|
|
@@ -115,6 +126,8 @@ class Serializable:
|
|
|
115
126
|
def read_json_file(cls: type[_T], path: Union[str, os.PathLike[str]]) -> _T:
|
|
116
127
|
"""
|
|
117
128
|
Construct an instance of this class from the JSON file at `path`.
|
|
129
|
+
|
|
130
|
+
Raises ValueError, as from_json does, if the file holds another type.
|
|
118
131
|
"""
|
|
119
132
|
with open(path) as f:
|
|
120
133
|
json_string = f.read()
|
|
@@ -182,3 +182,27 @@ def test_interop_with_legacy_serializable():
|
|
|
182
182
|
def test_nested_dataclass_serializable_roundtrip():
|
|
183
183
|
o = Outer(name="parent", inner=Inner(n=7))
|
|
184
184
|
eq_(Outer.from_json(o.to_json()), o)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass
|
|
188
|
+
class LabeledPoint(Point):
|
|
189
|
+
label: str = ""
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_from_json_returns_subclass_named_in_json():
|
|
193
|
+
p = LabeledPoint(1.0, 2.0, label="a")
|
|
194
|
+
restored = Point.from_json(p.to_json())
|
|
195
|
+
eq_(type(restored), LabeledPoint)
|
|
196
|
+
eq_(restored, p)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def test_from_json_rejects_other_class():
|
|
200
|
+
person_json = Person(name="Ada", age=36).to_json()
|
|
201
|
+
with pytest.raises(ValueError, match="holds Person, not Point"):
|
|
202
|
+
Point.from_json(person_json)
|
|
203
|
+
eq_(from_json(person_json), Person(name="Ada", age=36))
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def test_from_json_rejects_json_without_class():
|
|
207
|
+
with pytest.raises(ValueError, match="holds dict, not Point"):
|
|
208
|
+
Point.from_json('{"x": 1.0, "y": 2.0}')
|
|
@@ -12,9 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
import pickle
|
|
14
14
|
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
15
17
|
from serializable import (
|
|
16
18
|
Serializable,
|
|
19
|
+
from_json,
|
|
17
20
|
from_serializable_repr,
|
|
21
|
+
to_json,
|
|
18
22
|
to_serializable_repr,
|
|
19
23
|
)
|
|
20
24
|
|
|
@@ -30,6 +34,15 @@ class A(Serializable):
|
|
|
30
34
|
return {"x": self.x, "y": self.y}
|
|
31
35
|
|
|
32
36
|
|
|
37
|
+
class B(A):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Other(Serializable):
|
|
42
|
+
def __init__(self, x):
|
|
43
|
+
self.x = x
|
|
44
|
+
|
|
45
|
+
|
|
33
46
|
instance = A(1, 2)
|
|
34
47
|
|
|
35
48
|
|
|
@@ -50,3 +63,29 @@ def test_object_json_file(tmp_path):
|
|
|
50
63
|
path = tmp_path / "instance.json"
|
|
51
64
|
instance.write_json_file(path)
|
|
52
65
|
eq_(instance, A.read_json_file(path))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_from_json_returns_subclass_named_in_json():
|
|
69
|
+
derived = B(1, 2)
|
|
70
|
+
restored = A.from_json(derived.to_json())
|
|
71
|
+
eq_(type(restored), B)
|
|
72
|
+
eq_(restored, derived)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_from_json_rejects_other_class():
|
|
76
|
+
other_json = Other(1).to_json()
|
|
77
|
+
with pytest.raises(ValueError, match="holds Other, not A"):
|
|
78
|
+
A.from_json(other_json)
|
|
79
|
+
eq_(from_json(other_json), Other(1))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_from_json_rejects_json_without_class():
|
|
83
|
+
with pytest.raises(ValueError, match="holds dict, not A"):
|
|
84
|
+
A.from_json(to_json({"x": 1, "y": 2}))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_read_json_file_rejects_other_class(tmp_path):
|
|
88
|
+
path = tmp_path / "other.json"
|
|
89
|
+
Other(1).write_json_file(path)
|
|
90
|
+
with pytest.raises(ValueError, match="holds Other, not A"):
|
|
91
|
+
A.read_json_file(path)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|