Skip to content

Commit 5424e3b

Browse files
authored
gh-93649: Add Modules/_testcapi/type.c file (#129516)
Move PyType C API tests to a new file. Move following tests from test_capi.test_misc to test_capi.test_type: * BuiltinStaticTypesTests * test_get_type_name() * test_get_base_by_token()
1 parent 881984b commit 5424e3b

File tree

8 files changed

+434
-393
lines changed

8 files changed

+434
-393
lines changed

Lib/test/test_capi/test_misc.py

-174
Original file line numberDiff line numberDiff line change
@@ -1111,147 +1111,6 @@ class Data(_testcapi.ObjExtraData):
11111111
del d.extra
11121112
self.assertIsNone(d.extra)
11131113

1114-
def test_get_type_name(self):
1115-
class MyType:
1116-
pass
1117-
1118-
from _testcapi import (
1119-
get_type_name, get_type_qualname,
1120-
get_type_fullyqualname, get_type_module_name)
1121-
1122-
from collections import OrderedDict
1123-
ht = _testcapi.get_heaptype_for_name()
1124-
for cls, fullname, modname, qualname, name in (
1125-
(int,
1126-
'int',
1127-
'builtins',
1128-
'int',
1129-
'int'),
1130-
(OrderedDict,
1131-
'collections.OrderedDict',
1132-
'collections',
1133-
'OrderedDict',
1134-
'OrderedDict'),
1135-
(ht,
1136-
'_testcapi.HeapTypeNameType',
1137-
'_testcapi',
1138-
'HeapTypeNameType',
1139-
'HeapTypeNameType'),
1140-
(MyType,
1141-
f'{__name__}.CAPITest.test_get_type_name.<locals>.MyType',
1142-
__name__,
1143-
'CAPITest.test_get_type_name.<locals>.MyType',
1144-
'MyType'),
1145-
):
1146-
with self.subTest(cls=repr(cls)):
1147-
self.assertEqual(get_type_fullyqualname(cls), fullname)
1148-
self.assertEqual(get_type_module_name(cls), modname)
1149-
self.assertEqual(get_type_qualname(cls), qualname)
1150-
self.assertEqual(get_type_name(cls), name)
1151-
1152-
# override __module__
1153-
ht.__module__ = 'test_module'
1154-
self.assertEqual(get_type_fullyqualname(ht), 'test_module.HeapTypeNameType')
1155-
self.assertEqual(get_type_module_name(ht), 'test_module')
1156-
self.assertEqual(get_type_qualname(ht), 'HeapTypeNameType')
1157-
self.assertEqual(get_type_name(ht), 'HeapTypeNameType')
1158-
1159-
# override __name__ and __qualname__
1160-
MyType.__name__ = 'my_name'
1161-
MyType.__qualname__ = 'my_qualname'
1162-
self.assertEqual(get_type_fullyqualname(MyType), f'{__name__}.my_qualname')
1163-
self.assertEqual(get_type_module_name(MyType), __name__)
1164-
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
1165-
self.assertEqual(get_type_name(MyType), 'my_name')
1166-
1167-
# override also __module__
1168-
MyType.__module__ = 'my_module'
1169-
self.assertEqual(get_type_fullyqualname(MyType), 'my_module.my_qualname')
1170-
self.assertEqual(get_type_module_name(MyType), 'my_module')
1171-
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
1172-
self.assertEqual(get_type_name(MyType), 'my_name')
1173-
1174-
# PyType_GetFullyQualifiedName() ignores the module if it's "builtins"
1175-
# or "__main__" of it is not a string
1176-
MyType.__module__ = 'builtins'
1177-
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
1178-
MyType.__module__ = '__main__'
1179-
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
1180-
MyType.__module__ = 123
1181-
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
1182-
1183-
def test_get_base_by_token(self):
1184-
def get_base_by_token(src, key, comparable=True):
1185-
def run(use_mro):
1186-
find_first = _testcapi.pytype_getbasebytoken
1187-
ret1, result = find_first(src, key, use_mro, True)
1188-
ret2, no_result = find_first(src, key, use_mro, False)
1189-
self.assertIn(ret1, (0, 1))
1190-
self.assertEqual(ret1, result is not None)
1191-
self.assertEqual(ret1, ret2)
1192-
self.assertIsNone(no_result)
1193-
return result
1194-
1195-
found_in_mro = run(True)
1196-
found_in_bases = run(False)
1197-
if comparable:
1198-
self.assertIs(found_in_mro, found_in_bases)
1199-
return found_in_mro
1200-
return found_in_mro, found_in_bases
1201-
1202-
create_type = _testcapi.create_type_with_token
1203-
get_token = _testcapi.get_tp_token
1204-
1205-
Py_TP_USE_SPEC = _testcapi.Py_TP_USE_SPEC
1206-
self.assertEqual(Py_TP_USE_SPEC, 0)
1207-
1208-
A1 = create_type('_testcapi.A1', Py_TP_USE_SPEC)
1209-
self.assertTrue(get_token(A1) != Py_TP_USE_SPEC)
1210-
1211-
B1 = create_type('_testcapi.B1', id(self))
1212-
self.assertTrue(get_token(B1) == id(self))
1213-
1214-
tokenA1 = get_token(A1)
1215-
# find A1 from A1
1216-
found = get_base_by_token(A1, tokenA1)
1217-
self.assertIs(found, A1)
1218-
1219-
# no token in static types
1220-
STATIC = type(1)
1221-
self.assertEqual(get_token(STATIC), 0)
1222-
found = get_base_by_token(STATIC, tokenA1)
1223-
self.assertIs(found, None)
1224-
1225-
# no token in pure subtypes
1226-
class A2(A1): pass
1227-
self.assertEqual(get_token(A2), 0)
1228-
# find A1
1229-
class Z(STATIC, B1, A2): pass
1230-
found = get_base_by_token(Z, tokenA1)
1231-
self.assertIs(found, A1)
1232-
1233-
# searching for NULL token is an error
1234-
with self.assertRaises(SystemError):
1235-
get_base_by_token(Z, 0)
1236-
with self.assertRaises(SystemError):
1237-
get_base_by_token(STATIC, 0)
1238-
1239-
# share the token with A1
1240-
C1 = create_type('_testcapi.C1', tokenA1)
1241-
self.assertTrue(get_token(C1) == tokenA1)
1242-
1243-
# find C1 first by shared token
1244-
class Z(C1, A2): pass
1245-
found = get_base_by_token(Z, tokenA1)
1246-
self.assertIs(found, C1)
1247-
# B1 not found
1248-
found = get_base_by_token(Z, get_token(B1))
1249-
self.assertIs(found, None)
1250-
1251-
with self.assertRaises(TypeError):
1252-
_testcapi.pytype_getbasebytoken(
1253-
'not a type', id(self), True, False)
1254-
12551114
def test_gen_get_code(self):
12561115
def genf(): yield
12571116
gen = genf()
@@ -2922,39 +2781,6 @@ def test_linked_lifecycle_link_incref_unlink_decref(self):
29222781
0, get_refcount(interpid))
29232782

