|
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): |
@@ -215,7 +230,7 @@ class H2(int): pass |
215 | 230 |
|
216 | 231 | def test_freeze(self): |
217 | 232 | # test PyType_Freeze() |
218 | | - type_freeze = _testcapi.type_freeze |
| 233 | + type_freeze = _testlimitedcapi.type_freeze |
219 | 234 |
|
220 | 235 | # simple case, no inherante |
221 | 236 | class MyType: |
@@ -245,12 +260,15 @@ class D(A, C): pass |
245 | 260 | # as well |
246 | 261 | type_freeze(D) |
247 | 262 |
|
| 263 | + # CRASHES type_freeze(NULL) |
| 264 | + # CRASHES type_freeze(object()): argument must be a type |
| 265 | + |
248 | 266 | @unittest.skipIf( |
249 | 267 | Py_GIL_DISABLED and refleak_helper.hunting_for_refleaks(), |
250 | 268 | "Specialization failure triggers gh-127773") |
251 | 269 | def test_freeze_meta(self): |
252 | 270 | """test PyType_Freeze() with overridden MRO""" |
253 | | - type_freeze = _testcapi.type_freeze |
| 271 | + type_freeze = _testlimitedcapi.type_freeze |
254 | 272 |
|
255 | 273 | class Base: |
256 | 274 | value = 1 |
@@ -299,3 +317,181 @@ def test_extension_managed_weakref_nogc_type(self): |
299 | 317 | "flag but not Py_TPFLAGS_HAVE_GC flag") |
300 | 318 | with self.assertRaisesRegex(SystemError, msg): |
301 | 319 | _testcapi.create_managed_weakref_nogc_type() |
| 320 | + |
| 321 | + def test_type_ready(self): |
| 322 | + # Test PyType_Ready(): calling it on initialized types |
| 323 | + # must not raise an exception. |
| 324 | + type_ready = _testlimitedcapi.type_ready |
| 325 | + |
| 326 | + class HeapType: |
| 327 | + pass |
| 328 | + |
| 329 | + type_ready(int) |
| 330 | + type_ready(dict) |
| 331 | + type_ready(HeapType) |
| 332 | + |
| 333 | + # CRASHES type_ready(NULL) |
| 334 | + # CRASHES type_ready(123): argument must be a type |
| 335 | + |
| 336 | + def test_type_clearcache(self): |
| 337 | + # Test PyType_ClearCache() |
| 338 | + type_clearcache = _testlimitedcapi.type_clearcache |
| 339 | + version_tag = type_clearcache() |
| 340 | + self.assertEqual(type(version_tag), int) |
| 341 | + self.assertGreaterEqual(version_tag, 0) |
| 342 | + |
| 343 | + def test_type_getflags(self): |
| 344 | + # Test PyType_GetFlags() |
| 345 | + type_getflags = _testlimitedcapi.type_getflags |
| 346 | + |
| 347 | + from _testlimitedcapi import ( |
| 348 | + Py_TPFLAGS_HEAPTYPE, |
| 349 | + Py_TPFLAGS_HAVE_GC, |
| 350 | + Py_TPFLAGS_HAVE_FINALIZE, |
| 351 | + Py_TPFLAGS_HAVE_VERSION_TAG, |
| 352 | + Py_TPFLAGS_VALID_VERSION_TAG, |
| 353 | + Py_TPFLAGS_HAVE_VECTORCALL, |
| 354 | + Py_TPFLAGS_DISALLOW_INSTANTIATION, |
| 355 | + Py_TPFLAGS_IMMUTABLETYPE, |
| 356 | + Py_TPFLAGS_READY, |
| 357 | + Py_TPFLAGS_READYING, |
| 358 | + Py_TPFLAGS_LONG_SUBCLASS, |
| 359 | + Py_TPFLAGS_LIST_SUBCLASS, |
| 360 | + Py_TPFLAGS_TUPLE_SUBCLASS, |
| 361 | + Py_TPFLAGS_BYTES_SUBCLASS, |
| 362 | + Py_TPFLAGS_UNICODE_SUBCLASS, |
| 363 | + Py_TPFLAGS_DICT_SUBCLASS, |
| 364 | + Py_TPFLAGS_BASE_EXC_SUBCLASS, |
| 365 | + Py_TPFLAGS_TYPE_SUBCLASS, |
| 366 | + Py_TPFLAGS_IS_ABSTRACT, |
| 367 | + Py_TPFLAGS_BASETYPE, |
| 368 | + _Py_TPFLAGS_MATCH_SELF, |
| 369 | + Py_TPFLAGS_ITEMS_AT_END, |
| 370 | + Py_TPFLAGS_METHOD_DESCRIPTOR, |
| 371 | + ) |
| 372 | + from _testcapi import ( |
| 373 | + _Py_TPFLAGS_STATIC_BUILTIN, |
| 374 | + Py_TPFLAGS_SEQUENCE, |
| 375 | + Py_TPFLAGS_MAPPING, |
| 376 | + Py_TPFLAGS_INLINE_VALUES, |
| 377 | + Py_TPFLAGS_MANAGED_WEAKREF, |
| 378 | + Py_TPFLAGS_MANAGED_DICT, |
| 379 | + ) |
| 380 | + |
| 381 | + def check_flag(flags, flag, expected): |
| 382 | + self.assertEqual(bool(flags & flag), expected) |
| 383 | + |
| 384 | + def check_subclasses(test_type, flags): |
| 385 | + for flag, base_type in ( |
| 386 | + (Py_TPFLAGS_LONG_SUBCLASS, int), |
| 387 | + (Py_TPFLAGS_LIST_SUBCLASS, list), |
| 388 | + (Py_TPFLAGS_TUPLE_SUBCLASS, tuple), |
| 389 | + (Py_TPFLAGS_BYTES_SUBCLASS, bytes), |
| 390 | + (Py_TPFLAGS_UNICODE_SUBCLASS, str), |
| 391 | + (Py_TPFLAGS_DICT_SUBCLASS, dict), |
| 392 | + (Py_TPFLAGS_BASE_EXC_SUBCLASS, BaseException), |
| 393 | + (Py_TPFLAGS_TYPE_SUBCLASS, type), |
| 394 | + ): |
| 395 | + with self.subTest(test_type=test_type, flag=flag, base_type=base_type): |
| 396 | + check_flag(flags, flag, issubclass(test_type, base_type)) |
| 397 | + |
| 398 | + def check_type(test_type, static_type, have_gc=False, have_vectorcall=False, |
| 399 | + is_base_type=True, sequence=False, mapping=False, |
| 400 | + match_self=True, items_at_end=False): |
| 401 | + heap_type = not static_type |
| 402 | + |
| 403 | + flags = type_getflags(test_type) |
| 404 | + check_flag(flags, _Py_TPFLAGS_STATIC_BUILTIN, static_type) |
| 405 | + check_flag(flags, Py_TPFLAGS_HEAPTYPE, heap_type) |
| 406 | + check_flag(flags, Py_TPFLAGS_HAVE_GC, have_gc) |
| 407 | + check_subclasses(test_type, flags) |
| 408 | + check_flag(flags, Py_TPFLAGS_HAVE_VECTORCALL, have_vectorcall) |
| 409 | + check_flag(flags, Py_TPFLAGS_DISALLOW_INSTANTIATION, False) |
| 410 | + check_flag(flags, Py_TPFLAGS_IMMUTABLETYPE, static_type) |
| 411 | + check_flag(flags, Py_TPFLAGS_READY, True) |
| 412 | + check_flag(flags, Py_TPFLAGS_READYING, False) |
| 413 | + check_flag(flags, Py_TPFLAGS_IS_ABSTRACT, False) |
| 414 | + check_flag(flags, Py_TPFLAGS_BASETYPE, is_base_type) |
| 415 | + check_flag(flags, Py_TPFLAGS_SEQUENCE, sequence) |
| 416 | + check_flag(flags, Py_TPFLAGS_MAPPING, mapping) |
| 417 | + |
| 418 | + check_flag(flags, Py_TPFLAGS_INLINE_VALUES, heap_type) |
| 419 | + check_flag(flags, Py_TPFLAGS_MANAGED_WEAKREF, heap_type) |
| 420 | + check_flag(flags, Py_TPFLAGS_MANAGED_DICT, heap_type) |
| 421 | + check_flag(flags, Py_TPFLAGS_ITEMS_AT_END, items_at_end) |
| 422 | + check_flag(flags, Py_TPFLAGS_METHOD_DESCRIPTOR, False) |
| 423 | + |
| 424 | + check_flag(flags, _Py_TPFLAGS_MATCH_SELF, match_self) |
| 425 | + |
| 426 | + # Flags kept for backward compatibility |
| 427 | + check_flag(flags, Py_TPFLAGS_HAVE_FINALIZE, False) |
| 428 | + check_flag(flags, Py_TPFLAGS_HAVE_VERSION_TAG, False) |
| 429 | + check_flag(flags, Py_TPFLAGS_VALID_VERSION_TAG, False) |
| 430 | + |
| 431 | + # Scalar types |
| 432 | + check_type(int, static_type=True) |
| 433 | + check_type(bool, static_type=True, |
| 434 | + is_base_type=False) |
| 435 | + check_type(float, static_type=True) |
| 436 | + check_type(complex, static_type=True, |
| 437 | + match_self=False) |
| 438 | + check_type(bytes, static_type=True) |
| 439 | + check_type(bytearray, static_type=True) |
| 440 | + check_type(str, static_type=True) |
| 441 | + |
| 442 | + # Collection types |
| 443 | + check_type(tuple, static_type=True, have_gc=True, |
| 444 | + sequence=True) |
| 445 | + check_type(list, static_type=True, have_gc=True, |
| 446 | + sequence=True) |
| 447 | + check_type(dict, static_type=True, have_gc=True, |
| 448 | + mapping=True) |
| 449 | + check_type(frozendict, static_type=True, have_gc=True, |
| 450 | + mapping=True) |
| 451 | + check_type(set, static_type=True, have_gc=True) |
| 452 | + check_type(frozenset, static_type=True, have_gc=True) |
| 453 | + |
| 454 | + # Other types |
| 455 | + check_type(BaseException, static_type=True, have_gc=True, |
| 456 | + match_self=False) |
| 457 | + check_type(type, static_type=True, have_gc=True, |
| 458 | + have_vectorcall=True, |
| 459 | + match_self=False, |
| 460 | + items_at_end=True) |
| 461 | + |
| 462 | + # Heap type |
| 463 | + class HeapType: |
| 464 | + pass |
| 465 | + check_type(HeapType, static_type=False, have_gc=True, match_self=False) |
| 466 | + |
| 467 | + # CRASHES type_getflags(NULL) |
| 468 | + |
| 469 | + def test_type_issubtype(self): |
| 470 | + # Test PyType_IsSubtype() |
| 471 | + _type_issubtype = _testlimitedcapi.type_issubtype |
| 472 | + |
| 473 | + def type_issubtype(type1, type2): |
| 474 | + res = _type_issubtype(type1, type2) |
| 475 | + self.assertIn(res, (0, 1)) |
| 476 | + return bool(res) |
| 477 | + |
| 478 | + class MyList(list): |
| 479 | + pass |
| 480 | + |
| 481 | + self.assertTrue(type_issubtype(bool, int)) |
| 482 | + self.assertTrue(type_issubtype(MyList, list)) |
| 483 | + |
| 484 | + self.assertFalse(type_issubtype(int, type)) |
| 485 | + self.assertFalse(type_issubtype(frozendict, dict)) |
| 486 | + self.assertFalse(type_issubtype(MyList, tuple)) |
| 487 | + |
| 488 | + def test_type_modified(self): |
| 489 | + # Test PyType_Modified() |
| 490 | + type_modified = _testlimitedcapi.type_modified |
| 491 | + |
| 492 | + class MyType: |
| 493 | + pass |
| 494 | + type_modified(MyType) |
| 495 | + |
| 496 | + # CRASHES type_modified(NULL) |
| 497 | + # CRASHES type_modified({}): argument must be a type |
0 commit comments