Skip to content

Commit 480faa6

Browse files
committed
gh-139847: Add PyType_Lookup() function
1 parent d2deb8f commit 480faa6

File tree

12 files changed

+127
-4
lines changed

12 files changed

+127
-4
lines changed

Doc/c-api/type.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,21 @@ Type Objects
306306
307307
.. versionadded:: 3.14
308308
309+
310+
.. c:function:: int PyType_Lookup(PyTypeObject *type, PyObject *name, PyObject **attr)
311+
312+
Look for a type attribute through the type
313+
:term:`MRO <method resolution order>`.
314+
315+
*name* must be a :class:`str`.
316+
317+
* If found, set *\*attr* to a strong reference and return ``1``.
318+
* If not found, set *\*attr* to ``NULL`` and return ``0``.
319+
* On error, set an exception and return ``-1``.
320+
321+
.. versionadded:: next
322+
323+
309324
.. c:function:: int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
310325
311326
Attempt to assign a version tag to the given type.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Pending removal in Python 3.19
2+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3+
4+
* The following private functions are deprecated
5+
and planned for removal in Python 3.19:
6+
7+
* :c:func:`!_PyType_Lookup`: use :c:func:`PyType_Lookup`.
8+
* :c:func:`!_PyType_LookupRef`: use :c:func:`PyType_Lookup`.
9+
10+
The `pythoncapi-compat project
11+
<https://github.com/python/pythoncapi-compat/>`__ can be used to get
12+
these new public functions on Python 3.14 and older.
13+
(Contributed by Victor Stinner in :gh:`128863`.)

Doc/whatsnew/3.13.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2554,6 +2554,8 @@ Deprecated C APIs
25542554

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

2557+
.. include:: ../deprecations/c-api-pending-removal-in-3.19.rst
2558+
25572559
.. include:: ../deprecations/c-api-pending-removal-in-future.rst
25582560

25592561
.. _pythoncapi-compat project: https://github.com/python/pythoncapi-compat/

Doc/whatsnew/3.14.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3079,6 +3079,8 @@ Deprecated C APIs
30793079

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

3082+
.. include:: ../deprecations/c-api-pending-removal-in-3.19.rst
3083+
30823084
.. include:: ../deprecations/c-api-pending-removal-in-future.rst
30833085

30843086

Doc/whatsnew/3.15.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,10 @@ New features
852852

853853
(Contributed by Victor Stinner in :gh:`129813`.)
854854

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

856860
Porting to Python 3.15
857861
----------------------
@@ -869,6 +873,9 @@ Porting to Python 3.15
869873

870874
* Private functions promoted to public C APIs:
871875

876+
* :c:func:`!_PyType_Lookup`: use :c:func:`PyType_Lookup`.
877+
* :c:func:`!_PyType_LookupRef`: use :c:func:`PyType_Lookup`.
878+
872879
The |pythoncapi_compat_project| can be used to get most of these new
873880
functions on Python 3.14 and older.
874881

@@ -915,6 +922,14 @@ Deprecated C APIs
915922

916923
.. Add C API deprecations above alphabetically, not here at the end.
917924
925+
.. include:: ../deprecations/c-api-pending-removal-in-3.16.rst
926+
927+
.. include:: ../deprecations/c-api-pending-removal-in-3.18.rst
928+
929+
.. include:: ../deprecations/c-api-pending-removal-in-3.19.rst
930+
931+
.. include:: ../deprecations/c-api-pending-removal-in-future.rst
932+
918933
Removed C APIs
919934
--------------
920935

Include/cpython/object.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ struct _specialization_cache {
252252
// by the specialization machinery, and are invalidated by PyType_Modified.
253253
// The rules for using them are as follows:
254254
// - If getitem is non-NULL, then it is the same Python function that
255-
// PyType_Lookup(cls, "__getitem__") would return.
255+
// _PyType_Lookup(cls, "__getitem__") would return.
256256
// - If getitem is NULL, then getitem_version is meaningless.
257257
// - If getitem->func_version == getitem_version, then getitem can be called
258258
// with two positional arguments and no keyword arguments, and has neither
@@ -289,8 +289,16 @@ typedef struct _heaptypeobject {
289289
} PyHeapTypeObject;
290290

291291
PyAPI_FUNC(const char *) _PyType_Name(PyTypeObject *);
292-
PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
293-
PyAPI_FUNC(PyObject *) _PyType_LookupRef(PyTypeObject *, PyObject *);
292+
PyAPI_FUNC(int) PyType_Lookup(
293+
PyTypeObject *type,
294+
PyObject *name,
295+
PyObject **attr);
296+
_Py_DEPRECATED_EXTERNALLY(3.15) PyAPI_FUNC(PyObject *) _PyType_Lookup(
297+
PyTypeObject *type,
298+
PyObject *name);
299+
_Py_DEPRECATED_EXTERNALLY(3.15) PyAPI_FUNC(PyObject *) _PyType_LookupRef(
300+
PyTypeObject *type,
301+
PyObject *name);
294302
PyAPI_FUNC(PyObject *) PyType_GetDict(PyTypeObject *);
295303

296304
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);

Lib/test/test_capi/test_type.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,20 @@ def test_extension_managed_dict_type(self):
274274
obj.__dict__ = {'bar': 3}
275275
self.assertEqual(obj.__dict__, {'bar': 3})
276276
self.assertEqual(obj.bar, 3)
277+
278+
def test_type_lookup(self):
279+
type_lookup = _testcapi.type_lookup
280+
281+
class Parent:
282+
parent_attr = "parent"
283+
284+
class Child(Parent):
285+
child_attr = "child"
286+
287+
self.assertEqual(type_lookup(Child, "parent_attr"), "parent")
288+
self.assertEqual(type_lookup(Child, "child_attr"), "child")
289+
self.assertEqual(type_lookup(Child, "xxx"), AttributeError)
290+
291+
# name parameter must be a str
292+
self.assertRaises(TypeError, type_lookup, Child, b'name')
293+
self.assertRaises(TypeError, type_lookup, Child, 123)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyType_Lookup` function to look for a type attribute through
2+
the type :term:`MRO <method resolution order>`. Patch by Victor Stinner.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Deprecate the private functions :c:func:`!_PyType_Lookup` and
2+
:c:func:`!_PyType_LookupRef`: use the new public :c:func:`PyType_LookupRef`
3+
function instead. Patch by Victor Stinner.

Modules/_testcapi/gc.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,14 @@ slot_tp_del(PyObject *self)
9898
PyErr_SetRaisedException(exc);
9999
return;
100100
}
101+
101102
/* Execute __del__ method, if any. */
102-
del = _PyType_LookupRef(Py_TYPE(self), tp_del);
103+
if (PyType_Lookup(Py_TYPE(self), tp_del, &del) < 0) {
104+
Py_DECREF(tp_del);
105+
PyErr_FormatUnraisable("Exception ignored while deallocating");
106+
PyErr_SetRaisedException(exc);
107+
return;
108+
}
103109
Py_DECREF(tp_del);
104110
if (del != NULL) {
105111
res = PyObject_CallOneArg(del, self);

0 commit comments

Comments
 (0)