pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/646f16bdeed6ebe1069e1d64886fbaa26edac75c

07ff8eaaaff3a3.css" /> gh-124153: Implement `PyType_GetBaseByToken()` and `Py_tp_token` slot… · python/cpython@646f16b · GitHub
Skip to content

Commit 646f16b

Browse files
authored
gh-124153: Implement PyType_GetBaseByToken() and Py_tp_token slot (GH-124163)
1 parent 79a7410 commit 646f16b

File tree

18 files changed

+443
-13
lines changed

18 files changed

+443
-13
lines changed

Doc/c-api/type.rst

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,24 @@ Type Objects
264264
265265
.. versionadded:: 3.11
266266
267+
.. c:function:: int PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)
268+
269+
Find the first superclass in *type*'s :term:`method resolution order` whose
270+
:c:macro:`Py_tp_token` token is equal to the given one.
271+
272+
* If found, set *\*result* to a new :term:`strong reference`
273+
to it and return ``1``.
274+
* If not found, set *\*result* to ``NULL`` and return ``0``.
275+
* On error, set *\*result* to ``NULL`` and return ``-1`` with an
276+
exception set.
277+
278+
The *result* argument may be ``NULL``, in which case *\*result* is not set.
279+
Use this if you need only the return value.
280+
281+
The *token* argument may not be ``NULL``.
282+
283+
.. versionadded:: 3.14
284+
267285
.. c:function:: int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
268286
269287
Attempt to assign a version tag to the given type.
@@ -488,6 +506,11 @@ The following functions and structs are used to create
488506
* ``Py_nb_add`` to set :c:member:`PyNumberMethods.nb_add`
489507
* ``Py_sq_length`` to set :c:member:`PySequenceMethods.sq_length`
490508
509+
An additional slot is supported that does not correspond to a
510+
:c:type:`!PyTypeObject` struct field:
511+
512+
* :c:data:`Py_tp_token`
513+
491514
The following “offset” fields cannot be set using :c:type:`PyType_Slot`:
492515
493516
* :c:member:`~PyTypeObject.tp_weaklistoffset`
@@ -538,4 +561,47 @@ The following functions and structs are used to create
538561
The desired value of the slot. In most cases, this is a pointer
539562
to a function.
540563
541-
Slots other than ``Py_tp_doc`` may not be ``NULL``.
564+
*pfunc* values may not be ``NULL``, except for the following slots:
565+
566+
* ``Py_tp_doc``
567+
* :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC`
568+
rather than ``NULL``)
569+
570+
.. c:macro:: Py_tp_token
571+
572+
A :c:member:`~PyType_Slot.slot` that records a static memory layout ID
573+
for a class.
574+
575+
If the :c:type:`PyType_Spec` of the class is statically
576+
allocated, the token can be set to the spec using the special value
577+
:c:data:`Py_TP_USE_SPEC`:
578+
579+
.. code-block:: c
580+
581+
static PyType_Slot foo_slots[] = {
582+
{Py_tp_token, Py_TP_USE_SPEC},
583+
584+
It can also be set to an arbitrary pointer, but you must ensure that:
585+
586+
* The pointer outlives the class, so it's not reused for something else
587+
while the class exists.
588+
* It "belongs" to the extension module where the class lives, so it will not
589+
clash with other extensions.
590+
591+
Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has
592+
a given token -- that is, check whether the memory layout is compatible.
593+
594+
To get the token for a given class (without considering superclasses),
595+
use :c:func:`PyType_GetSlot` with ``Py_tp_token``.
596+
597+
.. versionadded:: 3.14
598+
599+
.. c:namespace:: NULL
600+
601+
.. c:macro:: Py_TP_USE_SPEC
602+
603+
Used as a value with :c:data:`Py_tp_token` to set the token to the
604+
class's :c:type:`PyType_Spec`.
605+
Expands to ``NULL``.
606+
607+
.. versionadded:: 3.14

Doc/data/stable_abi.dat

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.14.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,11 @@ New Features
554554

555555
(Contributed by Victor Stinner in :gh:`107954`.)
556556

557+
* Add :c:func:`PyType_GetBaseByToken` and :c:data:`Py_tp_token` slot for easier
558+
superclass identification, which attempts to resolve the `type checking issue
559+
<https://peps.python.org/pep-0630/#type-checking>`__ mentioned in :pep:`630`
560+
(:gh:`124153`).
561+
557562

558563
Porting to Python 3.14
559564
----------------------

