summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/type.rst17
-rw-r--r--Doc/c-api/typeobj.rst14
-rw-r--r--Include/cpython/object.h1
-rw-r--r--Misc/NEWS.d/next/C API/2023-06-13-14-24-55.gh-issue-105227.HDL9aF.rst5
-rw-r--r--Modules/_testcapimodule.c25
-rw-r--r--Objects/typeobject.c7
6 files changed, 68 insertions, 1 deletions
diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst
index c99c7ef..a5f333e 100644
--- a/Doc/c-api/type.rst
+++ b/Doc/c-api/type.rst
@@ -50,6 +50,23 @@ Type Objects
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)
Invalidate the internal lookup cache for the type and all of its
diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index 239c191..7249cfe 100644
--- a/Doc/c-api/typeobj.rst
+++ b/Doc/c-api/typeobj.rst
@@ -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
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
- 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:**
diff --git a/Include/cpython/object.h b/Include/cpython/object.h
index d8eff69..c5d0851 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -283,6 +283,7 @@ PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *
PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *);
PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *);
PyAPI_FUNC(PyObject *) PyType_GetModuleByDef(PyTypeObject *, PyModuleDef *);
+PyAPI_FUNC(PyObject *) PyType_GetDict(PyTypeObject *);
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
PyAPI_FUNC(void) _Py_BreakPoint(void);
diff --git a/Misc/NEWS.d/next/C API/2023-06-13-14-24-55.gh-issue-105227.HDL9aF.rst b/Misc/NEWS.d/next/C API/2023-06-13-14-24-55.gh-issue-105227.HDL9aF.rst
new file mode 100644
index 0000000..8466636
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-06-13-14-24-55.gh-issue-105227.HDL9aF.rst
@@ -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.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 04a8800..6523734 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -641,6 +641,30 @@ test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
}
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 *
pyobject_repr_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return PyObject_Repr(NULL);
@@ -3347,6 +3371,7 @@ static PyMethodDef TestMethods[] = {
{"test_get_statictype_slots", test_get_statictype_slots, 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_dict", test_get_type_dict, METH_NOARGS},
{"_test_thread_state", test_thread_state, METH_VARARGS},
#ifndef MS_WINDOWS
{"_spawn_pthread_waiter", spawn_pthread_waiter, METH_NOARGS},
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 966471e..6662379 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -238,6 +238,13 @@ _PyType_GetDict(PyTypeObject *self)
return lookup_tp_dict(self);
}
+PyObject *
+PyType_GetDict(PyTypeObject *self)
+{
+ PyObject *dict = lookup_tp_dict(self);
+ return _Py_XNewRef(dict);
+}
+
static inline void
set_tp_dict(PyTypeObject *self, PyObject *dict)
{