8000 gh-105227: Add PyType_GetDict() by ericsnowcurrently · Pull Request #105747 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-105227: Add PyType_GetDict() #105747

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

Merged
merged 10 commits into from
Jul 10, 2023
Merged
17 changes: 17 additions & 0 deletions Doc/c-api/type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion Doc/c-api/typeobj.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 10000 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``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the post merge review, but FYI, there's a directive for implementation details: .. impl-detail::

Instead, the dict for such types is stored on ``PyInterpreterState``.
Use :c:func:`PyType_GetDict` to get the dict for an arbitrary type.

**Inheritance:**

Expand Down
1 change: 1 addition & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
25 changes: 25 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,30 @@ test_get_type_qualname(PyObject *self, PyObject *Py_UNUSED(ignored))
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 *
pyobject_repr_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
{
Expand Down Expand Up @@ -3472,6 +3496,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},
Expand Down
7 changes: 7 additions & 0 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,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)
{
Expand Down
0