|
2 | 2 | import unittest |
3 | 3 |
|
4 | 4 | _testcapi = import_helper.import_module('_testcapi') |
| 5 | +_testlimitedcapi = import_helper.import_module('_testlimitedcapi') |
| 6 | + |
| 7 | +NULL = None |
5 | 8 |
|
6 | 9 |
|
7 | 10 | class BuiltinStaticTypesTests(unittest.TestCase): |
@@ -39,15 +42,18 @@ def test_tp_mro_is_set(self): |
39 | 42 |
|
40 | 43 | class TypeTests(unittest.TestCase): |
41 | 44 | def test_get_type_name(self): |
| 45 | + # Test PyType_GetName(), PyType_GetQualName(), |
| 46 | + # PyType_GetFullyQualifiedName() and PyType_GetModuleName(). |
| 47 | + |
42 | 48 | class MyType: |
43 | 49 | pass |
44 | 50 |
|
45 | | - from _testcapi import ( |
| 51 | + from _testlimitedcapi import ( |
46 | 52 | get_type_name, get_type_qualname, |
47 | 53 | get_type_fullyqualname, get_type_module_name) |
48 | 54 |
|
49 | 55 | from collections import OrderedDict |
50 | | - ht = _testcapi.get_heaptype_for_name() |
| 56 | + ht = _testlimitedcapi.get_heaptype_for_name() |
51 | 57 | for cls, fullname, modname, qualname, name in ( |
52 | 58 | (int, |
53 | 59 | 'int', |
@@ -107,6 +113,15 @@ class MyType: |
107 | 113 | MyType.__module__ = 123 |
108 | 114 | self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname') |
109 | 115 |
|
| 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 | + |
110 | 125 | def test_get_base_by_token(self): |
111 | 126 | def get_base_by_token(src, key, comparable=True): |
112 | 127 | def run(use_mro): |
@@ -197,7 +212,7 @@ class H2(int): pass |
197 | 212 |
|
198 | 213 | def test_freeze(self): |
199 | 214 | # test PyType_Freeze() |
200 | | - type_freeze = _testcapi.type_freeze |
| 215 | + type_freeze = _testlimitedcapi.type_freeze |
201 | 216 |
|
202 | 217 | # simple case, no inherante |
203 | 218 | class MyType: |
@@ -227,12 +242,15 @@ class D(A, C): pass |
227 | 242 | # as well |
228 | 243 | type_freeze(D) |
229 | 244 |
|
| 245 | + # CRASHES type_freeze(NULL) |
| 246 | + # CRASHES type_freeze(object()): argument must be a type |
| 247 | + |
230 | 248 | @unittest.skipIf( |
231 | 249 | Py_GIL_DISABLED and refleak_helper.hunting_for_refleaks(), |
232 | 250 | "Specialization failure triggers gh-127773") |
233 | 251 | def test_freeze_meta(self): |
234 | 252 | """test PyType_Freeze() with overridden MRO""" |
235 | | - type_freeze = _testcapi.type_freeze |
| 253 | + type_freeze = _testlimitedcapi.type_freeze |
236 | 254 |
|
237 | 255 | class Base: |
238 | 256 | value = 1 |
@@ -274,3 +292,178 @@ def test_extension_managed_dict_type(self): |
274 | 292 | obj.__dict__ = {'bar': 3} |
275 | 293 | self.assertEqual(obj.__dict__, {'bar': 3}) |
276 | 294 | 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 |
0 commit comments