Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cc46476

Browse filesBrowse files
committed
gh-103743: Add PyUnstable_Object_GC_NewWithExtraData (#103743)
1 parent af53046 commit cc46476
Copy full SHA for cc46476

File tree

7 files changed

+141
-1
lines changed
Filter options

7 files changed

+141
-1
lines changed

‎Doc/c-api/gcsupport.rst

Copy file name to clipboardExpand all lines: Doc/c-api/gcsupport.rst
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ rules:
6565
Analogous to :c:func:`PyObject_NewVar` but for container objects with the
6666
:const:`Py_TPFLAGS_HAVE_GC` flag set.
6767
68+
.. c:function:: TYPE* PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)
69+
70+
Analogous to :c:func:`PyObject_GC_New` but for container objects that
71+
have additional data at the end of the object not managed by Python.
72+
73+
.. warning::
74+
The function is marked as unstable because the final mechanism
75+
for reserving extra data after an instance is not yet decided.
76+
Once :pep:`697` is implemented, the mechanism described there can
77+
be used to reserve the extra data.
78+
79+
.. versionadded:: 3.12
80+
6881
6982
.. c:function:: TYPE* PyObject_GC_Resize(TYPE, PyVarObject *op, Py_ssize_t newsize)
7083

‎Include/objimpl.h

Copy file name to clipboardExpand all lines: Include/objimpl.h
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ PyAPI_FUNC(PyVarObject *) PyObject_InitVar(PyVarObject *,
131131
PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *);
132132
PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t);
133133

134+
#if !defined(Py_LIMITED_API)
135+
PyAPI_FUNC(PyObject *) PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *, size_t);
136+
#endif
137+
134138
#define PyObject_New(type, typeobj) ((type *)_PyObject_New(typeobj))
135139

136140
// Alias to PyObject_New(). In Python 3.8, PyObject_NEW() called directly

‎Lib/test/test_capi/test_misc.py

Copy file name to clipboardExpand all lines: Lib/test/test_capi/test_misc.py
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,20 @@ class dictsub(dict): ... # dict subclasses must work
10431043
self.assertEqual(_testcapi.function_get_kw_defaults(some), None)
10441044
self.assertEqual(some.__kwdefaults__, None)
10451045

1046+
def test_unstable_gc_new_with_extra_data(self):
1047+
class Data(_testcapi.ObjExtraData):
1048+
__slots__ = ('x', 'y')
1049+
1050+
d = Data()
1051+
d.x = 10
1052+
d.y = 20
1053+
d.extra = 30
1054+
self.assertEqual(d.x, 10)
1055+
self.assertEqual(d.y, 20)
1056+
self.assertEqual(d.extra, 30)
1057+
del d.extra
1058+
self.assertIsNone(d.extra)
1059+
10461060

10471061
class TestPendingCalls(unittest.TestCase):
10481062

+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add ``PyUnstable_Object_GC_NewWithExtraData`` function that can be used to
2+
allocate additional memory after an object for data not managed by Python.

‎Modules/_testcapimodule.c

Copy file name to clipboardExpand all lines: Modules/_testcapimodule.c
+95-1Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3343,7 +3343,7 @@ test_gc_visit_objects_basic(PyObject *Py_UNUSED(self),
33433343
}
33443344
state.target = obj;
33453345
state.found = 0;
3346-
3346+
33473347
PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state);
33483348
Py_DECREF(obj);
33493349
if (!state.found) {
@@ -3380,6 +3380,95 @@ test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self),
33803380
Py_RETURN_NONE;
33813381
}
33823382

