diff --git a/docs/changelog.rst b/docs/changelog.rst index 4513151..a69b00f 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,8 @@ Changelog ========= +* 2026-10-07: Add ``Py_SETREF()`` and ``Py_XSETREF()`` to the limited C API + older than Python 3.16. * 2026-10-04: Add support for the limited C API (``Py_LIMITED_API``). * 2026-09-23: ``PyUnstable_Object_IsUniquelyReferenced()``, ``PyUnstable_TryIncRef()`` and ``PyUnstable_EnableTryIncRef()`` are no longer diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index 570ce85..2106740 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -137,6 +137,33 @@ static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) #endif +// Python 3.6 added Py_SETREF() and Py_XSETREF() macros. +// Python 3.16 adds them to the limited C API. +// Don't use the more advanced typeof()/__typeof__() or C++ implementation, +// but only the memcpy() implementation to keep the code short. memcpy() +// should be available in all C libraries. +#ifndef Py_SETREF +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \ + PyObject *_tmp_src = _PyObject_CAST(src); \ + memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \ + Py_DECREF(_tmp_old_dst); \ + } while (0) +#endif +#ifndef Py_XSETREF +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \ + PyObject *_tmp_src = _PyObject_CAST(src); \ + memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \ + Py_XDECREF(_tmp_old_dst); \ + } while (0) +#endif + + // bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index c9a91cc..62f354e 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -75,7 +75,6 @@ test_object(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) assert(Py_XNewRef(_Py_NULL) == _Py_NULL); -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03100000 // Py_SETREF() PyObject *setref = Py_NewRef(obj); PyObject *none = Py_None; @@ -100,7 +99,6 @@ test_object(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) Py_XSETREF(xsetref, _Py_NULL); assert(Py_REFCNT(obj) == refcnt); assert(xsetref == _Py_NULL); -#endif // Py_SET_REFCNT Py_SET_REFCNT(obj, Py_REFCNT(obj));