Skip to content

gh-93649: Add Modules/_testcapi/type.c file #129516

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 0 additions & 174 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,147 +1111,6 @@ class Data(_testcapi.ObjExtraData):
del d.extra
self.assertIsNone(d.extra)

def test_get_type_name(self):
class MyType:
pass

from _testcapi import (
get_type_name, get_type_qualname,
get_type_fullyqualname, get_type_module_name)

from collections import OrderedDict
ht = _testcapi.get_heaptype_for_name()
for cls, fullname, modname, qualname, name in (
(int,
'int',
'builtins',
'int',
'int'),
(OrderedDict,
'collections.OrderedDict',
'collections',
'OrderedDict',
'OrderedDict'),
(ht,
'_testcapi.HeapTypeNameType',
'_testcapi',
'HeapTypeNameType',
'HeapTypeNameType'),
(MyType,
f'{__name__}.CAPITest.test_get_type_name.<locals>.MyType',
__name__,
'CAPITest.test_get_type_name.<locals>.MyType',
'MyType'),
):
with self.subTest(cls=repr(cls)):
self.assertEqual(get_type_fullyqualname(cls), fullname)
self.assertEqual(get_type_module_name(cls), modname)
self.assertEqual(get_type_qualname(cls), qualname)
self.assertEqual(get_type_name(cls), name)

# override __module__
ht.__module__ = 'test_module'
self.assertEqual(get_type_fullyqualname(ht), 'test_module.HeapTypeNameType')
self.assertEqual(get_type_module_name(ht), 'test_module')
self.assertEqual(get_type_qualname(ht), 'HeapTypeNameType')
self.assertEqual(get_type_name(ht), 'HeapTypeNameType')

# override __name__ and __qualname__
MyType.__name__ = 'my_name'
MyType.__qualname__ = 'my_qualname'
self.assertEqual(get_type_fullyqualname(MyType), f'{__name__}.my_qualname')
self.assertEqual(get_type_module_name(MyType), __name__)
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
self.assertEqual(get_type_name(MyType), 'my_name')

# override also __module__
MyType.__module__ = 'my_module'
self.assertEqual(get_type_fullyqualname(MyType), 'my_module.my_qualname')
self.assertEqual(get_type_module_name(MyType), 'my_module')
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
self.assertEqual(get_type_name(MyType), 'my_name')

# PyType_GetFullyQualifiedName() ignores the module if it's "builtins"
# or "__main__" of it is not a string
MyType.__module__ = 'builtins'
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
MyType.__module__ = '__main__'
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
MyType.__module__ = 123
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')

def test_get_base_by_token(self):
def get_base_by_token(src, key, comparable=True):
def run(use_mro):
find_first = _testcapi.pytype_getbasebytoken
ret1, result = find_first(src, key, use_mro, True)
ret2, no_result = find_first(src, key, use_mro, False)
self.assertIn(ret1, (0, 1))
self.assertEqual(ret1, result is not None)
self.assertEqual(ret1, ret2)
self.assertIsNone(no_result)
return result

found_in_mro = run(True)
found_in_bases = run(False)
if comparable:
self.assertIs(found_in_mro, found_in_bases)
return found_in_mro
return found_in_mro, found_in_bases

create_type = _testcapi.create_type_with_token
get_token = _testcapi.get_tp_token

Py_TP_USE_SPEC = _testcapi.Py_TP_USE_SPEC
self.assertEqual(Py_TP_USE_SPEC, 0)

A1 = create_type('_testcapi.A1', Py_TP_USE_SPEC)
self.assertTrue(get_token(A1) != Py_TP_USE_SPEC)

B1 = create_type('_testcapi.B1', id(self))
self.assertTrue(get_token(B1) == id(self))

tokenA1 = get_token(A1)
# find A1 from A1
found = get_base_by_token(A1, tokenA1)
self.assertIs(found, A1)

# no token in static types
STATIC = type(1)
self.assertEqual(get_token(STATIC), 0)
found = get_base_by_token(STATIC, tokenA1)
self.assertIs(found, None)

# no token in pure subtypes
class A2(A1): pass
self.assertEqual(get_token(A2), 0)
# find A1
class Z(STATIC, B1, A2): pass
found = get_base_by_token(Z, tokenA1)
self.assertIs(found, A1)

