Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Doc/c-api/object.rst
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ Object Protocol

.. versionadded:: 3.13

.. seealso::
The :c:func:`PyType_Lookup` function.


.. c:function:: int PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result);

Expand Down
18 changes: 18 additions & 0 deletions Doc/c-api/type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,24 @@ Type Objects

.. versionadded:: 3.14


.. c:function:: int PyType_Lookup(PyTypeObject *type, PyObject *name, PyObject **attr)

Look for a type attribute through the type
:term:`MRO <method resolution order>`.

*name* must be a :class:`str`.

* If found, set *\*attr* to a :term:`strong reference` and return ``1``.
* If not found, set *\*attr* to ``NULL`` and return ``0``.
* On error, set an exception and return ``-1``.

.. versionadded:: next

.. seealso::
The :c:func:`PyObject_GetOptionalAttr` function.


.. c:function:: int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)

Attempt to assign a version tag to the given type.
Expand Down
13 changes: 13 additions & 0 deletions Doc/deprecations/c-api-pending-removal-in-3.19.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Pending removal in Python 3.19
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* The following private functions are deprecated
and planned for removal in Python 3.19:

* :c:func:`!_PyType_Lookup`: use :c:func:`PyType_Lookup`.
* :c:func:`!_PyType_LookupRef`: use :c:func:`PyType_Lookup`.

The `pythoncapi-compat project
<https://github.com/python/pythoncapi-compat/>`__ can be used to get
these new public functions on Python 3.14 and older.
(Contributed by Victor Stinner in :gh:`128863`.)
2 changes: 2 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2552,6 +2552,8 @@ Deprecated C APIs

.. include:: ../deprecations/c-api-pending-removal-in-3.18.rst

.. include:: ../deprecations/c-api-pending-removal-in-3.19.rst

.. include:: ../deprecations/c-api-pending-removal-in-future.rst

.. _pythoncapi-compat project: https://github.com/python/pythoncapi-compat/
Expand Down
2 changes: 2 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3079,6 +3079,8 @@ Deprecated C APIs

.. include:: ../deprecations/c-api-pending-removal-in-3.18.rst

.. include:: ../deprecations/c-api-pending-removal-in-3.19.rst

.. include:: ../deprecations/c-api-pending-removal-in-future.rst


Expand Down
15 changes: 15 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,10 @@ New features
* Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array.
(Contributed by Victor Stinner in :gh:`111489`.)

* Add :c:func:`PyType_Lookup` function to look for a type attribute through the
type :term:`MRO <method resolution order>`.
(Contributed by Victor Stinner in :gh:`139847`.)


Porting to Python 3.15
----------------------
Expand All @@ -872,6 +876,9 @@ Porting to Python 3.15

* Private functions promoted to public C APIs:

* :c:func:`!_PyType_Lookup`: use :c:func:`PyType_Lookup`.
* :c:func:`!_PyType_LookupRef`: use :c:func:`PyType_Lookup`.

The |pythoncapi_compat_project| can be used to get most of these new
functions on Python 3.14 and older.

Expand Down Expand Up @@ -918,6 +925,14 @@ Deprecated C APIs

.. Add C API deprecations above alphabetically, not here at the end.

.. include:: ../deprecations/c-api-pending-removal-in-3.16.rst

.. include:: ../deprecations/c-api-pending-removal-in-3.18.rst

.. include:: ../deprecations/c-api-pending-removal-in-3.19.rst

.. include:: ../deprecations/c-api-pending-removal-in-future.rst

Removed C APIs
--------------

