diff -r 7b9ad68db14e Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst Mon Jun 20 00:05:40 2016 +0300 +++ b/Doc/whatsnew/3.6.rst Mon Jun 20 00:30:46 2016 +0300 @@ -381,6 +381,16 @@ telnetlib Stéphane Wirtel in :issue:`25485`). +tkinter +------- + +:mod:`Tkinter ` now passes Python objects instead of strings to +Python function registered with Tk if one calls the +:meth:`~tkinter.Tk.wantobject` method of the :class:`~tkinter.Tk` object +with argument ``2`` or sets tkinter module global :data:`~tkinter.wantobject` +to ``2`` before creating the Tk object. + + typing ------ diff -r 7b9ad68db14e Lib/test/test_tcl.py --- a/Lib/test/test_tcl.py Mon Jun 20 00:05:40 2016 +0300 +++ b/Lib/test/test_tcl.py Mon Jun 20 00:30:46 2016 +0300 @@ -471,22 +471,29 @@ class TclTest(unittest.TestCase): return arg self.interp.createcommand('testfunc', testfunc) self.addCleanup(self.interp.tk.deletecommand, 'testfunc') - def check(value, expected=None, *, eq=self.assertEqual): - if expected is None: - expected = value + def check(value, arg1=None, arg2=None, *, eq=self.assertEqual): + expected = value + if self.wantobjects >= 2: + if arg2 is not None: + expected = arg2 + expected_type = type(expected) + else: + if arg1 is not None: + expected = arg1 + expected_type = str nonlocal result result = None r = self.interp.call('testfunc', value) - self.assertIsInstance(result, str) + self.assertIsInstance(result, expected_type) eq(result, expected) - self.assertIsInstance(r, str) + self.assertIsInstance(r, expected_type) eq(r, expected) def float_eq(actual, expected): self.assertAlmostEqual(float(actual), expected, delta=abs(expected) * 1e-10) - check(True, '1') - check(False, '0') + check(True, '1', 1) + check(False, '0', 0) check('string') check('string\xbd') check('string\u20ac') @@ -513,9 +520,13 @@ class TclTest(unittest.TestCase): check(float('inf'), eq=float_eq) check(-float('inf'), eq=float_eq) # XXX NaN representation can be not parsable by float() - check((), '') - check((1, (2,), (3, 4), '5 6', ()), '1 2 {3 4} {5 6} {}') - check([1, [2,], [3, 4], '5 6', []], '1 2 {3 4} {5 6} {}') + check((), '', '') + check((1, (2,), (3, 4), '5 6', ()), + '1 2 {3 4} {5 6} {}', + (1, (2,), (3, 4), '5 6', '')) + check([1, [2,], [3, 4], '5 6', []], + '1 2 {3 4} {5 6} {}', + (1, (2,), (3, 4), '5 6', '')) def test_splitlist(self): splitlist = self.interp.tk.splitlist diff -r 7b9ad68db14e Lib/tkinter/__init__.py --- a/Lib/tkinter/__init__.py Mon Jun 20 00:05:40 2016 +0300 +++ b/Lib/tkinter/__init__.py Mon Jun 20 00:30:46 2016 +0300 @@ -39,7 +39,7 @@ from tkinter.constants import * import re -wantobjects = 1 +wantobjects = 2 TkVersion = float(_tkinter.TK_VERSION) TclVersion = float(_tkinter.TCL_VERSION) diff -r 7b9ad68db14e Misc/NEWS --- a/Misc/NEWS Mon Jun 20 00:05:40 2016 +0300 +++ b/Misc/NEWS Mon Jun 20 00:30:46 2016 +0300 @@ -10,6 +10,11 @@ What's New in Python 3.6.0 alpha 3 Library ------- +- Issue #22214: Tkinter now passes Python objects instead of strings to + Python function registered with Tk if one calls the wantobject() method + of the Tk object with argument 2 or sets tkinter module global wantobject + to 2 before creating the Tk object. + - Issue #27319: Methods selection_set(), selection_add(), selection_remove() and selection_toggle() of ttk.TreeView now allow to pass multiple items as multiple arguments instead of passing them as a tuple. Deprecated diff -r 7b9ad68db14e Modules/_tkinter.c --- a/Modules/_tkinter.c Mon Jun 20 00:05:40 2016 +0300 +++ b/Modules/_tkinter.c Mon Jun 20 00:30:46 2016 +0300 @@ -2389,6 +2389,29 @@ PythonCmd_Error(Tcl_Interp *interp) return TCL_ERROR; } +static int +PythonCmd_Call(Tcl_Interp *interp, PyObject *func, PyObject *args) +{ + Tcl_Obj *obj_res; + PyObject *res = PyEval_CallObject(func, args); + Py_DECREF(args); + + if (res == NULL) + return PythonCmd_Error(interp); + + obj_res = AsObj(res); + if (obj_res == NULL) { + Py_DECREF(res); + return PythonCmd_Error(interp); + } + Tcl_SetObjResult(interp, obj_res); + Py_DECREF(res); + + LEAVE_PYTHON + + return TCL_OK; +} + /* This is the Tcl command that acts as a wrapper for Python * function or method. */ @@ -2396,50 +2419,54 @@ static int PythonCmd(ClientData clientData, Tcl_Interp *interp, int argc, const char *argv[]) { PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData; - PyObject *func, *arg, *res; - int i, rv; - Tcl_Obj *obj_res; + PyObject *args; + int i; ENTER_PYTHON - /* TBD: no error checking here since we know, via the - * Tkapp_CreateCommand() that the client data is a two-tuple - */ - func = data->func; - - /* Create argument list (argv1, ..., argvN) */ - if (!(arg = PyTuple_New(argc - 1))) + /* Create argument tuple (argv1, ..., argvN) */ + if (!(args = PyTuple_New(argc - 1))) return PythonCmd_Error(interp); for (i = 0; i < (argc - 1); i++) { PyObject *s = unicodeFromTclString(argv[i + 1]); if (!s) { - Py_DECREF(arg); + Py_DECREF(args); return PythonCmd_Error(interp); } - PyTuple_SET_ITEM(arg, i, s); + PyTuple_SET_ITEM(args, i, s); } - res = PyEval_CallObject(func, arg); - Py_DECREF(arg); - - if (res == NULL) + + return PythonCmd_Call(interp, data->func, args); +} + +/* This is the Tcl command that acts as a wrapper for Python + * function or method. + */ +static int +PythonObjCmd(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *const objv[]) +{ + PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData; + PyObject *args; + int i; + + ENTER_PYTHON + + /* Create argument tuple (objv1, ..., objvN) */ + if (!(args = PyTuple_New(objc - 1))) return PythonCmd_Error(interp); - obj_res = AsObj(res); - if (obj_res == NULL) { - Py_DECREF(res); - return PythonCmd_Error(interp); + for (i = 0; i < (objc - 1); i++) { + PyObject *s = FromObj(data->self, objv[i + 1]); + if (!s) { + Py_DECREF(args); + return PythonCmd_Error(interp); + } + PyTuple_SET_ITEM(args, i, s); } - else { - Tcl_SetObjResult(interp, obj_res); - rv = TCL_OK; - } - - Py_DECREF(res); - - LEAVE_PYTHON - - return rv; + + return PythonCmd_Call(interp, data->func, args); } static void @@ -2473,7 +2500,11 @@ typedef struct CommandEvent{ static int Tkapp_CommandProc(CommandEvent *ev, int flags) { - if (ev->create) + if (ev->create == 2) + *ev->status = Tcl_CreateObjCommand( + ev->interp, ev->name, PythonObjCmd, + ev->data, PythonCmdDelete) == NULL; + else if (ev->create) *ev->status = Tcl_CreateCommand( ev->interp, ev->name, PythonCmd, ev->data, PythonCmdDelete) == NULL; @@ -2533,7 +2564,7 @@ static PyObject * } ev->ev.proc = (Tcl_EventProc*)Tkapp_CommandProc; ev->interp = self->interp; - ev->create = 1; + ev->create = self->wantobjects >= 2 ? 2 : 1; ev->name = name; ev->data = (ClientData)data; ev->status = &err; @@ -2545,9 +2576,16 @@ static PyObject * #endif { ENTER_TCL - err = Tcl_CreateCommand( - Tkapp_Interp(self), name, PythonCmd, - (ClientData)data, PythonCmdDelete) == NULL; + if (self->wantobjects >= 2) { + err = Tcl_CreateObjCommand( + Tkapp_Interp(self), name, PythonObjCmd, + (ClientData)data, PythonCmdDelete) == NULL; + } + else { + err = Tcl_CreateCommand( + Tkapp_Interp(self), name, PythonCmd, + (ClientData)data, PythonCmdDelete) == NULL; + } LEAVE_TCL } if (err) { @@ -3062,7 +3100,7 @@ Tkapp_WantObjects(PyObject *self, PyObje if (!PyArg_ParseTuple(args, "|i:wantobjects", &wantobjects)) return NULL; if (wantobjects == -1) - return PyBool_FromLong(((TkappObject*)self)->wantobjects); + return PyLong_FromLong(((TkappObject*)self)->wantobjects); ((TkappObject*)self)->wantobjects = wantobjects; Py_RETURN_NONE;