Skip to content

Commit 58a7826

Browse files
Build Python 3.13 wheels (not free-threaded)
1 parent 2c37da0 commit 58a7826

4 files changed

Lines changed: 37 additions & 12 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,13 @@ jobs:
8080
env:
8181
CIBW_TEST_REQUIRES: "pytest msgpack pyyaml tomli tomli_w"
8282
CIBW_TEST_COMMAND: "pytest {project}/tests"
83-
CIBW_BUILD: "cp38-* cp39-* cp310-* cp311-* cp312-*"
83+
CIBW_BUILD: "cp38-* cp39-* cp310-* cp311-* cp312-* cp13-*"
8484
CIBW_SKIP: "*-win32 *_i686 *_s390x *_ppc64le"
8585
CIBW_ARCHS_MACOS: "x86_64 arm64"
8686
CIBW_ARCHS_LINUX: "x86_64 aarch64"
8787
CIBW_TEST_SKIP: "*_arm64 *-musllinux_*"
8888
CIBW_ENVIRONMENT: "CFLAGS=-g0"
89+
CIBW_PRERELEASE_PYTHONS: "true"
8990

9091
steps:
9192
- uses: actions/checkout@v2
@@ -99,10 +100,10 @@ jobs:
99100
- name: Set up Environment
100101
if: github.event_name != 'release'
101102
run: |
102-
echo "CIBW_SKIP=${CIBW_SKIP} *-musllinux_* cp38-*_aarch64 cp39-*_aarch64 cp311-*_aarch64 cp312-*_aarch64" >> $GITHUB_ENV
103+
echo "CIBW_SKIP=${CIBW_SKIP} *-musllinux_* cp38-*_aarch64 cp39-*_aarch64 cp311-*_aarch64 cp312-*_aarch64 cp313-*" >> $GITHUB_ENV
103104
104105
- name: Build & Test Wheels
105-
uses: pypa/cibuildwheel@v2.16.5
106+
uses: pypa/cibuildwheel@v2.19.2
106107

107108
- name: Upload artifact
108109
uses: actions/upload-artifact@v2

msgspec/_core.c

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define PY310_PLUS (PY_VERSION_HEX >= 0x030a0000)
2121
#define PY311_PLUS (PY_VERSION_HEX >= 0x030b0000)
2222
#define PY312_PLUS (PY_VERSION_HEX >= 0x030c0000)
23+
#define PY313_PLUS (PY_VERSION_HEX >= 0x030d0000)
2324

2425
/* Hint to the compiler not to store `x` in a register since it is likely to
2526
* change. Results in much higher performance on GCC, with smaller benefits on
@@ -497,7 +498,7 @@ find_keyword(PyObject *kwnames, PyObject *const *kwstack, PyObject *key)
497498
for (i = 0; i < nkwargs; i++) {
498499
PyObject *kwname = PyTuple_GET_ITEM(kwnames, i);
499500
assert(PyUnicode_Check(kwname));
500-
if (_PyUnicode_EQ(kwname, key)) {
501+
if (PyUnicode_Compare(kwname, key) == 0) {
501502
return kwstack[i];
502503
}
503504
}
@@ -4440,16 +4441,21 @@ typenode_collect_convert_structs(TypeNodeCollectState *state) {
44404441
*/
44414442
PyObject *tag_mapping = NULL, *tag_field = NULL, *set_item = NULL;
44424443
PyObject *struct_info = NULL;
4443-
Py_ssize_t set_pos = 0;
4444-
Py_hash_t set_hash;
44454444
bool array_like = false;
44464445
bool tags_are_strings = true;
44474446
int status = -1;
44484447

44494448
tag_mapping = PyDict_New();
44504449
if (tag_mapping == NULL) goto cleanup;
44514450