29242783

2925-
class BuiltinStaticTypesTests(unittest.TestCase):
2926-
2927-
TYPES = [
2928-
object,
2929-
type,
2930-
int,
2931-
str,
2932-
dict,
2933-
type(None),
2934-
bool,
2935-
BaseException,
2936-
Exception,
2937-
Warning,
2938-
DeprecationWarning, # Warning subclass
2939-
]
2940-
2941-
def test_tp_bases_is_set(self):
2942-
# PyTypeObject.tp_bases is documented as public API.
2943-
# See https://github.com/python/cpython/issues/105020.
2944-
for typeobj in self.TYPES:
2945-
with self.subTest(typeobj):
2946-
bases = _testcapi.type_get_tp_bases(typeobj)
2947-
self.assertIsNot(bases, None)
2948-
2949-
def test_tp_mro_is_set(self):
2950-
# PyTypeObject.tp_bases is documented as public API.
2951-
# See https://github.com/python/cpython/issues/105020.
2952-
for typeobj in self.TYPES:
2953-
with self.subTest(typeobj):
2954-
mro = _testcapi.type_get_tp_mro(typeobj)
2955-
self.assertIsNot(mro, None)
2956-
2957-
29582784
class TestStaticTypes(unittest.TestCase):
29592785

29602786
_has_run = False