3383+
typedef struct {
3384+
PyObject_HEAD
3385+
} ObjExtraData;
3386+
3387+
static PyObject *
3388+
obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
3389+
{
3390+
size_t extra_size = sizeof(PyObject *);
3391+
PyObject *obj = PyUnstable_Object_GC_NewWithExtraData(type, extra_size);
3392+
if (obj == NULL)
3393+
return PyErr_NoMemory();
3394+
memset(obj, '\0', type->tp_basicsize + extra_size);
3395+
PyObject_Init(obj, type);
3396+
PyObject_GC_Track(obj);
3397+
return obj;
3398+
}
3399+
3400+
static PyObject **
3401+
obj_extra_data_get_extra_storage(PyObject *self)
3402+
{
3403+
return (PyObject **)((char *)self + Py_TYPE(self)->tp_basicsize);
3404+
}
3405+
3406+
static PyObject *
3407+
obj_extra_data_get(PyObject *self, void *Py_UNUSED(ignored))
3408+
{
3409+
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
3410+
PyObject *value = *extra_storage;
3411+
if (!value)
3412+
Py_RETURN_NONE;
3413+
Py_INCREF(value);
3414+
return value;
3415+
}
3416+
3417+
static int
3418+
obj_extra_data_set(PyObject *self, PyObject *newval, void *Py_UNUSED(ignored))
3419+
{
3420+
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
3421+
Py_CLEAR(*extra_storage);
3422+
if (newval) {
3423+
Py_INCREF(newval);
3424+
*extra_storage = newval;
3425+
}
3426+
return 0;
3427+
}
3428+
3429+
static PyGetSetDef obj_extra_data_getset[] = {
3430+
{"extra", (getter)obj_extra_data_get, (setter)obj_extra_data_set, NULL},
3431+
{NULL}
3432+
};
3433+
3434+
static int
3435+
obj_extra_data_traverse(PyObject *self, visitproc visit, void *arg)
3436+
{
3437+
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
3438+
PyObject *value = *extra_storage;
3439+
Py_VISIT(value);
3440+
return 0;
3441+
}
3442+
3443+
static int
3444+
obj_extra_data_clear(PyObject *self)
3445+
{
3446+
PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
3447+
Py_CLEAR(*extra_storage);
3448+
return 0;
3449+
}
3450+
3451+
static void
3452+
obj_extra_data_dealloc(PyObject *self)
3453+
{
3454+
PyObject_GC_UnTrack(self);
3455+
obj_extra_data_clear(self);
3456+
Py_TYPE(self)->tp_free(self);
3457+
}
3458+
3459+
static PyTypeObject ObjExtraData_Type = {
3460+
PyVarObject_HEAD_INIT(NULL, 0)
3461+
"obj_with_extra_data",
3462+
sizeof(ObjExtraData),
3463+
0,
3464+
.tp_getset = obj_extra_data_getset,
3465+
.tp_dealloc = obj_extra_data_dealloc,
3466+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
3467+
.tp_traverse = (traverseproc)obj_extra_data_traverse,
3468+
.tp_clear = (inquiry)obj_extra_data_clear,
3469+
.tp_new = obj_extra_data_new,
3470+
.tp_free = PyObject_GC_Del,
3471+
};
33833472

33843473
struct atexit_data {
33853474
int called;
@@ -4103,6 +4192,11 @@ PyInit__testcapi(void)
41034192
Py_INCREF(&MethStatic_Type);
41044193
PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type);
41054194

4195+
if (PyType_Ready(&ObjExtraData_Type) < 0)
4196+
return NULL;
4197+
Py_INCREF(&ObjExtraData_Type);
4198+
PyModule_AddObject(m, "ObjExtraData", (PyObject *)&ObjExtraData_Type);
4199+
41064200
PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX));
41074201
PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN));
41084202
PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX));

‎Modules/gcmodule.c

Copy file name to clipboardExpand all lines: Modules/gcmodule.c
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2357,6 +2357,18 @@ _PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems)
23572357
return op;
23582358
}
23592359

2360+
PyObject *
2361+
PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *tp, size_t extra_size)
2362+
{
2363+
size_t presize = _PyType_PreHeaderSize(tp);
2364+
PyObject *op = gc_alloc(_PyObject_SIZE(tp) + extra_size, presize);
2365+
if (op == NULL) {
2366+
return NULL;
2367+
}
2368+
_PyObject_Init(op, tp);
2369+
return op;
2370+
}
2371+
23602372
PyVarObject *
23612373
_PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems)
23622374
{

‎Tools/c-analyzer/cpython/ignored.tsv

Copy file name to clipboardExpand all lines: Tools/c-analyzer/cpython/ignored.tsv
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ Modules/_testcapimodule.c - g_dict_watch_events -
508508
Modules/_testcapimodule.c - g_dict_watchers_installed -
509509
Modules/_testcapimodule.c - g_type_modified_events -
510510
Modules/_testcapimodule.c - g_type_watchers_installed -
511+
Modules/_testcapimodule.c - ObjExtraData_Type -
511512
Modules/_testimportmultiple.c - _barmodule -
512513
Modules/_testimportmultiple.c - _foomodule -
513514
Modules/_testimportmultiple.c - _testimportmultiple -

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.