diff --git a/python-msgspec.changes b/python-msgspec.changes index 562c5d3..017fee8 100644 --- a/python-msgspec.changes +++ b/python-msgspec.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Mon Oct 28 22:29:06 UTC 2024 - Dirk Müller + +- add python313.patch: support python 3.13 + ------------------------------------------------------------------- Tue Jan 30 18:06:33 UTC 2024 - Dirk Müller diff --git a/python-msgspec.spec b/python-msgspec.spec index 638d295..0c0171f 100644 --- a/python-msgspec.spec +++ b/python-msgspec.spec @@ -23,6 +23,8 @@ Summary: A fast serialization and validation library License: BSD-3-Clause URL: https://jcristharif.com/msgspec/ Source: https://github.com/jcrist/msgspec/archive/refs/tags/%{version}.tar.gz#/msgspec-%{version}.tar.gz +# PATCH-FIX-UPSTREAM: gh#jcrist/msgspec#711 +Patch1: python313.patch BuildRequires: %{python_module devel} BuildRequires: %{python_module pip} BuildRequires: %{python_module pytest} diff --git a/python313.patch b/python313.patch new file mode 100644 index 0000000..9b9020f --- /dev/null +++ b/python313.patch @@ -0,0 +1,300 @@ +From 7ade46952adea22f3b2bb9c2b8b3139e4f2831b7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= + <16805946+edgarrmondragon@users.noreply.github.com> +Date: Sun, 13 Oct 2024 13:25:41 -0600 +Subject: [PATCH] Support Python 3.13 (not free-threaded) (#711) + +* Build Python 3.13 wheels (not free-threaded) + +* Include cp313 wheels for testing + +* Build Python 3.13 wheels only for testing + +* Remove trove classifier + +* Update for Python 3.13.0rc1 + +* Clean up + +* Bump cibuildwheel to 2.21.1 + +* Upgrade deprecated artifact actions + +* Update for Python 3.13.0rc3 + +* Bump cibuildwheel to 2.21.3 to use Python 3.13.0 final + +* Squash _eval_type warning + +* Still use _PyUnicode_EQ for older pythons + +Keeps around the semantic meaning for when this function is added back +in Python 3.14. + +* Use `PyObject_GetIter` for all versions + +* Use PyLong_AsNativeBytes on Py3.13 + +* Suppress hashing error when parsing annotated types + +--------- + +Co-authored-by: Jim Crist-Harif +--- + .github/workflows/ci.yml | 11 +++++--- + msgspec/_core.c | 56 ++++++++++++++++++++++++++++++---------- + msgspec/_utils.py | 11 +++++++- + setup.cfg | 2 ++ + 4 files changed, 61 insertions(+), 19 deletions(-) + +Index: msgspec-0.18.6/.github/workflows/ci.yml +=================================================================== +--- msgspec-0.18.6.orig/.github/workflows/ci.yml ++++ msgspec-0.18.6/.github/workflows/ci.yml +@@ -80,7 +80,7 @@ jobs: + env: + CIBW_TEST_REQUIRES: "pytest msgpack pyyaml tomli tomli_w" + CIBW_TEST_COMMAND: "pytest {project}/tests" +- CIBW_BUILD: "cp38-* cp39-* cp310-* cp311-* cp312-*" ++ CIBW_BUILD: "cp38-* cp39-* cp310-* cp311-* cp312-* cp313-*" + CIBW_SKIP: "*-win32 *_i686 *_s390x *_ppc64le" + CIBW_ARCHS_MACOS: "x86_64 arm64" + CIBW_ARCHS_LINUX: "x86_64 aarch64" +@@ -129,6 +129,7 @@ jobs: + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: ++ name: artifact-sdist + path: dist/*.tar.gz + + upload_pypi: +@@ -138,8 +139,9 @@ jobs: + steps: + - uses: actions/download-artifact@v2 + with: +- name: artifact ++ merge-multiple: true + path: dist ++ pattern: artifact-* + + - uses: pypa/gh-action-pypi-publish@master + with: +Index: msgspec-0.18.6/msgspec/_core.c +=================================================================== +--- msgspec-0.18.6.orig/msgspec/_core.c ++++ msgspec-0.18.6/msgspec/_core.c +@@ -20,6 +20,7 @@ + #define PY310_PLUS (PY_VERSION_HEX >= 0x030a0000) + #define PY311_PLUS (PY_VERSION_HEX >= 0x030b0000) + #define PY312_PLUS (PY_VERSION_HEX >= 0x030c0000) ++#define PY313_PLUS (PY_VERSION_HEX >= 0x030d0000) + + /* Hint to the compiler not to store `x` in a register since it is likely to + * change. Results in much higher performance on GCC, with smaller benefits on +@@ -56,6 +57,12 @@ ms_popcount(uint64_t i) { + #define SET_SIZE(obj, size) (((PyVarObject *)obj)->ob_size = size) + #endif + ++#if PY313_PLUS ++#define MS_UNICODE_EQ(a, b) (PyUnicode_Compare(a, b) == 0) ++#else ++#define MS_UNICODE_EQ(a, b) _PyUnicode_EQ(a, b) ++#endif ++ + #define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) == ((d) < 0)) ? (((n) + (d)/2)/(d)) : (((n) - (d)/2)/(d))) + + /* These macros are used to manually unroll some loops */ +@@ -497,7 +504,7 @@ find_keyword(PyObject *kwnames, PyObject + for (i = 0; i < nkwargs; i++) { + PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); + assert(PyUnicode_Check(kwname)); +- if (_PyUnicode_EQ(kwname, key)) { ++ if (MS_UNICODE_EQ(kwname, key)) { + return kwstack[i]; + } + } +@@ -4438,10 +4445,8 @@ typenode_collect_convert_structs(TypeNod + * + * If any of these checks fails, an appropriate error is returned. + */ +- PyObject *tag_mapping = NULL, *tag_field = NULL, *set_item = NULL; ++ PyObject *tag_mapping = NULL, *tag_field = NULL, *set_iter = NULL, *set_item = NULL; + PyObject *struct_info = NULL; +- Py_ssize_t set_pos = 0; +- Py_hash_t set_hash; + bool array_like = false; + bool tags_are_strings = true; + int status = -1; +@@ -4449,7 +4454,8 @@ typenode_collect_convert_structs(TypeNod + tag_mapping = PyDict_New(); + if (tag_mapping == NULL) goto cleanup; + +- while (_PySet_NextEntry(state->structs_set, &set_pos, &set_item, &set_hash)) { ++ set_iter = PyObject_GetIter(state->structs_set); ++ while ((set_item = PyIter_Next(set_iter))) { + struct_info = StructInfo_Convert(set_item); + if (struct_info == NULL) goto cleanup; + +@@ -4557,6 +4563,7 @@ typenode_collect_convert_structs(TypeNod + status = 0; + + cleanup: ++ Py_XDECREF(set_iter); + Py_XDECREF(tag_mapping); + Py_XDECREF(struct_info); + return status; +@@ -4612,11 +4619,15 @@ typenode_origin_args_metadata( + * abstract -> concrete mapping. If present, this is an unparametrized + * collection of some form. This helps avoid compatibility issues in + * Python 3.8, where unparametrized collections still have __args__. */ +- origin = PyDict_GetItem(state->mod->concrete_types, t); ++ origin = PyDict_GetItemWithError(state->mod->concrete_types, t); + if (origin != NULL) { + Py_INCREF(origin); + break; + } ++ else { ++ /* Ignore all errors in this initial check */ ++ PyErr_Clear(); ++ } + + /* If `t` is a type instance, no need to inspect further */ + if (PyType_CheckExact(t)) { +@@ -7313,7 +7324,7 @@ Struct_vectorcall(PyTypeObject *cls, PyO + * check for parameters passed both as arg and kwarg */ + for (field_index = 0; field_index < nfields; field_index++) { + PyObject *field = PyTuple_GET_ITEM(fields, field_index); +- if (_PyUnicode_EQ(kwname, field)) { ++ if (MS_UNICODE_EQ(kwname, field)) { + if (MS_UNLIKELY(field_index < nargs)) { + PyErr_Format( + PyExc_TypeError, +@@ -7720,7 +7731,7 @@ struct_replace(PyObject *self, PyObject + } + for (field_index = 0; field_index < nfields; field_index++) { + PyObject *field = PyTuple_GET_ITEM(fields, field_index); +- if (_PyUnicode_EQ(kwname, field)) goto kw_found; ++ if (MS_UNICODE_EQ(kwname, field)) goto kw_found; + } + + /* Unknown keyword */ +@@ -11251,7 +11262,16 @@ ms_uuid_to_16_bytes(MsgspecState *mod, P + PyErr_SetString(PyExc_TypeError, "uuid.int must be an int"); + return -1; + } ++#if PY313_PLUS ++ int out = (int)PyLong_AsNativeBytes( ++ int128, ++ buf, ++ 16, ++ Py_ASNATIVEBYTES_BIG_ENDIAN | Py_ASNATIVEBYTES_UNSIGNED_BUFFER ++ ); ++#else + int out = _PyLong_AsByteArray((PyLongObject *)int128, buf, 16, 0, 0); ++#endif + Py_DECREF(int128); + return out; + } +@@ -12403,8 +12423,7 @@ mpack_encode_list(EncoderState *self, Py + static int + mpack_encode_set(EncoderState *self, PyObject *obj) + { +- Py_ssize_t len, ppos = 0; +- Py_hash_t hash; ++ Py_ssize_t len = 0; + PyObject *item; + int status = -1; + +@@ -12423,13 +12442,18 @@ mpack_encode_set(EncoderState *self, PyO + + if (mpack_encode_array_header(self, len, "set") < 0) return -1; + if (Py_EnterRecursiveCall(" while serializing an object")) return -1; +- while (_PySet_NextEntry(obj, &ppos, &item, &hash)) { ++ ++ PyObject *iter = PyObject_GetIter(obj); ++ if (iter == NULL) goto cleanup; ++ ++ while ((item = PyIter_Next(iter))) { + if (mpack_encode_inline(self, item) < 0) goto cleanup; + } + status = 0; + + cleanup: + Py_LeaveRecursiveCall(); ++ Py_XDECREF(iter); + return status; + } + +@@ -13725,8 +13749,7 @@ json_encode_tuple(EncoderState *self, Py + static int + json_encode_set(EncoderState *self, PyObject *obj) + { +- Py_ssize_t len, ppos = 0; +- Py_hash_t hash; ++ Py_ssize_t len = 0; + PyObject *item; + int status = -1; + +@@ -13745,7 +13768,11 @@ json_encode_set(EncoderState *self, PyOb + + if (ms_write(self, "[", 1) < 0) return -1; + if (Py_EnterRecursiveCall(" while serializing an object")) return -1; +- while (_PySet_NextEntry(obj, &ppos, &item, &hash)) { ++ ++ PyObject *iter = PyObject_GetIter(obj); ++ if (iter == NULL) goto cleanup; ++ ++ while ((item = PyIter_Next(iter))) { + if (json_encode_inline(self, item) < 0) goto cleanup; + if (ms_write(self, ",", 1) < 0) goto cleanup; + } +@@ -13754,6 +13781,7 @@ json_encode_set(EncoderState *self, PyOb + status = 0; + cleanup: + Py_LeaveRecursiveCall(); ++ Py_XDECREF(iter); + return status; + } + +Index: msgspec-0.18.6/msgspec/_utils.py +=================================================================== +--- msgspec-0.18.6.orig/msgspec/_utils.py ++++ msgspec-0.18.6/msgspec/_utils.py +@@ -51,6 +51,15 @@ else: + return typing.ForwardRef(value, is_argument=False, is_class=True) + + ++# Python 3.13 adds a new mandatory type_params kwarg to _eval_type ++if sys.version_info >= (3, 13): ++ ++ def _eval_type(t, globalns, localns): ++ return typing._eval_type(t, globalns, localns, ()) ++else: ++ _eval_type = typing._eval_type ++ ++ + def _apply_params(obj, mapping): + if params := getattr(obj, "__parameters__", None): + args = tuple(mapping.get(p, p) for p in params) +@@ -127,7 +136,7 @@ def get_class_annotations(obj): + value = type(None) + elif isinstance(value, str): + value = _forward_ref(value) +- value = typing._eval_type(value, cls_locals, cls_globals) ++ value = _eval_type(value, cls_locals, cls_globals) + if mapping is not None: + value = _apply_params(value, mapping) + hints[name] = value +Index: msgspec-0.18.6/setup.cfg +=================================================================== +--- msgspec-0.18.6.orig/setup.cfg ++++ msgspec-0.18.6/setup.cfg +@@ -12,6 +12,8 @@ omit = + markers = + mypy + pyright ++filterwarnings = ++ error + + [versioneer] + VCS = git