self.assertEqual(main_attr_id, subinterp_attr_id)
+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 TestThreadState(unittest.TestCase):
@threading_helper.reap_threads
}
+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);
+}
+
+
// Test PyThreadState C API
static PyObject *
test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args))
{"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_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},
{"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
{"frame_getlocals", frame_getlocals, METH_O, NULL},
{"frame_getglobals", frame_getglobals, METH_O, NULL},
static inline PyObject *
lookup_tp_bases(PyTypeObject *self)
{
- if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
- assert(state != NULL);
- return state->tp_bases;
- }
return self->tp_bases;
}
static inline void
set_tp_bases(PyTypeObject *self, PyObject *bases)
{
+ assert(PyTuple_CheckExact(bases));
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
- assert(state != NULL);
- state->tp_bases = bases;
- return;
+ // XXX tp_bases can probably be statically allocated for each
+ // static builtin type.
+ assert(_Py_IsMainInterpreter(_PyInterpreterState_GET()));
+ assert(self->tp_bases == NULL);
+ if (PyTuple_GET_SIZE(bases) == 0) {
+ assert(self->tp_base == NULL);
+ }
+ else {
+ assert(PyTuple_GET_SIZE(bases) == 1);
+ assert(PyTuple_GET_ITEM(bases, 0) == (PyObject *)self->tp_base);
+ assert(self->tp_base->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
+ assert(_Py_IsImmortal(self->tp_base));
+ }
+ _Py_SetImmortal(bases);
}
self->tp_bases = bases;
}
clear_tp_bases(PyTypeObject *self)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
- assert(state != NULL);
- Py_CLEAR(state->tp_bases);
+ if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
+ if (self->tp_bases != NULL
+ && PyTuple_GET_SIZE(self->tp_bases) > 0)
+ {
+ assert(_Py_IsImmortal(self->tp_bases));
+ _Py_ClearImmortal(self->tp_bases);
+ }
+ }
return;
}
Py_CLEAR(self->tp_bases);
static inline PyObject *
lookup_tp_mro(PyTypeObject *self)
{
- if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
- assert(state != NULL);
- return state->tp_mro;
- }
return self->tp_mro;
}
static inline void
set_tp_mro(PyTypeObject *self, PyObject *mro)
{
+ assert(PyTuple_CheckExact(mro));
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
- assert(state != NULL);
- state->tp_mro = mro;
- return;
+ // XXX tp_mro can probably be statically allocated for each
+ // static builtin type.
+ assert(_Py_IsMainInterpreter(_PyInterpreterState_GET()));
+ assert(self->tp_mro == NULL);
+ /* Other checks are done via set_tp_bases. */
+ _Py_SetImmortal(mro);
}
self->tp_mro = mro;
}
clear_tp_mro(PyTypeObject *self)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
- assert(state != NULL);
- Py_CLEAR(state->tp_mro);
+ if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
+ if (self->tp_mro != NULL
+ && PyTuple_GET_SIZE(self->tp_mro) > 0)
+ {
+ assert(_Py_IsImmortal(self->tp_mro));
+ _Py_ClearImmortal(self->tp_mro);
+ }
+ }
return;
}
Py_CLEAR(self->tp_mro);
static int
type_ready_mro(PyTypeObject *type)
{
+ if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
+ if (!_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
+ assert(lookup_tp_mro(type) != NULL);
+ return 0;
+ }
+ assert(lookup_tp_mro(type) == NULL);
+ }
+
/* Calculate method resolution order */
if (mro_internal(type, NULL) < 0) {
return -1;