4451+
#if PY313_PLUS
4452+
PyObject *iter_set = PyObject_GetIter(state->structs_set);
4453+
while ((set_item = PyIter_Next(iter_set)) != NULL) {
4454+
#else
4455+
Py_ssize_t set_pos = 0;
4456+
Py_hash_t set_hash;
44524457
while (_PySet_NextEntry(state->structs_set, &set_pos, &set_item, &set_hash)) {
4458+
#endif
44534459
struct_info = StructInfo_Convert(set_item);
44544460
if (struct_info == NULL) goto cleanup;
44554461

@@ -7322,7 +7328,7 @@ Struct_vectorcall(PyTypeObject *cls, PyObject *const *args, size_t nargsf, PyObj
73227328
* check for parameters passed both as arg and kwarg */
73237329
for (field_index = 0; field_index < nfields; field_index++) {
73247330
PyObject *field = PyTuple_GET_ITEM(fields, field_index);
7325-
if (_PyUnicode_EQ(kwname, field)) {
7331+
if (PyUnicode_Compare(kwname, field) == 0) {
73267332
if (MS_UNLIKELY(field_index < nargs)) {
73277333
PyErr_Format(
73287334
PyExc_TypeError,
@@ -7729,7 +7735,7 @@ struct_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject
77297735
}
77307736
for (field_index = 0; field_index < nfields; field_index++) {
77317737
PyObject *field = PyTuple_GET_ITEM(fields, field_index);
7732-
if (_PyUnicode_EQ(kwname, field)) goto kw_found;
7738+
if (PyUnicode_Compare(kwname, field) == 0) goto kw_found;
77337739
}
77347740

77357741
/* Unknown keyword */
@@ -11255,7 +11261,11 @@ ms_uuid_to_16_bytes(MsgspecState *mod, PyObject *obj, unsigned char *buf) {
1125511261
PyErr_SetString(PyExc_TypeError, "uuid.int must be an int");
1125611262
return -1;
1125711263
}
11264+
#if PY313_PLUS
11265+
int out = _PyLong_AsByteArray((PyLongObject *)int128, buf, 16, 0, 0, 1);
11266+
#else
1125811267
int out = _PyLong_AsByteArray((PyLongObject *)int128, buf, 16, 0, 0);
11268+
#endif
1125911269
Py_DECREF(int128);
1126011270
return out;
1126111271
}
@@ -12404,8 +12414,7 @@ mpack_encode_list(EncoderState *self, PyObject *obj)
1240412414
static int
1240512415
mpack_encode_set(EncoderState *self, PyObject *obj)
1240612416
{
12407-
Py_ssize_t len, ppos = 0;
12408-
Py_hash_t hash;
12417+
Py_ssize_t len = 0;
1240912418
PyObject *item;
1241012419
int status = -1;
1241112420

@@ -12424,7 +12433,14 @@ mpack_encode_set(EncoderState *self, PyObject *obj)
1242412433

1242512434
if (mpack_encode_array_header(self, len, "set") < 0) return -1;
1242612435
if (Py_EnterRecursiveCall(" while serializing an object")) return -1;
12436+
#if PY313_PLUS
12437+
PyObject *iter_set = PyObject_GetIter(obj);
12438+
while ((item = PyIter_Next(iter_set)) != NULL) {
12439+
#else
12440+
Py_ssize_t ppos = 0;
12441+
Py_hash_t hash;
1242712442
while (_PySet_NextEntry(obj, &ppos, &item, &hash)) {
12443+
#endif
1242812444
if (mpack_encode_inline(self, item) < 0) goto cleanup;
1242912445
}
1243012446
status = 0;
@@ -13707,8 +13723,7 @@ json_encode_tuple(EncoderState *self, PyObject *obj)
1370713723
static int
1370813724
json_encode_set(EncoderState *self, PyObject *obj)
1370913725
{
13710-
Py_ssize_t len, ppos = 0;
13711-
Py_hash_t hash;
13726+
Py_ssize_t len = 0;
1371213727
PyObject *item;
1371313728
int status = -1;
1371413729

@@ -13727,7 +13742,14 @@ json_encode_set(EncoderState *self, PyObject *obj)
1372713742

1372813743
if (ms_write(self, "[", 1) < 0) return -1;
1372913744
if (Py_EnterRecursiveCall(" while serializing an object")) return -1;
13745+
#if PY313_PLUS
13746+
PyObject *iter_set = PyObject_GetIter(obj);
13747+
while ((item = PyIter_Next(iter_set)) != NULL) {
13748+
#else
13749+
Py_ssize_t ppos = 0;
13750+
Py_hash_t hash;
1373013751
while (_PySet_NextEntry(obj, &ppos, &item, &hash)) {
13752+
#endif
1373113753
if (json_encode_inline(self, item) < 0) goto cleanup;
1373213754
if (ms_write(self, ",", 1) < 0) goto cleanup;
1373313755
}

msgspec/_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ def get_class_annotations(obj):
127127
value = type(None)
128128
elif isinstance(value, str):
129129
value = _forward_ref(value)
130+
# TODO: Pass `type_params` to `_eval_type` to fix deprecation warning
130131
value = typing._eval_type(value, cls_locals, cls_globals)
131132
if mapping is not None:
132133
value = _apply_params(value, mapping)

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"Programming Language :: Python :: 3.10",
8888
"Programming Language :: Python :: 3.11",
8989
"Programming Language :: Python :: 3.12",
90+
"Programming Language :: Python :: 3.13",
9091
],
9192
extras_require=extras_require,
9293
license="BSD",

0 commit comments

Comments
 (0)