Skip to content

gh-135228: Create __dict__ and __weakref__ descriptors for object #136966

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
7 changes: 7 additions & 0 deletions Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,13 @@ struct _Py_interp_cached_objects {
PyTypeObject *paramspecargs_type;
PyTypeObject *paramspeckwargs_type;
PyTypeObject *constevaluator_type;

/* Descriptors for __dict__ and __weakref__ */
#ifdef Py_GIL_DISABLED
PyMutex descriptor_mutex;
#endif
PyObject *dict_descriptor;
PyObject *weakref_descriptor;
};

struct _Py_interp_static_objects {
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ extern void _PyTypes_FiniTypes(PyInterpreterState *);
extern void _PyTypes_FiniExtTypes(PyInterpreterState *interp);
extern void _PyTypes_Fini(PyInterpreterState *);
extern void _PyTypes_AfterFork(void);
extern void _PyTypes_FiniCachedDescriptors(PyInterpreterState *);

static inline PyObject **
_PyStaticType_GET_WEAKREFS_LISTPTR(managed_static_type_state *state)
Expand Down
3 changes: 2 additions & 1 deletion Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1698,7 +1698,8 @@ def _shadowed_dict_from_weakref_mro_tuple(*weakref_mro):
class_dict = dunder_dict['__dict__']
if not (type(class_dict) is types.GetSetDescriptorType and
class_dict.__name__ == "__dict__" and
class_dict.__objclass__ is entry):
(class_dict.__objclass__ is object or
class_dict.__objclass__ is entry)):
return class_dict
return _sentinel

Expand Down
20 changes: 10 additions & 10 deletions Objects/descrobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,41 +39,41 @@ descr_name(PyDescrObject *descr)
}

static PyObject *
descr_repr(PyDescrObject *descr, const char *format)
descr_repr(PyDescrObject *descr, const char *kind)
{
PyObject *name = NULL;
if (descr->d_name != NULL && PyUnicode_Check(descr->d_name))
name = descr->d_name;

return PyUnicode_FromFormat(format, name, "?", descr->d_type->tp_name);
if (descr->d_type == &PyBaseObject_Type) {
return PyUnicode_FromFormat("<%s '%V'>", kind, name, "?");
}
return PyUnicode_FromFormat("<%s '%V' of '%s' objects>",
kind, name, "?", descr->d_type->tp_name);
}

static PyObject *
method_repr(PyObject *descr)
{
return descr_repr((PyDescrObject *)descr,
"<method '%V' of '%s' objects>");
return descr_repr((PyDescrObject *)descr, "method");
}

static PyObject *
member_repr(PyObject *descr)
{
return descr_repr((PyDescrObject *)descr,
"<member '%V' of '%s' objects>");
return descr_repr((PyDescrObject *)descr, "member");
}

static PyObject *
getset_repr(PyObject *descr)
{
return descr_repr((PyDescrObject *)descr,
"<attribute '%V' of '%s' objects>");
return descr_repr((PyDescrObject *)descr, "attribute");
}

static PyObject *
wrapperdescr_repr(PyObject *descr)
{
return descr_repr((PyDescrObject *)descr,
"<slot wrapper '%V' of '%s' objects>");
return descr_repr((PyDescrObject *)descr, "slot wrapper");
}

static int
Expand Down
100 changes: 68 additions & 32 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -4036,26 +4036,15 @@ subtype_getweakref(PyObject *obj, void *context)
return Py_NewRef(result);
}

/* Three variants on the subtype_getsets list. */

static PyGetSetDef subtype_getsets_full[] = {
{"__dict__", subtype_dict, subtype_setdict,
PyDoc_STR("dictionary for instance variables")},
{"__weakref__", subtype_getweakref, NULL,
PyDoc_STR("list of weak references to the object")},
{0}
};