Include/cpython/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ typedef struct _heaptypeobject {
269269
struct _dictkeysobject *ht_cached_keys;
270270
PyObject *ht_module;
271271
char *_ht_tpname; // Storage for "tp_name"; see PyType_FromModuleAndSpec
272+
void *ht_token; // Storage for the "Py_tp_token" slot
272273
struct _specialization_cache _spec_cache; // For use by the specializer.
273274
#ifdef Py_GIL_DISABLED
274275
Py_ssize_t unique_id; // ID used for thread-local refcounting

Include/object.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,10 @@ PyAPI_FUNC(PyObject *) PyType_FromMetaclass(PyTypeObject*, PyObject*, PyType_Spe
391391
PyAPI_FUNC(void *) PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls);
392392
PyAPI_FUNC(Py_ssize_t) PyType_GetTypeDataSize(PyTypeObject *cls);
393393
#endif
394+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
395+
PyAPI_FUNC(int) PyType_GetBaseByToken(PyTypeObject *, void *, PyTypeObject **);
396+
#define Py_TP_USE_SPEC NULL
397+
#endif
394398

395399
/* Generic type check */
396400
PyAPI_FUNC(int) PyType_IsSubtype(PyTypeObject *, PyTypeObject *);

Include/typeslots.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,7 @@
9090
/* New in 3.14 */
9191
#define Py_tp_vectorcall 82
9292
#endif
93+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
94+
/* New in 3.14 */
95+
#define Py_tp_token 83
96+
#endif

Lib/test/test_capi/test_misc.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,77 @@ class MyType:
11441144
MyType.__module__ = 123
11451145
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
11461146

1147+
def test_get_base_by_token(self):
1148+
def get_base_by_token(src, key, comparable=True):
1149+
def run(use_mro):
1150+
find_first = _testcapi.pytype_getbasebytoken
1151+
ret1, result = find_first(src, key, use_mro, True)
1152+
ret2, no_result = find_first(src, key, use_mro, False)
1153+
self.assertIn(ret1, (0, 1))
1154+
self.assertEqual(ret1, result is not None)
1155+
self.assertEqual(ret1, ret2)
1156+
self.assertIsNone(no_result)
1157+
return result
1158+
1159+
found_in_mro = run(True)
1160+
found_in_bases = run(False)
1161+
if comparable:
1162+
self.assertIs(found_in_mro, found_in_bases)
1163+
return found_in_mro
1164+
return found_in_mro, found_in_bases
1165+
1166+
create_type = _testcapi.create_type_with_token
1167+
get_token = _testcapi.get_tp_token
1168+
1169+
Py_TP_USE_SPEC = _testcapi.Py_TP_USE_SPEC
1170+
self.assertEqual(Py_TP_USE_SPEC, 0)
1171+
1172+
A1 = create_type('_testcapi.A1', Py_TP_USE_SPEC)
1173+
self.assertTrue(get_token(A1) != Py_TP_USE_SPEC)
1174+
1175+
B1 = create_type('_testcapi.B1', id(self))
1176+
self.assertTrue(get_token(B1) == id(self))
1177+
1178+
tokenA1 = get_token(A1)
1179+
# find A1 from A1
1180+
found = get_base_by_token(A1, tokenA1)
1181+
self.assertIs(found, A1)
1182+
1183+
# no token in static types
1184+
STATIC = type(1)
1185+
self.assertEqual(get_token(STATIC), 0)
1186+
found = get_base_by_token(STATIC, tokenA1)
1187+
self.assertIs(found, None)
1188+
1189+
# no token in pure subtypes
1190+
class A2(A1): pass
1191+
self.assertEqual(get_token(A2), 0)
1192+
# find A1
1193+
class Z(STATIC, B1, A2): pass
1194+
found = get_base_by_token(Z, tokenA1)
1195+
self.assertIs(found, A1)
1196+
1197+
# searching for NULL token is an error
1198+
with self.assertRaises(SystemError):
1199+
get_base_by_token(Z, 0)
1200+
with self.assertRaises(SystemError):
1201+
get_base_by_token(STATIC, 0)
1202+
1203+
# share the token with A1
1204+
C1 = create_type('_testcapi.C1', tokenA1)
1205+
self.assertTrue(get_token(C1) == tokenA1)
1206+
1207+
# find C1 first by shared token
1208+
class Z(C1, A2): pass
1209+
found = get_base_by_token(Z, tokenA1)
1210+
self.assertIs(found, C1)
1211+
# B1 not found
1212+
found = get_base_by_token(Z, get_token(B1))
1213+
self.assertIs(found, None)
1214+
1215+
with self.assertRaises(TypeError):
1216+
_testcapi.pytype_getbasebytoken(
1217+
'not a type', id(self), True, False)
11471218

11481219
def test_gen_get_code(self):
11491220
def genf(): yield

Lib/test/test_stable_abi_ctypes.py

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_sys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1718,7 +1718,7 @@ def delx(self): del self.__x
17181718
'3P' # PyMappingMethods
17191719
'10P' # PySequenceMethods
17201720
'2P' # PyBufferProcs
1721-
'6P'
1721+
'7P'
17221722
'1PIP' # Specializer cache
17231723
+ typeid # heap type id (free-threaded only)
17241724
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyType_GetBaseByToken` and :c:data:`Py_tp_token` slot for easier
2+
type checking, related to :pep:`489` and :pep:`630`.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy