structtype 0.4.0__tar.gz → 0.5.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 (60) hide show
  1. {structtype-0.4.0 → structtype-0.5.0}/Makefile +2 -2
  2. {structtype-0.4.0/src/structtype.egg-info → structtype-0.5.0}/PKG-INFO +8 -2
  3. {structtype-0.4.0 → structtype-0.5.0}/README.md +7 -1
  4. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/__init__.py +1 -0
  5. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/__init__.pyi +23 -20
  6. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/_adapter.py +43 -19
  7. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/_core.c +772 -284
  8. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/_inspect.py +1 -1
  9. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/_version.py +2 -2
  10. {structtype-0.4.0 → structtype-0.5.0/src/structtype.egg-info}/PKG-INFO +8 -2
  11. {structtype-0.4.0 → structtype-0.5.0}/src/structtype.egg-info/SOURCES.txt +0 -11
  12. {structtype-0.4.0 → structtype-0.5.0}/tests/test_adapter.py +88 -1
  13. {structtype-0.4.0 → structtype-0.5.0}/tests/test_constraints.py +7 -1
  14. {structtype-0.4.0 → structtype-0.5.0}/tests/test_inspect.py +9 -1
  15. {structtype-0.4.0 → structtype-0.5.0}/tests/test_json.py +345 -0
  16. {structtype-0.4.0 → structtype-0.5.0}/tests/test_schema.py +11 -0
  17. {structtype-0.4.0 → structtype-0.5.0}/tests/test_struct.py +64 -34
  18. {structtype-0.4.0 → structtype-0.5.0}/uv.lock +162 -86
  19. structtype-0.4.0/graphify-out/.graphify_labels.json +0 -108
  20. structtype-0.4.0/graphify-out/.graphify_labels.json.sig +0 -1
  21. structtype-0.4.0/graphify-out/.graphify_root +0 -1
  22. structtype-0.4.0/graphify-out/2026-08-09/.graphify_labels.json +0 -93
  23. structtype-0.4.0/graphify-out/2026-08-09/GRAPH_REPORT.md +0 -385
  24. structtype-0.4.0/graphify-out/2026-08-09/graph.json +0 -75971
  25. structtype-0.4.0/graphify-out/2026-08-09/manifest.json +0 -332
  26. structtype-0.4.0/graphify-out/GRAPH_REPORT.md +0 -448
  27. structtype-0.4.0/graphify-out/graph.html +0 -320
  28. structtype-0.4.0/graphify-out/graph.json +0 -77910
  29. structtype-0.4.0/graphify-out/manifest.json +0 -327
  30. {structtype-0.4.0 → structtype-0.5.0}/.opencode/opencode.json +0 -0
  31. {structtype-0.4.0 → structtype-0.5.0}/.opencode/plugins/graphify.js +0 -0
  32. {structtype-0.4.0 → structtype-0.5.0}/AGENTS.md +0 -0
  33. {structtype-0.4.0 → structtype-0.5.0}/LICENSE +0 -0
  34. {structtype-0.4.0 → structtype-0.5.0}/MANIFEST.in +0 -0
  35. {structtype-0.4.0 → structtype-0.5.0}/pyproject.toml +0 -0
  36. {structtype-0.4.0 → structtype-0.5.0}/setup.cfg +0 -0
  37. {structtype-0.4.0 → structtype-0.5.0}/setup.py +0 -0
  38. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/_json_schema.py +0 -0
  39. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/_utils.py +0 -0
  40. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/atof.h +0 -0
  41. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/atof_consts.h +0 -0
  42. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/common.h +0 -0
  43. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/itoa.h +0 -0
  44. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/py.typed +0 -0
  45. {structtype-0.4.0 → structtype-0.5.0}/src/structtype/ryu.h +0 -0
  46. {structtype-0.4.0 → structtype-0.5.0}/src/structtype.egg-info/dependency_links.txt +0 -0
  47. {structtype-0.4.0 → structtype-0.5.0}/src/structtype.egg-info/top_level.txt +0 -0
  48. {structtype-0.4.0 → structtype-0.5.0}/tests/__init__.py +0 -0
  49. {structtype-0.4.0 → structtype-0.5.0}/tests/conftest.py +0 -0
  50. {structtype-0.4.0 → structtype-0.5.0}/tests/test_JSONTestSuite.py +0 -0
  51. {structtype-0.4.0 → structtype-0.5.0}/tests/test_check.py +0 -0
  52. {structtype-0.4.0 → structtype-0.5.0}/tests/test_cpylint.py +0 -0
  53. {structtype-0.4.0 → structtype-0.5.0}/tests/test_free_threading.py +0 -0
  54. {structtype-0.4.0 → structtype-0.5.0}/tests/test_memory.py +0 -0
  55. {structtype-0.4.0 → structtype-0.5.0}/tests/test_msgspec.py +0 -0
  56. {structtype-0.4.0 → structtype-0.5.0}/tests/test_pydantic.py +0 -0
  57. {structtype-0.4.0 → structtype-0.5.0}/tests/test_raw.py +0 -0
  58. {structtype-0.4.0 → structtype-0.5.0}/tests/test_struct_meta.py +0 -0
  59. {structtype-0.4.0 → structtype-0.5.0}/tests/test_utils.py +0 -0
  60. {structtype-0.4.0 → structtype-0.5.0}/tests/utils.py +0 -0
