commit b7215ffbae167b04d1c845c8f994eccfd19f5be1 Author: Victor Stinner Date: Wed Jan 26 02:06:30 2022 +0100 Add PyDescr_New() diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index 3e55206..f4b2f30 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -33,6 +33,16 @@ extern "C" { #endif +// Py_UNUSED() was added to Python 3.4.0b2. +#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) +# if defined(__GNUC__) || defined(__clang__) +# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) +# else +# define Py_UNUSED(name) _unused_ ## name +# endif +#endif + + // Cast argument to PyObject* type. #ifndef _PyObject_CAST # define _PyObject_CAST(op) ((PyObject*)(op)) @@ -372,13 +382,40 @@ _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) { #endif -// Py_UNUSED() was added to Python 3.4.0b2. -#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) -# if defined(__GNUC__) || defined(__clang__) -# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) -# else -# define Py_UNUSED(name) _unused_ ## name -# endif +// bpo-45476 added PyDescr_New() to Python 3.11.0a5 +#if PY_VERSION_HEX < 0x030B00A5 +PyDescrObject * +PyDescr_New(PyTypeObject *descr_type, PyTypeObject *type, const char *name) +{ + assert(descr_type != NULL); + assert(type != NULL); + assert(name != NULL); + + PyObject *name_obj; +#if PY_MAJOR_VERSION >= 3 + name_obj = PyUnicode_InternFromString(name); +#else + name_obj = PyString_InternFromString(name); +#endif + if (name_obj == NULL) { + return NULL; + } + + PyDescrObject *descr = (PyDescrObject *)PyType_GenericAlloc(descr_type, 0); + if (descr == NULL) { + Py_DECREF(name_obj); + return NULL; + } + + + descr->d_type = (PyTypeObject*)Py_NewRef(type); + descr->d_name = name_obj; + // PyDescrObject.d_qualname was added to Python 3.3.0a1 +#if PY_VERSION_HEX >= 0x030300A1 + descr->d_qualname = NULL; +#endif + return descr; +} #endif diff --git a/tests/test_pythoncapi_compat.py b/tests/test_pythoncapi_compat.py index ea93a77..12d44ba 100644 --- a/tests/test_pythoncapi_compat.py +++ b/tests/test_pythoncapi_compat.py @@ -40,7 +40,7 @@ def build_ext(): display_title("Build the C extension") if os.path.exists("build"): shutil.rmtree("build") - os.environ['CFLAGS'] = "-I .." + os.environ['CFLAGS'] = "-I .. -O0 -Wp,-U_FORTIFY_SOURCE" cmd = [sys.executable, "setup.py", "build"] if VERBOSE: run_command(cmd) diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index a5fabf1..5ba00c7 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -395,6 +395,77 @@ error: return NULL; } +static void +mydescr_dealloc(PyDescrObject *descr) { + PyObject_GC_UnTrack(descr); +#if PY_MAJOR_VERSION >= 3 + Py_XDECREF(PyDescr_TYPE(descr)); + Py_XDECREF(PyDescr_NAME(descr)); +#else + Py_XDECREF(descr->d_type); + Py_XDECREF(descr->d_name); +#endif + Py_TYPE(descr)->tp_free(descr); +} + + +static int +mydescr_traverse( +#if PY_VERSION_HEX >= 0x03090000 + PyObject *self, visitproc visit, void *arg +#else + PyObject *Py_UNUSED(self), visitproc Py_UNUSED(visit), void *Py_UNUSED(arg) +#endif + ) +{ +#if PY_VERSION_HEX >= 0x03090000 + PyDescrObject *descr = (PyDescrObject *)self; + Py_VISIT(PyDescr_TYPE(descr)); +#endif + return 0; +} + +PyTypeObject MyDescr_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "mydescr", + .tp_basicsize = sizeof(PyDescrObject), + .tp_dealloc = (destructor)mydescr_dealloc, + .tp_traverse = mydescr_traverse, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, +}; + + +static PyObject * +test_pydescr_new(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) +{ + // PyDescr_New() + PyTypeObject *descr_type = &MyDescr_Type; + PyTypeObject *type = &PyLong_Type; + + if (PyType_Ready(&MyDescr_Type) < 0) { + return NULL; + } + + PyDescrObject *descr = PyDescr_New(descr_type, type, "name"); + if (descr == NULL) { + return NULL; + } + assert(Py_REFCNT(descr) == 1); +#if !defined(PYPY_VERSION) + assert(PyObject_GC_IsTracked((PyObject*)descr)); +#endif +#if PY_MAJOR_VERSION >= 3 + assert(PyDescr_TYPE(descr) == type); + assert(PyUnicode_CompareWithASCIIString(PyDescr_NAME(descr), "name") == 0); +#else + assert(descr->d_type == type); + assert(strcmp(PyString_AsString(descr->d_name), "name") == 0); +#endif + + Py_DECREF(descr); + Py_RETURN_NONE; +} + static struct PyMethodDef methods[] = { {"test_object", test_object, METH_NOARGS, NULL}, @@ -408,6 +479,7 @@ static struct PyMethodDef methods[] = { {"test_calls", test_calls, METH_NOARGS, NULL}, {"test_gc", test_gc, METH_NOARGS, NULL}, {"test_module", test_module, METH_NOARGS, NULL}, + {"test_pydescr_new", test_pydescr_new, METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} };