serializable 1.2.0__tar.gz → 1.3.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.
Files changed (31) hide show
  1. {serializable-1.2.0 → serializable-1.3.0}/PKG-INFO +8 -2
  2. {serializable-1.2.0 → serializable-1.3.0}/README.md +3 -0
  3. {serializable-1.2.0 → serializable-1.3.0}/pyproject.toml +13 -1
  4. {serializable-1.2.0 → serializable-1.3.0}/serializable/dataclass_serializable.py +10 -6
  5. {serializable-1.2.0 → serializable-1.3.0}/serializable/helpers.py +20 -11
  6. serializable-1.3.0/serializable/py.typed +0 -0
  7. {serializable-1.2.0 → serializable-1.3.0}/serializable/serializable.py +21 -15
  8. {serializable-1.2.0 → serializable-1.3.0}/serializable/version.py +1 -1
  9. {serializable-1.2.0 → serializable-1.3.0}/serializable.egg-info/PKG-INFO +8 -2
  10. {serializable-1.2.0 → serializable-1.3.0}/serializable.egg-info/SOURCES.txt +3 -1
  11. {serializable-1.2.0 → serializable-1.3.0}/serializable.egg-info/requires.txt +1 -1
  12. {serializable-1.2.0 → serializable-1.3.0}/tests/test_primitive_values.py +19 -1
  13. serializable-1.3.0/tests/test_typing.py +48 -0
  14. {serializable-1.2.0 → serializable-1.3.0}/LICENSE +0 -0
  15. {serializable-1.2.0 → serializable-1.3.0}/serializable/__init__.py +0 -0
  16. {serializable-1.2.0 → serializable-1.3.0}/serializable/primitive_types.py +0 -0
  17. {serializable-1.2.0 → serializable-1.3.0}/serializable.egg-info/dependency_links.txt +0 -0
  18. {serializable-1.2.0 → serializable-1.3.0}/serializable.egg-info/top_level.txt +0 -0
  19. {serializable-1.2.0 → serializable-1.3.0}/setup.cfg +0 -0
  20. {serializable-1.2.0 → serializable-1.3.0}/setup.py +0 -0
  21. {serializable-1.2.0 → serializable-1.3.0}/tests/test_class.py +0 -0
  22. {serializable-1.2.0 → serializable-1.3.0}/tests/test_dataclass_serializable.py +0 -0
  23. {serializable-1.2.0 → serializable-1.3.0}/tests/test_default_to_dict_implementation.py +0 -0
  24. {serializable-1.2.0 → serializable-1.3.0}/tests/test_function.py +0 -0
  25. {serializable-1.2.0 → serializable-1.3.0}/tests/test_keyword_aliases.py +0 -0
  26. {serializable-1.2.0 → serializable-1.3.0}/tests/test_namedtuple.py +0 -0
  27. {serializable-1.2.0 → serializable-1.3.0}/tests/test_nesting.py +0 -0
  28. {serializable-1.2.0 → serializable-1.3.0}/tests/test_object.py +0 -0
  29. {serializable-1.2.0 → serializable-1.3.0}/tests/test_pickle.py +0 -0
  30. {serializable-1.2.0 → serializable-1.3.0}/tests/test_set.py +0 -0
  31. {serializable-1.2.0 → serializable-1.3.0}/tests/test_tuple.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serializable
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Base class with serialization helpers for user-defined Python objects
5
5
  Author-email: Alex Rubinsteyn <alex.rubinsteyn@unc.edu>
6
6
  License: Apache License
@@ -217,15 +217,18 @@ Classifier: Programming Language :: Python :: 3.9
217
217
  Classifier: Programming Language :: Python :: 3.10
218
218
  Classifier: Programming Language :: Python :: 3.11
219
219
  Classifier: Programming Language :: Python :: 3.12
220
+ Classifier: Programming Language :: Python :: 3.13
221
+ Classifier: Programming Language :: Python :: 3.14
222
+ Classifier: Typing :: Typed
220
223
  Requires-Python: >=3.9
221
224
  Description-Content-Type: text/markdown
222
225
  License-File: LICENSE
223
226
  Requires-Dist: typechecks>=0.0.2
224
- Requires-Dist: simplejson>=3.0.0
225
227
  Provides-Extra: dev
