diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 82447cc98cd8d03..622a079ee6b4f22 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -986,6 +986,7 @@ func,Py_REFCNT,3.14,, macro,Py_RELATIVE_OFFSET,3.12,, func,Py_ReprEnter,3.2,, func,Py_ReprLeave,3.2,, +macro,Py_SETREF,3.16,, func,Py_SET_SIZE,3.15,, func,Py_SIZE,3.15,, func,Py_SetRecursionLimit,3.2,, @@ -1020,6 +1021,7 @@ macro,Py_UNBLOCK_THREADS,3.2,, func,Py_VaBuildValue,3.2,, data,Py_Version,3.11,, func,Py_XNewRef,3.10,, +macro,Py_XSETREF,3.16,, macro,Py_am_aiter,3.5,, macro,Py_am_anext,3.5,, macro,Py_am_await,3.5,, diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 65db9bd6bf70ba0..4b16abb3031c481 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -1181,6 +1181,10 @@ New features * Add :c:func:`Py_HashBuffer` to the limited C API. (Contributed by Victor Stinner in :gh:`148233`.) +* Add :c:macro:`Py_SETREF` and :c:macro:`Py_XSETREF` macros to the limited C + API. + (Contributed by Nathan Goldbaum and Victor Stinner in :gh:`158842`.) + Porting to Python 3.16 ---------------------- diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 5d35f983d2833c3..12d2e614623e30b 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -326,87 +326,6 @@ _PyObject_GenericSetAttrWithDict(PyObject *, PyObject *, PyAPI_FUNC(PyObject *) _PyObject_FunctionStr(PyObject *); -/* Safely decref `dst` and set `dst` to `src`. - * - * As in case of Py_CLEAR "the obvious" code can be deadly: - * - * Py_DECREF(dst); - * dst = src; - * - * The safe way is: - * - * Py_SETREF(dst, src); - * - * That arranges to set `dst` to `src` _before_ decref'ing, so that any code - * triggered as a side-effect of `dst` getting torn down no longer believes - * `dst` points to a valid object. - * - * Temporary variables are used to only evaluate macro arguments once and so - * avoid the duplication of side effects. _Py_TYPEOF(), C++ auto, or memcpy() - * is used to avoid a miscompilation caused by type punning. See Py_CLEAR() - * comment for implementation details about type punning. - * - * The memcpy() implementation does not emit a compiler warning if 'src' has - * not the same type than 'src': any pointer type is accepted for 'src'. - */ -#ifdef _Py_TYPEOF -#define Py_SETREF(dst, src) \ - do { \ - _Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \ - _Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \ - *_tmp_dst_ptr = (src); \ - Py_DECREF(_tmp_old_dst); \ - } while (0) -#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) -#define Py_SETREF(dst, src) \ - do { \ - auto _tmp_dst_ptr = &(dst); \ - auto _tmp_old_dst = (*_tmp_dst_ptr); \ - *_tmp_dst_ptr = (src); \ - Py_DECREF(_tmp_old_dst); \ - } while (0) -#else -#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 - -/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of - * Py_DECREF(). - */ -#ifdef _Py_TYPEOF -#define Py_XSETREF(dst, src) \ - do { \ - _Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \ - _Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \ - *_tmp_dst_ptr = (src); \ - Py_XDECREF(_tmp_old_dst); \ - } while (0) -#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) -#define Py_XSETREF(dst, src) \ - do { \ - auto _tmp_dst_ptr = &(dst); \ - auto _tmp_old_dst = (*_tmp_dst_ptr); \ - *_tmp_dst_ptr = (src); \ - Py_XDECREF(_tmp_old_dst); \ - } while (0) -#else -#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 - - /* Define a pair of assertion macros: _PyObject_ASSERT_FROM(), _PyObject_ASSERT_WITH_MSG() and _PyObject_ASSERT(). diff --git a/Include/refcount.h b/Include/refcount.h index 39661680e85a183..afbecd48ab31a1b 100644 --- a/Include/refcount.h +++ b/Include/refcount.h @@ -516,6 +516,87 @@ static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op) #endif +/* Safely decref `dst` and set `dst` to `src`. + * + * As in case of Py_CLEAR "the obvious" code can be deadly: + * + * Py_DECREF(dst); + * dst = src; + * + * The safe way is: + * + * Py_SETREF(dst, src); + * + * That arranges to set `dst` to `src` _before_ decref'ing, so that any code + * triggered as a side-effect of `dst` getting torn down no longer believes + * `dst` points to a valid object. + * + * Temporary variables are used to only evaluate macro arguments once and so + * avoid the duplication of side effects. _Py_TYPEOF(), C++ auto, or memcpy() + * is used to avoid a miscompilation caused by type punning. See Py_CLEAR() + * comment for implementation details about type punning. + * + * The memcpy() implementation does not emit a compiler warning if 'src' has + * not the same type than 'src': any pointer type is accepted for 'src'. + */ +#ifdef _Py_TYPEOF +#define Py_SETREF(dst, src) \ + do { \ + _Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \ + _Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = (src); \ + Py_DECREF(_tmp_old_dst); \ + } while (0) +#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) +#define Py_SETREF(dst, src) \ + do { \ + auto _tmp_dst_ptr = &(dst); \ + auto _tmp_old_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = (src); \ + Py_DECREF(_tmp_old_dst); \ + } while (0) +#else +#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 + +/* Py_XSETREF() is a variant of Py_SETREF() that uses Py_XDECREF() instead of + * Py_DECREF(). + */ +#ifdef _Py_TYPEOF +#define Py_XSETREF(dst, src) \ + do { \ + _Py_TYPEOF(&(dst)) _tmp_dst_ptr = &(dst); \ + _Py_TYPEOF(dst) _tmp_old_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = (src); \ + Py_XDECREF(_tmp_old_dst); \ + } while (0) +#elif defined(__cplusplus) && (__cplusplus >= 201103L || _MSVC_LANG >= 201103L) +#define Py_XSETREF(dst, src) \ + do { \ + auto _tmp_dst_ptr = &(dst); \ + auto _tmp_old_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = (src); \ + Py_XDECREF(_tmp_old_dst); \ + } while (0) +#else +#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 + + /* Function to use in case the object pointer can be NULL: */ static inline void Py_XINCREF(PyObject *op) { diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index e392b66f19cc4ab..03d85646f29a804 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -104,7 +104,6 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_CLEAR(slots[0]); assert(slots[0] == _Py_NULL); -#ifndef Py_LIMITED_API // Test Py_SETREF(): use typeof()/__typeof__() if available, or memcpy() obj = Py_None; Py_SETREF(obj, _Py_NULL); @@ -124,7 +123,6 @@ test_macros(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) slots[0] = Py_None; Py_XSETREF(slots[0], _Py_NULL); assert(slots[0] == _Py_NULL); -#endif // Test that Py_BEGIN_CRITICAL_SECTION is available dict = PyDict_New(); diff --git a/Misc/NEWS.d/next/C_API/2026-10-05-15-24-22.gh-issue-158842.g7eLtE.rst b/Misc/NEWS.d/next/C_API/2026-10-05-15-24-22.gh-issue-158842.g7eLtE.rst new file mode 100644 index 000000000000000..222d5279a3fa3ae --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-10-05-15-24-22.gh-issue-158842.g7eLtE.rst @@ -0,0 +1,2 @@ +Add :c:macro:`Py_SETREF` and :c:macro:`Py_XSETREF` macros to the limited C +API. Patch by Nathan Goldbaum and Victor Stinner. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 0425fba42cbe2c7..751c186674c7c91 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2884,3 +2884,7 @@ [function.Py_HashBuffer] added = '3.16' +[macro.Py_SETREF] + added = '3.16' +[macro.Py_XSETREF] + added = '3.16' diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 425ec540b62dcc0..8fcb86e33e55a83 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -246,63 +246,6 @@ test_py_set_immortal(PyObject *self, PyObject *unused) Py_RETURN_NONE; } -static PyObject * -_test_incref(PyObject *ob) -{ - return Py_NewRef(ob); -} - -static PyObject * -test_xincref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - PyObject *obj = PyLong_FromLong(0); - Py_XINCREF(_test_incref(obj)); - Py_DECREF(obj); - Py_DECREF(obj); - Py_DECREF(obj); - Py_RETURN_NONE; -} - - -static PyObject * -test_incref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - PyObject *obj = PyLong_FromLong(0); - Py_INCREF(_test_incref(obj)); - Py_DECREF(obj); - Py_DECREF(obj); - Py_DECREF(obj); - Py_RETURN_NONE; -} - - -static PyObject * -test_xdecref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - Py_XDECREF(PyLong_FromLong(0)); - Py_RETURN_NONE; -} - - -static PyObject * -test_decref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - Py_DECREF(PyLong_FromLong(0)); - Py_RETURN_NONE; -} - - -static PyObject * -test_incref_decref_API(PyObject *ob, PyObject *Py_UNUSED(ignored)) -{ - PyObject *obj = PyLong_FromLong(0); - Py_IncRef(obj); - Py_DecRef(obj); - Py_DecRef(obj); - Py_RETURN_NONE; -} - - #ifdef Py_REF_DEBUG static PyObject * negative_refcount(PyObject *self, PyObject *Py_UNUSED(args)) @@ -343,186 +286,6 @@ decref_freed_object(PyObject *self, PyObject *Py_UNUSED(args)) #endif -// Test Py_CLEAR() macro -static PyObject* -test_py_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - // simple case with a variable - PyObject *obj = PyList_New(0); - if (obj == NULL) { - return NULL; - } - Py_CLEAR(obj); - assert(obj == NULL); - - // gh-98724: complex case, Py_CLEAR() argument has a side effect - PyObject* array[1]; - array[0] = PyList_New(0); - if (array[0] == NULL) { - return NULL; - } - - PyObject **p = array; - Py_CLEAR(*p++); - assert(array[0] == NULL); - assert(p == array + 1); - - Py_RETURN_NONE; -} - - -// Test Py_SETREF() and Py_XSETREF() macros, similar to test_py_clear() -static PyObject* -test_py_setref(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - // Py_SETREF() simple case with a variable - PyObject *obj = PyList_New(0); - if (obj == NULL) { - return NULL; - } - Py_SETREF(obj, NULL); - assert(obj == NULL); - - // Py_XSETREF() simple case with a variable - PyObject *obj2 = PyList_New(0); - if (obj2 == NULL) { - return NULL; - } - Py_XSETREF(obj2, NULL); - assert(obj2 == NULL); - // test Py_XSETREF() when the argument is NULL - Py_XSETREF(obj2, NULL); - assert(obj2 == NULL); - - // gh-98724: complex case, Py_SETREF() argument has a side effect - PyObject* array[1]; - array[0] = PyList_New(0); - if (array[0] == NULL) { - return NULL; - } - - PyObject **p = array; - Py_SETREF(*p++, NULL); - assert(array[0] == NULL); - assert(p == array + 1); - - // gh-98724: complex case, Py_XSETREF() argument has a side effect - PyObject* array2[1]; - array2[0] = PyList_New(0); - if (array2[0] == NULL) { - return NULL; - } - - PyObject **p2 = array2; - Py_XSETREF(*p2++, NULL); - assert(array2[0] == NULL); - assert(p2 == array2 + 1); - - // test Py_XSETREF() when the argument is NULL - p2 = array2; - Py_XSETREF(*p2++, NULL); - assert(array2[0] == NULL); - assert(p2 == array2 + 1); - - Py_RETURN_NONE; -} - - -#define TEST_REFCOUNT() \ - do { \ - PyObject *obj = PyList_New(0); \ - if (obj == NULL) { \ - return NULL; \ - } \ - assert(Py_REFCNT(obj) == 1); \ - \ - /* test Py_NewRef() */ \ - PyObject *ref = Py_NewRef(obj); \ - assert(ref == obj); \ - assert(Py_REFCNT(obj) == 2); \ - Py_DECREF(ref); \ - \ - /* test Py_XNewRef() */ \ - PyObject *xref = Py_XNewRef(obj); \ - assert(xref == obj); \ - assert(Py_REFCNT(obj) == 2); \ - Py_DECREF(xref); \ - \ - assert(Py_XNewRef(NULL) == NULL); \ - \ - Py_DECREF(obj); \ - Py_RETURN_NONE; \ - } while (0) - - -// Test Py_NewRef() and Py_XNewRef() macros -static PyObject* -test_refcount_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - TEST_REFCOUNT(); -} - -#undef Py_NewRef -#undef Py_XNewRef - -// Test Py_NewRef() and Py_XNewRef() functions, after undefining macros. -static PyObject* -test_refcount_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - TEST_REFCOUNT(); -} - - -// Test Py_Is() function -#define TEST_PY_IS() \ - do { \ - PyObject *o_none = Py_None; \ - PyObject *o_true = Py_True; \ - PyObject *o_false = Py_False; \ - PyObject *obj = PyList_New(0); \ - if (obj == NULL) { \ - return NULL; \ - } \ - \ - /* test Py_Is() */ \ - assert(Py_Is(obj, obj)); \ - assert(!Py_Is(obj, o_none)); \ - \ - /* test Py_None */ \ - assert(Py_Is(o_none, o_none)); \ - assert(!Py_Is(obj, o_none)); \ - \ - /* test Py_True */ \ - assert(Py_Is(o_true, o_true)); \ - assert(!Py_Is(o_false, o_true)); \ - assert(!Py_Is(obj, o_true)); \ - \ - /* test Py_False */ \ - assert(Py_Is(o_false, o_false)); \ - assert(!Py_Is(o_true, o_false)); \ - assert(!Py_Is(obj, o_false)); \ - \ - Py_DECREF(obj); \ - Py_RETURN_NONE; \ - } while (0) - -// Test Py_Is() macro -static PyObject* -test_py_is_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - TEST_PY_IS(); -} - -#undef Py_Is - -// Test Py_Is() function, after undefining its macro. -static PyObject* -test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - TEST_PY_IS(); -} - - static PyObject * clear_managed_dict(PyObject *self, PyObject *obj) { @@ -597,21 +360,10 @@ static PyMethodDef test_methods[] = { {"pyobject_is_unique_temporary_new_object", pyobject_is_unique_temporary_new_object, METH_NOARGS}, {"test_py_try_inc_ref", test_py_try_inc_ref, METH_NOARGS}, {"test_py_set_immortal", test_py_set_immortal, METH_NOARGS}, - {"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS}, - {"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS}, - {"test_xdecref_doesnt_leak",test_xdecref_doesnt_leak, METH_NOARGS}, - {"test_decref_doesnt_leak", test_decref_doesnt_leak, METH_NOARGS}, - {"test_incref_decref_API", test_incref_decref_API, METH_NOARGS}, #ifdef Py_REF_DEBUG {"negative_refcount", negative_refcount, METH_NOARGS}, {"decref_freed_object", decref_freed_object, METH_NOARGS}, #endif - {"test_py_clear", test_py_clear, METH_NOARGS}, - {"test_py_setref", test_py_setref, METH_NOARGS}, - {"test_refcount_macros", test_refcount_macros, METH_NOARGS}, - {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS}, - {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, - {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {"clear_managed_dict", clear_managed_dict, METH_O, NULL}, {"is_uniquely_referenced", is_uniquely_referenced, METH_O}, {"pyobject_dump", pyobject_dump, METH_VARARGS}, diff --git a/Modules/_testlimitedcapi/object.c b/Modules/_testlimitedcapi/object.c index 594ba60f9abf204..03debb56b33c4b9 100644 --- a/Modules/_testlimitedcapi/object.c +++ b/Modules/_testlimitedcapi/object.c @@ -64,10 +64,259 @@ test_constants(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_RETURN_NONE; } + +static PyObject * +_test_incref(PyObject *ob) +{ + return Py_NewRef(ob); +} + +static PyObject * +test_xincref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + PyObject *obj = PyLong_FromLong(0); + Py_XINCREF(_test_incref(obj)); + Py_DECREF(obj); + Py_DECREF(obj); + Py_DECREF(obj); + Py_RETURN_NONE; +} + + +static PyObject * +test_incref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + PyObject *obj = PyLong_FromLong(0); + Py_INCREF(_test_incref(obj)); + Py_DECREF(obj); + Py_DECREF(obj); + Py_DECREF(obj); + Py_RETURN_NONE; +} + + +static PyObject * +test_xdecref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + Py_XDECREF(PyLong_FromLong(0)); + Py_RETURN_NONE; +} + + +static PyObject * +test_decref_doesnt_leak(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + Py_DECREF(PyLong_FromLong(0)); + Py_RETURN_NONE; +} + + +static PyObject * +test_incref_decref_API(PyObject *ob, PyObject *Py_UNUSED(ignored)) +{ + PyObject *obj = PyLong_FromLong(0); + Py_IncRef(obj); + Py_DecRef(obj); + Py_DecRef(obj); + Py_RETURN_NONE; +} + + +// Test Py_CLEAR() macro +static PyObject* +test_py_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + // simple case with a variable + PyObject *obj = PyList_New(0); + if (obj == NULL) { + return NULL; + } + Py_CLEAR(obj); + assert(obj == NULL); + + // gh-98724: complex case, Py_CLEAR() argument has a side effect + PyObject* array[1]; + array[0] = PyList_New(0); + if (array[0] == NULL) { + return NULL; + } + + PyObject **p = array; + Py_CLEAR(*p++); + assert(array[0] == NULL); + assert(p == array + 1); + + Py_RETURN_NONE; +} + + +// Test Py_SETREF() and Py_XSETREF() macros, similar to test_py_clear() +static PyObject* +test_py_setref(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + // Py_SETREF() simple case with a variable + PyObject *obj = PyList_New(0); + if (obj == NULL) { + return NULL; + } + Py_SETREF(obj, NULL); + assert(obj == NULL); + + // Py_XSETREF() simple case with a variable + PyObject *obj2 = PyList_New(0); + if (obj2 == NULL) { + return NULL; + } + Py_XSETREF(obj2, NULL); + assert(obj2 == NULL); + // test Py_XSETREF() when the argument is NULL + Py_XSETREF(obj2, NULL); + assert(obj2 == NULL); + + // gh-98724: complex case, Py_SETREF() argument has a side effect + PyObject* array[1]; + array[0] = PyList_New(0); + if (array[0] == NULL) { + return NULL; + } + + PyObject **p = array; + Py_SETREF(*p++, NULL); + assert(array[0] == NULL); + assert(p == array + 1); + + // gh-98724: complex case, Py_XSETREF() argument has a side effect + PyObject* array2[1]; + array2[0] = PyList_New(0); + if (array2[0] == NULL) { + return NULL; + } + + PyObject **p2 = array2; + Py_XSETREF(*p2++, NULL); + assert(array2[0] == NULL); + assert(p2 == array2 + 1); + + // test Py_XSETREF() when the argument is NULL + p2 = array2; + Py_XSETREF(*p2++, NULL); + assert(array2[0] == NULL); + assert(p2 == array2 + 1); + + Py_RETURN_NONE; +} + + +#define TEST_REFCOUNT() \ + do { \ + PyObject *obj = PyList_New(0); \ + if (obj == NULL) { \ + return NULL; \ + } \ + assert(Py_REFCNT(obj) == 1); \ + \ + /* test Py_NewRef() */ \ + PyObject *ref = Py_NewRef(obj); \ + assert(ref == obj); \ + assert(Py_REFCNT(obj) == 2); \ + Py_DECREF(ref); \ + \ + /* test Py_XNewRef() */ \ + PyObject *xref = Py_XNewRef(obj); \ + assert(xref == obj); \ + assert(Py_REFCNT(obj) == 2); \ + Py_DECREF(xref); \ + \ + assert(Py_XNewRef(NULL) == NULL); \ + \ + Py_DECREF(obj); \ + Py_RETURN_NONE; \ + } while (0) + + +// Test Py_NewRef() and Py_XNewRef() macros +static PyObject* +test_refcount_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_REFCOUNT(); +} + +#undef Py_NewRef +#undef Py_XNewRef + +// Test Py_NewRef() and Py_XNewRef() functions, after undefining macros. +static PyObject* +test_refcount_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_REFCOUNT(); +} + + +// Test Py_Is() function +#define TEST_PY_IS() \ + do { \ + PyObject *o_none = Py_None; \ + PyObject *o_true = Py_True; \ + PyObject *o_false = Py_False; \ + PyObject *obj = PyList_New(0); \ + if (obj == NULL) { \ + return NULL; \ + } \ + \ + /* test Py_Is() */ \ + assert(Py_Is(obj, obj)); \ + assert(!Py_Is(obj, o_none)); \ + \ + /* test Py_None */ \ + assert(Py_Is(o_none, o_none)); \ + assert(!Py_Is(obj, o_none)); \ + \ + /* test Py_True */ \ + assert(Py_Is(o_true, o_true)); \ + assert(!Py_Is(o_false, o_true)); \ + assert(!Py_Is(obj, o_true)); \ + \ + /* test Py_False */ \ + assert(Py_Is(o_false, o_false)); \ + assert(!Py_Is(o_true, o_false)); \ + assert(!Py_Is(obj, o_false)); \ + \ + Py_DECREF(obj); \ + Py_RETURN_NONE; \ + } while (0) + +// Test Py_Is() macro +static PyObject* +test_py_is_macros(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_PY_IS(); +} + +#undef Py_Is + +// Test Py_Is() function, after undefining its macro. +static PyObject* +test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + TEST_PY_IS(); +} + + static PyMethodDef test_methods[] = { {"get_constant", get_constant, METH_VARARGS}, {"get_constant_borrowed", get_constant_borrowed, METH_VARARGS}, {"test_constants", test_constants, METH_NOARGS}, + {"test_xincref_doesnt_leak",test_xincref_doesnt_leak, METH_NOARGS}, + {"test_incref_doesnt_leak", test_incref_doesnt_leak, METH_NOARGS}, + {"test_xdecref_doesnt_leak",test_xdecref_doesnt_leak, METH_NOARGS}, + {"test_decref_doesnt_leak", test_decref_doesnt_leak, METH_NOARGS}, + {"test_incref_decref_API", test_incref_decref_API, METH_NOARGS}, + {"test_py_clear", test_py_clear, METH_NOARGS}, + {"test_py_setref", test_py_setref, METH_NOARGS}, + {"test_refcount_macros", test_refcount_macros, METH_NOARGS}, + {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS}, + {"test_py_is_macros", test_py_is_macros, METH_NOARGS}, + {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS}, {NULL}, };