Skip to content

Commit d9dde70

Browse files
authored
[3.14] gh-155503: Add more PyType C API tests (#155505) (#159085) (#159089)
[3.15] gh-155503: Add more PyType C API tests (#155505) (#159085) gh-155503: Add more PyType C API tests (#155505) Add tests on functions: * PyType_ClearCache() * PyType_GetFlags() * PyType_IsSubtype() * PyType_Ready() Move PyType limited C API tests from _testcapi to _testlimitedcapi. Add a new Modules/_testlimitedcapi/type.c file. (cherry picked from commit 29837a9) (cherry picked from commit 18c0067)
1 parent 5110c9f commit d9dde70

10 files changed

Lines changed: 471 additions & 105 deletions

File tree

‎Include/object.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ given type object has a specified feature.
533533
#define Py_TPFLAGS_SEQUENCE (1 << 5)
534534
/* Set if instances of the type object are treated as mappings for pattern matching */
535535
#define Py_TPFLAGS_MAPPING (1 << 6)
536-
#endif
536+
#endif // Py_LIMITED_API
537537

538538
/* Disallow creating instances of the type: set tp_new to NULL and don't create
539539
* the "__new__" key in the type dictionary. */

‎Lib/test/test_capi/test_type.py‎

Lines changed: 197 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import unittest
33

44
_testcapi = import_helper.import_module('_testcapi')
5+
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
6+
7+
NULL = None
58

69

710
class BuiltinStaticTypesTests(unittest.TestCase):
@@ -39,15 +42,18 @@ def test_tp_mro_is_set(self):
3942

4043
class TypeTests(unittest.TestCase):
4144
def test_get_type_name(self):
45+
# Test PyType_GetName(), PyType_GetQualName(),
46+
# PyType_GetFullyQualifiedName() and PyType_GetModuleName().
47+
4248
class MyType:
4349
pass
4450

45-
from _testcapi import (
51+
from _testlimitedcapi import (
4652
get_type_name, get_type_qualname,
4753
get_type_fullyqualname, get_type_module_name)
4854

4955
from collections import OrderedDict
50-
ht = _testcapi.get_heaptype_for_name()
56+
ht = _testlimitedcapi.get_heaptype_for_name()
5157
for cls, fullname, modname, qualname, name in (
5258
(int,
5359
'int',
@@ -107,6 +113,15 @@ class MyType:
107113
MyType.__module__ = 123
108114
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
109115

116+
# CRASHES get_type_name(NULL)
117+
# CRASHES get_type_qualname(NULL)
118+
# CRASHES get_type_fullyqualname(NULL)
119+
# CRASHES get_type_module_name(NULL)
120+
# CRASHES get_type_name(object()): argument must be a type
121+
# CRASHES get_type_qualname(object()): argument must be a type
122+
# CRASHES get_type_fullyqualname(object()): argument must be a type
123+
# CRASHES get_type_module_name(object()): argument must be a type
124+
110125
def test_get_base_by_token(self):
111126
def get_base_by_token(src, key, comparable=True):
112127
def run(use_mro):
@@ -197,7 +212,7 @@ class H2(int): pass
197212

198213
def test_freeze(self):
199214
# test PyType_Freeze()
200-
type_freeze = _testcapi.type_freeze
215+
type_freeze = _testlimitedcapi.type_freeze
201216

202217
# simple case, no inherante
203218
class MyType:
@@ -227,12 +242,15 @@ class D(A, C): pass
227242
# as well
228243
type_freeze(D)
229244

245+
# CRASHES type_freeze(NULL)
246+
# CRASHES type_freeze(object()): argument must be a type
247+
230248
@unittest.skipIf(
231249
Py_GIL_DISABLED and refleak_helper.hunting_for_refleaks(),
232250
"Specialization failure triggers gh-127773")
233251
def test_freeze_meta(self):
234252
"""test PyType_Freeze() with overridden MRO"""
235-
type_freeze = _testcapi.type_freeze
253+
type_freeze = _testlimitedcapi.type_freeze
236254

237255
class Base:
238256
value = 1
@@ -274,3 +292,178 @@ def test_extension_managed_dict_type(self):
274292
obj.__dict__ = {'bar': 3}
275293
self.assertEqual(obj.__dict__, {'bar': 3})
276294
self.assertEqual(obj.bar, 3)
295+
296+
def test_type_ready(self):
297+
# Test PyType_Ready(): calling it on initialized types
298+
# must not raise an exception.
299+
type_ready = _testlimitedcapi.type_ready
300+
301+
class HeapType:
302+
pass
303+
304+
type_ready(int)
305+
type_ready(dict)
306+
type_ready(HeapType)
307+
308+
# CRASHES type_ready(NULL)
309+
# CRASHES type_ready(123): argument must be a type
310+
311+
def test_type_clearcache(self):
312+
# Test PyType_ClearCache()
313+
type_clearcache = _testlimitedcapi.type_clearcache
314+
version_tag = type_clearcache()
315+
self.assertEqual(type(version_tag), int)
316+
self.assertGreaterEqual(version_tag, 0)
317+
318+
def test_type_getflags(self):
319+
# Test PyType_GetFlags()
320+
type_getflags = _testlimitedcapi.type_getflags
321+
322+
from _testlimitedcapi import (
323+
Py_TPFLAGS_HEAPTYPE,
324+
Py_TPFLAGS_HAVE_GC,
325+
Py_TPFLAGS_HAVE_FINALIZE,
326+
Py_TPFLAGS_HAVE_VERSION_TAG,
327+
Py_TPFLAGS_VALID_VERSION_TAG,
328+
Py_TPFLAGS_HAVE_VECTORCALL,
329+
Py_TPFLAGS_DISALLOW_INSTANTIATION,
330+
Py_TPFLAGS_IMMUTABLETYPE,
331+
Py_TPFLAGS_READY,
332+
Py_TPFLAGS_READYING,
333+
Py_TPFLAGS_LONG_SUBCLASS,
334+
Py_TPFLAGS_LIST_SUBCLASS,
335+
Py_TPFLAGS_TUPLE_SUBCLASS,
336+
Py_TPFLAGS_BYTES_SUBCLASS,
337+
Py_TPFLAGS_UNICODE_SUBCLASS,
338+
Py_TPFLAGS_DICT_SUBCLASS,
339+
Py_TPFLAGS_BASE_EXC_SUBCLASS,
340+
Py_TPFLAGS_TYPE_SUBCLASS,
341+
Py_TPFLAGS_IS_ABSTRACT,
342+
Py_TPFLAGS_BASETYPE,
343+
_Py_TPFLAGS_MATCH_SELF,
344+
Py_TPFLAGS_ITEMS_AT_END,
345+
Py_TPFLAGS_METHOD_DESCRIPTOR,
346+
)
347+
from _testcapi import (
348+
_Py_TPFLAGS_STATIC_BUILTIN,
349+
Py_TPFLAGS_SEQUENCE,
350+
Py_TPFLAGS_MAPPING,
351+
Py_TPFLAGS_INLINE_VALUES,
352+
Py_TPFLAGS_MANAGED_WEAKREF,
353+
Py_TPFLAGS_MANAGED_DICT,
354+
)
355+
356+
def check_flag(flags, flag, expected):
357+
self.assertEqual(bool(flags & flag), expected)
358+
359+
def check_subclasses(test_type, flags):
360+
for flag, base_type in (
361+
(Py_TPFLAGS_LONG_SUBCLASS, int),
362+
(Py_TPFLAGS_LIST_SUBCLASS, list),
363+
(Py_TPFLAGS_TUPLE_SUBCLASS, tuple),
364+
(Py_TPFLAGS_BYTES_SUBCLASS, bytes),
365+
(Py_TPFLAGS_UNICODE_SUBCLASS, str),
366+
(Py_TPFLAGS_DICT_SUBCLASS, dict),
367+
(Py_TPFLAGS_BASE_EXC_SUBCLASS, BaseException),
368+
(Py_TPFLAGS_TYPE_SUBCLASS, type),
369+
):
370+
with self.subTest(test_type=test_type, flag=flag, base_type=base_type):
371+
check_flag(flags, flag, issubclass(test_type, base_type))
372+
373+
def check_type(test_type, static_type, have_gc=False, have_vectorcall=False,
374+
is_base_type=True, sequence=False, mapping=False,
375+
match_self=True, items_at_end=False):
376+
heap_type = not static_type
377+
378+
flags = type_getflags(test_type)
379+
check_flag(flags, _Py_TPFLAGS_STATIC_BUILTIN, static_type)
380+
check_flag(flags, Py_TPFLAGS_HEAPTYPE, heap_type)
381+
check_flag(flags, Py_TPFLAGS_HAVE_GC, have_gc)
382+
check_subclasses(test_type, flags)
383+
check_flag(flags, Py_TPFLAGS_HAVE_VECTORCALL, have_vectorcall)
384+
check_flag(flags, Py_TPFLAGS_DISALLOW_INSTANTIATION, False)
385+
check_flag(flags, Py_TPFLAGS_IMMUTABLETYPE, static_type)
386+
check_flag(flags, Py_TPFLAGS_READY, True)
387+
check_flag(flags, Py_TPFLAGS_READYING, False)
388+
check_flag(flags, Py_TPFLAGS_IS_ABSTRACT, False)
389+
check_flag(flags, Py_TPFLAGS_BASETYPE, is_base_type)
390+
check_flag(flags, Py_TPFLAGS_SEQUENCE, sequence)
391+
check_flag(flags, Py_TPFLAGS_MAPPING, mapping)
392+
393+
check_flag(flags, Py_TPFLAGS_INLINE_VALUES, heap_type)
394+
check_flag(flags, Py_TPFLAGS_MANAGED_WEAKREF, heap_type)
395+
check_flag(flags, Py_TPFLAGS_MANAGED_DICT, heap_type)
396+
check_flag(flags, Py_TPFLAGS_ITEMS_AT_END, items_at_end)
397+
check_flag(flags, Py_TPFLAGS_METHOD_DESCRIPTOR, False)
398+
399+
check_flag(flags, _Py_TPFLAGS_MATCH_SELF, match_self)
400+
401+
# Flags kept for backward compatibility
402+
check_flag(flags, Py_TPFLAGS_HAVE_FINALIZE, False)
403+
check_flag(flags, Py_TPFLAGS_HAVE_VERSION_TAG, False)
404+
check_flag(flags, Py_TPFLAGS_VALID_VERSION_TAG, False)
405+
406+
# Scalar types
407+
check_type(int, static_type=True)
408+
check_type(bool, static_type=True,
409+
is_base_type=False)
410+
check_type(float, static_type=True)
411+
check_type(complex, static_type=True,
412+
match_self=False)
413+
check_type(bytes, static_type=True)
414+
check_type(bytearray, static_type=True)
415+
check_type(str, static_type=True)
416+
417+
# Collection types
418+
check_type(tuple, static_type=True, have_gc=True,
419+
sequence=True)
420+
check_type(list, static_type=True, have_gc=True,
421+
sequence=True)
422+
check_type(dict, static_type=True, have_gc=True,
423+
mapping=True)
424+
check_type(set, static_type=True, have_gc=True)
425+
check_type(frozenset, static_type=True, have_gc=True)
426+
427+
# Other types
428+
check_type(BaseException, static_type=True, have_gc=True,
429+
match_self=False)
430+
check_type(type, static_type=True, have_gc=True,
431+
have_vectorcall=True,
432+
match_self=False,
433+
items_at_end=True)
434+
435+
# Heap type
436+
class HeapType:
437+
pass
438+
check_type(HeapType, static_type=False, have_gc=True, match_self=False)
439+
440+
# CRASHES type_getflags(NULL)
441+
442+
def test_type_issubtype(self):
443+
# Test PyType_IsSubtype()
444+
_type_issubtype = _testlimitedcapi.type_issubtype
445+
446+
def type_issubtype(type1, type2):
447+
res = _type_issubtype(type1, type2)
448+
self.assertIn(res, (0, 1))
449+
return bool(res)
450+
451+
class MyList(list):
452+
pass
453+
454+
self.assertTrue(type_issubtype(bool, int))
455+
self.assertTrue(type_issubtype(MyList, list))
456+
457+
self.assertFalse(type_issubtype(int, type))
458+
self.assertFalse(type_issubtype(MyList, tuple))
459+
460+
def test_type_modified(self):
461+
# Test PyType_Modified()
462+
type_modified = _testlimitedcapi.type_modified
463+
464+
class MyType:
465+
pass
466+
type_modified(MyType)
467+
468+
# CRASHES type_modified(NULL)
469+
# CRASHES type_modified({}): argument must be a type

‎Lib/test/test_type_cache.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
except ImportError:
1111
_clear_type_cache = None
1212

13-
# Skip this test if the _testcapi module isn't available.
13+
# Skip this test if the _testcapi modules are not available.
1414
_testcapi = import_helper.import_module("_testcapi")
15+
_testlimitedcapi = import_helper.import_module("_testlimitedcapi")
1516
_testinternalcapi = import_helper.import_module("_testinternalcapi")
17+
1618
type_get_version = _testcapi.type_get_version
1719
type_assign_specific_version_unsafe = _testinternalcapi.type_assign_specific_version_unsafe
1820
type_assign_version = _testcapi.type_assign_version
19-
type_modified = _testcapi.type_modified
21+
type_modified = _testlimitedcapi.type_modified
2022

2123
def clear_type_cache():
2224
with warnings.catch_warnings():

‎Modules/Setup.stdlib.in‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@
176176
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
177177
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c
178178
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/weakref.c
179-
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/capsule.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slice.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c _testlimitedcapi/weakref.c _testlimitedcapi/run.c
179+
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/capsule.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slice.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c _testlimitedcapi/weakref.c _testlimitedcapi/run.c _testlimitedcapi/type.c
180180
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
181181
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
182182

0 commit comments

Comments
 (0)