]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-93649: Add Modules/_testcapi/type.c file (#129516)
authorVictor Stinner <vstinner@python.org>
Fri, 31 Jan 2025 14:03:54 +0000 (15:03 +0100)
committerGitHub <noreply@github.com>
Fri, 31 Jan 2025 14:03:54 +0000 (15:03 +0100)
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()

Lib/test/test_capi/test_misc.py
Lib/test/test_capi/test_type.py
Modules/Setup.stdlib.in
Modules/_testcapi/parts.h
Modules/_testcapi/type.c [new file with mode: 0644]
Modules/_testcapimodule.c
PCbuild/_testcapi.vcxproj
PCbuild/_testcapi.vcxproj.filters

index 1087b38c225085ea9b3bbe6ab3d7c2d191265b25..efa01a8416700218c96530c3e59db4663f7b3dc1 100644 (file)
@@ -1111,147 +1111,6 @@ class CAPITest(unittest.TestCase):
         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()
@@ -2922,39 +2781,6 @@ class InterpreterIDTests(unittest.TestCase):
             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
index ffcaae73bca2364e25478da43cacc73b326b1f95..7e5d013d737ab0cbfd23e80953883bf9778b33b0 100644 (file)
@@ -4,7 +4,181 @@ import unittest
 _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
index 1fd7c8bd2e2d734176f653928333773b3e4e5d3b..a276fa642de42e19cc840fa89cc4058d300ca5bf 100644 (file)
 @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
index e5d2502edf536297c56cdea6f5bc283ad210865b..c4b73459b3712b96e9fe99092105a2480e768b14 100644 (file)
@@ -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
diff --git a/Modules/_testcapi/type.c b/Modules/_testcapi/type.c
new file mode 100644 (file)
index 0000000..9bef58d
--- /dev/null
@@ -0,0 +1,251 @@
+#include "parts.h"
+#include "util.h"
+
+
+static PyType_Slot HeapTypeNameType_slots[] = {
+    {0},
+};
+
+static PyType_Spec HeapTypeNameType_Spec = {
+    .name = "_testcapi.HeapTypeNameType",
+    .basicsize = sizeof(PyObject),
+    .flags = Py_TPFLAGS_DEFAULT,
+    .slots = HeapTypeNameType_slots,
+};
+
+static PyObject *
+get_heaptype_for_name(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    return PyType_FromSpec(&HeapTypeNameType_Spec);
+}
+
+
+static PyObject *
+get_type_name(PyObject *self, PyObject *type)
+{
+    assert(PyType_Check(type));
+    return PyType_GetName((PyTypeObject *)type);
+}
+
+
+static PyObject *
+get_type_qualname(PyObject *self, PyObject *type)
+{
+    assert(PyType_Check(type));
+    return PyType_GetQualName((PyTypeObject *)type);
+}
+
+
+static PyObject *
+get_type_fullyqualname(PyObject *self, PyObject *type)
+{
+    assert(PyType_Check(type));
+    return PyType_GetFullyQualifiedName((PyTypeObject *)type);
+}
+
+
+static PyObject *
+get_type_module_name(PyObject *self, PyObject *type)
+{
+    assert(PyType_Check(type));
+    return PyType_GetModuleName((PyTypeObject *)type);
+}
+
+
+static PyObject *
+test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    /* Test for PyType_GetDict */
+
+    // Assert ints have a `to_bytes` method
+    PyObject *long_dict = PyType_GetDict(&PyLong_Type);
+    assert(long_dict);
+    assert(PyDict_GetItemString(long_dict, "to_bytes")); // borrowed ref
+    Py_DECREF(long_dict);
+
+    // Make a new type, add an attribute to it and assert it's there
+    PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec);
+    assert(HeapTypeNameType);
+    assert(PyObject_SetAttrString(
+        HeapTypeNameType, "new_attr", Py_NewRef(Py_None)) >= 0);
+    PyObject *type_dict = PyType_GetDict((PyTypeObject*)HeapTypeNameType);
+    assert(type_dict);
+    assert(PyDict_GetItemString(type_dict, "new_attr")); // borrowed ref
+    Py_DECREF(HeapTypeNameType);
+    Py_DECREF(type_dict);
+    Py_RETURN_NONE;
+}
+
+
+static PyObject *
+test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    newfunc tp_new = PyType_GetSlot(&PyLong_Type, Py_tp_new);
+    if (PyLong_Type.tp_new != tp_new) {
+        PyErr_SetString(PyExc_AssertionError, "mismatch: tp_new of long");
+        return NULL;
+    }
+
+    reprfunc tp_repr = PyType_GetSlot(&PyLong_Type, Py_tp_repr);
+    if (PyLong_Type.tp_repr != tp_repr) {
+        PyErr_SetString(PyExc_AssertionError, "mismatch: tp_repr of long");
+        return NULL;
+    }
+
+    ternaryfunc tp_call = PyType_GetSlot(&PyLong_Type, Py_tp_call);
+    if (tp_call != NULL) {
+        PyErr_SetString(PyExc_AssertionError, "mismatch: tp_call of long");
+        return NULL;
+    }
+
+    binaryfunc nb_add = PyType_GetSlot(&PyLong_Type, Py_nb_add);
+    if (PyLong_Type.tp_as_number->nb_add != nb_add) {
+        PyErr_SetString(PyExc_AssertionError, "mismatch: nb_add of long");
+        return NULL;
+    }
+
+    lenfunc mp_length = PyType_GetSlot(&PyLong_Type, Py_mp_length);
+    if (mp_length != NULL) {
+        PyErr_SetString(PyExc_AssertionError, "mismatch: mp_length of long");
+        return NULL;
+    }
+
+    void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1);
+    if (over_value != NULL) {
+        PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long");
+        return NULL;
+    }
+
+    tp_new = PyType_GetSlot(&PyLong_Type, 0);
+    if (tp_new != NULL) {
+        PyErr_SetString(PyExc_AssertionError, "mismatch: slot 0 of long");
+        return NULL;
+    }
+    if (PyErr_ExceptionMatches(PyExc_SystemError)) {
+        // This is the right exception
+        PyErr_Clear();
+    }
+    else {
+        return NULL;
+    }
+
+    Py_RETURN_NONE;
+}
+
+
+// Get type->tp_version_tag
+static PyObject *
+type_get_version(PyObject *self, PyObject *type)
+{
+    if (!PyType_Check(type)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a type");
+        return NULL;
+    }
+    PyObject *res = PyLong_FromUnsignedLong(
+        ((PyTypeObject *)type)->tp_version_tag);
+    if (res == NULL) {
+        assert(PyErr_Occurred());
+        return NULL;
+    }
+    return res;
+}
+
+static PyObject *
+type_modified(PyObject *self, PyObject *arg)
+{
+    if (!PyType_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a type");
+        return NULL;
+    }
+    PyTypeObject *type = (PyTypeObject*)arg;
+
+    PyType_Modified(type);
+    Py_RETURN_NONE;
+}
+
+
+static PyObject *
+type_assign_version(PyObject *self, PyObject *arg)
+{
+    if (!PyType_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a type");
+        return NULL;
+    }
+    PyTypeObject *type = (PyTypeObject*)arg;
+
+    int res = PyUnstable_Type_AssignVersionTag(type);
+    return PyLong_FromLong(res);
+}
+
+
+static PyObject *
+type_get_tp_bases(PyObject *self, PyObject *arg)
+{
+    if (!PyType_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a type");
+        return NULL;
+    }
+    PyTypeObject *type = (PyTypeObject*)arg;
+
+    PyObject *bases = type->tp_bases;
+    if (bases == NULL) {
+        Py_RETURN_NONE;
+    }
+    return Py_NewRef(bases);
+}
+
+static PyObject *
+type_get_tp_mro(PyObject *self, PyObject *arg)
+{
+    if (!PyType_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a type");
+        return NULL;
+    }
+    PyTypeObject *type = (PyTypeObject*)arg;
+
+    PyObject *mro = ((PyTypeObject *)type)->tp_mro;
+    if (mro == NULL) {
+        Py_RETURN_NONE;
+    }
+    return Py_NewRef(mro);
+}
+
+
+static PyObject *
+type_freeze(PyObject *module, PyObject *arg)
+{
+    if (!PyType_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a type");
+        return NULL;
+    }
+    PyTypeObject *type = (PyTypeObject*)arg;
+
+    if (PyType_Freeze(type) < 0) {
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+
+
+static PyMethodDef test_methods[] = {
+    {"get_heaptype_for_name", get_heaptype_for_name, METH_NOARGS},
+    {"get_type_name", get_type_name, METH_O},
+    {"get_type_qualname",  get_type_qualname, METH_O},
+    {"get_type_fullyqualname", get_type_fullyqualname, METH_O},
+    {"get_type_module_name", get_type_module_name, METH_O},
+    {"test_get_type_dict", test_get_type_dict, METH_NOARGS},
+    {"test_get_statictype_slots", test_get_statictype_slots,     METH_NOARGS},
+    {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")},
+    {"type_modified", type_modified, METH_O, PyDoc_STR("PyType_Modified")},
+    {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_Type_AssignVersionTag")},
+    {"type_get_tp_bases", type_get_tp_bases, METH_O},
+    {"type_get_tp_mro", type_get_tp_mro, METH_O},
+    {"type_freeze", type_freeze, METH_O},
+    {NULL},
+};
+
+int
+_PyTestCapi_Init_Type(PyObject *m)
+{
+    return PyModule_AddFunctions(m, test_methods);
+}
index d25e61dbc3d588239136742293f47ed3328c1bae..ad9d836120b506dbcf5caba44c7f4295c013ce2c 100644 (file)
@@ -530,136 +530,6 @@ test_buildvalue_N(PyObject *self, PyObject *Py_UNUSED(ignored))
 }
 
 
-static PyObject *
-test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored))
-{
-    newfunc tp_new = PyType_GetSlot(&PyLong_Type, Py_tp_new);
-    if (PyLong_Type.tp_new != tp_new) {
-        PyErr_SetString(PyExc_AssertionError, "mismatch: tp_new of long");
-        return NULL;
-    }
-
-    reprfunc tp_repr = PyType_GetSlot(&PyLong_Type, Py_tp_repr);
-    if (PyLong_Type.tp_repr != tp_repr) {
-        PyErr_SetString(PyExc_AssertionError, "mismatch: tp_repr of long");
-        return NULL;
-    }
-
-    ternaryfunc tp_call = PyType_GetSlot(&PyLong_Type, Py_tp_call);
-    if (tp_call != NULL) {
-        PyErr_SetString(PyExc_AssertionError, "mismatch: tp_call of long");
-        return NULL;
-    }
-
-    binaryfunc nb_add = PyType_GetSlot(&PyLong_Type, Py_nb_add);
-    if (PyLong_Type.tp_as_number->nb_add != nb_add) {
-        PyErr_SetString(PyExc_AssertionError, "mismatch: nb_add of long");
-        return NULL;
-    }
-
-    lenfunc mp_length = PyType_GetSlot(&PyLong_Type, Py_mp_length);
-    if (mp_length != NULL) {
-        PyErr_SetString(PyExc_AssertionError, "mismatch: mp_length of long");
-        return NULL;
-    }
-
-    void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1);
-    if (over_value != NULL) {
-        PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long");
-        return NULL;
-    }
-
-    tp_new = PyType_GetSlot(&PyLong_Type, 0);
-    if (tp_new != NULL) {
-        PyErr_SetString(PyExc_AssertionError, "mismatch: slot 0 of long");
-        return NULL;
-    }
-    if (PyErr_ExceptionMatches(PyExc_SystemError)) {
-        // This is the right exception
-        PyErr_Clear();
-    }
-    else {
-        return NULL;
-    }
-
-    Py_RETURN_NONE;
-}
-
-
-static PyType_Slot HeapTypeNameType_slots[] = {
-    {0},
-};
-
-static PyType_Spec HeapTypeNameType_Spec = {
-    .name = "_testcapi.HeapTypeNameType",
-    .basicsize = sizeof(PyObject),
-    .flags = Py_TPFLAGS_DEFAULT,
-    .slots = HeapTypeNameType_slots,
-};
-
-static PyObject *
-get_heaptype_for_name(PyObject *self, PyObject *Py_UNUSED(ignored))
-{
-    return PyType_FromSpec(&HeapTypeNameType_Spec);
-}
-
-
-static PyObject *
-get_type_name(PyObject *self, PyObject *type)
-{
-    assert(PyType_Check(type));
-    return PyType_GetName((PyTypeObject *)type);
-}
-
-
-static PyObject *
-get_type_qualname(PyObject *self, PyObject *type)
-{
-    assert(PyType_Check(type));
-    return PyType_GetQualName((PyTypeObject *)type);
-}
-
-
-static PyObject *
-get_type_fullyqualname(PyObject *self, PyObject *type)
-{
-    assert(PyType_Check(type));
-    return PyType_GetFullyQualifiedName((PyTypeObject *)type);
-}
-
-
-static PyObject *
-get_type_module_name(PyObject *self, PyObject *type)
-{
-    assert(PyType_Check(type));
-    return PyType_GetModuleName((PyTypeObject *)type);
-}
-
-
-static PyObject *
-test_get_type_dict(PyObject *self, PyObject *Py_UNUSED(ignored))
-{
-    /* Test for PyType_GetDict */
-
-    // Assert ints have a `to_bytes` method
-    PyObject *long_dict = PyType_GetDict(&PyLong_Type);
-    assert(long_dict);
-    assert(PyDict_GetItemString(long_dict, "to_bytes")); // borrowed ref
-    Py_DECREF(long_dict);
-
-    // Make a new type, add an attribute to it and assert it's there
-    PyObject *HeapTypeNameType = PyType_FromSpec(&HeapTypeNameType_Spec);
-    assert(HeapTypeNameType);
-    assert(PyObject_SetAttrString(
-        HeapTypeNameType, "new_attr", Py_NewRef(Py_None)) >= 0);
-    PyObject *type_dict = PyType_GetDict((PyTypeObject*)HeapTypeNameType);
-    assert(type_dict);
-    assert(PyDict_GetItemString(type_dict, "new_attr")); // borrowed ref
-    Py_DECREF(HeapTypeNameType);
-    Py_DECREF(type_dict);
-    Py_RETURN_NONE;
-}
-
 static PyObject *
 pyobject_repr_from_null(PyObject *self, PyObject *Py_UNUSED(ignored))
 {
@@ -2380,68 +2250,6 @@ test_py_is_funcs(PyObject *self, PyObject *Py_UNUSED(ignored))
 }
 
 
