Skip to content
95 changes: 72 additions & 23 deletions Modules/_tkinter.c
Original file line number Diff line number Diff line change
Expand Up @@ -589,10 +589,14 @@ Tkapp_New(const char *screenName, const char *className,
int interactive, int wantobjects, int wantTk, int sync,
const char *use)
{
PyTypeObject *type;
TkappObject *v;
char *argv0;

v = PyObject_New(TkappObject, (PyTypeObject *) Tkapp_Type);
type = (PyTypeObject *)Tkapp_Type;
assert(type != NULL);
assert(type->tp_alloc != NULL);
v = (TkappObject *)type->tp_alloc(type, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to move the variable definition aside to their first assignment:

Suggested change
type = (PyTypeObject *)Tkapp_Type;
assert(type != NULL);
assert(type->tp_alloc != NULL);
v = (TkappObject *)type->tp_alloc(type, 0);
PyTypeObject *type = (PyTypeObject *)Tkapp_Type;
assert(type != NULL);
assert(type->tp_alloc != NULL);
TkappObject *v = (TkappObject *)type->tp_alloc(type, 0);

Same below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the existing style. Do you want me to move the char *arg as well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want me to move the char *arg as well?

As you want. You can leave argv0 as it is to keep the diff small.

Copy link
Member Author

@picnixz picnixz Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would be more comfortable not changing the style of this function. It's a long function, and if we were to use gotos instead for some refactoring, we could benefit for having everything declared at the top. Do you mind I leave it as is?

if (v == NULL)
return NULL;

Expand Down Expand Up @@ -2742,9 +2746,13 @@ _tkinter_tktimertoken_deletetimerhandler_impl(TkttObject *self)
static TkttObject *
Tktt_New(PyObject *func)
{
PyTypeObject *type;
TkttObject *v;

v = PyObject_New(TkttObject, (PyTypeObject *) Tktt_Type);
type = (PyTypeObject *)Tktt_Type;
assert(type != NULL);
assert(type->tp_alloc != NULL);
v = (TkttObject *)type->tp_alloc(type, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these types cannot be subclassed. So we can simply replace PyObject_New/PyObject_Free with PyObject_GC_New/PyObject_GC_Del. I think that this would look clearer than with tp_alloc/tp_free.

Copy link
Member Author

@picnixz picnixz Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but now the docs explicitly say "don't call those functions directly, use tp_alloc/tp_free". I agree that when possible, we could directly call them, but @ZeroIntensity suggested to follow the docs here.

Copy link
Member

@ZeroIntensity ZeroIntensity Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like using tp_alloc/tp_free more because it's less refactoring if we need to change the type flags. People follow CPython's source code for inspiration in their extensions, so we should follow what's documented. If we want to change the preference, let's update the docs.

Alternatively, we could have a function like this for limited API users:

PyObject *
PyType_InvokeAlloc(PyTypeObject *tp)
{
    assert(tp != NULL);
    assert(type->tp_alloc != NULL);
    return type->tp_alloc(type, 0);
}

Copy link
Member Author

@picnixz picnixz Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point, I would prefer having a macro to avoid the extra cast everytime... Something that unifies PyObject_[GC_]New(typename, type).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type = (PyTypeObject *)Tktt_Type;
assert(type != NULL);
assert(type->tp_alloc != NULL);
v = (TkttObject *)type->tp_alloc(type, 0);
v = (TkttObject *)((PyTypeObject *)Tktt_Type)->tp_alloc(type, 0);

Copy link
Member Author

@picnixz picnixz Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, we would need

 (TkttObject *)((PyTypeObject *)Tktt_Type)->tp_alloc((PyTypeObject *)Tktt_Type, 0);

which is a bit unreadable IMO. So I'll keep the temporary type variable. I would prefer having a macro for calling tp_alloc because it becomes quite annoying if we need to cast to PyTypeObject* everytime twice.

#define _PyObject_New(T, type)	\
	((T *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0))

if (v == NULL)
return NULL;

Expand All @@ -2755,19 +2763,33 @@ Tktt_New(PyObject *func)
return (TkttObject*)Py_NewRef(v);
}

static void
Tktt_Dealloc(PyObject *self)
static int
Tktt_Clear(PyObject *op)
{
TkttObject *v = TkttObject_CAST(self);
PyObject *func = v->func;
PyObject *tp = (PyObject *) Py_TYPE(self);

Py_XDECREF(func);
TkttObject *self = TkttObject_CAST(op);
Py_CLEAR(self->func);
return 0;
}

PyObject_Free(self);
static void
Tktt_Dealloc(PyObject *op)
{
PyTypeObject *tp = Py_TYPE(op);
PyObject_GC_UnTrack(op);
(void)Tktt_Clear(op);
tp->tp_free(op);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tp->tp_free(op);
Py_TYPE(op)->tp_free(op);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this temporary variable is needed as we need Py_DECREF(Py_TYPE(op)) otherwise. The rest of the code base also does this.

Py_DECREF(tp);
}

static int
Tktt_Traverse(PyObject *op, visitproc visit, void *arg)
{
TkttObject *self = TkttObject_CAST(op);
Py_VISIT(Py_TYPE(op));
Py_VISIT(self->func);
return 0;
}

static PyObject *
Tktt_Repr(PyObject *self)
{
Expand Down Expand Up @@ -3058,21 +3080,38 @@ _tkinter_tkapp_willdispatch_impl(TkappObject *self)

/**** Tkapp Type Methods ****/

static int
Tkapp_Clear(PyObject *op)
{
TkappObject *self = TkappObject_CAST(op);
Py_CLEAR(self->trace);
return 0;
}

static void
Tkapp_Dealloc(PyObject *op)
{
PyTypeObject *tp = Py_TYPE(op);
PyObject_GC_UnTrack(op);
TkappObject *self = TkappObject_CAST(op);
PyTypeObject *tp = Py_TYPE(self);
/*CHECK_TCL_APPARTMENT;*/
ENTER_TCL
Tcl_DeleteInterp(Tkapp_Interp(self));
LEAVE_TCL
Py_XDECREF(self->trace);
PyObject_Free(self);
(void)Tkapp_Clear(op);
tp->tp_free(self);
Py_DECREF(tp);
DisableEventHook();
}

static int
Tkapp_Traverse(PyObject *op, visitproc visit, void *arg)
{
TkappObject *self = TkappObject_CAST(op);
Py_VISIT(Py_TYPE(op));
Py_VISIT(self->trace);
return 0;
}


/**** Tkinter Module ****/
Expand Down Expand Up @@ -3260,18 +3299,23 @@ static PyMethodDef Tktt_methods[] =
};

static PyType_Slot Tktt_Type_slots[] = {
{Py_tp_clear, Tktt_Clear},
{Py_tp_dealloc, Tktt_Dealloc},
{Py_tp_traverse, Tktt_Traverse},
{Py_tp_repr, Tktt_Repr},
{Py_tp_methods, Tktt_methods},
{0, 0}
};

static PyType_Spec Tktt_Type_spec = {
"_tkinter.tktimertoken",
sizeof(TkttObject),
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
Tktt_Type_slots,
.name = "_tkinter.tktimertoken",
.basicsize = sizeof(TkttObject),
.flags = (
Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_DISALLOW_INSTANTIATION
| Py_TPFLAGS_HAVE_GC
),
.slots = Tktt_Type_slots,
};


Expand Down Expand Up @@ -3316,18 +3360,23 @@ static PyMethodDef Tkapp_methods[] =
};

static PyType_Slot Tkapp_Type_slots[] = {
{Py_tp_clear, Tkapp_Clear},
{Py_tp_dealloc, Tkapp_Dealloc},
{Py_tp_traverse, Tkapp_Traverse},
{Py_tp_methods, Tkapp_methods},
{0, 0}
};


static PyType_Spec Tkapp_Type_spec = {
"_tkinter.tkapp",
sizeof(TkappObject),
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
Tkapp_Type_slots,
.name = "_tkinter.tkapp",
.basicsize = sizeof(TkappObject),
.flags = (
Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_DISALLOW_INSTANTIATION
| Py_TPFLAGS_HAVE_GC
),
.slots = Tkapp_Type_slots,
};

static PyMethodDef moduleMethods[] =
Expand Down
Loading