structtype 0.9.0__tar.gz → 0.10.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.
- {structtype-0.9.0 → structtype-0.10.0}/AGENTS.md +7 -4
- {structtype-0.9.0 → structtype-0.10.0}/Makefile +18 -6
- {structtype-0.9.0/src/structtype.egg-info → structtype-0.10.0}/PKG-INFO +2 -1
- {structtype-0.9.0 → structtype-0.10.0}/pyproject.toml +1 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/__init__.pyi +33 -8
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/_adapter.py +28 -12
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/_config.py +1 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/_core.c +213 -386
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/_json_schema.py +16 -2
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/_version.py +2 -2
- {structtype-0.9.0 → structtype-0.10.0/src/structtype.egg-info}/PKG-INFO +2 -1
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_adapter.py +154 -2
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_annotations.py +264 -1
- structtype-0.10.0/tests/test_check.py +798 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_constraints.py +93 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_free_threading.py +37 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_json.py +441 -2
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_raw.py +1 -4
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_schema.py +35 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_struct.py +89 -1
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_struct_meta.py +62 -6
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_utils.py +14 -1
- {structtype-0.9.0 → structtype-0.10.0}/tests/typecheck/fields.py +10 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/utils.py +6 -0
- {structtype-0.9.0 → structtype-0.10.0}/uv.lock +245 -245
- structtype-0.9.0/tests/test_check.py +0 -389
- {structtype-0.9.0 → structtype-0.10.0}/.opencode/opencode.json +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/.opencode/plugins/graphify.js +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/LICENSE +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/MANIFEST.in +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/README.md +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/setup.cfg +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/setup.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/__init__.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/_inspect.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/_utils.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/atof.h +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/atof_consts.h +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/common.h +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/itoa.h +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/py.typed +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype/ryu.h +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype.egg-info/SOURCES.txt +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype.egg-info/dependency_links.txt +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/src/structtype.egg-info/top_level.txt +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/__init__.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/conftest.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_JSONTestSuite.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_attrs.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_cpylint.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_inspect.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_memory.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_msgspec.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_pydantic.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/test_typecheck.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/typecheck/api.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/typecheck/basic.py +0 -0
- {structtype-0.9.0 → structtype-0.10.0}/tests/typecheck/types.py +0 -0
|
@@ -18,9 +18,11 @@ All commands go through `make`.
|
|
|
18
18
|
|
|
19
19
|
| Task | Command |
|
|
20
20
|
|---|---|
|
|
21
|
-
| Unit tests (reinstall +
|
|
21
|
+
| Unit tests (reinstall + full suite) | `make test` |
|
|
22
22
|
| Targeted tests | `uv run --reinstall pytest tests/test_json.py -k test_something` |
|
|
23
|
-
| Coverage | `make test-cov` |
|
|
23
|
+
| Coverage (Python) | `make test-cov` |
|
|
24
|
+
| Coverage (Python + C) | `make test-cov-c` |
|
|
25
|
+
| Tests in all supported Pythons | `make test-all` |
|
|
24
26
|
| Build docs | `make docs` |
|
|
25
27
|
| Format | `make format` |
|
|
26
28
|
| Lint | `make ruff-check` |
|
|
@@ -49,7 +51,7 @@ All commands go through `make`.
|
|
|
49
51
|
|
|
50
52
|
### Struct Methods
|
|
51
53
|
|
|
52
|
-
- `obj.struct_dump_json(*,
|
|
54
|
+
- `obj.struct_dump_json(*, decimal_as_number=False, uuid_as_hex=False, sort_keys=False)` — serialize to JSON bytes
|
|
53
55
|
- `obj.struct_dump(*, sort_keys=False, str_keys=False, builtin_types=None)` — convert to built-in Python types (uses `alias` for keys)
|
|
54
56
|
- `obj.struct_check_types()` — validate field values against types + constraints (pure type-check, no conversion)
|
|
55
57
|
- `cls.struct_validate_json(buf, *, strict=True)` — deserialize from JSON
|
|
@@ -63,7 +65,8 @@ Struct instances support the mapping protocol:
|
|
|
63
65
|
|
|
64
66
|
## Gotchas
|
|
65
67
|
|
|
66
|
-
- `make test-cov` reinstalls the C extension before running (
|
|
68
|
+
- `make test-cov` reinstalls the C extension before running. `make test-cov-c` builds an `-O0 --coverage` instrumented extension **in place**; afterwards any reinstalling target (`make test`, `make test-cov`) restores the optimized build.
|
|
69
|
+
- C coverage requires `lcov`/`genhtml`; report lands in `htmlcov-c/`.
|
|
67
70
|
- Validation matches keys by the **alias** name only, except `struct_validate(obj, from_attributes=True)` on a **non-dict object**, which matches by both the python field name and the alias. Dict/JSON input (even with `from_attributes=True`) and all dump/serialization use only the alias name.
|
|
68
71
|
|
|
69
72
|
## graphify
|
|
@@ -23,12 +23,22 @@ wheels: ## build wheels
|
|
|
23
23
|
|
|
24
24
|
##@ Quality
|
|
25
25
|
.PHONY: test-cov
|
|
26
|
-
test-cov:
|
|
27
|
-
uv run pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=structtype
|
|
28
|
-
|
|
29
|
-
.PHONY: test-
|
|
30
|
-
test-
|
|
31
|
-
|
|
26
|
+
test-cov: ## Run tests with coverage
|
|
27
|
+
uv run --reinstall-package structtype pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=structtype
|
|
28
|
+
|
|
29
|
+
.PHONY: test-cov-c
|
|
30
|
+
test-cov-c: ## Run tests with Python + C coverage (lcov report in htmlcov-c/)
|
|
31
|
+
rm -rf build coverage-c.info coverage-c.info.* htmlcov-c
|
|
32
|
+
STRUCTTYPE_COVERAGE=1 uv run --with setuptools python setup.py build_ext --inplace --force
|
|
33
|
+
./.venv/bin/python -m pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=structtype
|
|
34
|
+
lcov --capture --directory build --output-file coverage-c.info
|
|
35
|
+
lcov --extract coverage-c.info "*/src/structtype/*" --output-file coverage-c.info
|
|
36
|
+
lcov --summary coverage-c.info
|
|
37
|
+
genhtml coverage-c.info --output-directory htmlcov-c >/dev/null
|
|
38
|
+
|
|
39
|
+
.PHONY: test
|
|
40
|
+
test: ## Run tests in current Python
|
|
41
|
+
uv run --reinstall pytest
|
|
32
42
|
|
|
33
43
|
.PHONY: test-doc
|
|
34
44
|
test-doc: ## Run doctests
|
|
@@ -110,7 +120,9 @@ clean: ## Delete all temporary files
|
|
|
110
120
|
rm -rf **/__pycache__
|
|
111
121
|
rm -rf build
|
|
112
122
|
rm -rf dist
|
|
123
|
+
rm -rf htmlcov-c
|
|
113
124
|
rm -f .coverage
|
|
125
|
+
rm -f coverage-c.info coverage-c.info.*
|
|
114
126
|
|
|
115
127
|
.PHONY: install
|
|
116
128
|
install: install-uv ## Install virtual environment
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structtype
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.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
|
|
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.15
|
|
21
|
+
Classifier: Programming Language :: Python :: Free Threading
|
|
21
22
|
Requires-Python: >=3.10
|
|
22
23
|
Description-Content-Type: text/markdown
|
|
23
24
|
License-File: LICENSE
|
|
@@ -81,10 +81,8 @@ class Struct(metaclass=StructMeta):
|
|
|
81
81
|
def struct_dump_json(
|
|
82
82
|
self,
|
|
83
83
|
*,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
| None = None,
|
|
87
|
-
uuid_format: Literal["canonical", "hex"] | None = None,
|
|
84
|
+
decimal_as_number: bool = False,
|
|
85
|
+
uuid_as_hex: bool = False,
|
|
88
86
|
sort_keys: bool = False,
|
|
89
87
|
) -> bytes: ...
|
|
90
88
|
def struct_dump(
|
|
@@ -166,12 +164,41 @@ class Constraint:
|
|
|
166
164
|
|
|
167
165
|
@final
|
|
168
166
|
class NumericConstraint(Constraint):
|
|
167
|
+
# Numeric bounds. You can't mix:
|
|
168
|
+
# - `gt` and `ge`
|
|
169
|
+
# - `lt` and `le`
|
|
170
|
+
# Cross-pair combinations (`gt` with `le`, `ge` with `lt`) are valid,
|
|
171
|
+
# so all four pairings are enumerated as separate overloads. The C
|
|
172
|
+
# runtime raises `ValueError` for the invalid pairings.
|
|
173
|
+
@overload
|
|
174
|
+
def __init__(
|
|
175
|
+
self,
|
|
176
|
+
*,
|
|
177
|
+
gt: int | float | None = None,
|
|
178
|
+
lt: int | float | None = None,
|
|
179
|
+
multiple_of: int | float | None = None,
|
|
180
|
+
) -> None: ...
|
|
181
|
+
@overload
|
|
169
182
|
def __init__(
|
|
170
183
|
self,
|
|
171
184
|
*,
|
|
172
185
|
gt: int | float | None = None,
|
|
186
|
+
le: int | float | None = None,
|
|
187
|
+
multiple_of: int | float | None = None,
|
|
188
|
+
) -> None: ...
|
|
189
|
+
@overload
|
|
190
|
+
def __init__(
|
|
191
|
+
self,
|
|
192
|
+
*,
|
|
173
193
|
ge: int | float | None = None,
|
|
174
194
|
lt: int | float | None = None,
|
|
195
|
+
multiple_of: int | float | None = None,
|
|
196
|
+
) -> None: ...
|
|
197
|
+
@overload
|
|
198
|
+
def __init__(
|
|
199
|
+
self,
|
|
200
|
+
*,
|
|
201
|
+
ge: int | float | None = None,
|
|
175
202
|
le: int | float | None = None,
|
|
176
203
|
multiple_of: int | float | None = None,
|
|
177
204
|
) -> None: ...
|
|
@@ -263,10 +290,8 @@ class StructAdapter:
|
|
|
263
290
|
self,
|
|
264
291
|
obj: Any,
|
|
265
292
|
*,
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
| None = None,
|
|
269
|
-
uuid_format: Literal["canonical", "hex"] | None = None,
|
|
293
|
+
decimal_as_number: bool = False,
|
|
294
|
+
uuid_as_hex: bool = False,
|
|
270
295
|
sort_keys: bool = False,
|
|
271
296
|
) -> bytes: ...
|
|
272
297
|
def struct_validate(
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from typing import Any, get_args
|
|
2
2
|
|
|
3
|
-
from ._core import (
|
|
3
|
+
from ._core import (
|
|
4
|
+
JSONDecoder as _JSONDecoder,
|
|
5
|
+
)
|
|
6
|
+
from ._core import (
|
|
4
7
|
Serializer as _Serializer,
|
|
5
8
|
)
|
|
6
9
|
from ._core import ( # type: ignore
|
|
7
10
|
_dump,
|
|
8
|
-
_json_decode,
|
|
9
11
|
_json_encode,
|
|
10
12
|
_validate,
|
|
11
13
|
)
|
|
@@ -46,7 +48,7 @@ class StructAdapter:
|
|
|
46
48
|
[1, 2, 3]
|
|
47
49
|
"""
|
|
48
50
|
|
|
49
|
-
__slots__ = ("_type"
|
|
51
|
+
__slots__ = ("_decoder_loose", "_decoder_strict", "_type")
|
|
50
52
|
|
|
51
53
|
def __init__(self, type: Any):
|
|
52
54
|
if _has_serializer(type):
|
|
@@ -57,6 +59,8 @@ class StructAdapter:
|
|
|
57
59
|
"`Struct` instead"
|
|
58
60
|
)
|
|
59
61
|
self._type = type
|
|
62
|
+
self._decoder_loose = None
|
|
63
|
+
self._decoder_strict = None
|
|
60
64
|
|
|
61
65
|
def struct_validate_json(self, buf, *, strict=True):
|
|
62
66
|
"""Validate JSON bytes and decode into the adapter's type.
|
|
@@ -68,14 +72,24 @@ class StructAdapter:
|
|
|
68
72
|
strict : bool, optional
|
|
69
73
|
If True (default), unmatched fields cause an error.
|
|
70
74
|
"""
|
|
71
|
-
|
|
75
|
+
if strict:
|
|
76
|
+
decoder = self._decoder_strict
|
|
77
|
+
if decoder is None:
|
|
78
|
+
decoder = _JSONDecoder(self._type, strict=True)
|
|
79
|
+
self._decoder_strict = decoder
|
|
80
|
+
else:
|
|
81
|
+
decoder = self._decoder_loose
|
|
82
|
+
if decoder is None:
|
|
83
|
+
decoder = _JSONDecoder(self._type, strict=False)
|
|
84
|
+
self._decoder_loose = decoder
|
|
85
|
+
return decoder.decode(buf)
|
|
72
86
|
|
|
73
87
|
def struct_dump_json(
|
|
74
88
|
self,
|
|
75
89
|
obj,
|
|
76
90
|
*,
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
decimal_as_number=False,
|
|
92
|
+
uuid_as_hex=False,
|
|
79
93
|
sort_keys=False,
|
|
80
94
|
):
|
|
81
95
|
"""Encode a validated object to JSON bytes.
|
|
@@ -84,17 +98,19 @@ class StructAdapter:
|
|
|
84
98
|
----------
|
|
85
99
|
obj : Any
|
|
86
100
|
A value to encode. Must match the adapter's type.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
decimal_as_number : bool, optional
|
|
102
|
+
If True, ``Decimal`` values are encoded as JSON numbers instead
|
|
103
|
+
of strings (may lose precision when decoded).
|
|
104
|
+
uuid_as_hex : bool, optional
|
|
105
|
+
If True, ``UUID`` values are encoded as hex strings instead of
|
|
106
|
+
canonical form.
|
|
91
107
|
sort_keys : bool, optional
|
|
92
108
|
If True, sort dict keys and set elements for deterministic output.
|
|
93
109
|
"""
|
|
94
110
|
return _json_encode(
|
|
95
111
|
obj,
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
decimal_as_number=decimal_as_number,
|
|
113
|
+
uuid_as_hex=uuid_as_hex,
|
|
98
114
|
sort_keys=sort_keys,
|
|
99
115
|
)
|
|
100
116
|
|
|
@@ -7,6 +7,7 @@ class StructConfig(TypedDict, total=False):
|
|
|
7
7
|
|
|
8
8
|
Used as a class-body ``struct_config`` attribute. Keys not present inherit
|
|
9
9
|
from the base class; ``__struct_config__`` returns the fully-resolved dict.
|
|
10
|
+
``struct_config`` returns exactly what the user wrote (sparse dict).
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
frozen: bool
|