-// type->tp_version_tag
-static PyObject *
-type_get_version(PyObject *self, PyObject *type)
-{
-    if (!PyType_Check(type)) {
-        PyErr_SetString(PyExc_TypeError, "argument must be a type");
-        return NULL;
-    }
-    PyObject *res = PyLong_FromUnsignedLong(
-        ((PyTypeObject *)type)->tp_version_tag);
-    if (res == NULL) {
-        assert(PyErr_Occurred());
-        return NULL;
-    }
-    return res;
-}
-
-static PyObject *
-type_modified(PyObject *self, PyObject *type)
-{
-    if (!PyType_Check(type)) {
-        PyErr_SetString(PyExc_TypeError, "argument must be a type");
-        return NULL;
-    }
-    PyType_Modified((PyTypeObject *)type);
-    Py_RETURN_NONE;
-}
-
-
-static PyObject *
-type_assign_version(PyObject *self, PyObject *type)
-{
-    if (!PyType_Check(type)) {
-        PyErr_SetString(PyExc_TypeError, "argument must be a type");
-        return NULL;
-    }
-    int res = PyUnstable_Type_AssignVersionTag((PyTypeObject *)type);
-    return PyLong_FromLong(res);
-}
-
-
-static PyObject *
-type_get_tp_bases(PyObject *self, PyObject *type)
-{
-    PyObject *bases = ((PyTypeObject *)type)->tp_bases;
-    if (bases == NULL) {
-        Py_RETURN_NONE;
-    }
-    return Py_NewRef(bases);
-}
-
-static PyObject *
-type_get_tp_mro(PyObject *self, PyObject *type)
-{
-    PyObject *mro = ((PyTypeObject *)type)->tp_mro;
-    if (mro == NULL) {
-        Py_RETURN_NONE;
-    }
-    return Py_NewRef(mro);
-}
-
-
 /* We only use 2 in test_capi/test_misc.py. */
 #define NUM_BASIC_STATIC_TYPES 2
 static PyTypeObject BasicStaticTypes[NUM_BASIC_STATIC_TYPES] = {
@@ -3205,19 +3013,6 @@ finalize_thread_hang(PyObject *self, PyObject *callback)
 }
 
 
