Skip to content

[WIP] bpo-43916: PyType_FromSpec() supports Py_tp_new=NULL slot #25733

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ extern int _Py_CheckSlotResult(
// See also the Py_TPFLAGS_READY flag.
#define _PyType_IsReady(type) ((type)->tp_dict != NULL)

extern int _PyType_Ready(PyTypeObject *type, int disallow_instantiation);

#ifdef __cplusplus
}
#endif
Expand Down
6 changes: 6 additions & 0 deletions Include/structseq.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ PyAPI_FUNC(void) PyStructSequence_InitType(PyTypeObject *type,
PyAPI_FUNC(int) PyStructSequence_InitType2(PyTypeObject *type,
PyStructSequence_Desc *desc);
#endif
#ifdef Py_BUILD_CORE
PyAPI_FUNC(int) _PyStructSequence_InitType(
PyTypeObject *type,
PyStructSequence_Desc *desc,
int disallow_instantiation);
#endif
PyAPI_FUNC(PyTypeObject*) PyStructSequence_NewType(PyStructSequence_Desc *desc);

PyAPI_FUNC(PyObject *) PyStructSequence_New(PyTypeObject* type);
Expand Down
2 changes: 1 addition & 1 deletion Modules/_curses_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ static PyMethodDef PyCursesPanel_Methods[] = {
static PyType_Slot PyCursesPanel_Type_slots[] = {
{Py_tp_dealloc, PyCursesPanel_Dealloc},
{Py_tp_methods, PyCursesPanel_Methods},
{Py_tp_new, NULL},
{0, 0},
};

Expand Down Expand Up @@ -656,7 +657,6 @@ _curses_panel_exec(PyObject *mod)
if (state->PyCursesPanel_Type == NULL) {
return -1;
}
((PyTypeObject *)state->PyCursesPanel_Type)->tp_new = NULL;

if (PyModule_AddType(mod, state->PyCursesPanel_Type) < 0) {
return -1;
Expand Down
14 changes: 3 additions & 11 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -4793,25 +4793,17 @@ PyInit__curses(void)
#ifdef NCURSES_VERSION
/* ncurses_version */
if (NcursesVersionType.tp_name == NULL) {
if (PyStructSequence_InitType2(&NcursesVersionType,
&ncurses_version_desc) < 0)
if (_PyStructSequence_InitType(&NcursesVersionType,
&ncurses_version_desc, 1) < 0) {
return NULL;
}
}
v = make_ncurses_version();
if (v == NULL) {
return NULL;
}
PyDict_SetItemString(d, "ncurses_version", v);
Py_DECREF(v);

/* prevent user from creating new instances */
NcursesVersionType.tp_init = NULL;
NcursesVersionType.tp_new = NULL;
if (PyDict_DelItemString(NcursesVersionType.tp_dict, "__new__") < 0 &&
PyErr_ExceptionMatches(PyExc_KeyError))
{
PyErr_Clear();
}
#endif /* NCURSES_VERSION */

SetDictInt("ERR", ERR);
Expand Down
6 changes: 3 additions & 3 deletions Modules/_tkinter.c
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,7 @@ static PyType_Slot PyTclObject_Type_slots[] = {
{Py_tp_getattro, PyObject_GenericGetAttr},
{Py_tp_richcompare, PyTclObject_richcompare},
{Py_tp_getset, PyTclObject_getsetlist},
{Py_tp_new, NULL},
{0, 0}
};

Expand Down Expand Up @@ -3287,6 +3288,7 @@ static PyType_Slot Tktt_Type_slots[] = {
{Py_tp_dealloc, Tktt_Dealloc},
{Py_tp_repr, Tktt_Repr},
{Py_tp_methods, Tktt_methods},
{Py_tp_new, NULL},
{0, 0}
};

Expand Down Expand Up @@ -3341,6 +3343,7 @@ static PyMethodDef Tkapp_methods[] =
static PyType_Slot Tkapp_Type_slots[] = {
{Py_tp_dealloc, Tkapp_Dealloc},
{Py_tp_methods, Tkapp_methods},
{Py_tp_new, NULL},
{0, 0}
};

Expand Down Expand Up @@ -3537,7 +3540,6 @@ PyInit__tkinter(void)
Py_DECREF(m);
return NULL;
}
((PyTypeObject *)o)->tp_new = NULL;
if (PyModule_AddObject(m, "TkappType", o)) {
Py_DECREF(o);
Py_DECREF(m);
Expand All @@ -3550,7 +3552,6 @@ PyInit__tkinter(void)
Py_DECREF(m);
return NULL;
}
((PyTypeObject *)o)->tp_new = NULL;
if (PyModule_AddObject(m, "TkttType", o)) {
Py_DECREF(o);
Py_DECREF(m);
Expand All @@ -3563,7 +3564,6 @@ PyInit__tkinter(void)
Py_DECREF(m);
return NULL;
}
((PyTypeObject *)o)->tp_new = NULL;
if (PyModule_AddObject(m, "Tcl_Obj", o)) {
Py_DECREF(o);
Py_DECREF(m);
Expand Down
18 changes: 15 additions & 3 deletions Objects/structseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,8 @@ initialize_members(PyStructSequence_Desc *desc, PyMemberDef* members,
}

int
PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
_PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc,
int disallow_instantiation)
{
PyMemberDef *members;
Py_ssize_t n_members, n_unnamed_members;
Expand All @@ -487,7 +488,12 @@ PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
type->tp_doc = desc->doc;
type->tp_base = &PyTuple_Type;
type->tp_methods = structseq_methods;
type->tp_new = structseq_new;
if (!disallow_instantiation) {
type->tp_new = structseq_new;
}
else {
type->tp_new = NULL;
}
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC;
type->tp_traverse = (traverseproc) structseq_traverse;

Expand All @@ -500,7 +506,7 @@ PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
initialize_members(desc, members, n_members);
type->tp_members = members;

if (PyType_Ready(type) < 0) {
if (_PyType_Ready(type, disallow_instantiation) < 0) {
PyMem_Free(members);
return -1;
}
Expand All @@ -516,6 +522,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)
{
Expand Down
82 changes: 53 additions & 29 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ _Py_IDENTIFIER(__weakref__);
_Py_IDENTIFIER(builtins);
_Py_IDENTIFIER(mro);

// Forward declarations
static PyObject *
slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);

Expand Down Expand Up @@ -3287,6 +3288,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
char *res_start;
short slot_offset, subslot_offset;

int disallow_instantiation = 0;
nmembers = weaklistoffset = dictoffset = vectorcalloffset = 0;
for (slot = spec->slots; slot->slot; slot++) {
if (slot->slot == Py_tp_members) {
Expand All @@ -3313,6 +3315,9 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
}
}
}
if (slot->slot == Py_tp_new && slot->pfunc == NULL) {
disallow_instantiation = 1;
}
}

res = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, nmembers);
Expand Down Expand Up @@ -3464,7 +3469,7 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
type->tp_vectorcall_offset = vectorcalloffset;
}

