Skip to content

Commit 6f43d14

Browse files
committed
Add Py_SETREF/Py_XSETREF to old limited C API versions
1 parent a397338 commit 6f43d14

3 files changed

Lines changed: 30 additions & 2 deletions

File tree

‎docs/changelog.rst‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Changelog
22
=========
33

4+
* 2026-11-04: Add ``Py_SETREF()`` and ``Py_XSETREF()`` to the limited C API
5+
older than Python 3.16.
46
* 2026-10-12: Add functions:
57

68
* ``PyEval_GetFrameBuiltins()``

‎pythoncapi_compat.h‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,34 @@ static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt)
169169
#endif
170170

171171

172+
// Python 3.6 added Py_SETREF() and Py_XSETREF() macros.
173+
// Python 3.16 adds them to the limited C API.
174+
// Don't use the more advanced typeof()/__typeof__() or C++ (auto)
175+
// implementation, but only the memcpy() implementation to keep the code short.
176+
#ifndef Py_SETREF
177+
#define Py_SETREF(dst, src) \
178+
do { \
179+
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
180+
PyObject *_tmp_old_dst; \
181+
PyObject *_tmp_src = _PyObject_CAST(src); \
182+
memcpy(&_tmp_old_dst, _tmp_dst_ptr, sizeof(PyObject*)); \
183+
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
184+
Py_DECREF(_tmp_old_dst); \
185+
} while (0)
186+
#endif
187+
#ifndef Py_XSETREF
188+
#define Py_XSETREF(dst, src) \
189+
do { \
190+
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \
191+
PyObject *_tmp_old_dst; \
192+
PyObject *_tmp_src = _PyObject_CAST(src); \
193+
memcpy(&_tmp_old_dst, _tmp_dst_ptr, sizeof(PyObject*)); \
194+
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject*)); \
195+
Py_XDECREF(_tmp_old_dst); \
196+
} while (0)
197+
#endif
198+
199+
172200
// bpo-39573 added Py_SET_TYPE() to Python 3.9
173201
#if !defined(Py_SET_TYPE) && PY_VERSION_HEX < _Py_PACK_VERSION(3, 9)
174202
static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type)

‎tests/test_pythoncapi_compat_cext.c‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ test_object(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
7575

7676
assert(Py_XNewRef(_Py_NULL) == _Py_NULL);
7777

78-
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 16)
7978
// Py_SETREF()
8079
PyObject *setref = Py_NewRef(obj);
8180
PyObject *none = Py_None;
@@ -100,7 +99,6 @@ test_object(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
10099
Py_XSETREF(xsetref, _Py_NULL);
101100
assert(Py_REFCNT(obj) == refcnt);
102101
assert(xsetref == _Py_NULL);
103-
#endif
104102

105103
// Py_SET_REFCNT
106104
Py_SET_REFCNT(obj, Py_REFCNT(obj));

0 commit comments

Comments
 (0)