structtype 0.10.0__tar.gz → 0.11.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.10.0 → structtype-0.11.0}/Makefile +2 -2
- {structtype-0.10.0/src/structtype.egg-info → structtype-0.11.0}/PKG-INFO +1 -1
- {structtype-0.10.0 → structtype-0.11.0}/pyproject.toml +4 -1
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/__init__.py +0 -2
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/__init__.pyi +1 -11
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/_adapter.py +1 -5
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/_core.c +75 -358
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/_inspect.py +18 -22
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/_json_schema.py +1 -2
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/_version.py +2 -2
- {structtype-0.10.0 → structtype-0.11.0/src/structtype.egg-info}/PKG-INFO +1 -1
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype.egg-info/SOURCES.txt +1 -1
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_annotations.py +1 -2
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_check.py +0 -1
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_inspect.py +0 -4
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_json.py +0 -71
- structtype-0.11.0/tests/test_roundtrip.py +220 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_schema.py +0 -4
- {structtype-0.10.0 → structtype-0.11.0}/tests/typecheck/types.py +1 -6
- structtype-0.10.0/tests/test_raw.py +0 -138
- {structtype-0.10.0 → structtype-0.11.0}/.opencode/opencode.json +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/.opencode/plugins/graphify.js +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/AGENTS.md +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/LICENSE +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/MANIFEST.in +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/README.md +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/setup.cfg +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/setup.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/_config.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/_utils.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/atof.h +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/atof_consts.h +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/common.h +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/itoa.h +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/py.typed +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype/ryu.h +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype.egg-info/dependency_links.txt +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/src/structtype.egg-info/top_level.txt +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/__init__.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/conftest.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_JSONTestSuite.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_adapter.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_attrs.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_constraints.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_cpylint.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_free_threading.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_memory.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_msgspec.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_pydantic.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_struct.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_struct_meta.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_typecheck.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/test_utils.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/typecheck/api.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/typecheck/basic.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/typecheck/fields.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/tests/utils.py +0 -0
- {structtype-0.10.0 → structtype-0.11.0}/uv.lock +0 -0
|
@@ -18,8 +18,8 @@ docs-serve: ## Open built docs in browser
|
|
|
18
18
|
uv run python -m webbrowser site/index.html
|
|
19
19
|
|
|
20
20
|
.PHONY: wheels
|
|
21
|
-
wheels: ## build wheels
|
|
22
|
-
uvx --from cibuildwheel==4.2.0 cibuildwheel
|
|
21
|
+
wheels: ## build wheels (musllinux skipped; unset CIBW_SKIP or run cibuildwheel directly to include it)
|
|
22
|
+
CIBW_SKIP='*-musllinux_*' uvx --from cibuildwheel==4.2.0 cibuildwheel
|
|
23
23
|
|
|
24
24
|
##@ Quality
|
|
25
25
|
.PHONY: test-cov
|
|
@@ -69,12 +69,15 @@ editable_mode = "compat"
|
|
|
69
69
|
|
|
70
70
|
[tool.cibuildwheel]
|
|
71
71
|
build = "cp3*"
|
|
72
|
-
skip = ["pp*", "*-manylinux_i686", "*-
|
|
72
|
+
skip = ["pp*", "*-manylinux_i686", "*-win32"]
|
|
73
73
|
test-command = "pip install pytest>=9 && pytest {project}/tests"
|
|
74
74
|
|
|
75
75
|
[tool.ruff]
|
|
76
76
|
target-version = "py310"
|
|
77
77
|
|
|
78
|
+
[tool.ruff.lint.isort]
|
|
79
|
+
combine-as-imports = true
|
|
80
|
+
|
|
78
81
|
[tool.coverage.report]
|
|
79
82
|
exclude_lines = [
|
|
80
83
|
'pragma: no cover',
|
|
@@ -12,7 +12,6 @@ from ._core import ( # type: ignore
|
|
|
12
12
|
Factory,
|
|
13
13
|
Field,
|
|
14
14
|
NumericConstraint,
|
|
15
|
-
Raw,
|
|
16
15
|
Serializer,
|
|
17
16
|
StrConstraint,
|
|
18
17
|
Struct,
|
|
@@ -38,7 +37,6 @@ __all__ = [
|
|
|
38
37
|
"Field",
|
|
39
38
|
"FieldInfo",
|
|
40
39
|
"NumericConstraint",
|
|
41
|
-
"Raw",
|
|
42
40
|
"Serializer",
|
|
43
41
|
"StrConstraint",
|
|
44
42
|
"Struct",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ruff: noqa: PYI041, PYI015
|
|
1
|
+
# ruff: noqa: PYI041, PYI015
|
|
2
2
|
import enum
|
|
3
3
|
from collections.abc import Callable, Iterable, Iterator, Mapping
|
|
4
4
|
from inspect import Signature
|
|
@@ -109,16 +109,6 @@ class Struct(metaclass=StructMeta):
|
|
|
109
109
|
from_attributes: bool = False,
|
|
110
110
|
) -> Self: ...
|
|
111
111
|
|
|
112
|
-
# Lie and say `Raw` is a subclass of `bytes`, so mypy will accept it in most
|
|
113
|
-
# places where an object that implements the buffer protocol is valid
|
|
114
|
-
@final
|
|
115
|
-
class Raw(bytes):
|
|
116
|
-
@overload
|
|
117
|
-
def __new__(cls) -> "Raw": ...
|
|
118
|
-
@overload
|
|
119
|
-
def __new__(cls, msg: Buffer | str) -> "Raw": ...
|
|
120
|
-
def copy(self) -> "Raw": ...
|
|
121
|
-
|
|
122
112
|
#: We can't represent this in types, only via a name:
|
|
123
113
|
_NonNegativeInt: TypeAlias = int
|
|
124
114
|
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
from typing import Any, get_args
|
|
2
2
|
|
|
3
|
-
from ._core import (
|
|
3
|
+
from ._core import ( # type: ignore
|
|
4
4
|
JSONDecoder as _JSONDecoder,
|
|
5
|
-
)
|
|
6
|
-
from ._core import (
|
|
7
5
|
Serializer as _Serializer,
|
|
8
|
-
)
|
|
9
|
-
from ._core import ( # type: ignore
|
|
10
6
|
_dump,
|
|
11
7
|
_json_encode,
|
|
12
8
|
_validate,
|
|
@@ -1417,242 +1417,6 @@ static PyTypeObject StrLookup_Type = {
|
|
|
1417
1417
|
.tp_traverse = (traverseproc) StrLookup_traverse,
|
|
1418
1418
|
};
|
|
1419
1419
|
|
|
1420
|
-
/*************************************************************************
|
|
1421
|
-
* Raw *
|
|
1422
|
-
*************************************************************************/
|
|
1423
|
-
|
|
1424
|
-
static PyTypeObject Raw_Type;
|
|
1425
|
-
|
|
1426
|
-
typedef struct Raw {
|
|
1427
|
-
PyObject_HEAD
|
|
1428
|
-
PyObject *base;
|
|
1429
|
-
char *buf;
|
|
1430
|
-
Py_ssize_t len;
|
|
1431
|
-
bool is_view;
|
|
1432
|
-
Py_buffer view;
|
|
1433
|
-
} Raw;
|
|
1434
|
-
|
|
1435
|
-
static PyObject *
|
|
1436
|
-
Raw_New(PyObject *msg) {
|
|
1437
|
-
Raw *out = (Raw *)Raw_Type.tp_alloc(&Raw_Type, 0);
|
|
1438
|
-
if (out == NULL) return NULL;
|
|
1439
|
-
if (PyBytes_CheckExact(msg)) {
|
|
1440
|
-
Py_INCREF(msg);
|
|
1441
|
-
out->base = msg;
|
|
1442
|
-
out->buf = PyBytes_AS_STRING(msg);
|
|
1443
|
-
out->len = PyBytes_GET_SIZE(msg);
|
|
1444
|
-
out->is_view = false;
|
|
1445
|
-
}
|
|
1446
|
-
else if (PyUnicode_CheckExact(msg)) {
|
|
1447
|
-
out->base = msg;
|
|
1448
|
-
out->buf = (char *)unicode_str_and_size(msg, &out->len);
|
|
1449
|
-
if (out->buf == NULL) return NULL;
|
|
1450
|
-
Py_INCREF(msg);
|
|
1451
|
-
out->is_view = false;
|
|
1452
|
-
}
|
|
1453
|
-
else {
|
|
1454
|
-
Py_buffer buffer;
|
|
1455
|
-
if (PyObject_GetBuffer(msg, &buffer, PyBUF_CONTIG_RO) < 0) {
|
|
1456
|
-
Py_DECREF(out);
|
|
1457
|
-
return NULL;
|
|
1458
|
-
}
|
|
1459
|
-
out->view = buffer;
|
|
1460
|
-
out->base = buffer.obj;
|
|
1461
|
-
out->buf = buffer.buf;
|
|
1462
|
-
out->len = buffer.len;
|
|
1463
|
-
out->is_view = true;
|
|
1464
|
-
}
|
|
1465
|
-
return (PyObject *)out;
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
PyDoc_STRVAR(Raw__doc__,
|
|
1469
|
-
"Raw(msg="", /)\n"
|
|
1470
|
-
"--\n"
|
|
1471
|
-
"\n"
|
|
1472
|
-
"A buffer containing an encoded message.\n"
|
|
1473
|
-
"\n"
|
|
1474
|
-
"Raw objects have two common uses:\n"
|
|
1475
|
-
"\n"
|
|
1476
|
-
"- During decoding. Fields annotated with the ``Raw`` type won't be decoded\n"
|
|
1477
|
-
" immediately, but will instead return a ``Raw`` object with a view into the\n"
|
|
1478
|
-
" original message where that field is encoded. This is useful for decoding\n"
|
|
1479
|
-
" fields whose type may only be inferred after decoding other fields.\n"
|
|
1480
|
-
"- During encoding. Raw objects wrap pre-encoded messages. These can be added\n"
|
|
1481
|
-
" as components of larger messages without having to pay the cost of decoding\n"
|
|
1482
|
-
" and re-encoding them.\n"
|
|
1483
|
-
"\n"
|
|
1484
|
-
"Parameters\n"
|
|
1485
|
-
"----------\n"
|
|
1486
|
-
"msg: bytes, bytearray, memoryview, or str, optional\n"
|
|
1487
|
-
" A buffer containing an encoded message. One of bytes, bytearray, memoryview,\n"
|
|
1488
|
-
" str, or any object that implements the buffer protocol. If not present,\n"
|
|
1489
|
-
" defaults to an empty buffer."
|
|
1490
|
-
);
|
|
1491
|
-
static PyObject *
|
|
1492
|
-
Raw_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
|
|
1493
|
-
PyObject *msg;
|
|
1494
|
-
Py_ssize_t nargs, nkwargs;
|
|
1495
|
-
|
|
1496
|
-
nargs = PyTuple_GET_SIZE(args);
|
|
1497
|
-
nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs);
|
|
1498
|
-
|
|
1499
|
-
if (nkwargs != 0) {
|
|
1500
|
-
PyErr_SetString(
|
|
1501
|
-
PyExc_TypeError,
|
|
1502
|
-
"Raw takes no keyword arguments"
|
|
1503
|
-
);
|
|
1504
|
-
return NULL;
|
|
1505
|
-
}
|
|
1506
|
-
else if (nargs == 0) {
|
|
1507
|
-
msg = PyBytes_FromStringAndSize(NULL, 0);
|
|
1508
|
-
if (msg == NULL) return NULL;
|
|
1509
|
-
/* This looks weird, but is safe since the empty bytes object is an
|
|
1510
|
-
* immortal singleton */
|
|
1511
|
-
Py_DECREF(msg);
|
|
1512
|
-
}
|
|
1513
|
-
else if (nargs == 1) {
|
|
1514
|
-
msg = PyTuple_GET_ITEM(args, 0);
|
|
1515
|
-
}
|
|
1516
|
-
else {
|
|
1517
|
-
PyErr_Format(
|
|
1518
|
-
PyExc_TypeError,
|
|
1519
|
-
"Raw expected at most 1 arguments, got %zd",
|
|
1520
|
-
nargs
|
|
1521
|
-
);
|
|
1522
|
-
return NULL;
|
|
1523
|
-
}
|
|
1524
|
-
return Raw_New(msg);
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
static void
|
|
1528
|
-
Raw_dealloc(Raw *self)
|
|
1529
|
-
{
|
|
1530
|
-
if (self->base != NULL) {
|
|
1531
|
-
if (!self->is_view) {
|
|
1532
|
-
Py_DECREF(self->base);
|
|
1533
|
-
}
|
|
1534
|
-
else {
|
|
1535
|
-
ms_release_buffer(&self->view);
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
Py_TYPE(self)->tp_free((PyObject *)self);
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
static PyObject *
|
|
1542
|
-
Raw_FromView(PyObject *buffer_obj, char *data, Py_ssize_t len) {
|
|
1543
|
-
Raw *out = (Raw *)Raw_Type.tp_alloc(&Raw_Type, 0);
|
|
1544
|
-
if (out == NULL) return NULL;
|
|
1545
|
-
|
|
1546
|
-
Py_buffer buffer;
|
|
1547
|
-
if (ms_get_buffer(buffer_obj, &buffer) < 0) {
|
|
1548
|
-
Py_DECREF(out);
|
|
1549
|
-
return NULL;
|
|
1550
|
-
}
|
|
1551
|
-
out->view = buffer;
|
|
1552
|
-
out->base = buffer.obj;
|
|
1553
|
-
out->buf = data;
|
|
1554
|
-
out->len = len;
|
|
1555
|
-
out->is_view = true;
|
|
1556
|
-
return (PyObject *)out;
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
static PyObject *
|
|
1560
|
-
Raw_richcompare(Raw *self, PyObject *other, int op) {
|
|
1561
|
-
if (Py_TYPE(other) != &Raw_Type) {
|
|
1562
|
-
Py_RETURN_NOTIMPLEMENTED;
|
|
1563
|
-
}
|
|
1564
|
-
if (op != Py_EQ && op != Py_NE) {
|
|
1565
|
-
Py_RETURN_NOTIMPLEMENTED;
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
Raw *raw_other = (Raw *)other;
|
|
1569
|
-
bool equal = (
|
|
1570
|
-
self == raw_other || (
|
|
1571
|
-
(self->len == raw_other->len) &&
|
|
1572
|
-
(memcmp(self->buf, raw_other->buf, self->len) == 0)
|
|
1573
|
-
)
|
|
1574
|
-
);
|
|
1575
|
-
bool result = (op == Py_EQ) ? equal : !equal;
|
|
1576
|
-
if (result) {
|
|
1577
|
-
Py_RETURN_TRUE;
|
|
1578
|
-
}
|
|
1579
|
-
Py_RETURN_FALSE;
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
static int
|
|
1583
|
-
Raw_buffer_getbuffer(Raw *self, Py_buffer *view, int flags)
|
|
1584
|
-
{
|
|
1585
|
-
return PyBuffer_FillInfo(view, (PyObject *)self, self->buf, self->len, 1, flags);
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
static PyBufferProcs Raw_as_buffer = {
|
|
1589
|
-
.bf_getbuffer = (getbufferproc)Raw_buffer_getbuffer
|
|
1590
|
-
};
|
|
1591
|
-
|
|
1592
|
-
static Py_ssize_t
|
|
1593
|
-
Raw_length(Raw *self) {
|
|
1594
|
-
return self->len;
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
static PySequenceMethods Raw_as_sequence = {
|
|
1598
|
-
.sq_length = (lenfunc)Raw_length
|
|
1599
|
-
};
|
|
1600
|
-
|
|
1601
|
-
static PyObject *
|
|
1602
|
-
Raw_reduce(Raw *self, PyObject *unused)
|
|
1603
|
-
{
|
|
1604
|
-
if (!self->is_view) {
|
|
1605
|
-
return Py_BuildValue("O(O)", &Raw_Type, self->base);
|
|
1606
|
-
}
|
|
1607
|
-
return Py_BuildValue("O(y#)", &Raw_Type, self->buf, self->len);
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
PyDoc_STRVAR(Raw_copy__doc__,
|
|
1611
|
-
"copy(self)\n"
|
|
1612
|
-
"--\n"
|
|
1613
|
-
"\n"
|
|
1614
|
-
"Copy a Raw object.\n"
|
|
1615
|
-
"\n"
|
|
1616
|
-
"If the raw message is backed by a memoryview into a larger buffer (as happens\n"
|
|
1617
|
-
"during decoding), the message is copied and the reference to the larger buffer\n"
|
|
1618
|
-
"released. This may be useful to reduce memory usage if a Raw object created\n"
|
|
1619
|
-
"during decoding will be kept in memory for a while rather than immediately\n"
|
|
1620
|
-
"decoded and dropped."
|
|
1621
|
-
);
|
|
1622
|
-
static PyObject *
|
|
1623
|
-
Raw_copy(Raw *self, PyObject *unused)
|
|
1624
|
-
{
|
|
1625
|
-
if (!self->is_view) {
|
|
1626
|
-
Py_INCREF(self);
|
|
1627
|
-
return (PyObject *)self;
|
|
1628
|
-
}
|
|
1629
|
-
PyObject *buf = PyBytes_FromStringAndSize(self->buf, self->len);
|
|
1630
|
-
if (buf == NULL) return NULL;
|
|
1631
|
-
PyObject *out = Raw_New(buf);
|
|
1632
|
-
Py_DECREF(buf);
|
|
1633
|
-
return out;
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
static PyMethodDef Raw_methods[] = {
|
|
1637
|
-
{"__reduce__", (PyCFunction)Raw_reduce, METH_NOARGS},
|
|
1638
|
-
{"copy", (PyCFunction)Raw_copy, METH_NOARGS, Raw_copy__doc__},
|
|
1639
|
-
{NULL, NULL},
|
|
1640
|
-
};
|
|
1641
|
-
|
|
1642
|
-
static PyTypeObject Raw_Type = {
|
|
1643
|
-
PyVarObject_HEAD_INIT(NULL, 0)
|
|
1644
|
-
.tp_name = "structtype.Raw",
|
|
1645
|
-
.tp_doc = Raw__doc__,
|
|
1646
|
-
.tp_basicsize = sizeof(Raw),
|
|
1647
|
-
.tp_itemsize = sizeof(char),
|
|
1648
|
-
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
1649
|
-
.tp_new = Raw_new,
|
|
1650
|
-
.tp_dealloc = (destructor) Raw_dealloc,
|
|
1651
|
-
.tp_as_buffer = &Raw_as_buffer,
|
|
1652
|
-
.tp_as_sequence = &Raw_as_sequence,
|
|
1653
|
-
.tp_methods = Raw_methods,
|
|
1654
|
-
.tp_richcompare = (richcmpfunc) Raw_richcompare,
|
|
1655
|
-
};
|
|
1656
1420
|
|
|
1657
1421
|
static bool
|
|
1658
1422
|
ensure_is_string(PyObject *val, const char *param) {
|
|
@@ -6855,9 +6619,6 @@ typenode_collect_type(TypeNodeCollectState *state, PyObject *obj) {
|
|
|
6855
6619
|
else if (t == state->mod->DecimalType) {
|
|
6856
6620
|
state->types |= MS_TYPE_DECIMAL;
|
|
6857
6621
|
}
|
|
6858
|
-
else if (t == (PyObject *)(&Raw_Type)) {
|
|
6859
|
-
/* Raw is marked with a typecode of 0, nothing to do */
|
|
6860
|
-
}
|
|
6861
6622
|
else if (Py_TYPE(t) == (PyTypeObject *)(state->mod->typing_typevar)) {
|
|
6862
6623
|
out = typenode_collect_typevar(state, t);
|
|
6863
6624
|
}
|
|
@@ -10380,7 +10141,7 @@ PyDoc_STRVAR(Struct_dump__doc__,
|
|
|
10380
10141
|
"struct_dump(self, *, sort_keys=False, str_keys=False, builtin_types=None)\n"
|
|
10381
10142
|
"--\n"
|
|
10382
10143
|
"\n"
|
|
10383
|
-
"Convert this struct to built-in Python types.\n"
|
|
10144
|
+
"Convert this struct to built-in Python types compatible with JSON basic types.\n"
|
|
10384
10145
|
"\n"
|
|
10385
10146
|
"This applies the same semantics as ``struct_dump_json`` but produces\n"
|
|
10386
10147
|
"Python objects instead of JSON bytes:\n"
|
|
@@ -10430,8 +10191,7 @@ PyDoc_STRVAR(Struct_validate__doc__,
|
|
|
10430
10191
|
"strict : bool, optional\n"
|
|
10431
10192
|
" Whether type coercion rules should be strict. If ``False``, enables a\n"
|
|
10432
10193
|
" wider set of coercion rules from string to non-string types for all\n"
|
|
10433
|
-
" values.
|
|
10434
|
-
" ``builtin_types=None``. Default is ``True``.\n"
|
|
10194
|
+
" values, including dict keys. Default is ``True``.\n"
|
|
10435
10195
|
"from_attributes : bool, optional\n"
|
|
10436
10196
|
" If ``True``, input objects may be coerced to struct, dataclass, or\n"
|
|
10437
10197
|
" attrs types by extracting attributes matching output field names.\n"
|
|
@@ -14491,13 +14251,6 @@ json_encode_memoryview(EncoderState *self, PyObject *obj)
|
|
|
14491
14251
|
return out;
|
|
14492
14252
|
}
|
|
14493
14253
|
|
|
14494
|
-
static int
|
|
14495
|
-
json_encode_raw(EncoderState *self, PyObject *obj)
|
|
14496
|
-
{
|
|
14497
|
-
Raw *raw = (Raw *)obj;
|
|
14498
|
-
return ms_write(self, raw->buf, raw->len);
|
|
14499
|
-
}
|
|
14500
|
-
|
|
14501
14254
|
static int json_encode_dict_key_noinline(EncoderState *, PyObject *);
|
|
14502
14255
|
|
|
14503
14256
|
/* Extract an enum member's value. Enum members store their value as a plain
|
|
@@ -14779,8 +14532,8 @@ json_encode_dict_key_noinline(EncoderState *self, PyObject *obj) {
|
|
|
14779
14532
|
else if (type == &PyFloat_Type) {
|
|
14780
14533
|
return json_encode_float_as_str(self, obj);
|
|
14781
14534
|
}
|
|
14782
|
-
else if (
|
|
14783
|
-
return
|
|
14535
|
+
else if (type == (PyTypeObject *)(self->mod->DecimalType)) {
|
|
14536
|
+
return json_encode_decimal(self, obj);
|
|
14784
14537
|
}
|
|
14785
14538
|
else if (type == PyDateTimeAPI->DateTimeType) {
|
|
14786
14539
|
return json_encode_datetime(self, obj);
|
|
@@ -14794,15 +14547,15 @@ json_encode_dict_key_noinline(EncoderState *self, PyObject *obj) {
|
|
|
14794
14547
|
else if (type == PyDateTimeAPI->DeltaType) {
|
|
14795
14548
|
return json_encode_timedelta(self, obj);
|
|
14796
14549
|
}
|
|
14797
|
-
else if (type
|
|
14798
|
-
return
|
|
14799
|
-
}
|
|
14800
|
-
else if (type == (PyTypeObject *)(self->mod->DecimalType)) {
|
|
14801
|
-
return json_encode_decimal(self, obj);
|
|
14550
|
+
else if (PyType_IsSubtype(Py_TYPE(type), self->mod->EnumMetaType)) {
|
|
14551
|
+
return json_encode_enum(self, obj, true);
|
|
14802
14552
|
}
|
|
14803
14553
|
else if (PyType_IsSubtype(type, (PyTypeObject *)(self->mod->UUIDType))) {
|
|
14804
14554
|
return json_encode_uuid(self, obj);
|
|
14805
14555
|
}
|
|
14556
|
+
else if (type == &PyBytes_Type) {
|
|
14557
|
+
return json_encode_bytes(self, obj);
|
|
14558
|
+
}
|
|
14806
14559
|
if (self->codecs != NULL) {
|
|
14807
14560
|
PyObject *dump = codecs_lookup(self->codecs, type);
|
|
14808
14561
|
if (dump != NULL) {
|
|
@@ -15160,18 +14913,12 @@ json_encode_struct(EncoderState *self, PyObject *obj)
|
|
|
15160
14913
|
|
|
15161
14914
|
static MS_NOINLINE int
|
|
15162
14915
|
json_encode_uncommon(EncoderState *self, PyTypeObject *type, PyObject *obj) {
|
|
15163
|
-
if (obj
|
|
15164
|
-
return ms_write(self, "null", 4);
|
|
15165
|
-
}
|
|
15166
|
-
else if (obj == Py_True) {
|
|
15167
|
-
return ms_write(self, "true", 4);
|
|
15168
|
-
}
|
|
15169
|
-
else if (obj == Py_False) {
|
|
15170
|
-
return ms_write(self, "false", 5);
|
|
15171
|
-
}
|
|
15172
|
-
else if (PyTuple_Check(obj)) {
|
|
14916
|
+
if (PyTuple_Check(obj)) {
|
|
15173
14917
|
return json_encode_tuple(self, obj);
|
|
15174
14918
|
}
|
|
14919
|
+
else if (type == (PyTypeObject *)(self->mod->DecimalType)) {
|
|
14920
|
+
return json_encode_decimal(self, obj);
|
|
14921
|
+
}
|
|
15175
14922
|
else if (type == PyDateTimeAPI->DateTimeType) {
|
|
15176
14923
|
return json_encode_datetime(self, obj);
|
|
15177
14924
|
}
|
|
@@ -15184,30 +14931,24 @@ json_encode_uncommon(EncoderState *self, PyTypeObject *type, PyObject *obj) {
|
|
|
15184
14931
|
else if (type == PyDateTimeAPI->DeltaType) {
|
|
15185
14932
|
return json_encode_timedelta(self, obj);
|
|
15186
14933
|
}
|
|
15187
|
-
else if (type == &PyBytes_Type) {
|
|
15188
|
-
return json_encode_bytes(self, obj);
|
|
15189
|
-
}
|
|
15190
|
-
else if (type == &PyByteArray_Type) {
|
|
15191
|
-
return json_encode_bytearray(self, obj);
|
|
15192
|
-
}
|
|
15193
|
-
else if (type == &PyMemoryView_Type) {
|
|
15194
|
-
return json_encode_memoryview(self, obj);
|
|
15195
|
-
}
|
|
15196
|
-
else if (type == &Raw_Type) {
|
|
15197
|
-
return json_encode_raw(self, obj);
|
|
15198
|
-
}
|
|
15199
14934
|
else if (PyType_IsSubtype(Py_TYPE(type), self->mod->EnumMetaType)) {
|
|
15200
14935
|
return json_encode_enum(self, obj, false);
|
|
15201
14936
|
}
|
|
15202
14937
|
else if (PyType_IsSubtype(type, (PyTypeObject *)(self->mod->UUIDType))) {
|
|
15203
14938
|
return json_encode_uuid(self, obj);
|
|
15204
14939
|
}
|
|
15205
|
-
else if (type == (PyTypeObject *)(self->mod->DecimalType)) {
|
|
15206
|
-
return json_encode_decimal(self, obj);
|
|
15207
|
-
}
|
|
15208
14940
|
else if (PyAnySet_Check(obj)) {
|
|
15209
14941
|
return json_encode_set(self, obj);
|
|
15210
14942
|
}
|
|
14943
|
+
else if (type == &PyBytes_Type) {
|
|
14944
|
+
return json_encode_bytes(self, obj);
|
|
14945
|
+
}
|
|
14946
|
+
else if (type == &PyByteArray_Type) {
|
|
14947
|
+
return json_encode_bytearray(self, obj);
|
|
14948
|
+
}
|
|
14949
|
+
else if (type == &PyMemoryView_Type) {
|
|
14950
|
+
return json_encode_memoryview(self, obj);
|
|
14951
|
+
}
|
|
15211
14952
|
if (self->codecs != NULL) {
|
|
15212
14953
|
PyObject *dump = codecs_lookup(self->codecs, type);
|
|
15213
14954
|
if (dump != NULL) {
|
|
@@ -15299,7 +15040,16 @@ json_encode_inline(EncoderState *self, PyObject *obj)
|
|
|
15299
15040
|
{
|
|
15300
15041
|
PyTypeObject *type = Py_TYPE(obj);
|
|
15301
15042
|
|
|
15302
|
-
if (
|
|
15043
|
+
if (obj == Py_None) {
|
|
15044
|
+
return ms_write(self, "null", 4);
|
|
15045
|
+
}
|
|
15046
|
+
else if (obj == Py_True) {
|
|
15047
|
+
return ms_write(self, "true", 4);
|
|
15048
|
+
}
|
|
15049
|
+
else if (obj == Py_False) {
|
|
15050
|
+
return ms_write(self, "false", 5);
|
|
15051
|
+
}
|
|
15052
|
+
else if (PyUnicode_Check(obj)) {
|
|
15303
15053
|
return json_encode_str(self, obj);
|
|
15304
15054
|
}
|
|
15305
15055
|
else if (type == &PyLong_Type) {
|
|
@@ -17572,16 +17322,6 @@ json_maybe_decode_number(JSONDecoderState *self, TypeNode *type, PathNode *path)
|
|
|
17572
17322
|
return out;
|
|
17573
17323
|
}
|
|
17574
17324
|
|
|
17575
|
-
static MS_NOINLINE PyObject *
|
|
17576
|
-
json_decode_raw(JSONDecoderState *self) {
|
|
17577
|
-
unsigned char c;
|
|
17578
|
-
if (MS_UNLIKELY(!json_peek_skip_ws(self, &c))) return NULL;
|
|
17579
|
-
const unsigned char *start = self->input_pos;
|
|
17580
|
-
if (json_skip(self) < 0) return NULL;
|
|
17581
|
-
Py_ssize_t size = self->input_pos - start;
|
|
17582
|
-
return Raw_FromView(self->buffer_obj, (char *)start, size);
|
|
17583
|
-
}
|
|
17584
|
-
|
|
17585
17325
|
/* Fast path for the common `str`/`Any` string decode, inlined so the hot path
|
|
17586
17326
|
* avoids the large non-inline `json_decode_string` (which also handles
|
|
17587
17327
|
* datetime, uuid, decimal, bytes, enum, ...). */
|
|
@@ -17631,9 +17371,6 @@ static PyObject *
|
|
|
17631
17371
|
json_decode(
|
|
17632
17372
|
JSONDecoderState *self, TypeNode *type, PathNode *path
|
|
17633
17373
|
) {
|
|
17634
|
-
if (MS_UNLIKELY(type->types == 0)) {
|
|
17635
|
-
return json_decode_raw(self);
|
|
17636
|
-
}
|
|
17637
17374
|
PyObject *obj = json_decode_nocustom(self, type, path);
|
|
17638
17375
|
if (MS_UNLIKELY(type->types & (MS_TYPE_CUSTOM | MS_TYPE_CUSTOM_GENERIC))) {
|
|
17639
17376
|
return ms_decode_custom(obj, type, path);
|
|
@@ -18574,26 +18311,26 @@ dump_obj(DumpState *self, PyObject *obj, bool is_key) {
|
|
|
18574
18311
|
) {
|
|
18575
18312
|
goto builtin;
|
|
18576
18313
|
}
|
|
18577
|
-
else if (
|
|
18578
|
-
|
|
18579
|
-
return dump_binary(
|
|
18580
|
-
self, PyBytes_AS_STRING(obj), PyBytes_GET_SIZE(obj)
|
|
18581
|
-
);
|
|
18314
|
+
else if (PyList_Check(obj)) {
|
|
18315
|
+
return dump_list(self, obj);
|
|
18582
18316
|
}
|
|
18583
|
-
else if (
|
|
18584
|
-
|
|
18585
|
-
return dump_binary(
|
|
18586
|
-
self, PyByteArray_AS_STRING(obj), PyByteArray_GET_SIZE(obj)
|
|
18587
|
-
);
|
|
18317
|
+
else if (PyTuple_Check(obj)) {
|
|
18318
|
+
return dump_tuple(self, obj, is_key);
|
|
18588
18319
|
}
|
|
18589
|
-
else if (
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
|
|
18593
|
-
|
|
18594
|
-
|
|
18595
|
-
|
|
18596
|
-
|
|
18320
|
+
else if (PyDict_Check(obj)) {
|
|
18321
|
+
return dump_dict(self, obj);
|
|
18322
|
+
}
|
|
18323
|
+
#if PY315_PLUS
|
|
18324
|
+
else if (PyFrozenDict_Check(obj)) {
|
|
18325
|
+
return dump_frozendict(self, obj);
|
|
18326
|
+
}
|
|
18327
|
+
#endif
|
|
18328
|
+
else if (ms_is_struct_type(type)) {
|
|
18329
|
+
return dump_struct(self, obj, is_key);
|
|
18330
|
+
}
|
|
18331
|
+
else if (type == (PyTypeObject *)(self->mod->DecimalType)) {
|
|
18332
|
+
if (self->builtin_types & MS_BUILTIN_DECIMAL) goto builtin;
|
|
18333
|
+
return dump_decimal(self, obj);
|
|
18597
18334
|
}
|
|
18598
18335
|
else if (type == PyDateTimeAPI->DateTimeType) {
|
|
18599
18336
|
if (self->builtin_types & MS_BUILTIN_DATETIME) goto builtin;
|
|
@@ -18611,38 +18348,10 @@ dump_obj(DumpState *self, PyObject *obj, bool is_key) {
|
|
|
18611
18348
|
if (self->builtin_types & MS_BUILTIN_TIMEDELTA) goto builtin;
|
|
18612
18349
|
return dump_timedelta(self, obj);
|
|
18613
18350
|
}
|
|
18614
|
-
else if (type == (PyTypeObject *)(self->mod->DecimalType)) {
|
|
18615
|
-
if (self->builtin_types & MS_BUILTIN_DECIMAL) goto builtin;
|
|
18616
|
-
return dump_decimal(self, obj);
|
|
18617
|
-
}
|
|
18618
|
-
else if (PyList_Check(obj)) {
|
|
18619
|
-
return dump_list(self, obj);
|
|
18620
|
-
}
|
|
18621
|
-
else if (PyTuple_Check(obj)) {
|
|
18622
|
-
return dump_tuple(self, obj, is_key);
|
|
18623
|
-
}
|
|
18624
|
-
else if (PyDict_Check(obj)) {
|
|
18625
|
-
return dump_dict(self, obj);
|
|
18626
|
-
}
|
|
18627
|
-
#if PY315_PLUS
|
|
18628
|
-
else if (PyFrozenDict_Check(obj)) {
|
|
18629
|
-
return dump_frozendict(self, obj);
|
|
18630
|
-
}
|
|
18631
|
-
#endif
|
|
18632
|
-
else if (ms_is_struct_type(type)) {
|
|
18633
|
-
return dump_struct(self, obj, is_key);
|
|
18634
|
-
}
|
|
18635
|
-
else if (!ms_is_struct_inst(obj) && PyObject_HasAttr(obj, self->mod->str___struct_fields__)) {
|
|
18636
|
-
/* External struct type (e.g. msgspec) — use Python-level attribute access */
|
|
18637
|
-
return dump_external_struct(self, obj);
|
|
18638
|
-
}
|
|
18639
18351
|
else if (PyType_IsSubtype(Py_TYPE(type), self->mod->EnumMetaType)) {
|
|
18640
18352
|
if (self->builtin_types & MS_BUILTIN_ENUM) goto builtin;
|
|
18641
18353
|
return dump_enum(self, obj);
|
|
18642
18354
|
}
|
|
18643
|
-
else if (is_key & PyUnicode_Check(obj)) {
|
|
18644
|
-
return PyObject_Str(obj);
|
|
18645
|
-
}
|
|
18646
18355
|
else if (PyType_IsSubtype(type, (PyTypeObject *)(self->mod->UUIDType))) {
|
|
18647
18356
|
if (self->builtin_types & MS_BUILTIN_UUID) goto builtin;
|
|
18648
18357
|
return dump_uuid(self, obj);
|
|
@@ -18650,6 +18359,27 @@ dump_obj(DumpState *self, PyObject *obj, bool is_key) {
|
|
|
18650
18359
|
else if (PyAnySet_Check(obj)) {
|
|
18651
18360
|
return dump_set(self, obj, is_key);
|
|
18652
18361
|
}
|
|
18362
|
+
else if (type == &PyBytes_Type) {
|
|
18363
|
+
if (self->builtin_types & MS_BUILTIN_BYTES) goto builtin;
|
|
18364
|
+
return dump_binary(
|
|
18365
|
+
self, PyBytes_AS_STRING(obj), PyBytes_GET_SIZE(obj)
|
|
18366
|
+
);
|
|
18367
|
+
}
|
|
18368
|
+
else if (type == &PyByteArray_Type) {
|
|
18369
|
+
if (self->builtin_types & MS_BUILTIN_BYTEARRAY) goto builtin;
|
|
18370
|
+
return dump_binary(
|
|
18371
|
+
self, PyByteArray_AS_STRING(obj), PyByteArray_GET_SIZE(obj)
|
|
18372
|
+
);
|
|
18373
|
+
}
|
|
18374
|
+
else if (type == &PyMemoryView_Type) {
|
|
18375
|
+
if (self->builtin_types & MS_BUILTIN_MEMORYVIEW) goto builtin;
|
|
18376
|
+
PyObject *out;
|
|
18377
|
+
Py_buffer buffer;
|
|
18378
|
+
if (PyObject_GetBuffer(obj, &buffer, PyBUF_CONTIG_RO) < 0) return NULL;
|
|
18379
|
+
out = dump_binary(self, buffer.buf, buffer.len);
|
|
18380
|
+
PyBuffer_Release(&buffer);
|
|
18381
|
+
return out;
|
|
18382
|
+
}
|
|
18653
18383
|
if (self->codecs != NULL) {
|
|
18654
18384
|
PyObject *dump = codecs_lookup(self->codecs, type);
|
|
18655
18385
|
if (dump != NULL) {
|
|
@@ -18682,6 +18412,10 @@ dump_obj(DumpState *self, PyObject *obj, bool is_key) {
|
|
|
18682
18412
|
Py_DECREF(dict);
|
|
18683
18413
|
return result;
|
|
18684
18414
|
}
|
|
18415
|
+
else if (!ms_is_struct_inst(obj) && PyObject_HasAttr(obj, self->mod->str___struct_fields__)) {
|
|
18416
|
+
/* External struct type (e.g. msgspec) — use Python-level attribute access */
|
|
18417
|
+
return dump_external_struct(self, obj);
|
|
18418
|
+
}
|
|
18685
18419
|
else if (!PyType_Check(obj) && type->tp_dict != NULL) {
|
|
18686
18420
|
PyObject *fields = PyObject_GetAttr(obj, self->mod->str___dataclass_fields__);
|
|
18687
18421
|
if (fields != NULL) {
|
|
@@ -19247,16 +18981,6 @@ validate_immutable(
|
|
|
19247
18981
|
return ms_validation_error(expected, type, path);
|
|
19248
18982
|
}
|
|
19249
18983
|
|
|
19250
|
-
static PyObject *
|
|
19251
|
-
validate_raw(
|
|
19252
|
-
ValidateState *self, PyObject *obj, TypeNode *type, PathNode *path
|
|
19253
|
-
) {
|
|
19254
|
-
if (type->types == 0) {
|
|
19255
|
-
Py_INCREF(obj);
|
|
19256
|
-
return obj;
|
|
19257
|
-
}
|
|
19258
|
-
return ms_validation_error("raw", type, path);
|
|
19259
|
-
}
|
|
19260
18984
|
|
|
19261
18985
|
static PyObject *
|
|
19262
18986
|
validate_seq_to_list(
|
|
@@ -20403,9 +20127,6 @@ validate_obj_dispatch(
|
|
|
20403
20127
|
else if (PyType_IsSubtype(Py_TYPE(pytype), self->mod->EnumMetaType)) {
|
|
20404
20128
|
return validate_enum(self, obj, type, path);
|
|
20405
20129
|
}
|
|
20406
|
-
else if (pytype == &Raw_Type) {
|
|
20407
|
-
return validate_raw(self, obj, type, path);
|
|
20408
|
-
}
|
|
20409
20130
|
else if (PyAnySet_Check(obj)) {
|
|
20410
20131
|
return validate_any_set(self, obj, type, path);
|
|
20411
20132
|
}
|
|
@@ -21084,8 +20805,6 @@ PyInit__core(void)
|
|
|
21084
20805
|
return NULL;
|
|
21085
20806
|
if (PyType_Ready(&StructMixinType) < 0)
|
|
21086
20807
|
return NULL;
|
|
21087
|
-
if (PyType_Ready(&Raw_Type) < 0)
|
|
21088
|
-
return NULL;
|
|
21089
20808
|
if (PyType_Ready(&JSONEncoder_Type) < 0)
|
|
21090
20809
|
return NULL;
|
|
21091
20810
|
if (PyType_Ready(&JSONDecoder_Type) < 0)
|
|
@@ -21116,8 +20835,6 @@ PyInit__core(void)
|
|
|
21116
20835
|
if (PyModule_AddObjectRef(m, "TimezoneConstraint", (PyObject *)&TimezoneConstraint_Type) < 0)
|
|
21117
20836
|
return NULL;
|
|
21118
20837
|
|
|
21119
|
-
if (PyModule_AddObjectRef(m, "Raw", (PyObject *)&Raw_Type) < 0)
|
|
21120
|
-
return NULL;
|
|
21121
20838
|
if (PyModule_AddObjectRef(m, "JSONEncoder", (PyObject *)&JSONEncoder_Type) < 0)
|
|
21122
20839
|
return NULL;
|
|
21123
20840
|
if (PyModule_AddObjectRef(m, "JSONDecoder", (PyObject *)&JSONDecoder_Type) < 0)
|