-static PyObject *
-type_freeze(PyObject *module, PyObject *args)
-{
-    PyTypeObject *type;
-    if (!PyArg_ParseTuple(args, "O!", &PyType_Type, &type)) {
-        return NULL;
-    }
-    if (PyType_Freeze(type) < 0) {
-        return NULL;
-    }
-    Py_RETURN_NONE;
-}
-
 struct atexit_data {
     int called;
     PyThreadState *tstate;
@@ -3415,13 +3210,6 @@ static PyMethodDef TestMethods[] = {
     {"py_buildvalue",            py_buildvalue,                  METH_VARARGS},
     {"py_buildvalue_ints",       py_buildvalue_ints,             METH_VARARGS},
     {"test_buildvalue_N",        test_buildvalue_N,              METH_NOARGS},
-    {"test_get_statictype_slots", test_get_statictype_slots,     METH_NOARGS},
-    {"get_heaptype_for_name",     get_heaptype_for_name,         METH_NOARGS},
-    {"get_type_name",            get_type_name,                  METH_O},
-    {"get_type_qualname",        get_type_qualname,              METH_O},
-    {"get_type_fullyqualname",   get_type_fullyqualname,         METH_O},
-    {"get_type_module_name",     get_type_module_name,           METH_O},
-    {"test_get_type_dict",        test_get_type_dict,            METH_NOARGS},
     {"test_reftracer",          test_reftracer,                  METH_NOARGS},
     {"_test_thread_state",      test_thread_state,               METH_VARARGS},
     {"gilstate_ensure_release", gilstate_ensure_release,         METH_NOARGS},
@@ -3489,11 +3277,6 @@ static PyMethodDef TestMethods[] = {
     {"test_refcount_funcs", test_refcount_funcs, METH_NOARGS},
     {"test_py_is_macros", test_py_is_macros, METH_NOARGS},
     {"test_py_is_funcs", test_py_is_funcs, METH_NOARGS},
-    {"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")},
-    {"type_modified", type_modified, METH_O, PyDoc_STR("PyType_Modified")},
-    {"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_Type_AssignVersionTag")},
-    {"type_get_tp_bases", type_get_tp_bases, METH_O},
-    {"type_get_tp_mro", type_get_tp_mro, METH_O},
     {"get_basic_static_type", get_basic_static_type, METH_VARARGS, NULL},
     {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
     {"gen_get_code", gen_get_code, METH_O, NULL},
@@ -3516,7 +3299,6 @@ static PyMethodDef TestMethods[] = {
     {"function_set_warning", function_set_warning, METH_NOARGS},
     {"test_critical_sections", test_critical_sections, METH_NOARGS},
     {"finalize_thread_hang", finalize_thread_hang, METH_O, NULL},
-    {"type_freeze", type_freeze, METH_VARARGS},
     {"test_atexit", test_atexit, METH_NOARGS},
     {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
     {"tracemalloc_track_race", tracemalloc_track_race, METH_NOARGS},
@@ -4296,6 +4078,9 @@ PyInit__testcapi(void)
     if (_PyTestCapi_Init_Frame(m) < 0) {
         return NULL;
     }
+    if (_PyTestCapi_Init_Type(m) < 0) {
+        return NULL;
+    }
 
     PyState_AddModule(m, &_testcapimodule);
     return m;
index e94b4c46e6dbb829f18d11b4493bb8f94cd519a6..09969331c6edd4a39f0d336ea6605a190f8ebbf3 100644 (file)
     <ClCompile Include="..\Modules\_testcapi\config.c" />
     <ClCompile Include="..\Modules\_testcapi\import.c" />
     <ClCompile Include="..\Modules\_testcapi\frame.c" />
+    <ClCompile Include="..\Modules\_testcapi\type.c" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc" />
index 782f91afb1996f30c775cd5dcb08c3b168e8e15e..52491643ad842f984c65c1d8de737b39f9b807e7 100644 (file)
     <ClCompile Include="..\Modules\_testcapi\frame.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\Modules\_testcapi\type.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\PC\python_nt.rc">