summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/whatsnew/3.13.rst10
-rw-r--r--Lib/idlelib/redirector.py1
-rw-r--r--Lib/test/test_tcl.py35
-rw-r--r--Lib/tkinter/__init__.py7
-rw-r--r--Misc/NEWS.d/next/Library/2022-10-24-12-05-19.gh-issue-66410.du4UKW.rst7
-rw-r--r--Modules/_tkinter.c17
-rw-r--r--Modules/clinic/_tkinter.c.h8
7 files changed, 60 insertions, 25 deletions
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index b084e78..daa8cf1 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 0872895..8e2ba68 100644
--- a/Lib/idlelib/redirector.py
+++ b/Lib/idlelib/redirector.py
@@ -106,6 +106,7 @@ class WidgetRedirector:
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 ebdb58f..553d543 100644
--- a/Lib/test/test_tcl.py
+++ b/Lib/test/test_tcl.py
@@ -482,29 +482,36 @@ 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, 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 @@ 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 --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index dc6ee9a..daecf4e 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -40,7 +40,7 @@ TclError = _tkinter.TclError
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 @@ class Misc:
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 0000000..044fd18
--- /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 fc8af24..c7e271f 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 192c49d..2b1ac95 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 @@ exit:
#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]*/