Expand Down
14 changes: 11 additions & 3 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ struct _specialization_cache {
// by the specialization machinery, and are invalidated by PyType_Modified.
// The rules for using them are as follows:
// - If getitem is non-NULL, then it is the same Python function that
// PyType_Lookup(cls, "__getitem__") would return.
// _PyType_Lookup(cls, "__getitem__") would return.
// - If getitem is NULL, then getitem_version is meaningless.
// - If getitem->func_version == getitem_version, then getitem can be called
// with two positional arguments and no keyword arguments, and has neither
Expand Down Expand Up @@ -289,8 +289,16 @@ typedef struct _heaptypeobject {
} PyHeapTypeObject;

PyAPI_FUNC(const char *) _PyType_Name(PyTypeObject *);
PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
PyAPI_FUNC(PyObject *) _PyType_LookupRef(PyTypeObject *, PyObject *);
PyAPI_FUNC(int) PyType_Lookup(
PyTypeObject *type,
PyObject *name,
PyObject **attr);
_Py_DEPRECATED_EXTERNALLY(3.15) PyAPI_FUNC(PyObject *) _PyType_Lookup(
PyTypeObject *type,
PyObject *name);
_Py_DEPRECATED_EXTERNALLY(3.15) PyAPI_FUNC(PyObject *) _PyType_LookupRef(
PyTypeObject *type,
PyObject *name);
PyAPI_FUNC(PyObject *) PyType_GetDict(PyTypeObject *);

PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_capi/test_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,20 @@ def test_extension_managed_dict_type(self):
obj.__dict__ = {'bar': 3}
self.assertEqual(obj.__dict__, {'bar': 3})
self.assertEqual(obj.bar, 3)

def test_type_lookup(self):
type_lookup = _testcapi.type_lookup

class Parent:
parent_attr = "parent"

class Child(Parent):
child_attr = "child"

self.assertEqual(type_lookup(Child, "parent_attr"), "parent")
self.assertEqual(type_lookup(Child, "child_attr"), "child")
self.assertEqual(type_lookup(Child, "xxx"), AttributeError)

# name parameter must be a str
self.assertRaises(TypeError, type_lookup, Child, b'name')
self.assertRaises(TypeError, type_lookup, Child, 123)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :c:func:`PyType_Lookup` function to look for a type attribute through
the type :term:`MRO <method resolution order>`. Patch by Victor Stinner.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Deprecate the private functions :c:func:`!_PyType_Lookup` and
:c:func:`!_PyType_LookupRef`: use the new public :c:func:`PyType_Lookup`
function instead. Patch by Victor Stinner.
8 changes: 7 additions & 1 deletion Modules/_testcapi/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,14 @@ slot_tp_del(PyObject *self)
PyErr_SetRaisedException(exc);
return;
}

/* Execute __del__ method, if any. */
del = _PyType_LookupRef(Py_TYPE(self), tp_del);
if (PyType_Lookup(Py_TYPE(self), tp_del, &del) < 0) {
Py_DECREF(tp_del);
PyErr_FormatUnraisable("Exception ignored while deallocating");
PyErr_SetRaisedException(exc);
return;
}
Py_DECREF(tp_del);
if (del != NULL) {
res = PyObject_CallOneArg(del, self);
Expand Down
26 changes: 26 additions & 0 deletions Modules/_testcapi/type.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,31 @@ type_freeze(PyObject *module, PyObject *arg)
}


static PyObject *
type_lookup(PyObject *self, PyObject *args)
{
PyTypeObject *type;
PyObject *name, *attr = UNINITIALIZED_PTR;
if (!PyArg_ParseTuple(args, "O!O", &PyType_Type, &type, &name)) {
return NULL;
}
NULLABLE(name);

switch (PyType_Lookup(type, name, &attr)) {
case -1:
assert(attr == NULL);
return NULL;
case 0:
assert(attr == NULL);
return Py_NewRef(PyExc_AttributeError);
case 1:
return attr;
default:
Py_FatalError("PyType_Lookup() returned invalid code");
Py_UNREACHABLE();
}
}

static PyMethodDef test_methods[] = {
{"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS},
{"get_type_name", get_type_name, METH_O},
Expand All @@ -241,6 +266,7 @@ static PyMethodDef test_methods[] = {
{"type_get_tp_bases", type_get_tp_bases, METH_O},
{"type_get_tp_mro", type_get_tp_mro, METH_O},
{"type_freeze", type_freeze, METH_O},
{"type_lookup", type_lookup, METH_VARARGS},
{NULL},
};

Expand Down
14 changes: 14 additions & 0 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -6202,6 +6202,20 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
return res;
}

int
PyType_Lookup(PyTypeObject *type, PyObject *name, PyObject **attr)
{
if (!PyUnicode_Check(name)) {
PyErr_Format(PyExc_TypeError, "name must be a str, got %T", name);
*attr = NULL;
return -1;
}

assert(PyType_Check(type));
*attr = _PyType_LookupRefAndVersion(type, name, NULL);
return (*attr != NULL);
}

int
_PyType_CacheInitForSpecialization(PyHeapTypeObject *type, PyObject *init,
unsigned int tp_version)
Expand Down
Loading