bpo-43916: Add Py_TPFLAGS_DISALLOW_INSTANTIATION type flag (GH-25721)
Add a new Py_TPFLAGS_DISALLOW_INSTANTIATION type flag to disallow
creating type instances: set tp_new to NULL and don't create the
"__new__" key in the type dictionary.
The flag is set automatically on static types if tp_base is NULL or
&PyBaseObject_Type and tp_new is NULL.
Use the flag on the following types:
* _curses.ncurses_version type
* _curses_panel.panel
* _tkinter.Tcl_Obj
* _tkinter.tkapp
* _tkinter.tktimertoken
* _xxsubinterpretersmodule.ChannelID
* sys.flags type
* sys.getwindowsversion() type
* sys.version_info type
Update MyStr example in the C API documentation to use
Py_TPFLAGS_DISALLOW_INSTANTIATION.
Add _PyStructSequence_InitType() function to create a structseq type
with the Py_TPFLAGS_DISALLOW_INSTANTIATION flag set.
type_new() calls _PyType_CheckConsistency() at exit.
diff --git a/Objects/structseq.c b/Objects/structseq.c
index 88e63b6..bf59f47 100644
--- a/Objects/structseq.c
+++ b/Objects/structseq.c
@@ -459,8 +459,10 @@ initialize_members(PyStructSequence_Desc *desc, PyMemberDef* members,
members[k].name = NULL;
}
+
int
-PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
+_PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc,
+ unsigned long tp_flags)
{
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;
@@ -488,7 +490,7 @@ PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
type->tp_base = &PyTuple_Type;
type->tp_methods = structseq_methods;
type->tp_new = structseq_new;
- type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC;
+ type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | tp_flags;
type->tp_traverse = (traverseproc) structseq_traverse;
n_members = count_members(desc, &n_unnamed_members);
@@ -516,6 +518,12 @@ PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
return 0;
}
+int
+PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
+{
+ return _PyStructSequence_InitType(type, desc, 0);
+}
+
void
PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc)
{
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 1f8e257..0f7f280 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -158,6 +158,11 @@ _PyType_CheckConsistency(PyTypeObject *type)
CHECK(!(type->tp_flags & Py_TPFLAGS_READYING));
CHECK(type->tp_dict != NULL);
+ if (type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION) {
+ CHECK(type->tp_new == NULL);
+ CHECK(_PyDict_ContainsId(type->tp_dict, &PyId___new__) == 0);
+ }
+
return 1;
#undef CHECK
}
@@ -1111,8 +1116,7 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (type->tp_new == NULL) {
_PyErr_Format(tstate, PyExc_TypeError,
- "cannot create '%.100s' instances",
- type->tp_name);
+ "cannot create '%s' instances", type->tp_name);
return NULL;
}
@@ -3185,6 +3189,8 @@ type_new_impl(type_new_ctx *ctx)
if (type_new_init_subclass(type, ctx->kwds) < 0) {
goto error;
}
+
+ assert(_PyType_CheckConsistency(type));
return (PyObject *)type;
error:
@@ -5651,7 +5657,6 @@ type_add_getset(PyTypeObject *type)
static void
inherit_special(PyTypeObject *type, PyTypeObject *base)
{
-
/* Copying tp_traverse and tp_clear is connected to the GC flags */
if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC) &&
(base->tp_flags & Py_TPFLAGS_HAVE_GC) &&
@@ -5662,23 +5667,7 @@ inherit_special(PyTypeObject *type, PyTypeObject *base)
if (type->tp_clear == NULL)
type->tp_clear = base->tp_clear;
}
- {
- /* The condition below could use some explanation.
- It appears that tp_new is not inherited for static types
- whose base class is 'object'; this seems to be a precaution
- so that old extension types don't suddenly become
- callable (object.__new__ wouldn't insure the invariants
- that the extension type's own factory function ensures).
- Heap types, of course, are under our control, so they do
- inherit tp_new; static extension types that specify some
- other built-in type as the default also
- inherit object.__new__. */
- if (base != &PyBaseObject_Type ||
- (type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
- if (type->tp_new == NULL)
- type->tp_new = base->tp_new;
- }
- }
+
if (type->tp_basicsize == 0)
type->tp_basicsize = base->tp_basicsize;
@@ -5941,6 +5930,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
}
static int add_operators(PyTypeObject *);
+static int add_tp_new_wrapper(PyTypeObject *type);
static int
@@ -5991,6 +5981,7 @@ type_ready_set_bases(PyTypeObject *type)
type->tp_base = base;
}
}
+ assert(type->tp_base != NULL || type == &PyBaseObject_Type);
/* Now the only way base can still be NULL is if type is
* &PyBaseObject_Type. */
@@ -6249,6 +6240,50 @@ type_ready_add_subclasses(PyTypeObject *type)
}
+// Set tp_new and the "__new__" key in the type dictionary.
+// Use the Py_TPFLAGS_DISALLOW_INSTANTIATION flag.
+static int
+type_ready_set_new(PyTypeObject *type)
+{
+ PyTypeObject *base = type->tp_base;
+ /* The condition below could use some explanation.
+
+ It appears that tp_new is not inherited for static types whose base
+ class is 'object'; this seems to be a precaution so that old extension
+ types don't suddenly become callable (object.__new__ wouldn't insure the
+ invariants that the extension type's own factory function ensures).
+
+ Heap types, of course, are under our control, so they do inherit tp_new;
+ static extension types that specify some other built-in type as the
+ default also inherit object.__new__. */
+ if (type->tp_new == NULL
+ && base == &PyBaseObject_Type
+ && !(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
+ {
+ type->tp_flags |= Py_TPFLAGS_DISALLOW_INSTANTIATION;
+ }
+
+ if (!(type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION)) {
+ if (type->tp_new != NULL) {
+ // If "__new__" key does not exists in the type dictionary,
+ // set it to tp_new_wrapper().
+ if (add_tp_new_wrapper(type) < 0) {
+ return -1;
+ }
+ }
+ else {
+ // tp_new is NULL: inherit tp_new from base
+ type->tp_new = base->tp_new;
+ }
+ }
+ else {
+ // Py_TPFLAGS_DISALLOW_INSTANTIATION sets tp_new to NULL
+ type->tp_new = NULL;
+ }
+ return 0;
+}
+
+
static int
type_ready(PyTypeObject *type)
{
@@ -6275,6 +6310,9 @@ type_ready(PyTypeObject *type)
if (type_ready_mro(type) < 0) {
return -1;
}
+ if (type_ready_set_new(type) < 0) {
+ return -1;
+ }
if (type_ready_fill_dict(type) < 0) {
return -1;
}
@@ -6898,8 +6936,8 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds)
"__new__() called with non-type 'self'");
return NULL;
}
-
type = (PyTypeObject *)self;
+
if (!PyTuple_Check(args) || PyTuple_GET_SIZE(args) < 1) {
PyErr_Format(PyExc_TypeError,
"%s.__new__(): not enough arguments",
@@ -6961,16 +6999,18 @@ static struct PyMethodDef tp_new_methoddef[] = {
static int
add_tp_new_wrapper(PyTypeObject *type)
{
- PyObject *func;
-
int r = _PyDict_ContainsId(type->tp_dict, &PyId___new__);
- if (r > 0)
+ if (r > 0) {
return 0;
- if (r < 0)
+ }
+ if (r < 0) {
return -1;
- func = PyCFunction_NewEx(tp_new_methoddef, (PyObject *)type, NULL);
- if (func == NULL)
+ }
+
+ PyObject *func = PyCFunction_NewEx(tp_new_methoddef, (PyObject *)type, NULL);
+ if (func == NULL) {
return -1;
+ }
r = _PyDict_SetItemId(type->tp_dict, &PyId___new__, func);
Py_DECREF(func);
return r;
@@ -8558,11 +8598,6 @@ add_operators(PyTypeObject *type)
Py_DECREF(descr);
}
}
- if (type->tp_new != NULL) {
- if (add_tp_new_wrapper(type) < 0) {
- return -1;
- }
- }
return 0;
}