gh-105227: Add PyType_GetDict() (GH-105747)

This compensates for static builtin types having `tp_dict` set to `NULL`.

Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
Eric Snow 2023-07-10 10:41:02 -06:00 committed by GitHub
parent 3e23fa71f4
commit a840806d33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 1 deletions

View File

@ -50,6 +50,23 @@ Type Objects
The return type is now ``unsigned long`` rather than ``long``. The return type is now ``unsigned long`` rather than ``long``.
.. c:function:: PyObject* PyType_GetDict(PyTypeObject* type)
Return the type object's internal namespace, which is otherwise only
exposed via a read-only proxy (``cls.__dict__``). This is a
replacement for accessing :c:member:`~PyTypeObject.tp_dict` directly.
The returned dictionary must be treated as read-only.
This function is meant for specific embedding and language-binding cases,
where direct access to the dict is necessary and indirect access
(e.g. via the proxy or :c:func:`PyObject_GetAttr`) isn't adequate.
Extension modules should continue to use ``tp_dict``,
directly or indirectly, when setting up their own types.
.. versionadded:: 3.12
.. c:function:: void PyType_Modified(PyTypeObject *type) .. c:function:: void PyType_Modified(PyTypeObject *type)
Invalidate the internal lookup cache for the type and all of its Invalidate the internal lookup cache for the type and all of its

View File

@ -1717,7 +1717,19 @@ and :c:type:`PyType_Type` effectively act as defaults.)
called; it may also be initialized to a dictionary containing initial attributes called; it may also be initialized to a dictionary containing initial attributes
for the type. Once :c:func:`PyType_Ready` has initialized the type, extra for the type. Once :c:func:`PyType_Ready` has initialized the type, extra
attributes for the type may be added to this dictionary only if they don't attributes for the type may be added to this dictionary only if they don't
correspond to overloaded operations (like :meth:`__add__`). correspond to overloaded operations (like :meth:`__add__`). Once
initialization for the type has finished, this field should be
treated as read-only.
Some types may not store their dictionary in this slot.
Use :c:func:`PyType_GetDict` to retreive the dictionary for an arbitrary
type.
.. versionchanged:: 3.12
Internals detail: For static builtin types, this is always ``NULL``.
Instead, the dict for such types is stored on ``PyInterpreterState``.
Use :c:func:`PyType_GetDict` to get the dict for an arbitrary type.
**Inheritance:** **Inheritance:**

View File

@ -284,6 +284,7 @@ PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *
PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *); PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *);
PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *); PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *);
PyAPI_FUNC(PyObject *) PyType_GetModuleByDef(PyTypeObject *, PyModuleDef *); PyAPI_FUNC(PyObject *) PyType_GetModuleByDef(PyTypeObject *, PyModuleDef *);
PyAPI_FUNC(PyObject *) PyType_GetDict(PyTypeObject *);
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int); PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
PyAPI_FUNC(void) _Py_BreakPoint(void); PyAPI_FUNC(void) _Py_BreakPoint(void);

View File

@ -0,0 +1,5 @@
The new :c:func:`PyType_GetDict` provides the dictionary for the given type
object that is normally exposed by ``cls.__dict__``. Normally it's
sufficient to use :c:member:`~PyTypeObject.tp_dict`, but for the static
builtin types :c:member:`!tp_dict` is now always ``NULL``. :c:func:`!PyType_GetDict()`
provides the correct dict object instead.

View File

@ -638,6 +638,30 @@ test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
Py_RETURN_NONE; Py_RETURN_NONE;
} }
static PyObject *
test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored))
{
/* Test for PyType_GetDict */
// Assert ints have a `to_bytes` method
PyObject *long_dict = PyType_GetDict(&PyLong_Type);
assert(long_dict);
assert(PyDict_GetItemString(long_dict, "to_bytes")); // borrowed ref
Py_DECREF(long_dict);
// Make a new type, add an attribute to it and assert it's there
PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec);
assert(HeapTypeNameType);
assert(PyObject_SetAttrString(
HeapTypeNameType, "new_attr", Py_NewRef(Py_None)) >= 0);
PyObject *type_dict = PyType_GetDict((PyTypeObject*)HeapTypeNameType);
assert(type_dict);
assert(PyDict_GetItemString(type_dict, "new_attr")); // borrowed ref
Py_DECREF(HeapTypeNameType);
Py_DECREF(type_dict);
Py_RETURN_NONE;
}
static PyObject * static PyObject *
pyobject_repr_from_null(PyObject *self, PyObject *Py_UNUSED(ignored)) pyobject_repr_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
{ {
@ -3472,6 +3496,7 @@ static PyMethodDef TestMethods[] = {
{"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS}, {"test_get_statictype_slots", test_get_statictype_slots, METH_NOARGS},
{"test_get_type_name", test_get_type_name, METH_NOARGS}, {"test_get_type_name", test_get_type_name, METH_NOARGS},
{"test_get_type_qualname", test_get_type_qualname, METH_NOARGS}, {"test_get_type_qualname", test_get_type_qualname, METH_NOARGS},
{"test_get_type_dict", test_get_type_dict, METH_NOARGS},
{"_test_thread_state", test_thread_state, METH_VARARGS}, {"_test_thread_state", test_thread_state, METH_VARARGS},
#ifndef MS_WINDOWS #ifndef MS_WINDOWS
{"_spawn_pthread_waiter", spawn_pthread_waiter, METH_NOARGS}, {"_spawn_pthread_waiter", spawn_pthread_waiter, METH_NOARGS},

View File

@ -237,6 +237,13 @@ _PyType_GetDict(PyTypeObject *self)
return lookup_tp_dict(self); return lookup_tp_dict(self);
} }
PyObject *
PyType_GetDict(PyTypeObject *self)
{
PyObject *dict = lookup_tp_dict(self);
return _Py_XNewRef(dict);
}
static inline void static inline void
set_tp_dict(PyTypeObject *self, PyObject *dict) set_tp_dict(PyTypeObject *self, PyObject *dict)
{ {