226
228
  Requires-Dist: pytest; extra == "dev"
227
229
  Requires-Dist: pytest-cov; extra == "dev"
228
230
  Requires-Dist: ruff; extra == "dev"
231
+ Requires-Dist: mypy; extra == "dev"
229
232
  Requires-Dist: coveralls; extra == "dev"
230
233
  Requires-Dist: build; extra == "dev"
231
234
  Requires-Dist: twine; extra == "dev"
@@ -288,6 +291,9 @@ If you change the keyword arguments to a class which derives from `Serializable`
288
291
  The format records Python classes and modules, which must remain importable when
289
292
  loading data. Keys starting with two underscores are reserved. Nested Python
290
293
  values require the serialization helpers rather than ordinary JSON encoding.
294
+ `to_json` raises `ValueError` for NaN or infinite floats, which standard JSON
295
+ can't represent; `from_json` still reads the `NaN` / `Infinity` literals which
296
+ older versions wrote.
291
297
 
292
298
  ## Documentation
293
299
 
@@ -55,6 +55,9 @@ If you change the keyword arguments to a class which derives from `Serializable`
55
55
  The format records Python classes and modules, which must remain importable when
56
56
  loading data. Keys starting with two underscores are reserved. Nested Python
57
57
  values require the serialization helpers rather than ordinary JSON encoding.
58
+ `to_json` raises `ValueError` for NaN or infinite floats, which standard JSON
59
+ can't represent; `from_json` still reads the `NaN` / `Infinity` literals which
60
+ older versions wrote.
58
61
 
59
62
  ## Documentation
60
63
 
@@ -22,11 +22,13 @@ classifiers = [
22
22
  "Programming Language :: Python :: 3.10",
23
23
  "Programming Language :: Python :: 3.11",
24
24
  "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Typing :: Typed",
25
28
  ]
26
29
  requires-python = ">=3.9"
27
30
  dependencies = [
28
31
  "typechecks>=0.0.2",
29
- "simplejson>=3.0.0",
30
32
  ]
31
33
 
32
34
  [project.optional-dependencies]
