From d6442068a8035ac65b61677650bb635d729917b0 Mon Sep 17 00:00:00 2001 From: Jurica Bradaric Date: Mon, 24 Apr 2023 09:55:41 +0200 Subject: [PATCH 1/8] gh-103743: Add PyUnstable_Object_GC_NewWithExtraData (#103743) --- Doc/c-api/gcsupport.rst | 13 +++ Include/objimpl.h | 4 + Lib/test/test_capi/test_misc.py | 14 +++ ...-04-24-10-31-59.gh-issue-103743.2xYA1K.rst | 2 + Modules/_testcapimodule.c | 96 ++++++++++++++++++- Modules/gcmodule.c | 12 +++ Tools/c-analyzer/cpython/ignored.tsv | 1 + 7 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/C API/2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index cb5d64a50487fe..8f47fb64bd0315 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -65,6 +65,19 @@ rules: Analogous to :c:func:`PyObject_NewVar` but for container objects with the :const:`Py_TPFLAGS_HAVE_GC` flag set. +.. c:function:: PyObject* PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size) + + Analogous to :c:func:`PyObject_GC_New` but for container objects that + have additional data at the end of the object not managed by Python. + + .. warning:: + The function is marked as unstable because the final mechanism + for reserving extra data after an instance is not yet decided. + Once :pep:`697` is implemented, the mechanism described there can + be used to reserve the extra data. + + .. versionadded:: 3.12 + .. c:function:: TYPE* PyObject_GC_Resize(TYPE, PyVarObject *op, Py_ssize_t newsize) diff --git a/Include/objimpl.h b/Include/objimpl.h index ef871c5ea93ebe..bd39b72f30c677 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -131,6 +131,10 @@ PyAPI_FUNC(PyVarObject *) PyObject_InitVar(PyVarObject *, PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *); PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t); +#if !defined(Py_LIMITED_API) +PyAPI_FUNC(PyObject *) PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *, size_t); +#endif + #define PyObject_New(type, typeobj) ((type *)_PyObject_New(typeobj)) // Alias to PyObject_New(). In Python 3.8, PyObject_NEW() called directly diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 637adc01a331ce..80b8ee178f7092 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1043,6 +1043,20 @@ class dictsub(dict): ... # dict subclasses must work self.assertEqual(_testcapi.function_get_kw_defaults(some), None) self.assertEqual(some.__kwdefaults__, None) + def test_unstable_gc_new_with_extra_data(self): + class Data(_testcapi.ObjExtraData): + __slots__ = ('x', 'y') + + d = Data() + d.x = 10 + d.y = 20 + d.extra = 30 + self.assertEqual(d.x, 10) + self.assertEqual(d.y, 20) + self.assertEqual(d.extra, 30) + del d.extra + self.assertIsNone(d.extra) + class TestPendingCalls(unittest.TestCase): diff --git a/Misc/NEWS.d/next/C API/2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst b/Misc/NEWS.d/next/C API/2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst new file mode 100644 index 00000000000000..e6ae7e45f19984 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst @@ -0,0 +1,2 @@ +Add ``PyUnstable_Object_GC_NewWithExtraData`` function that can be used to +allocate additional memory after an object for data not managed by Python. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 557a6d46ed4632..0e4f92f9052681 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3343,7 +3343,7 @@ test_gc_visit_objects_basic(PyObject *Py_UNUSED(self), } state.target = obj; state.found = 0; - + PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state); Py_DECREF(obj); if (!state.found) { @@ -3380,6 +3380,95 @@ test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self), Py_RETURN_NONE; } +typedef struct { + PyObject_HEAD +} ObjExtraData; + +static PyObject * +obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + size_t extra_size = sizeof(PyObject *); + PyObject *obj = PyUnstable_Object_GC_NewWithExtraData(type, extra_size); + if (obj == NULL) + return PyErr_NoMemory(); + memset(obj, '\0', type->tp_basicsize + extra_size); + PyObject_Init(obj, type); + PyObject_GC_Track(obj); + return obj; +} + +static PyObject ** +obj_extra_data_get_extra_storage(PyObject *self) +{ + return (PyObject **)((char *)self + Py_TYPE(self)->tp_basicsize); +} + +static PyObject * +obj_extra_data_get(PyObject *self, void *Py_UNUSED(ignored)) +{ + PyObject **extra_storage = obj_extra_data_get_extra_storage(self); + PyObject *value = *extra_storage; + if (!value) + Py_RETURN_NONE; + Py_INCREF(value); + return value; +} + +static int +obj_extra_data_set(PyObject *self, PyObject *newval, void *Py_UNUSED(ignored)) +{ + PyObject **extra_storage = obj_extra_data_get_extra_storage(self); + Py_CLEAR(*extra_storage); + if (newval) { + Py_INCREF(newval); + *extra_storage = newval; + } + return 0; +} + +static PyGetSetDef obj_extra_data_getset[] = { + {"extra", (getter)obj_extra_data_get, (setter)obj_extra_data_set, NULL}, + {NULL} +}; + +static int +obj_extra_data_traverse(PyObject *self, visitproc visit, void *arg) +{ + PyObject **extra_storage = obj_extra_data_get_extra_storage(self); + PyObject *value = *extra_storage; + Py_VISIT(value); + return 0; +} + +static int +obj_extra_data_clear(PyObject *self) +{ + PyObject **extra_storage = obj_extra_data_get_extra_storage(self); + Py_CLEAR(*extra_storage); + return 0; +} + +static void +obj_extra_data_dealloc(PyObject *self) +{ + PyObject_GC_UnTrack(self); + obj_extra_data_clear(self); + Py_TYPE(self)->tp_free(self); +} + +static PyTypeObject ObjExtraData_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "obj_with_extra_data", + sizeof(ObjExtraData), + 0, + .tp_getset = obj_extra_data_getset, + .tp_dealloc = obj_extra_data_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + .tp_traverse = (traverseproc)obj_extra_data_traverse, + .tp_clear = (inquiry)obj_extra_data_clear, + .tp_new = obj_extra_data_new, + .tp_free = PyObject_GC_Del, +}; struct atexit_data { int called; @@ -4103,6 +4192,11 @@ PyInit__testcapi(void) Py_INCREF(&MethStatic_Type); PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type); + if (PyType_Ready(&ObjExtraData_Type) < 0) + return NULL; + Py_INCREF(&ObjExtraData_Type); + PyModule_AddObject(m, "ObjExtraData", (PyObject *)&ObjExtraData_Type); + PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX)); PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN)); PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX)); diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 966c1e615502ef..a9e7a58399ff4f 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2357,6 +2357,18 @@ _PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems) return op; } +PyObject * +PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *tp, size_t extra_size) +{ + size_t presize = _PyType_PreHeaderSize(tp); + PyObject *op = gc_alloc(_PyObject_SIZE(tp) + extra_size, presize); + if (op == NULL) { + return NULL; + } + _PyObject_Init(op, tp); + return op; +} + PyVarObject * _PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems) { diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index a8ba88efc732fb..7d426039d9169e 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -508,6 +508,7 @@ Modules/_testcapimodule.c - g_dict_watch_events - Modules/_testcapimodule.c - g_dict_watchers_installed - Modules/_testcapimodule.c - g_type_modified_events - Modules/_testcapimodule.c - g_type_watchers_installed - +Modules/_testcapimodule.c - ObjExtraData_Type - Modules/_testimportmultiple.c - _barmodule - Modules/_testimportmultiple.c - _foomodule - Modules/_testimportmultiple.c - _testimportmultiple - From 52135dcaeceb8808d9f95540f0a76472d3706910 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 27 Apr 2023 17:48:08 +0200 Subject: [PATCH 2/8] Don't suggest PEP 697 will be a solution --- Doc/c-api/gcsupport.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index 8f47fb64bd0315..64c27ddccf75ac 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -73,8 +73,9 @@ rules: .. warning:: The function is marked as unstable because the final mechanism for reserving extra data after an instance is not yet decided. - Once :pep:`697` is implemented, the mechanism described there can - be used to reserve the extra data. + For allocating a variable number of fields, prefer using + :c:type:`PyVarObject` and :c:member:`~PyTypeObject.tp_itemsize` + instead. .. versionadded:: 3.12 From e2c4b37bcfa2f2cf97a35591438ca4b58d3cdb62 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 27 Apr 2023 17:48:53 +0200 Subject: [PATCH 3/8] Move declaration to Include/cpython That way it doesn't need the Py_LIMITED_API guard --- Include/cpython/objimpl.h | 3 +++ Include/objimpl.h | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Include/cpython/objimpl.h b/Include/cpython/objimpl.h index 0b038d31080be9..5a8cdd57c7845b 100644 --- a/Include/cpython/objimpl.h +++ b/Include/cpython/objimpl.h @@ -90,3 +90,6 @@ PyAPI_FUNC(int) PyObject_IS_GC(PyObject *obj); PyAPI_FUNC(int) PyType_SUPPORTS_WEAKREFS(PyTypeObject *type); PyAPI_FUNC(PyObject **) PyObject_GET_WEAKREFS_LISTPTR(PyObject *op); + +PyAPI_FUNC(PyObject *) PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *, + size_t); diff --git a/Include/objimpl.h b/Include/objimpl.h index bd39b72f30c677..ef871c5ea93ebe 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -131,10 +131,6 @@ PyAPI_FUNC(PyVarObject *) PyObject_InitVar(PyVarObject *, PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *); PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t); -#if !defined(Py_LIMITED_API) -PyAPI_FUNC(PyObject *) PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *, size_t); -#endif - #define PyObject_New(type, typeobj) ((type *)_PyObject_New(typeobj)) // Alias to PyObject_New(). In Python 3.8, PyObject_NEW() called directly From ea9e9acee1f6eb10c5b798ab883cc0a4b269df39 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 27 Apr 2023 17:55:29 +0200 Subject: [PATCH 4/8] Style nitpicks --- Modules/_testcapimodule.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 85a4bac67b2e65..fd43a69c6c6cb7 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3409,8 +3409,9 @@ obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { size_t extra_size = sizeof(PyObject *); PyObject *obj = PyUnstable_Object_GC_NewWithExtraData(type, extra_size); - if (obj == NULL) + if (obj == NULL) { return PyErr_NoMemory(); + } memset(obj, '\0', type->tp_basicsize + extra_size); PyObject_Init(obj, type); PyObject_GC_Track(obj); @@ -3428,10 +3429,10 @@ obj_extra_data_get(PyObject *self, void *Py_UNUSED(ignored)) { PyObject **extra_storage = obj_extra_data_get_extra_storage(self); PyObject *value = *extra_storage; - if (!value) + if (!value) { Py_RETURN_NONE; - Py_INCREF(value); - return value; + } + return Py_NewRef(value); } static int @@ -3440,8 +3441,7 @@ obj_extra_data_set(PyObject *self, PyObject *newval, void *Py_UNUSED(ignored)) PyObject **extra_storage = obj_extra_data_get_extra_storage(self); Py_CLEAR(*extra_storage); if (newval) { - Py_INCREF(newval); - *extra_storage = newval; + *extra_storage = Py_NewRef(newval); } return 0; } From f9817ca9a652e55f47319f97f37e5d4a4b258e0f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 27 Apr 2023 18:01:42 +0200 Subject: [PATCH 5/8] Clarify the docs a bit --- Doc/c-api/gcsupport.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index 64c27ddccf75ac..8d0f019f4792b2 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -67,8 +67,12 @@ rules: .. c:function:: PyObject* PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size) - Analogous to :c:func:`PyObject_GC_New` but for container objects that - have additional data at the end of the object not managed by Python. + Analogous to :c:func:`PyObject_GC_New` but allocates *extra_size* + bytes at the end of the object (at offset + :c:member:`~PyTypeObject.tp_basicsize`). + + The extra data will be deallocated with the object, but otherwise it is + not managed by Python. .. warning:: The function is marked as unstable because the final mechanism From f0098d6edca83881c7438a242da4f8fb31265653 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 28 Apr 2023 14:13:42 +0200 Subject: [PATCH 6/8] Convert the test class to a heap type --- Modules/_testcapimodule.c | 45 +++++++++++++++++----------- Tools/c-analyzer/cpython/ignored.tsv | 1 - 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index fd43a69c6c6cb7..9f2ed18a870ad8 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3471,23 +3471,28 @@ obj_extra_data_clear(PyObject *self) static void obj_extra_data_dealloc(PyObject *self) { + PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); obj_extra_data_clear(self); - Py_TYPE(self)->tp_free(self); -} + tp->tp_free(self); + Py_DECREF(tp); +} + +static PyType_Slot ObjExtraData_Slots[] = { + {Py_tp_getset, obj_extra_data_getset}, + {Py_tp_dealloc, obj_extra_data_dealloc}, + {Py_tp_traverse, obj_extra_data_traverse}, + {Py_tp_clear, obj_extra_data_clear}, + {Py_tp_new, obj_extra_data_new}, + {Py_tp_free, PyObject_GC_Del}, + {0, NULL}, +}; -static PyTypeObject ObjExtraData_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "obj_with_extra_data", - sizeof(ObjExtraData), - 0, - .tp_getset = obj_extra_data_getset, - .tp_dealloc = obj_extra_data_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - .tp_traverse = (traverseproc)obj_extra_data_traverse, - .tp_clear = (inquiry)obj_extra_data_clear, - .tp_new = obj_extra_data_new, - .tp_free = PyObject_GC_Del, +static PyType_Spec ObjExtraData_TypeSpec = { + .name = "_testcapi.ObjExtraData", + .basicsize = sizeof(ObjExtraData), + .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + .slots = ObjExtraData_Slots, }; struct atexit_data { @@ -4213,10 +4218,16 @@ PyInit__testcapi(void) Py_INCREF(&MethStatic_Type); PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type); - if (PyType_Ready(&ObjExtraData_Type) < 0) + PyObject *ObjExtraData_Type = PyType_FromModuleAndSpec( + m, &ObjExtraData_TypeSpec, NULL); + if (ObjExtraData_Type == 0) { + return NULL; + } + int ret = PyModule_AddType(m, (PyTypeObject*)ObjExtraData_Type); + Py_DECREF(&ObjExtraData_Type); + if (ret < 0) { return NULL; - Py_INCREF(&ObjExtraData_Type); - PyModule_AddObject(m, "ObjExtraData", (PyObject *)&ObjExtraData_Type); + } PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX)); PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN)); diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 79211633d396bb..7a5d7d45f5184b 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -509,7 +509,6 @@ Modules/_testcapimodule.c - g_dict_watch_events - Modules/_testcapimodule.c - g_dict_watchers_installed - Modules/_testcapimodule.c - g_type_modified_events - Modules/_testcapimodule.c - g_type_watchers_installed - -Modules/_testcapimodule.c - ObjExtraData_Type - Modules/_testimportmultiple.c - _barmodule - Modules/_testimportmultiple.c - _foomodule - Modules/_testimportmultiple.c - _testimportmultiple - From cfa59d7c717759aa11b0c7215322a08b66a131ad Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 28 Apr 2023 14:57:07 +0200 Subject: [PATCH 7/8] Initialize the memory This is a further workaround: this time for the fact that we can't rely on tp_alloc, which zeroes the memory (including memory for subclasses, like slots in the test) for normal classes. For the test type, this means the instances aren't initialized twice (once in PyUnstable_Object_GC_NewWithExtraData, once again with PyObject_Init after zeroing). Initializing twice throws off the global refcount total in debug builds. --- Doc/c-api/gcsupport.rst | 3 ++- Modules/_testcapimodule.c | 2 -- Modules/gcmodule.c | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index 8d0f019f4792b2..c371f76880c010 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -59,7 +59,6 @@ rules: Analogous to :c:func:`PyObject_New` but for container objects with the :const:`Py_TPFLAGS_HAVE_GC` flag set. - .. c:function:: TYPE* PyObject_GC_NewVar(TYPE, PyTypeObject *type, Py_ssize_t size) Analogous to :c:func:`PyObject_NewVar` but for container objects with the @@ -70,6 +69,8 @@ rules: Analogous to :c:func:`PyObject_GC_New` but allocates *extra_size* bytes at the end of the object (at offset :c:member:`~PyTypeObject.tp_basicsize`). + The allocated memory is initialized: to zero, except for the + :c:type:`Python object header `. The extra data will be deallocated with the object, but otherwise it is not managed by Python. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 9f2ed18a870ad8..a5d23b1b3d50ec 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3412,8 +3412,6 @@ obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (obj == NULL) { return PyErr_NoMemory(); } - memset(obj, '\0', type->tp_basicsize + extra_size); - PyObject_Init(obj, type); PyObject_GC_Track(obj); return obj; } diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index a9e7a58399ff4f..f5d66b7ba55e13 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -2365,6 +2365,7 @@ PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *tp, size_t extra_size) if (op == NULL) { return NULL; } + memset(op, 0, _PyObject_SIZE(tp) + extra_size); _PyObject_Init(op, tp); return op; } From 525705a284913d13f6722541d183e09f97a1ae36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jurica=20Bradari=C4=87?= Date: Sat, 29 Apr 2023 09:52:48 +0200 Subject: [PATCH 8/8] Doc style fixes Fix indentation and reword some parts to sound more natural. Co-authored-by: Erlend E. Aasland --- Doc/c-api/gcsupport.rst | 14 +++++++------- .../2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index c371f76880c010..c3260a21bc7f8b 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -69,18 +69,18 @@ rules: Analogous to :c:func:`PyObject_GC_New` but allocates *extra_size* bytes at the end of the object (at offset :c:member:`~PyTypeObject.tp_basicsize`). - The allocated memory is initialized: to zero, except for the - :c:type:`Python object header `. + The allocated memory is initialized to zeros, + except for the :c:type:`Python object header `. The extra data will be deallocated with the object, but otherwise it is not managed by Python. .. warning:: - The function is marked as unstable because the final mechanism - for reserving extra data after an instance is not yet decided. - For allocating a variable number of fields, prefer using - :c:type:`PyVarObject` and :c:member:`~PyTypeObject.tp_itemsize` - instead. + The function is marked as unstable because the final mechanism + for reserving extra data after an instance is not yet decided. + For allocating a variable number of fields, prefer using + :c:type:`PyVarObject` and :c:member:`~PyTypeObject.tp_itemsize` + instead. .. versionadded:: 3.12 diff --git a/Misc/NEWS.d/next/C API/2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst b/Misc/NEWS.d/next/C API/2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst index e6ae7e45f19984..d074350ed3ebbe 100644 --- a/Misc/NEWS.d/next/C API/2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst +++ b/Misc/NEWS.d/next/C API/2023-04-24-10-31-59.gh-issue-103743.2xYA1K.rst @@ -1,2 +1,2 @@ -Add ``PyUnstable_Object_GC_NewWithExtraData`` function that can be used to +Add :c:func:`PyUnstable_Object_GC_NewWithExtraData` function that can be used to allocate additional memory after an object for data not managed by Python.