https://github.com/python/cpython/commit/65f5e586a1239ed1a66d8284773d7b02ce40e480
commit: 65f5e586a1239ed1a66d8284773d7b02ce40e480
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-05-07T12:07:32Z
summary:
gh-66410: Do not stringify arguments of Tkinter callback (GH-98592)
Callbacks registered in the tkinter module now take arguments as
various Python objects (int, float, bytes, tuple), not just str.
To restore the previous behavior set tkinter module global wantobject to 1
before creating the Tk object or call the wantobject() method of the Tk object
with argument 1.
Calling it with argument 2 restores the current default behavior.
files:
A Misc/NEWS.d/next/Library/2022-10-24-12-05-19.gh-issue-66410.du4UKW.rst
M Doc/whatsnew/3.13.rst
M Lib/idlelib/redirector.py
M Lib/test/test_tcl.py
M Lib/tkinter/__init__.py
M Modules/_tkinter.c
M Modules/clinic/_tkinter.c.h
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index b084e78889c81f..daa8cf194204fa 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -1859,6 +1859,16 @@ Changes in the Python API
to :c:func:`PyUnstable_Code_GetFirstFree`.
(Contributed by Bogdan Romanyuk in :gh:`115781`.)
+* Callbacks registered in the :mod:`tkinter` module now take arguments as
+ various Python objects (``int``, ``float``, ``bytes``, ``tuple``),
+ not just ``str``.
+ To restore the previous behavior set :mod:`!tkinter` module global
+ :data:`!wantobject` to ``1`` before creating the
+ :class:`!Tk` object or call the :meth:`!wantobject`
+ method of the :class:`!Tk` object with argument ``1``.
+ Calling it with argument ``2`` restores the current default behavior.
+ (Contributed by Serhiy Storchaka in :gh:`66410`.)
+
Build Changes
=============
diff --git a/Lib/idlelib/redirector.py b/Lib/idlelib/redirector.py
index 08728956abd900..8e2ba68d3815bf 100644
--- a/Lib/idlelib/redirector.py
+++ b/Lib/idlelib/redirector.py
@@ -106,6 +106,7 @@ def dispatch(self, operation, *args):
to *args to accomplish that. For an example, see colorizer.py.
'''
+ operation = str(operation) # can be a Tcl_Obj
m = self._operations.get(operation)
try:
if m:
diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py
index ebdb58f91d3d8a..553d54329d7939 100644
--- a/Lib/test/test_tcl.py
+++ b/Lib/test/test_tcl.py
@@ -482,29 +482,36 @@ def testfunc(arg):
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, expected1=None, expected2=None, *,
eq=self.assertEqual):
+ expected = value
+ if self.wantobjects >= 2:
+ if expected2 is not None:
+ expected = expected2
+ expected_type = type(expected)
+ else:
+ if expected1 is not None:
+ expected = expected1
+ 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')
check('string\U0001f4bb')
if sys.platform != 'win32':
- check('<\udce2\udc82\udcac>', '<\u20ac>')
- check('<\udced\udca0\udcbd\udced\udcb2\udcbb>', '<\U0001f4bb>')
+ check('<\udce2\udc82\udcac>', '<\u20ac>', '<\u20ac>')
+ check('<\udced\udca0\udcbd\udced\udcb2\udcbb>', '<\U0001f4bb>',
'<\U0001f4bb>')
check('')
check(b'string', 'string')
check(b'string\xe2\x82\xac', 'string\xe2\x82\xac')
@@ -526,9 +533,13 @@ def float_eq(actual, expected):
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 --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index dc6ee9a1b47225..daecf4eb2ea522 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -40,7 +40,7 @@
from tkinter.constants import *
import re
-wantobjects = 1
+wantobjects = 2
_debug = False # set to True to print executed Tcl/Tk commands
TkVersion = float(_tkinter.TK_VERSION)
@@ -1762,7 +1762,10 @@ def getint_event(s):
try:
e.type = EventType(T)
except ValueError:
- e.type = T
+ try:
+ e.type = EventType(str(T)) # can be int
+ except ValueError:
+ e.type = T
try:
e.widget = self._nametowidget(W)
except KeyError:
diff --git
a/Misc/NEWS.d/next/Library/2022-10-24-12-05-19.gh-issue-66410.du4UKW.rst
b/Misc/NEWS.d/next/Library/2022-10-24-12-05-19.gh-issue-66410.du4UKW.rst
new file mode 100644
index 00000000000000..044fd1876acd3e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-10-24-12-05-19.gh-issue-66410.du4UKW.rst
@@ -0,0 +1,7 @@
+Callbacks registered in the :mod:`tkinter` module now take arguments as
+various Python objects (``int``, ``float``, ``bytes``, ``tuple``), not just
+``str``. To restore the previous behavior set :mod:`!tkinter` module global
+:data:`~tkinter.wantobject` to ``1`` before creating the
+:class:`~tkinter.Tk` object or call the :meth:`~tkinter.Tk.wantobject`
+method of the :class:`!Tk` object with argument ``1``. Calling it with
+argument ``2`` restores the current default behavior.
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index fc8af244adf4ed..c7e271faa4cf34 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -2248,7 +2248,7 @@ _tkinter_tkapp_splitlist(TkappObject *self, PyObject *arg)
/* Client data struct */
typedef struct {
- PyObject *self;
+ TkappObject *self;
PyObject *func;
} PythonCmd_ClientData;
@@ -2272,6 +2272,7 @@ PythonCmd(ClientData clientData, Tcl_Interp *interp,
PyObject *args, *res;
int i;
Tcl_Obj *obj_res;
+ int objargs = data->self->wantobjects >= 2;
ENTER_PYTHON
@@ -2280,7 +2281,8 @@ PythonCmd(ClientData clientData, Tcl_Interp *interp,
return PythonCmd_Error(interp);
for (i = 0; i < (objc - 1); i++) {
- PyObject *s = unicodeFromTclObj(objv[i + 1]);
+ PyObject *s = objargs ? FromObj(data->self, objv[i + 1])
+ : unicodeFromTclObj(objv[i + 1]);
if (!s) {
Py_DECREF(args);
return PythonCmd_Error(interp);
@@ -2383,7 +2385,8 @@ _tkinter_tkapp_createcommand_impl(TkappObject *self,
const char *name,
data = PyMem_NEW(PythonCmd_ClientData, 1);
if (!data)
return PyErr_NoMemory();
- data->self = Py_NewRef(self);
+ Py_INCREF(self);
+ data->self = self;
data->func = Py_NewRef(func);
if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) {
Tcl_Condition cond = NULL;
@@ -2897,10 +2900,10 @@ Tkapp_WantObjects(PyObject *self, PyObject *args)
{
int wantobjects = -1;
- if (!PyArg_ParseTuple(args, "|p:wantobjects", &wantobjects))
+ 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;
@@ -3086,7 +3089,7 @@ _tkinter.create
baseName: str = ""
className: str = "Tk"
interactive: bool = False
- wantobjects: bool = False
+ wantobjects: int = 0
wantTk: bool = True
if false, then Tk_Init() doesn't get called
sync: bool = False
@@ -3102,7 +3105,7 @@ _tkinter_create_impl(PyObject *module, const char
*screenName,
const char *baseName, const char *className,
int interactive, int wantobjects, int wantTk, int sync,
const char *use)
-/*[clinic end generated code: output=e3315607648e6bb4 input=09afef9adea70a19]*/
+/*[clinic end generated code: output=e3315607648e6bb4 input=7e382ba431bed537]*/
{
/* XXX baseName is not used anymore;
* try getting rid of it. */
diff --git a/Modules/clinic/_tkinter.c.h b/Modules/clinic/_tkinter.c.h
index 192c49dba216e2..2b1ac954b4d570 100644
--- a/Modules/clinic/_tkinter.c.h
+++ b/Modules/clinic/_tkinter.c.h
@@ -676,7 +676,7 @@ PyDoc_STRVAR(_tkinter__flatten__doc__,
PyDoc_STRVAR(_tkinter_create__doc__,
"create($module, screenName=None, baseName=\'\', className=\'Tk\',\n"
-" interactive=False, wantobjects=False, wantTk=True, sync=False,\n"
+" interactive=False, wantobjects=0, wantTk=True, sync=False,\n"
" use=None, /)\n"
"--\n"
"\n"
@@ -777,8 +777,8 @@ _tkinter_create(PyObject *module, PyObject *const *args,
Py_ssize_t nargs)
if (nargs < 5) {
goto skip_optional;
}
- wantobjects = PyObject_IsTrue(args[4]);
- if (wantobjects < 0) {
+ wantobjects = PyLong_AsInt(args[4]);
+ if (wantobjects == -1 && PyErr_Occurred()) {
goto exit;
}
if (nargs < 6) {
@@ -888,4 +888,4 @@ _tkinter_getbusywaitinterval(PyObject *module, PyObject
*Py_UNUSED(ignored))
#ifndef _TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF
#define _TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF
#endif /* !defined(_TKINTER_TKAPP_DELETEFILEHANDLER_METHODDEF) */
-/*[clinic end generated code: output=86a515890d48a2ce input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d90c1a9850c63249 input=a9049054013a1b77]*/
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]