Skip to content

Commit b680117

Browse files
Add a regression test.
1 parent e4337e2 commit b680117

File tree

2 files changed

+86
-1
lines changed

2 files changed

+86
-1
lines changed

Lib/test/test_capi/test_misc.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Run the _testcapi module tests (tests for the Python/C API): by defn,
22
# these are all functions _testcapi exports whose name begins with 'test_'.
33

4-
from collections import OrderedDict
54
import _thread
5+
from collections import OrderedDict
6+
import contextlib
67
import importlib.machinery
78
import importlib.util
89
import os
@@ -1626,6 +1627,34 @@ def test_tp_mro_is_set(self):
16261627
self.assertIsNot(mro, None)
16271628

16281629

1630+
class TestStaticTypes(unittest.TestCase):
1631+
1632+
def test_pytype_ready_always_sets_tp_type(self):
1633+
# The point of this test is to prevent something like
1634+
# https://github.com/python/cpython/issues/104614
1635+
# from happening again.
1636+
1637+
@contextlib.contextmanager
1638+
def basic_static_type(*args):
1639+
cls = _testcapi.get_basic_static_type(*args)
1640+
try:
1641+
yield cls
1642+
finally:
1643+
_testcapi.clear_basic_static_type(cls)
1644+
1645+
# First check when tp_base/tp_bases is *not* set before PyType_Ready().
1646+
with basic_static_type() as cls:
1647+
self.assertIs(cls.__base__, object);
1648+
self.assertEqual(cls.__bases__, (object,));
1649+
self.assertIs(type(cls), type(object));
1650+
1651+
# Then check when we *do* set tp_base/tp_bases first.
1652+
with basic_static_type(object) as cls:
1653+
self.assertIs(cls.__base__, object);
1654+
self.assertEqual(cls.__bases__, (object,));
1655+
self.assertIs(type(cls), type(object));
1656+
1657+
16291658
class TestThreadState(unittest.TestCase):
16301659

16311660
@threading_helper.reap_threads

Modules/_testcapimodule.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2627,6 +2627,60 @@ type_get_tp_mro(PyObject *self, PyObject *type)
26272627
}
26282628

26292629

2630+
static PyTypeObject BasicStaticType = {
2631+
PyVarObject_HEAD_INIT(NULL, 0)
2632+
.tp_name = "BasicStaticType",
2633+
.tp_basicsize = sizeof(PyObject),
2634+
};
2635+
2636+
static PyObject * clear_basic_static_type(PyObject *, PyObject *);
2637+
2638+
static PyObject *
2639+
get_basic_static_type(PyObject *self, PyObject *args)
2640+
{
2641+
PyObject *base = NULL;
2642+
if (!PyArg_ParseTuple(args, "|O", &base)) {
2643+
return NULL;
2644+
}
2645+
assert(base == NULL || PyType_Check(base));
2646+
2647+
PyTypeObject *cls = &BasicStaticType;
2648+
assert(!(cls->tp_flags & Py_TPFLAGS_READY));
2649+
2650+
if (base != NULL) {
2651+
cls->tp_base = (PyTypeObject *)Py_NewRef(base);
2652+
cls->tp_bases = Py_BuildValue("(O)", base);
2653+
if (cls->tp_bases == NULL) {
2654+
clear_basic_static_type(self, (PyObject *)cls);
2655+
return NULL;
2656+
}
2657+
}
2658+
if (PyType_Ready(cls) < 0) {
2659+
clear_basic_static_type(self, (PyObject *)cls);
2660+
return NULL;
2661+
}
2662+
Py_INCREF(cls);
2663+
return (PyObject *)cls;
2664+
}
2665+
2666+
static PyObject *
2667+
clear_basic_static_type(PyObject *self, PyObject *clsobj)
2668+
{
2669+
// Reset it back to the statically initialized state.
2670+
PyTypeObject *cls = (PyTypeObject *)clsobj;
2671+
Py_CLEAR(cls->ob_base.ob_base.ob_type);
2672+
Py_CLEAR(cls->tp_base);
2673+
Py_CLEAR(cls->tp_bases);
2674+
Py_CLEAR(cls->tp_mro);
2675+
Py_CLEAR(cls->tp_subclasses);
2676+
Py_CLEAR(cls->tp_dict);
2677+
cls->tp_flags &= ~Py_TPFLAGS_READY;
2678+
cls->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
2679+
cls->tp_version_tag = 0;
2680+
Py_RETURN_NONE;
2681+
}
2682+
2683+
26302684
// Test PyThreadState C API
26312685
static PyObject *
26322686
test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args))
@@ -3384,6 +3438,8 @@ static PyMethodDef TestMethods[] = {
33843438
{"type_assign_version", type_assign_version, METH_O, PyDoc_STR("PyUnstable_Type_AssignVersionTag")},
33853439
{"type_get_tp_bases", type_get_tp_bases, METH_O},
33863440
{"type_get_tp_mro", type_get_tp_mro, METH_O},
3441+
{"get_basic_static_type", get_basic_static_type, METH_VARARGS, NULL},
3442+
{"clear_basic_static_type", clear_basic_static_type, METH_O, NULL},
33873443
{"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
33883444
{"frame_getlocals", frame_getlocals, METH_O, NULL},
33893445
{"frame_getglobals", frame_getglobals, METH_O, NULL},

0 commit comments

Comments
 (0)