8000 [3.12] gh-105227: Add PyType_GetDict() (GH-105747) (#106600) · python/cpython@41057b2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 41057b2

Browse files
miss-islingtonericsnowcurrentlyencukou
authored
[3.12] gh-105227: Add PyType_GetDict() (GH-105747) (#106600)
gh-105227: Add PyType_GetDict() (GH-105747) This compensates for static builtin types having `tp_dict` set to `NULL`. (cherry picked from commit a840806) Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com> Co-authored-by: Petr Viktorin <encukou@gmail.com>
1 parent 0481b80 commit 41057b2

File tree

6 files changed

+68
-1
lines changed

6 files changed

+68
-1
lines changed

Doc/c-api/type.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,23 @@ Type Objects
5050
The return type is now ``unsigned long`` rather than ``long``.
5151
5252
53+
.. c:function:: PyObject* PyType_GetDict(PyTypeObject* type)
54+
55+
Return the type object's internal namespace, which is otherwise only
56+
exposed via a read-only proxy (``cls.__dict__``). This is a
57+
replacement for accessing :c:member:`~PyTypeObject.tp_dict` directly.
58+
The returned dictionary must be treated as read-only.
59+
60+
This function is meant for specific embedding and language-binding cases,
61+
where direct access to the dict is necessary and indirect access
62+
(e.g. via the proxy or :c:func:`PyObject_GetAttr`) isn't adequate.
63+
64+
Extension modules should continue to use ``tp_dict``,
65+
directly or indirectly, when setting up their own types.
66+
67+
.. versionadded:: 3.12
68+
69+
5370
.. c:function:: void PyType_Modified(PyTypeObject *type)
5471
5572
Invalidate the internal lookup cache for the type and all of its

Doc/c-api/typeobj.rst

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1717,7 +1717,19 @@ and :c:type:`PyType_Type` effectively act as defaults.)
17171717
called; it may also be initialized to a dictionary containing initial attributes
17181718
for the type. Once :c:func:`PyType_Ready` has initialized the type, extra
17191719
attributes for the type may be added to this dictionary only if they don't
1720-
correspond to overloaded operations (like :meth:`__add__`).
1720+
correspond to overloaded operations (like :meth:`__add__`). Once
1721+
initialization for the type has finished, this field should be
1722+
treated as read-only.
1723+
1724+
Some types may not store their dictionary in this slot.
1725+
Use :c:func:`PyType_GetDict` to retreive the dictionary for an arbitrary
1726+
type.
1727+
1728+
.. versionchanged:: 3.12
1729+
1730+
Internals detail: For static builtin types, this is always ``NULL``.
1731+
Instead, the dict for such types is stored on ``PyInterpreterState``.
1732+
Use :c:func:`PyType_GetDict` to get the dict for an arbitrary type.
17211733

17221734
**Inheritance:**
17231735

Include/cpython/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *
283283
PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *);
284284
PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *);
285285
PyAPI_FUNC(PyObject *) PyType_GetModuleByDef(PyTypeObject *, PyModuleDef *);
286+
PyAPI_FUNC(PyObject *) PyType_GetDict(PyTypeObject *);
286287

287288
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
288289
PyAPI_FUNC(void) _Py_BreakPoint(void);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
The new :c:func:`PyType_GetDict` provides the dictionary for the given type
2+
object that is normally exposed by ``cls.__dict__``. Normally it's
3+
sufficient to use :c:member:`~PyTypeObject.tp_dict`, but for the static
4+
builtin types :c:member:`!tp_dict` is now always ``NULL``. :c:func:`!PyType_GetDict()`
5+
provides the correct dict object instead.

Modules/_testcapimodule.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,30 @@ test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
640640
Py_RETURN_NONE;
641641
}
642642

643+
static PyObject *
644+
test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored))
645+
{
646+
/* Test for PyType_GetDict */
647+
648+
// Assert ints have a `to_bytes` method
649+
PyObject *long_dict = PyType_GetDict(&PyLong_Type);
650+
assert(long_dict);
651+
assert(PyDict_GetItemString(long_dict, "to_bytes")); // borrowed ref
652+
Py_DECREF(long_dict);
653+
654+
// Make a new type, add an attribute to it and assert it's there
655+
PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec);
656+
assert(HeapTypeNameType);
657+
assert(PyObject_SetAttrString(
658+
HeapTypeNameType, "new_attr", Py_NewRef(Py_None)) >= 0);
659+
PyObject *type_dict = PyType_GetDict((PyTypeObject*)HeapTypeNameType);
660+
assert(type_dict);
661+
assert(PyDict_GetItemString(type_dict, "new_attr")); // borrowed ref
662+
Py_DECREF(HeapTypeNameType);
663+
Py_DECREF(type_dict);
664+
Py_RETURN_NONE;
665+
}
666+
643667
static PyObject *
644668
pyobject_repr_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
645669
{
@@ -3347,6 +3371,7 @@ static PyMethodDef TestMethods[] = {
33473371
{"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS},
33483372
{"test_get_type_name", test_get_type_name, METH_NOARGS},
33493373
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
3374+
{"test_get_type_dict", test_get_type_dict, METH_NOARGS},
33503375
{"_test_thread_state", test_thread_state, METH_VARARGS},
33513376
#ifndef MS_WINDOWS
33523377
{"_spawn_pthread_waiter", spawn_pthread_waiter, METH_NOARGS},

Objects/typeobject.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,13 @@ _PyType_GetDict(PyTypeObject *self)
238238
return lookup_tp_dict(self);
239239
}
240240

241+
PyObject *
242+
PyType_GetDict(PyTypeObject *self)
243+
{
244+
PyObject *dict = lookup_tp_dict(self);
245+
return _Py_XNewRef(dict);
246+
}
247+
241248
static inline void
242249
set_tp_dict(PyTypeObject *self, PyObject *dict)
243250
{

0 commit comments

Comments
 (0)
0