@@ -23,7 +23,7 @@ wheels: ## build wheels
23
23
 
24
24
  ##@ Quality
25
25
  .PHONY: test-cov
26
- test-cov: ## Run tests with coverage
26
+ test-cov: build ## Run tests with coverage
27
27
  uv run pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=structtype
28
28
 
29
29
  .PHONY: test-lf
@@ -37,7 +37,7 @@ test-doc: ## Run doctests
37
37
  .PHONY: test-all
38
38
  test-all: ## Run tests in all supporte Python versions
39
39
  for py_v in $(PY_VERSIONS); do \
40
- uv run --isolated -p $$py_v pytest; \
40
+ uv run --isolated --reinstall-package structtype -p $$py_v pytest; \
41
41
  done
42
42
 
43
43
  UNAME_S := $(shell uname -s)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: structtype
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Fast Struct type with validation and JSON serialization for Python.
5
5
  Maintainer-email: Wolfgang Langner <tds333@mailbox.org>
6
6
  License-Expression: BSD-3-Clause
@@ -72,7 +72,13 @@ Full documentation is available at **https://tds333.github.io/structtype/**.
72
72
 
73
73
  ## Benchmarks
74
74
 
75
- structtype is as fast as msgspec and about 3-5x faster than pydantic.
75
+ structtype is as fast as msgspec and about 3-5x faster than pydantic. See the
76
+ [benchmarks page](https://tds333.github.io/structtype/benchmarks.html) for the full numbers.
77
+
78
+ ## Links
79
+
80
+ - [Changelog](https://tds333.github.io/structtype/changelog.html)
81
+ - [Source code](https://github.com/tds333/structtype)
76
82
 
77
83
  ## License
78
84
 
@@ -49,7 +49,13 @@ Full documentation is available at **https://tds333.github.io/structtype/**.
49
49
 
50
50
  ## Benchmarks
51
51
 
52
- structtype is as fast as msgspec and about 3-5x faster than pydantic.
52
+ structtype is as fast as msgspec and about 3-5x faster than pydantic. See the
53
+ [benchmarks page](https://tds333.github.io/structtype/benchmarks.html) for the full numbers.
54
+
55
+ ## Links
56
+
57
+ - [Changelog](https://tds333.github.io/structtype/changelog.html)
58
+ - [Source code](https://github.com/tds333/structtype)
53
59
 
54
60
  ## License
55
61
 
@@ -5,6 +5,7 @@ from ._core import (
5
5
  UNSET,
6
6
  DecodeError,
7
7
  EncodeError,
8
+ Factory,
8
9
  Field,
9
10
  Raw,
10
11
  Struct,
@@ -110,7 +110,6 @@ class Struct(metaclass=StructMeta):
110
110
  def struct_dump_json(
111
111
  self,
112
112
  *,
113
- enc_hook: Callable[[Any], Any] | None = None,
114
113
  decimal_format: Literal["string", "number"]
115
114
  | Callable[[Any], Any]
116
115
  | None = None,
@@ -120,7 +119,6 @@ class Struct(metaclass=StructMeta):
120
119
  def struct_dump(
121
120
  self,
122
121
  *,
123
- enc_hook: Callable[[Any], Any] | None = None,
124
122
  sort_keys: bool = False,
125
123
  str_keys: bool = False,
126
124
  builtin_types: Iterable[type] | None = None,
@@ -132,7 +130,6 @@ class Struct(metaclass=StructMeta):
132
130
  buf: str | Buffer,
133
131
  *,
134
132
  strict: bool = True,
135
- dec_hook: Callable[[type[Any], Any], Any] | None = None,
136
133
  ) -> _T: ...
137
134
  @classmethod
138
135
  def struct_validate(
@@ -141,7 +138,6 @@ class Struct(metaclass=StructMeta):
141
138
  *,
142
139
  strict: bool = True,
143
140
  from_attributes: bool = False,
144
- dec_hook: Callable[[type[Any], Any], Any] | None = None,
145
141
  ) -> _T: ...
146
142
 
147
143
  # Lie and say `Raw` is a subclass of `bytes`, so mypy will accept it in most
@@ -157,6 +153,11 @@ class Raw(bytes):
157
153
  #: We can't represent this in types, only via a name:
158
154
  _NonNegativeInt: TypeAlias = int
159
155
 
156
+ @final
157
+ class Factory:
158
+ def __init__(self, factory: Callable[[], Any]) -> None: ...
159
+ factory: Final[Callable[[], Any]]
160
+
160
161
  @final
161
162
  class Field:
162
163
  # Numeric:
@@ -167,13 +168,14 @@ class Field:
167
168
  gt: int | float | None = None,
168
169
  lt: int | float | None = None,
169
170
  multiple_of: int | float | None = None,
170
- default: Any = NODEFAULT,
171
- default_factory: Callable[[], Any] | None = None,
172
171
  alias: str | None = None,
173
172
  title: str | None = None,
174
173
  description: str | None = None,
175
174
  json_schema_extra: dict[str, Any] | None = None,
176
175
  examples: list[Any] | None = None,
176
+ deprecated: bool | None = None,
177
+ dump: Callable[[Any], Any] | None = None,
178
+ validate: Callable[[Any], Any] | None = None,
177
179
  ) -> None: ...
178
180
  @overload
179
181
  def __init__(
@@ -182,13 +184,14 @@ class Field:
182
184
  gt: int | float | None = None,
183
185
  le: int | float | None = None,
184
186
  multiple_of: int | float | None = None,
185
- default: Any = NODEFAULT,
186
- default_factory: Callable[[], Any] | None = None,
187
187
  alias: str | None = None,
188
188
  title: str | None = None,
189
189
  description: str | None = None,
190
190
  json_schema_extra: dict[str, Any] | None = None,
191
191
  examples: list[Any] | None = None,
192
+ deprecated: bool | None = None,
193
+ dump: Callable[[Any], Any] | None = None,
194
+ validate: Callable[[Any], Any] | None = None,
192
195
  ) -> None: ...
193
196
  @overload
194
197
  def __init__(
@@ -197,13 +200,14 @@ class Field:
197
200
  ge: int | float | None = None,
198
201
  lt: int | float | None = None,
199
202
  multiple_of: int | float | None = None,
200
- default: Any = NODEFAULT,
201
- default_factory: Callable[[], Any] | None = None,
202
203
  alias: str | None = None,
203
204
  title: str | None = None,
204
205
  description: str | None = None,
205
206
  json_schema_extra: dict[str, Any] | None = None,
206
207
  examples: list[Any] | None = None,
208
+ deprecated: bool | None = None,
209
+ dump: Callable[[Any], Any] | None = None,
210
+ validate: Callable[[Any], Any] | None = None,
207
211
  ) -> None: ...
208
212
  @overload
209
213
  def __init__(
@@ -212,13 +216,14 @@ class Field:
212
216
  ge: int | float | None = None,
213
217
  le: int | float | None = None,
214
218
  multiple_of: int | float | None = None,
215
- default: Any = NODEFAULT,
216
- default_factory: Callable[[], Any] | None = None,
217
219
  alias: str | None = None,
218
220
  title: str | None = None,
219
221
  description: str | None = None,
220
222
  json_schema_extra: dict[str, Any] | None = None,
221
223
  examples: list[Any] | None = None,
224
+ deprecated: bool | None = None,
225
+ dump: Callable[[Any], Any] | None = None,
226
+ validate: Callable[[Any], Any] | None = None,
222
227
  ) -> None: ...
223
228
  # Other (string/datetime):
224
229
  @overload
@@ -229,16 +234,15 @@ class Field:
229
234
  min_length: _NonNegativeInt | None = None,
230
235
  max_length: _NonNegativeInt | None = None,
231
236
  tz: bool | None = None,
232
- default: Any = NODEFAULT,
233
- default_factory: Callable[[], Any] | None = None,
234
237
  alias: str | None = None,
235
238
  title: str | None = None,
236
239
  description: str | None = None,
237
240
  json_schema_extra: dict[str, Any] | None = None,
238
241
  examples: list[Any] | None = None,
242
+ deprecated: bool | None = None,
243
+ dump: Callable[[Any], Any] | None = None,
244
+ validate: Callable[[Any], Any] | None = None,
239
245
  ) -> None: ...
240
- default: Final[Any]
241
- default_factory: Final[Callable[[], Any] | None]
242
246
  alias: Final[str | None]
243
247
  gt: Final[int | float | None]
244
248
  ge: Final[int | float | None]
@@ -252,7 +256,10 @@ class Field:
252
256
  title: Final[str | None]
253
257
  description: Final[str | None]
254
258
  examples: Final[list[Any] | None]
259
+ deprecated: Final[bool | None]
255
260
  json_schema_extra: Final[dict[str, Any] | None]
261
+ dump: Final[Callable[[Any], Any] | None]
262
+ validate: Final[Callable[[Any], Any] | None]
256
263
  def __rich_repr__(self) -> list[tuple[str, Any]]: ...
257
264
 
258
265
  class StructConfig:
@@ -308,13 +315,11 @@ class StructAdapter:
308
315
  buf: str | Buffer,
309
316
  *,
310
317
  strict: bool = True,
311
- dec_hook: Callable[[type[Any], Any], Any] | None = None,
312
318
  ) -> Any: ...
313
319
  def struct_dump_json(
314
320
  self,
315
321
  obj: Any,
316
322
  *,
317
- enc_hook: Callable[[Any], Any] | None = None,
318
323
  decimal_format: Literal["string", "number"]
319
324
  | Callable[[Any], Any]
320
325
  | None = None,
@@ -326,14 +331,12 @@ class StructAdapter:
326
331
  obj: Any,
327
332
  *,
328
333
  strict: bool = True,
329
- dec_hook: Callable[[type[Any], Any], Any] | None = None,
330
334
  from_attributes: bool = False,
331
335
  ) -> Any: ...
332
336
  def struct_dump(
333
337
  self,
334
338
  obj: Any,
335
339
  *,
336
- enc_hook: Callable[[Any], Any] | None = None,
337
340
  sort_keys: bool = False,
338
341
  str_keys: bool = False,
339
342
  builtin_types: Iterable[type] | None = None,
@@ -1,6 +1,32 @@
1
- from typing import Any
2
-
3
- from ._core import _dump, _json_decode, _json_encode, _validate
1
+ from typing import Any, get_args
2
+
3
+ from ._core import ( # type: ignore
4
+ Field as _Field,
5
+ )
6
+ from ._core import (
7
+ _dump,
8
+ _json_decode,
9
+ _json_encode,
10
+ _validate,
11
+ )
12
+
13
+
14
+ def _has_codec(ann):
15
+ """True if the annotation carries a ``Field`` with ``dump``/``validate``."""
16
+ metadata = getattr(ann, "__metadata__", None)
17
+ if metadata is not None:
18
+ for meta in metadata:
19
+ if isinstance(meta, _Field) and (
20
+ meta.dump is not None or meta.validate is not None
21
+ ):
22
+ return True
23
+ supertype = getattr(ann, "__supertype__", None) # NewType
24
+ if supertype is not None and _has_codec(supertype):
25
+ return True
26
+ value = getattr(ann, "__value__", None) # PEP 695 type alias
27
+ if value is not None and _has_codec(value):
28
+ return True
29
+ return any(_has_codec(arg) for arg in get_args(ann))
4
30
 
5
31
 
6
32
  class StructAdapter:
@@ -9,6 +35,11 @@ class StructAdapter:
9
35
  Useful when you want to validate or serialize plain Python types
10
36
  (e.g. ``list[int]``) without defining a full ``Struct`` subclass.
11
37
 
38
+ ``Field(dump=...)`` / ``Field(validate=...)`` codecs are not supported on
39
+ ``StructAdapter`` — annotations carrying one are rejected. Implement the
40
+ ``struct_dump`` / ``struct_validate`` protocol methods on the custom type,
41
+ or use a ``Struct``.
42
+
12
43
  >>> from structtype import StructAdapter
13
44
  >>> adapter = StructAdapter(list[int])
14
45
  >>> adapter.struct_validate_json(b"[1, 2, 3]")
@@ -18,9 +49,15 @@ class StructAdapter:
18
49
  __slots__ = ("_type",)
19
50
 
20
51
  def __init__(self, type: Any):
52
+ if _has_codec(type):
53
+ raise TypeError(
54
+ "`Field(dump=...)`/`Field(validate=...)` codecs are not supported "
55
+ "on StructAdapter; define `struct_dump`/`struct_validate` methods "
56
+ "on the custom type, or use a `Struct` instead"
57
+ )
21
58
  self._type = type
22
59
 
23
- def struct_validate_json(self, buf, *, strict=True, dec_hook=None):
60
+ def struct_validate_json(self, buf, *, strict=True):
24
61
  """Validate JSON bytes and decode into the adapter's type.
25
62
 
26
63
  Parameters
@@ -29,16 +66,13 @@ class StructAdapter:
29
66
  The JSON message to decode.
30
67
  strict : bool, optional
31
68
  If True (default), unmatched fields cause an error.
32
- dec_hook : callable, optional
33
- A callback for customizing decoding of specific types.
34
69
  """
35
- return _json_decode(buf, type=self._type, strict=strict, dec_hook=dec_hook)
70
+ return _json_decode(buf, type=self._type, strict=strict)
36
71
 
37
72
  def struct_dump_json(
38
73
  self,
39
74
  obj,
40
75
  *,
41
- enc_hook=None,
42
76
  decimal_format=None,
43
77
  uuid_format=None,
44
78
  sort_keys=False,
@@ -49,8 +83,6 @@ class StructAdapter:
49
83
  ----------
50
84
  obj : Any
51
85
  A value to encode. Must match the adapter's type.
52
- enc_hook : callable, optional
53
- A callback for customizing encoding of specific types.
54
86
  decimal_format : str or callable, optional
55
87
  Controls how ``Decimal`` values are encoded.
56
88
  uuid_format : str, optional
@@ -60,15 +92,12 @@ class StructAdapter:
60
92
  """
61
93
  return _json_encode(
62
94
  obj,
63
- enc_hook=enc_hook,
64
95
  decimal_format=decimal_format,
65
96
  uuid_format=uuid_format,
66
97
  sort_keys=sort_keys,
67
98
  )
68
99
 
69
- def struct_validate(
70
- self, obj, *, strict=True, dec_hook=None, from_attributes=False
71
- ):
100
+ def struct_validate(self, obj, *, strict=True, from_attributes=False):
72
101
  """Validate a Python object against the adapter's type.
73
102
 
74
103
  Parameters
@@ -77,8 +106,6 @@ class StructAdapter:
77
106
  A Python object to validate and convert.
78
107
  strict : bool, optional
79
108
  If True (default), unmatched fields cause an error.
80
- dec_hook : callable, optional
81
- A callback for customizing decoding of specific types.
82
109
  from_attributes : bool, optional
83
110
  If True, accept objects with attributes instead of dict keys.
84
111
  """
@@ -86,7 +113,6 @@ class StructAdapter:
86
113
  obj,
87
114
  self._type,
88
115
  strict=strict,
89
- dec_hook=dec_hook,
90
116
  from_attributes=from_attributes,
91
117
  )
92
118
 
@@ -94,7 +120,6 @@ class StructAdapter:
94
120
  self,
95
121
  obj,
96
122
  *,
97
- enc_hook=None,
98
123
  sort_keys=False,
99
124
  str_keys=False,
100
125
  builtin_types=None,
@@ -104,7 +129,6 @@ class StructAdapter:
104
129
  obj,
105
130
  builtin_types=builtin_types,
106
131
  str_keys=str_keys,
107
- enc_hook=enc_hook,
108
132
  sort_keys=sort_keys,
109
133
  )
110
134