Lib/test/test_capi/test_type.py

+174
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,181 @@
44
_testcapi = import_helper.import_module('_testcapi')
55

66

7+
class BuiltinStaticTypesTests(unittest.TestCase):
8+
9+
TYPES = [
10+
object,
11+
type,
12+
int,
13+
str,
14+
dict,
15+
type(None),
16+
bool,
17+
BaseException,
18+
Exception,
19+
Warning,
20+
DeprecationWarning, # Warning subclass
21+
]
22+
23+
def test_tp_bases_is_set(self):
24+
# PyTypeObject.tp_bases is documented as public API.
25+
# See https://github.com/python/cpython/issues/105020.
26+
for typeobj in self.TYPES:
27+
with self.subTest(typeobj):
28+
bases = _testcapi.type_get_tp_bases(typeobj)
29+
self.assertIsNot(bases, None)
30+
31+
def test_tp_mro_is_set(self):
32+
# PyTypeObject.tp_bases is documented as public API.
33+
# See https://github.com/python/cpython/issues/105020.
34+
for typeobj in self.TYPES:
35+
with self.subTest(typeobj):
36+
mro = _testcapi.type_get_tp_mro(typeobj)
37+
self.assertIsNot(mro, None)
38+
39+
740
class TypeTests(unittest.TestCase):
41+
def test_get_type_name(self):
42+
class MyType:
43+
pass
44+
45+
from _testcapi import (
46+
get_type_name, get_type_qualname,
47+
get_type_fullyqualname, get_type_module_name)
48+
49+
from collections import OrderedDict
50+
ht = _testcapi.get_heaptype_for_name()
51+
for cls, fullname, modname, qualname, name in (
52+
(int,
53+
'int',
54+
'builtins',
55+
'int',
56+
'int'),
57+
(OrderedDict,
58+
'collections.OrderedDict',
59+
'collections',
60+
'OrderedDict',
61+
'OrderedDict'),
62+
(ht,
63+
'_testcapi.HeapTypeNameType',
64+
'_testcapi',
65+
'HeapTypeNameType',
66+
'HeapTypeNameType'),
67+
(MyType,
68+
f'{__name__}.TypeTests.test_get_type_name.<locals>.MyType',
69+
__name__,
70+
'TypeTests.test_get_type_name.<locals>.MyType',
71+
'MyType'),
72+
):
73+
with self.subTest(cls=repr(cls)):
74+
self.assertEqual(get_type_fullyqualname(cls), fullname)
75+
self.assertEqual(get_type_module_name(cls), modname)
76+
self.assertEqual(get_type_qualname(cls), qualname)
77+
self.assertEqual(get_type_name(cls), name)
78+
79+
# override __module__
80+
ht.__module__ = 'test_module'
81+
self.assertEqual(get_type_fullyqualname(ht), 'test_module.HeapTypeNameType')
82+
self.assertEqual(get_type_module_name(ht), 'test_module')
83+
self.assertEqual(get_type_qualname(ht), 'HeapTypeNameType')
84+
self.assertEqual(get_type_name(ht), 'HeapTypeNameType')
85+
86+
# override __name__ and __qualname__
87+
MyType.__name__ = 'my_name'
88+
MyType.__qualname__ = 'my_qualname'
89+
self.assertEqual(get_type_fullyqualname(MyType), f'{__name__}.my_qualname')
90+
self.assertEqual(get_type_module_name(MyType), __name__)
91+
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
92+
self.assertEqual(get_type_name(MyType), 'my_name')
93+
94+
# override also __module__
95+
MyType.__module__ = 'my_module'
96+
self.assertEqual(get_type_fullyqualname(MyType), 'my_module.my_qualname')
97+
self.assertEqual(get_type_module_name(MyType), 'my_module')
98+
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
99+
self.assertEqual(get_type_name(MyType), 'my_name')
100+
101+
# PyType_GetFullyQualifiedName() ignores the module if it's "builtins"
102+
# or "__main__" of it is not a string
103+
MyType.__module__ = 'builtins'
104+
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
105+
MyType.__module__ = '__main__'
106+
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
107+
MyType.__module__ = 123
108+
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
109+
110+
def test_get_base_by_token(self):
111+
def get_base_by_token(src, key, comparable=True):
112+
def run(use_mro):
113+
find_first = _testcapi.pytype_getbasebytoken
114+
ret1, result = find_first(src, key, use_mro, True)
115+
ret2, no_result = find_first(src, key, use_mro, False)
116+
self.assertIn(ret1, (0, 1))
117+
self.assertEqual(ret1, result is not None)
118+
self.assertEqual(ret1, ret2)
119+
self.assertIsNone(no_result)
120+
return result
121+
122+
found_in_mro = run(True)
123+
found_in_bases = run(False)
124+
if comparable:
125+
self.assertIs(found_in_mro, found_in_bases)
126+
return found_in_mro
127+
return found_in_mro, found_in_bases
128+
129+
create_type = _testcapi.create_type_with_token
130+
get_token = _testcapi.get_tp_token
131+
132+
Py_TP_USE_SPEC = _testcapi.Py_TP_USE_SPEC
133+
self.assertEqual(Py_TP_USE_SPEC, 0)
134+
135+
A1 = create_type('_testcapi.A1', Py_TP_USE_SPEC)
136+
self.assertTrue(get_token(A1) != Py_TP_USE_SPEC)
137+
138+
B1 = create_type('_testcapi.B1', id(self))
139+
self.assertTrue(get_token(B1) == id(self))
140+
141+
tokenA1 = get_token(A1)
142+
# find A1 from A1
143+
found = get_base_by_token(A1, tokenA1)
144+
self.assertIs(found, A1)
145+
146+
# no token in static types
147+
STATIC = type(1)
148+
self.assertEqual(get_token(STATIC), 0)
149+
found = get_base_by_token(STATIC, tokenA1)
150+
self.assertIs(found, None)
151+
152+
# no token in pure subtypes
153+
class A2(A1): pass
154+
self.assertEqual(get_token(A2), 0)
155+
# find A1
156+
class Z(STATIC, B1, A2): pass
157+
found = get_base_by_token(Z, tokenA1)
158+
self.assertIs(found, A1)
159+
160+
# searching for NULL token is an error
161+
with self.assertRaises(SystemError):
162+
get_base_by_token(Z, 0)
163+
with self.assertRaises(SystemError):
164+
get_base_by_token(STATIC, 0)
165+
166+
# share the token with A1
167+
C1 = create_type('_testcapi.C1', tokenA1)
168+
self.assertTrue(get_token(C1) == tokenA1)
169+
170+
# find C1 first by shared token
171+
class Z(C1, A2): pass
172+
found = get_base_by_token(Z, tokenA1)
173+
self.assertIs(found, C1)
174+
# B1 not found
175+
found = get_base_by_token(Z, get_token(B1))
176+
self.assertIs(found, None)
177+
178+
with self.assertRaises(TypeError):
179+
_testcapi.pytype_getbasebytoken(
180+
'not a type', id(self), True, False)
181+
8182
def test_freeze(self):
9183
# test PyType_Freeze()
10184
type_freeze = _testcapi.type_freeze

Modules/Setup.stdlib.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
163163
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
164164
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
165-
@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
165+
@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
166166
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.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/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
167167
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
168168
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c

Modules/_testcapi/parts.h

+1
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,6 @@ int _PyTestCapi_Init_Object(PyObject *module);
6363
int _PyTestCapi_Init_Config(PyObject *mod);
6464
int _PyTestCapi_Init_Import(PyObject *mod);
6565
int _PyTestCapi_Init_Frame(PyObject *mod);
66+
int _PyTestCapi_Init_Type(PyObject *mod);
6667

6768
#endif // Py_TESTCAPI_PARTS_H

0 commit comments

Comments
 (0)