# searching for NULL token is an error
with self.assertRaises(SystemError):
get_base_by_token(Z, 0)
with self.assertRaises(SystemError):
get_base_by_token(STATIC, 0)

# share the token with A1
C1 = create_type('_testcapi.C1', tokenA1)
self.assertTrue(get_token(C1) == tokenA1)

# find C1 first by shared token
class Z(C1, A2): pass
found = get_base_by_token(Z, tokenA1)
self.assertIs(found, C1)
# B1 not found
found = get_base_by_token(Z, get_token(B1))
self.assertIs(found, None)

with self.assertRaises(TypeError):
_testcapi.pytype_getbasebytoken(
'not a type', id(self), True, False)

def test_gen_get_code(self):
def genf(): yield
gen = genf()
Expand Down Expand Up @@ -2922,39 +2781,6 @@ def test_linked_lifecycle_link_incref_unlink_decref(self):
0, get_refcount(interpid))


class BuiltinStaticTypesTests(unittest.TestCase):

TYPES = [
object,
type,
int,
str,
dict,
type(None),
bool,
BaseException,
Exception,
Warning,
DeprecationWarning, # Warning subclass
]

def test_tp_bases_is_set(self):
# PyTypeObject.tp_bases is documented as public API.
# See https://github.com/python/cpython/issues/105020.
for typeobj in self.TYPES:
with self.subTest(typeobj):
bases = _testcapi.type_get_tp_bases(typeobj)
self.assertIsNot(bases, None)

def test_tp_mro_is_set(self):
# PyTypeObject.tp_bases is documented as public API.
# See https://github.com/python/cpython/issues/105020.
for typeobj in self.TYPES:
with self.subTest(typeobj):
mro = _testcapi.type_get_tp_mro(typeobj)
self.assertIsNot(mro, None)


class TestStaticTypes(unittest.TestCase):

_has_run = False
Expand Down
174 changes: 174 additions & 0 deletions Lib/test/test_capi/test_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,181 @@
_testcapi = import_helper.import_module('_testcapi')


class BuiltinStaticTypesTests(unittest.TestCase):

TYPES = [
object,
type,
int,
str,
dict,
type(None),
bool,
BaseException,
Exception,
Warning,
DeprecationWarning, # Warning subclass
]

def test_tp_bases_is_set(self):
# PyTypeObject.tp_bases is documented as public API.
# See https://github.com/python/cpython/issues/105020.
for typeobj in self.TYPES:
with self.subTest(typeobj):
bases = _testcapi.type_get_tp_bases(typeobj)
self.assertIsNot(bases, None)

def test_tp_mro_is_set(self):
# PyTypeObject.tp_bases is documented as public API.
# See https://github.com/python/cpython/issues/105020.
for typeobj in self.TYPES:
with self.subTest(typeobj):
mro = _testcapi.type_get_tp_mro(typeobj)
self.assertIsNot(mro, None)


class TypeTests(unittest.TestCase):
def test_get_type_name(self):
class MyType:
pass

from _testcapi import (
get_type_name, get_type_qualname,
get_type_fullyqualname, get_type_module_name)

from collections import OrderedDict
ht = _testcapi.get_heaptype_for_name()
for cls, fullname, modname, qualname, name in (
(int,
'int',
'builtins',
'int',
'int'),
(OrderedDict,
'collections.OrderedDict',
'collections',
'OrderedDict',
'OrderedDict'),
(ht,
'_testcapi.HeapTypeNameType',
'_testcapi',
'HeapTypeNameType',
'HeapTypeNameType'),
(MyType,
f'{__name__}.TypeTests.test_get_type_name.<locals>.MyType',
__name__,
'TypeTests.test_get_type_name.<locals>.MyType',
'MyType'),
):
with self.subTest(cls=repr(cls)):
self.assertEqual(get_type_fullyqualname(cls), fullname)
self.assertEqual(get_type_module_name(cls), modname)
self.assertEqual(get_type_qualname(cls), qualname)
self.assertEqual(get_type_name(cls), name)

# override __module__
ht.__module__ = 'test_module'
self.assertEqual(get_type_fullyqualname(ht), 'test_module.HeapTypeNameType')
self.assertEqual(get_type_module_name(ht), 'test_module')
self.assertEqual(get_type_qualname(ht), 'HeapTypeNameType')
self.assertEqual(get_type_name(ht), 'HeapTypeNameType')

