diff options
author | Victor Stinner <vstinner@python.org> | 2021-04-30 12:46:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-30 12:46:15 +0200 |
commit | 3bb09947ec4837de75532e21dd4bd25db0a1f1b7 (patch) | |
tree | 46469716b18eda1c8af7c311b6187fda39a146c1 /Objects/typeobject.c | |
parent | b73b5fb9ea08156991a065c1696e8d8cf7622482 (diff) | |
download | cpython-git-3bb09947ec4837de75532e21dd4bd25db0a1f1b7.tar.gz |
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.
Diffstat (limited to 'Objects/typeobject.c')
-rw-r--r-- | Objects/typeobject.c | 99 |
1 files changed, 67 insertions, 32 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 1f8e2572a2..0f7f280bca 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; } |