From 6357bc272c83fb450fbe7de7e7ca15d815e75174 Mon Sep 17 00:00:00 2001 From: Julien Stephan Date: Sat, 21 Feb 2026 09:09:34 +0100 Subject: [PATCH 1/4] ci: add riscv64 manylinux/musllinux wheels (#664) Now that cibuildwheel and PyPI support riscv64, we can start building riscv64 wheels. Because there is no native riscv64 runner available, this PR adds a QEMU-based riscv64 job to the cibuildwheel workflow. Signed-off-by: Julien Stephan --- .github/workflows/wheel.yml | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wheel.yml b/.github/workflows/wheel.yml index 531abbc3..f3fbe29e 100644 --- a/.github/workflows/wheel.yml +++ b/.github/workflows/wheel.yml @@ -12,9 +12,19 @@ jobs: strategy: matrix: # macos-13 is for intel - os: ["ubuntu-24.04", "ubuntu-24.04-arm", "windows-latest", "windows-11-arm", "macos-13", "macos-latest"] + include: + - os: ubuntu-24.04 + - os: ubuntu-24.04-arm + - os: windows-latest + - os: windows-11-arm + - os: macos-13 + - os: macos-latest + - os: ubuntu-24.04 + cibw_archs: riscv64 + name_suffix: "-riscv64" + runs-on: ${{ matrix.os }} - name: Build wheels on ${{ matrix.os }} + name: Build wheels on ${{ matrix.os }}${{ matrix.name_suffix || '' }} steps: - uses: actions/checkout@v6 @@ -28,12 +38,19 @@ jobs: pip install -r requirements.txt make cython + - name: Set up QEMU for emulation + if: matrix.cibw_archs == 'riscv64' + uses: docker/setup-qemu-action@v3 + with: + platforms: ${{ matrix.cibw_archs }} + - name: Build uses: pypa/cibuildwheel@v3.3.0 env: CIBW_TEST_REQUIRES: "pytest" CIBW_TEST_COMMAND: "pytest {package}/test" CIBW_SKIP: "pp* cp38-* cp39-* cp310-win_arm64" + CIBW_ARCHS: ${{ matrix.cibw_archs || 'auto' }} - name: Build sdist if: runner.os == 'Linux' && runner.arch == 'X64' @@ -44,7 +61,7 @@ jobs: - name: Upload Wheels to artifact uses: actions/upload-artifact@v4 with: - name: wheels-${{ matrix.os }} + name: wheels-${{ matrix.os }}${{ matrix.name_suffix || '' }} path: wheelhouse # combine all wheels into one artifact From 156bb05a15b9e58acf88b138185d5cb2068dad88 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Tue, 21 Apr 2026 09:22:06 +0200 Subject: [PATCH 2/4] fix: check `unpack_callback_uint32` result (#666) Similar to #665, just a return value check to propagate the error in case one happens (instead of silently ignoring it). Note that as opposed to the previous lines, we don't need to `PyErr_SetString` since `unpack_callback_uint32` calls `PyLong_FromSize_t` which itself should set whatever Python error is relevant; we just need to make it clear to the caller that an error occurred. --- msgpack/unpack_container_header.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/msgpack/unpack_container_header.h b/msgpack/unpack_container_header.h index c14a3c2b..e48d43ba 100644 --- a/msgpack/unpack_container_header.h +++ b/msgpack/unpack_container_header.h @@ -45,7 +45,10 @@ static inline int unpack_container_header(unpack_context* ctx, const char* data, PyErr_SetString(PyExc_ValueError, "Unexpected type header on stream"); return -1; } - unpack_callback_uint32(&ctx->user, size, &ctx->stack[0].obj); + + if (unpack_callback_uint32(&ctx->user, size, &ctx->stack[0].obj) < 0) + return -1; + return 1; } From 5d465bdb772d977d0b825a63e8061bd2fab5588b Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Tue, 21 Apr 2026 11:06:10 +0200 Subject: [PATCH 3/4] fix: re-raise existing exception when available (#667) --- msgpack/_unpacker.pyx | 7 ++++++- test/test_except.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/msgpack/_unpacker.pyx b/msgpack/_unpacker.pyx index f0cf96d7..e25986ee 100644 --- a/msgpack/_unpacker.pyx +++ b/msgpack/_unpacker.pyx @@ -205,7 +205,10 @@ def unpackb(object packed, *, object object_hook=None, object list_hook=None, raise FormatError elif ret == -3: raise StackError - raise ValueError("Unpack failed: error = %d" % (ret,)) + elif PyErr_Occurred(): + raise + else: + raise ValueError("Unpack failed: error = %d" % (ret,)) cdef class Unpacker: @@ -481,6 +484,8 @@ cdef class Unpacker: raise FormatError elif ret == -3: raise StackError + elif PyErr_Occurred(): + raise else: raise ValueError("Unpack failed: error = %d" % (ret,)) diff --git a/test/test_except.py b/test/test_except.py index b77ac800..c56a6a30 100644 --- a/test/test_except.py +++ b/test/test_except.py @@ -4,7 +4,7 @@ from pytest import raises -from msgpack import FormatError, OutOfData, StackError, Unpacker, packb, unpackb +from msgpack import ExtType, FormatError, OutOfData, StackError, Unpacker, packb, unpackb class DummyException(Exception): @@ -32,6 +32,34 @@ def hook(obj): ) +def test_raise_from_list_hook(): + def hook(lst: list) -> list: + raise DummyException + + with raises(DummyException): + unpackb(packb([1, 2, 3]), list_hook=hook) + + with raises(DummyException): + unpacker = Unpacker(list_hook=hook) + unpacker.feed(packb([1, 2, 3])) + unpacker.unpack() + + +def test_raise_from_ext_hook(): + def hook(code: int, data: bytes) -> ExtType: + raise DummyException + + packed = packb(ExtType(42, b"hello")) + + with raises(DummyException): + unpackb(packed, ext_hook=hook) + + with raises(DummyException): + unpacker = Unpacker(ext_hook=hook) + unpacker.feed(packed) + unpacker.unpack() + + def test_invalidvalue(): incomplete = b"\xd9\x97#DL_" # raw8 - length=0x97 with raises(ValueError): From 95c8be516db9cb5d8c1e10aa973091dcf6c82ff8 Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Tue, 21 Apr 2026 11:27:11 +0200 Subject: [PATCH 4/4] fix: check return code in `unpack_callback_int64` (#665) --- msgpack/unpack.h | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/msgpack/unpack.h b/msgpack/unpack.h index 58a2f4f5..0f9ffc05 100644 --- a/msgpack/unpack.h +++ b/msgpack/unpack.h @@ -70,12 +70,7 @@ static inline int unpack_callback_uint32(unpack_user* u, uint32_t d, msgpack_unp static inline int unpack_callback_uint64(unpack_user* u, uint64_t d, msgpack_unpack_object* o) { - PyObject *p; - if (d > LONG_MAX) { - p = PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)d); - } else { - p = PyLong_FromLong((long)d); - } + PyObject *p = PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)d); if (!p) return -1; *o = p; @@ -103,12 +98,9 @@ static inline int unpack_callback_int8(unpack_user* u, int8_t d, msgpack_unpack_ static inline int unpack_callback_int64(unpack_user* u, int64_t d, msgpack_unpack_object* o) { - PyObject *p; - if (d > LONG_MAX || d < LONG_MIN) { - p = PyLong_FromLongLong((PY_LONG_LONG)d); - } else { - p = PyLong_FromLong((long)d); - } + PyObject *p = PyLong_FromLongLong((PY_LONG_LONG)d); + if (!p) + return -1; *o = p; return 0; }