# override __name__ and __qualname__
MyType.__name__ = 'my_name'
MyType.__qualname__ = 'my_qualname'
self.assertEqual(get_type_fullyqualname(MyType), f'{__name__}.my_qualname')
self.assertEqual(get_type_module_name(MyType), __name__)
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
self.assertEqual(get_type_name(MyType), 'my_name')

# override also __module__
MyType.__module__ = 'my_module'
self.assertEqual(get_type_fullyqualname(MyType), 'my_module.my_qualname')
self.assertEqual(get_type_module_name(MyType), 'my_module')
self.assertEqual(get_type_qualname(MyType), 'my_qualname')
self.assertEqual(get_type_name(MyType), 'my_name')

# PyType_GetFullyQualifiedName() ignores the module if it's "builtins"
# or "__main__" of it is not a string
MyType.__module__ = 'builtins'
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
MyType.__module__ = '__main__'
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')
MyType.__module__ = 123
self.assertEqual(get_type_fullyqualname(MyType), 'my_qualname')

def test_get_base_by_token(self):
def get_base_by_token(src, key, comparable=True):
def run(use_mro):
find_first = _testcapi.pytype_getbasebytoken
ret1, result = find_first(src, key, use_mro, True)
ret2, no_result = find_first(src, key, use_mro, False)
self.assertIn(ret1, (0, 1))
self.assertEqual(ret1, result is not None)
self.assertEqual(ret1, ret2)
self.assertIsNone(no_result)
return result

found_in_mro = run(True)
found_in_bases = run(False)
if comparable:
self.assertIs(found_in_mro, found_in_bases)
return found_in_mro
return found_in_mro, found_in_bases

create_type = _testcapi.create_type_with_token
get_token = _testcapi.get_tp_token

Py_TP_USE_SPEC = _testcapi.Py_TP_USE_SPEC
self.assertEqual(Py_TP_USE_SPEC, 0)

A1 = create_type('_testcapi.A1', Py_TP_USE_SPEC)
self.assertTrue(get_token(A1) != Py_TP_USE_SPEC)

B1 = create_type('_testcapi.B1', id(self))
self.assertTrue(get_token(B1) == id(self))

tokenA1 = get_token(A1)
# find A1 from A1
found = get_base_by_token(A1, tokenA1)
self.assertIs(found, A1)

# no token in static types
STATIC = type(1)
self.assertEqual(get_token(STATIC), 0)
found = get_base_by_token(STATIC, tokenA1)
self.assertIs(found, None)

# no token in pure subtypes
class A2(A1): pass
self.assertEqual(get_token(A2), 0)
# find A1
class Z(STATIC, B1, A2): pass
found = get_base_by_token(Z, tokenA1)
self.assertIs(found, A1)

# searching for NULL token is an error
with self.assertRaises(SystemError):
get_base_by_token(Z, 0)
with self.assertRaises(SystemError):
get_base_by_token(STATIC, 0)

# share the token with A1
C1 = create_type('_testcapi.C1', tokenA1)
self.assertTrue(get_token(C1) == tokenA1)

# find C1 first by shared token
class Z(C1, A2): pass
found = get_base_by_token(Z, tokenA1)
self.assertIs(found, C1)
# B1 not found
found = get_base_by_token(Z, get_token(B1))
self.assertIs(found, None)

with self.assertRaises(TypeError):
_testcapi.pytype_getbasebytoken(
'not a type', id(self), True, False)

def test_freeze(self):
# test PyType_Freeze()
type_freeze = _testcapi.type_freeze
Expand Down
2 changes: 1 addition & 1 deletion Modules/Setup.stdlib.in
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
@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
@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
@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
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
Expand Down
1 change: 1 addition & 0 deletions Modules/_testcapi/parts.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,6 @@ int _PyTestCapi_Init_Object(PyObject *module);
int _PyTestCapi_Init_Config(PyObject *mod);
int _PyTestCapi_Init_Import(PyObject *mod);
int _PyTestCapi_Init_Frame(PyObject *mod);
int _PyTestCapi_Init_Type(PyObject *mod);

#endif // Py_TESTCAPI_PARTS_H
Loading
Loading