@@ -34,6 +36,7 @@ dev = [
34
36
  "pytest",
35
37
  "pytest-cov",
36
38
  "ruff",
39
+ "mypy",
37
40
  "coveralls",
38
41
  "build",
39
42
  "twine",
@@ -49,6 +52,9 @@ version = {attr = "serializable.version.__version__"}
49
52
  [tool.setuptools.packages.find]
50
53
  include = ["serializable*"]
51
54
 
55
+ [tool.setuptools.package-data]
56
+ serializable = ["py.typed"]
57
+
52
58
  [tool.ruff]
53
59
  target-version = "py39"
54
60
  line-length = 100
@@ -81,6 +87,7 @@ ignore = [
81
87
  "B905", # zip() without explicit strict
82
88
  "SIM108", # use ternary operator instead of if-else
83
89
  "UP007", # use X | Y for type unions (need Python 3.10+)
90
+ "UP045", # use X | None for Optional (need Python 3.10+)
84
91
  ]
85
92
 
86
93
  [tool.ruff.lint.per-file-ignores]
@@ -96,6 +103,11 @@ ignore = [
96
103
  [tool.ruff.lint.isort]
97
104
  known-first-party = ["serializable"]
98
105
 
106
+ [tool.mypy]
107
+ # Annotations are never evaluated at runtime (`from __future__ import annotations`),
108
+ # so checking them as 3.10 also covers 3.9, which mypy 2 can no longer target.
109
+ python_version = "3.10"
110
+
99
111
  [tool.pytest.ini_options]
100
112
  testpaths = ["tests"]
101
113
  python_files = "test_*.py"
@@ -40,11 +40,14 @@ Example::
40
40
 
41
41
  from __future__ import annotations
42
42
 
43
+ from collections.abc import Mapping
43
44
  from dataclasses import fields
44
- from typing import Any, ClassVar
45
+ from typing import Any, ClassVar, Optional, TypeVar
45
46
 
46
47
  from .helpers import from_json, to_json
47
48
 
49
+ _T = TypeVar("_T", bound="DataclassSerializable")
50
+
48
51
 
49
52
  class DataclassSerializable:
50
53
  """Mixin providing ``to_dict`` / ``from_dict`` / ``to_json`` / ``from_json``
@@ -57,16 +60,17 @@ class DataclassSerializable:
57
60
  ``Serializable``.
58
61
  """
59
62
 
60
- _SERIALIZABLE_KEYWORD_ALIASES: ClassVar[dict[str, str | None]] = {}
63
+ _SERIALIZABLE_KEYWORD_ALIASES: ClassVar[dict[str, Optional[str]]] = {}
61
64
 
62
65
  def to_dict(self) -> dict[str, Any]:
63
66
  """Return a dict mapping each dataclass field name to its current
64
67
  value. Keys match the ``__init__`` keyword arguments, so
65
68
  ``cls(**obj.to_dict())`` reconstructs an equal instance."""
66
- return {f.name: getattr(self, f.name) for f in fields(self)}
69
+ # subclasses are dataclasses, the mixin itself isn't
70
+ return {f.name: getattr(self, f.name) for f in fields(self)} # type: ignore[arg-type]
67
71
 
68
72
  @classmethod
69
- def from_dict(cls, state_dict: dict[str, Any]):
73
+ def from_dict(cls: type[_T], state_dict: Mapping[str, Any]) -> _T:
70
74
  """Reconstruct an instance from a ``to_dict``-shaped dictionary,
71
75
  applying ``_SERIALIZABLE_KEYWORD_ALIASES`` for backwards compat."""
72
76
  kwargs = dict(state_dict)
@@ -83,10 +87,10 @@ class DataclassSerializable:
83
87
  return to_json(self)
84
88
 
85
89
  @classmethod
86
- def from_json(cls, json_string: str):
90
+ def from_json(cls: type[_T], json_string: str) -> _T:
87
91
  return from_json(json_string)
88
92
 
89
- def __reduce__(self):
93
+ def __reduce__(self) -> tuple[Any, ...]:
90
94
  """Pickle via the same to_dict / from_dict path used for JSON so
91
95
  pickled objects round-trip even when field order or internal
92
96
  representation changes between releases. Pickles written before
@@ -15,15 +15,17 @@ Helper functions for deconstructing classes, functions, and user-defined
15
15
  objects into serializable types.
16
16
  """
17
17
 
18
+ from __future__ import annotations
19
+
20
+ import json
18
21
  from functools import lru_cache
19
22
  from types import BuiltinFunctionType, FunctionType
20
-
21
- import simplejson as json
23
+ from typing import Any, Union
22
24
 
23
25
  from .primitive_types import PRIMITIVE_TYPES
24
26
 
25
27
 
26
- def init_arg_names(obj):
28
+ def init_arg_names(obj: Any) -> tuple[str, ...]:
27
29
  """
28
30
  Names of the positional arguments to the __init__ method of this
29
31
  object's class (excluding self).
@@ -31,7 +33,8 @@ def init_arg_names(obj):
31
33
  Keyword-only arguments are deliberately excluded, so a class can accept
32
34
  constructor options which aren't part of its serialized state.
33
35
  """
34
- return _class_init_arg_names(type(obj))
36
+ # mypy doesn't consider classes Hashable
37
+ return _class_init_arg_names(type(obj)) # type: ignore[arg-type]
35
38
 
36
39
 
37
40
  # Cached per class since this is called on every to_dict/__eq__/__hash__ of
@@ -53,7 +56,7 @@ def _class_init_arg_names(cls):
53
56
  return init_code.co_varnames[1 : init_code.co_argcount]
54
57
 
55
58
 
56
- def simple_object_to_dict(self):
59
+ def simple_object_to_dict(self: object) -> dict[str, Any]:
57
60
  return {name: getattr(self, name) for name in init_arg_names(self)}
58
61
 
59
62
 
@@ -246,7 +249,7 @@ def list_to_serializable_repr(x):
246
249
  return [to_serializable_repr(element) for element in x]
247
250
 
248
251
 
249
- def to_dict(obj):
252
+ def to_dict(obj: Any) -> dict[str, Any]:
250
253
  """
251
254
  If value isn't a primitive scalar or collection then it needs to
252
255
  either implement to_dict (instances of Serializable) or have member
@@ -262,7 +265,7 @@ def to_dict(obj):
262
265
  raise ValueError(f"Cannot convert {obj} : {type(obj)} to dictionary") from exc
263
266
 
264
267
 
265
- def to_serializable_repr(x):
268
+ def to_serializable_repr(x: Any) -> Any:
266
269
  """
267
270
  Convert an instance of Serializable or a primitive collection containing
268
271
  such instances into serializable types.
@@ -288,7 +291,7 @@ def to_serializable_repr(x):
288
291
  return state_dictionary
289
292
 
290
293
 
291
- def from_serializable_repr(x):
294
+ def from_serializable_repr(x: Any) -> Any:
292
295
  """
293
296
  Inverse of to_serializable_repr. Does not modify `x`.
294
297
  """
@@ -302,17 +305,23 @@ def from_serializable_repr(x):
302
305
  raise TypeError(f"Cannot convert {x} : {type(x)} from serializable representation to object")
303
306
 
304
307
 
305
- def to_json(x):
308
+ def to_json(x: Any) -> str:
306
309
  """
307
310
  Returns JSON representation of a given Serializable instance or
308
311
  other primitive object.
312
+
313
+ Raises ValueError for NaN or infinite floats, which have no standard
314
+ JSON representation.
309
315
  """
310
- return json.dumps(to_serializable_repr(x))
316
+ return json.dumps(to_serializable_repr(x), allow_nan=False)
311
317
 
312
318
 
313
- def from_json(json_string):
319
+ def from_json(json_string: Union[str, bytes]) -> Any:
314
320
  """
315
321
  Inverse of to_json. Objects are reconstructed bottom-up while parsing,
316
322
  equivalent to (but faster than) from_serializable_repr(json.loads(...)).
323
+
324
+ Accepts the NaN and Infinity literals which older versions of
325
+ serializable wrote.
317
326
  """
318
327
  return json.loads(json_string, object_hook=_from_reconstructed_dict)
File without changes
@@ -11,10 +11,16 @@
11
11
  # limitations under the License.
12
12
 
13
13
 
14
- from typing import ClassVar
14
+ from __future__ import annotations
15
+
16
+ import os
17
+ from collections.abc import Mapping
18
+ from typing import Any, ClassVar, Optional, TypeVar, Union
15
19
 
16
20
  from .helpers import from_json, simple_object_to_dict, to_json
17
21
 
22
+ _T = TypeVar("_T", bound="Serializable")
23
+
18
24
 
19
25
  class Serializable:
20
26
  """
@@ -24,17 +30,17 @@ class Serializable:
24
30
  Relies on the keys of to_dict() matching the arguments to __init__.
25
31
  """
26
32
 
27
- def __str__(self):
33
+ def __str__(self) -> str:
28
34
  fields = ", ".join(f"{k}={v}" for (k, v) in self.to_dict().items())
29
35
  return f"{self.__class__.__name__}({fields})"
30
36
 
31
- def __repr__(self):
37
+ def __repr__(self) -> str:
32
38
  return str(self)
33
39
 
34
- def __eq__(self, other):
40
+ def __eq__(self, other) -> bool:
35
41
  return self.__class__ is other.__class__ and self.to_dict() == other.to_dict()
36
42
 
37
- def to_dict(self):
43
+ def to_dict(self) -> dict[str, Any]:
38
44
  """
39
45
  Returns a dictionary which can be used to reconstruct an instance
40
46
  of a derived class (typically by matching args to __init__). The values
@@ -49,11 +55,11 @@ class Serializable:
49
55
  """
50
56
  return simple_object_to_dict(self)
51
57
 
52
- def __hash__(self):
58
+ def __hash__(self) -> int:
53
59
  return hash(tuple(sorted(self.to_dict().items())))
54
60
 
55
61
  @classmethod
56
- def _reconstruct_nested_objects(cls, state_dict):
62
+ def _reconstruct_nested_objects(cls, state_dict: dict[str, Any]) -> dict[str, Any]:
57
63
  """
58
64
  Nested serializable objects will be represented as dictionaries so we
59
65
  allow manual reconstruction of those objects in this method.
@@ -64,10 +70,10 @@ class Serializable:
64
70
 
65
71
  # dictionary mapping old keywords to either new names or
66
72
  # None if the keyword has been removed from a class
67
- _SERIALIZABLE_KEYWORD_ALIASES: ClassVar[dict] = {}
73
+ _SERIALIZABLE_KEYWORD_ALIASES: ClassVar[dict[str, Optional[str]]] = {}
68
74
 
69
75
  @classmethod
70
- def _update_kwargs(cls, kwargs):
76
+ def _update_kwargs(cls, kwargs: dict[str, Any]) -> dict[str, Any]:
71
77
  """
72
78
  Rename any old keyword arguments to preserve backwards compatibility
73
79
  """
@@ -83,7 +89,7 @@ class Serializable:
83
89
  return kwargs
84
90
 
85
91
  @classmethod
86
- def from_dict(cls, state_dict):
92
+ def from_dict(cls: type[_T], state_dict: Mapping[str, Any]) -> _T:
87
93
  """
88
94
  Given a dictionary of flattened fields (result of calling to_dict()),
89
95
  returns an instance. Does not modify `state_dict`.
@@ -91,20 +97,20 @@ class Serializable:
91
97
  state_dict = cls._reconstruct_nested_objects(dict(state_dict))
92
98
  return cls(**cls._update_kwargs(state_dict))
93
99
 
94
- def to_json(self):
100
+ def to_json(self) -> str:
95
101
  """
96
102
  Returns a string containing a JSON representation of this object.
97
103
  """
98
104
  return to_json(self)
99
105
 
100
106
  @classmethod
101
- def from_json(cls, json_string):
107
+ def from_json(cls: type[_T], json_string: str) -> _T:
102
108
  """
103
109
  Reconstruct an instance from a JSON string.
104
110
  """
105
111
  return from_json(json_string)
106
112
 
107
- def write_json_file(self, path):
113
+ def write_json_file(self, path: Union[str, os.PathLike[str]]) -> None:
108
114
  """
109
115
  Serialize this object to JSON and write it to the text file at `path`.
110
116
  """
@@ -112,7 +118,7 @@ class Serializable:
112
118
  f.write(self.to_json())
113
119
 
114
120
  @classmethod
115
- def read_json_file(cls, path):
121
+ def read_json_file(cls: type[_T], path: Union[str, os.PathLike[str]]) -> _T:
116
122
  """
117
123
  Construct an instance of this class from the JSON file at `path`.
118
124
  """
@@ -120,7 +126,7 @@ class Serializable:
120
126
  json_string = f.read()
121
127
  return cls.from_json(json_string)
122
128
 
123
- def __reduce__(self):
129
+ def __reduce__(self) -> tuple[Any, ...]:
124
130
  """
125
131
  Overriding this method directs the default pickler to reconstruct
126
132
  this object using our from_dict method, so pickles survive the same
@@ -1,4 +1,4 @@
1
- __version__ = "1.2.0"
1
+ __version__ = "1.3.0"
2
2
 
3
3
 
4
4
  def print_version():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serializable
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Base class with serialization helpers for user-defined Python objects
5
5
  Author-email: Alex Rubinsteyn <alex.rubinsteyn@unc.edu>
6
6
  License: Apache License
@@ -217,15 +217,18 @@ Classifier: Programming Language :: Python :: 3.9
217
217
  Classifier: Programming Language :: Python :: 3.10
218
218
  Classifier: Programming Language :: Python :: 3.11
219
219
  Classifier: Programming Language :: Python :: 3.12
220
+ Classifier: Programming Language :: Python :: 3.13
221
+ Classifier: Programming Language :: Python :: 3.14
222
+ Classifier: Typing :: Typed
220
223
  Requires-Python: >=3.9
221
224
  Description-Content-Type: text/markdown
222
225
  License-File: LICENSE
223
226
  Requires-Dist: typechecks>=0.0.2
224
- Requires-Dist: simplejson>=3.0.0
225
227
  Provides-Extra: dev
226
228
  Requires-Dist: pytest; extra == "dev"
227
229
  Requires-Dist: pytest-cov; extra == "dev"
228
230
  Requires-Dist: ruff; extra == "dev"
231
+ Requires-Dist: mypy; extra == "dev"
229
232
  Requires-Dist: coveralls; extra == "dev"
230
233
  Requires-Dist: build; extra == "dev"
231
234
  Requires-Dist: twine; extra == "dev"
@@ -288,6 +291,9 @@ If you change the keyword arguments to a class which derives from `Serializable`
288
291
  The format records Python classes and modules, which must remain importable when
289
292
  loading data. Keys starting with two underscores are reserved. Nested Python
290
293
  values require the serialization helpers rather than ordinary JSON encoding.
294
+ `to_json` raises `ValueError` for NaN or infinite floats, which standard JSON
295
+ can't represent; `from_json` still reads the `NaN` / `Infinity` literals which
296
+ older versions wrote.
291
297
 
292
298
  ## Documentation
293
299
 
@@ -6,6 +6,7 @@ serializable/__init__.py
6
6
  serializable/dataclass_serializable.py
7
7
  serializable/helpers.py
8
8
  serializable/primitive_types.py
9
+ serializable/py.typed
9
10
  serializable/serializable.py
10
11
  serializable/version.py
11
12
  serializable.egg-info/PKG-INFO
@@ -24,4 +25,5 @@ tests/test_object.py
24
25
  tests/test_pickle.py
25
26
  tests/test_primitive_values.py
26
27
  tests/test_set.py
27
- tests/test_tuple.py
28
+ tests/test_tuple.py
29
+ tests/test_typing.py
@@ -1,10 +1,10 @@
1
1
  typechecks>=0.0.2
2
- simplejson>=3.0.0
3
2
 
4
3
  [dev]
5
4
  pytest
6
5
  pytest-cov
7
6
  ruff
7
+ mypy
8
8
  coveralls
9
9
  build
10
10
  twine
@@ -10,7 +10,11 @@
10
10
  # See the License for the specific language governing permissions and
11
11
  # limitations under the License.
12
12
 
13
- from serializable import from_serializable_repr, to_serializable_repr
13
+ import math
14
+
15
+ import pytest
16
+
17
+ from serializable import from_json, from_serializable_repr, to_json, to_serializable_repr
14
18
 
15
19
  from .common import eq_
16
20
 
@@ -29,3 +33,17 @@ def test_bool():
29
33
 
30
34
  def test_str():
31
35
  eq_("waffles", from_serializable_repr(to_serializable_repr("waffles")))
36
+
37
+
38
+ @pytest.mark.parametrize("value", [math.nan, math.inf, -math.inf])
39
+ def test_to_json_rejects_non_finite_floats(value):
40
+ # NaN and infinities have no standard JSON representation
41
+ with pytest.raises(ValueError, match="Out of range float values"):
42
+ to_json([value])
43
+
44
+
45
+ def test_from_json_accepts_non_finite_literals():
46
+ # serializable used to write these (with simplejson < 3.19)
47
+ values = from_json("[NaN, Infinity, -Infinity]")
48
+ assert math.isnan(values[0])
49
+ eq_(values[1:], [math.inf, -math.inf])
@@ -0,0 +1,48 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ import inspect
14
+ import typing
15
+ from dataclasses import dataclass
16
+
17
+ import pytest
18
+
19
+ from serializable import DataclassSerializable, Serializable, helpers
20
+
21
+
22
+ class Point(Serializable):
23
+ def __init__(self, x):
24
+ self.x = x
25
+
26
+
27
+ @dataclass
28
+ class DataclassPoint(DataclassSerializable):
29
+ x: int
30
+
31
+
32
+ def annotated_objects():
33
+ yield Point
34
+ yield DataclassPoint
35
+ for cls in (Serializable, DataclassSerializable):
36
+ for _, method in inspect.getmembers(cls, callable):
37
+ if (getattr(method, "__module__", None) or "").startswith("serializable"):
38
+ yield method
39
+ for _, function in inspect.getmembers(helpers, inspect.isfunction):
40
+ if function.__module__ == helpers.__name__:
41
+ yield function
42
+
43
+
44
+ @pytest.mark.parametrize("obj", list(annotated_objects()), ids=lambda obj: obj.__qualname__)
45
+ def test_annotations_evaluate_at_runtime(obj):
46
+ # annotations must stay evaluable on the oldest supported Python (no
47
+ # `X | Y` unions before 3.10) for tools which call get_type_hints
48
+ typing.get_type_hints(obj)
File without changes
File without changes
File without changes