if (PyType_Ready(type) < 0)
if (_PyType_Ready(type, disallow_instantiation) < 0)
goto fail;

if (type->tp_dictoffset) {
Expand Down Expand Up @@ -5582,39 +5587,24 @@ 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) &&
(!type->tp_traverse && !type->tp_clear)) {
(!type->tp_traverse && !type->tp_clear))
{
type->tp_flags |= Py_TPFLAGS_HAVE_GC;
if (type->tp_traverse == NULL)
if (type->tp_traverse == NULL) {
type->tp_traverse = base->tp_traverse;
if (type->tp_clear == NULL)
}
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)
if (type->tp_basicsize == 0) {
type->tp_basicsize = base->tp_basicsize;
}

/* Copy other non-function slots */

#define COPYVAL(SLOT) \
if (type->SLOT == 0) { type->SLOT = base->SLOT; }

Expand Down Expand Up @@ -6089,11 +6079,14 @@ type_ready_inherit_as_structs(PyTypeObject *type, PyTypeObject *base)


static int
type_ready_inherit(PyTypeObject *type)
type_ready_inherit(PyTypeObject *type, int disallow_instantiation)
{
/* Inherit special flags from dominant base */
PyTypeObject *base = type->tp_base;
if (base != NULL) {
if (type->tp_new == NULL && !disallow_instantiation) {
type->tp_new = base->tp_new;
}
inherit_special(type, base);
}

Expand Down Expand Up @@ -6176,7 +6169,7 @@ type_ready_add_subclasses(PyTypeObject *type)


static int
type_ready(PyTypeObject *type)
type_ready(PyTypeObject *type, int disallow_instantiation)
{
if (type_ready_checks(type) < 0) {
return -1;
Expand All @@ -6201,10 +6194,31 @@ type_ready(PyTypeObject *type)
if (type_ready_mro(type) < 0) {
return -1;
}

/* 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
&& type->tp_base == &PyBaseObject_Type
&& !(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
{
disallow_instantiation = 1;
}
if (disallow_instantiation) {
type->tp_new = NULL;
}

if (type_ready_fill_dict(type) < 0) {
return -1;
}
if (type_ready_inherit(type) < 0) {
if (type_ready_inherit(type, disallow_instantiation) < 0) {
return -1;
}
if (type_ready_set_hash(type) < 0) {
Expand All @@ -6213,12 +6227,16 @@ type_ready(PyTypeObject *type)
if (type_ready_add_subclasses(type) < 0) {
return -1;
}
if (disallow_instantiation) {
assert(type->tp_new == NULL);
assert(_PyDict_ContainsId(type->tp_dict, &PyId___new__) == 0);
}
return 0;
}


int
PyType_Ready(PyTypeObject *type)
_PyType_Ready(PyTypeObject *type, int disallow_instantiation)
{
if (type->tp_flags & Py_TPFLAGS_READY) {
assert(_PyType_CheckConsistency(type));
Expand All @@ -6234,7 +6252,7 @@ PyType_Ready(PyTypeObject *type)
type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
}

if (type_ready(type) < 0) {
if (type_ready(type, disallow_instantiation) < 0) {
type->tp_flags &= ~Py_TPFLAGS_READYING;
return -1;
}
Expand All @@ -6245,6 +6263,12 @@ PyType_Ready(PyTypeObject *type)
return 0;
}

int
PyType_Ready(PyTypeObject *type)
{
return _PyType_Ready(type, 0);
}


static int
add_subclass(PyTypeObject *base, PyTypeObject *type)
Expand Down
Loading