static PyGetSetDef subtype_getsets_dict_only[] = {
{"__dict__", subtype_dict, subtype_setdict,
PyDoc_STR("dictionary for instance variables")},
{0}
/* getset definitions for common descriptors */
static PyGetSetDef subtype_getset_dict = {
"__dict__", subtype_dict, subtype_setdict,
PyDoc_STR("dictionary for instance variables"),
};

static PyGetSetDef subtype_getsets_weakref_only[] = {
{"__weakref__", subtype_getweakref, NULL,
PyDoc_STR("list of weak references to the object")},
{0}
static PyGetSetDef subtype_getset_weakref = {
"__weakref__", subtype_getweakref, NULL,
PyDoc_STR("list of weak references to the object"),
};

static int
Expand Down Expand Up @@ -4591,10 +4580,36 @@ type_new_classmethod(PyObject *dict, PyObject *attr)
return 0;
}

/* Add __dict__ or __weakref__ descriptor */
static int
type_add_common_descriptor(PyInterpreterState *interp,
PyObject **cache,
PyGetSetDef *getset_def,
PyObject *dict)
{
#ifdef Py_GIL_DISABLED
PyMutex_Lock(&interp->cached_objects.descriptor_mutex);
#endif
PyObject *descr = *cache;
if (!descr) {
descr = PyDescr_NewGetSet(&PyBaseObject_Type, getset_def);
*cache = descr;
}
#ifdef Py_GIL_DISABLED
PyMutex_Unlock(&interp->cached_objects.descriptor_mutex);
#endif
if (!descr) {
return -1;
}
if (PyDict_SetDefaultRef(dict, PyDescr_NAME(descr), descr, NULL) < 0) {
return -1;
}
return 0;
}

/* Add descriptors for custom slots from __slots__, or for __dict__ */
static int
type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type)
type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict)
{
PyHeapTypeObject *et = (PyHeapTypeObject *)type;
Py_ssize_t slotoffset = ctx->base->tp_basicsize;
Expand Down Expand Up @@ -4632,25 +4647,38 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type)
type->tp_basicsize = slotoffset;
type->tp_itemsize = ctx->base->tp_itemsize;
type->tp_members = _PyHeapType_GET_MEMBERS(et);

PyInterpreterState *interp = _PyInterpreterState_GET();

if (type->tp_dictoffset) {
if (type_add_common_descriptor(
interp,
&interp->cached_objects.dict_descriptor,
&subtype_getset_dict,
dict) < 0)
{
return -1;
}
}
if (type->tp_weaklistoffset) {
if (type_add_common_descriptor(
interp,
&interp->cached_objects.weakref_descriptor,
&subtype_getset_weakref,
dict) < 0)
{
return -1;
}
}

return 0;
}


static void
type_new_set_slots(const type_new_ctx *ctx, PyTypeObject *type)
{
if (type->tp_weaklistoffset && type->tp_dictoffset) {
type->tp_getset = subtype_getsets_full;
}
else if (type->tp_weaklistoffset && !type->tp_dictoffset) {
type->tp_getset = subtype_getsets_weakref_only;
}
else if (!type->tp_weaklistoffset && type->tp_dictoffset) {
type->tp_getset = subtype_getsets_dict_only;
}
else {
type->tp_getset = NULL;
}
type->tp_getset = NULL;

/* Special case some slots */
if (type->tp_dictoffset != 0 || ctx->nslot > 0) {
Expand Down Expand Up @@ -4755,7 +4783,7 @@ type_new_set_attrs(const type_new_ctx *ctx, PyTypeObject *type)
return -1;
}

if (type_new_descriptors(ctx, type) < 0) {
if (type_new_descriptors(ctx, type, dict) < 0) {
return -1;
}

Expand Down Expand Up @@ -6639,6 +6667,14 @@ _PyStaticType_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type)
}


void
_PyTypes_FiniCachedDescriptors(PyInterpreterState *interp)
{
Py_CLEAR(interp->cached_objects.dict_descriptor);
Py_CLEAR(interp->cached_objects.weakref_descriptor);
}


static void
type_dealloc(PyObject *self)
{
Expand Down
1 change: 1 addition & 0 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,7 @@ finalize_interp_clear(PyThreadState *tstate)
_PyXI_Fini(tstate->interp);
_PyExc_ClearExceptionGroupType(tstate->interp);
_Py_clear_generic_types(tstate->interp);
_PyTypes_FiniCachedDescriptors(tstate->interp);

/* Clear interpreter state and all thread states */
_PyInterpreterState_Clear(tstate);
Expand Down
1 change: 1 addition & 0 deletions Tools/c-analyzer/cpython/_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
'PyMethodDef',
'PyMethodDef[]',
'PyMemberDef[]',
'PyGetSetDef',
'PyGetSetDef[]',
'PyNumberMethods',
'PySequenceMethods',
Expand Down
Loading