diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 2dc44ad36a7f73..8bfa17957913f9 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -70,10 +70,7 @@ Tkinter Modules Most of the time, :mod:`tkinter` is all you really need, but a number of additional modules are available as well. The Tk interface is located in a -binary module named :mod:`_tkinter`. This module contains the low-level -interface to Tk, and should never be used directly by application programmers. -It is usually a shared library (or DLL), but might in some cases be statically -linked with the Python interpreter. +binary module named :mod:`_tkinter`. In addition to the Tk interface module, :mod:`tkinter` includes a number of Python modules, :mod:`tkinter.constants` being one of the most important. @@ -82,10 +79,6 @@ so, usually, to use Tkinter all you need is a simple import statement:: import tkinter -Or, more often:: - - from tkinter import * - .. class:: Tk(screenName=None, baseName=None, className='Tk', useTk=1) @@ -95,6 +88,38 @@ Or, more often:: .. FIXME: The following keyword arguments are currently recognized: + Below are a few of the methods provided by the :class:`Tk` class. + + .. sectionauthor:: Richard Sheridan + + .. method:: mainloop(threshold) + + Enters the main loop of Tkinter. This repeatedly dispatches Tcl events + until either :meth:`Tk.quit` is called, the number of open windows drops + below ``threshold``, or an error occurs while executing events. Usually + the default threshold of 0 is appropriate. + + .. method:: quit() + + Signals the Tkinter main loop to stop dispatching. + The main loop will exit AFTER the current Tcl event handler is + finished calling. If quit is called outside the context of a Tcl + event, for example from a thread, the main loop will not exit + until after the NEXT event is dispatched. If no more events are + forthcoming, main loop will keep blocking even if quit has been + called. In that case, call ``after(0, quit)`` instead. + + .. method:: dispatching() + + Determines if the Tkinter main loop is running. Returns True if the main + loop is running, or returns False if the main loop is not running. It is + possible for some entity other than the main loop to be dispatching + events. Some examples are: calling the :meth:`Tk.update` command, + :meth:`_tkinter.tkapp.doonevent`, and the python command line EventHook. + :meth:`Tk.dispatching` does not provide any information about entities + dispatching other than :meth:`Tk.mainloop`. + + .. versionadded:: 3.10 .. function:: Tcl(screenName=None, baseName=None, className='Tk', useTk=0) @@ -106,7 +131,6 @@ Or, more often:: created by the :func:`Tcl` object can have a Toplevel window created (and the Tk subsystem initialized) by calling its :meth:`loadtk` method. - Other modules that provide Tk support include: :mod:`tkinter.colorchooser` diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index a3b53ba48e9b7c..696e1e38269cf9 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -117,6 +117,22 @@ Add :data:`sys.orig_argv` attribute: the list of the original command line arguments passed to the Python executable. (Contributed by Victor Stinner in :issue:`23427`.) +tkinter +------- + +XXX Added :meth:`tkinter.dispatching`, :meth:`tkinter.Misc.dispatching`, and +:meth:`_tkinter.tkapp.dispatching`. +You can reliably call these to determine if the tkinter mainloop is running. + +Some internal thread waiting behavior has been deprecated to preserve the +correctness of the new dispatching methods. + +Added another new method :meth:`_tkinter.tkapp.setmainloopwaitattempts` which +can be used to obtain future thread waiting behavior. + +(see also Deprecated section) +(Contributed by Richard Sheridan in :issue:`41176`.) + Optimizations ============= @@ -130,6 +146,13 @@ Optimizations Deprecated ========== +XXX The :mod:`tkinter` module has deprected the undocumented behavior where a +thread will implicitly wait up to one second for the main thread to enter +the main loop and then fail. +The undocumented :meth:`_tkinter.tkapp.willdispatch` method which sidesteps +this behavior has also been deprecated. +(See also tkinter in the Improved Modules section) +(Contributed by Richard Sheridan in :issue:`41176`.) Removed ======= diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index a3378d012fb41a..bb940cf7dd64f8 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -590,9 +590,20 @@ def get(self): raise ValueError("invalid literal for getboolean()") -def mainloop(n=0): - """Run the main loop of Tcl.""" - _default_root.tk.mainloop(n) +def mainloop(threshold=0): + """Call the main loop of Tkinter.""" + _default_root.tk.mainloop(threshold) + + +def dispatching(): + """Determine if the Tkinter main loop is running. + + Returns True if the main loop is running. + Returns False if the main loop is not running. + + NOTE: Using update will dispatch events without the main + loop. Dispatching will return False in these cases.""" + return _default_root.tk.dispatching() getint = int @@ -1303,13 +1314,13 @@ def winfo_y(self): self.tk.call('winfo', 'y', self._w)) def update(self): - """Enter event loop until all pending events have been processed by Tcl.""" + """Process all events, including idle tasks, in the Tcl queue.""" self.tk.call('update') def update_idletasks(self): - """Enter event loop until all idle callbacks have been called. This - will update the display of windows but not process events caused by - the user.""" + """Process all idle callbacks in the Tcl queue. + This will update the display of windows but not process events + caused by the user.""" self.tk.call('update', 'idletasks') def bindtags(self, tagList=None): @@ -1417,12 +1428,30 @@ def unbind_class(self, className, sequence): all functions.""" self.tk.call('bind', className , sequence, '') - def mainloop(self, n=0): - """Call the mainloop of Tk.""" - self.tk.mainloop(n) + def mainloop(self, threshold=0): + """Call the main loop of Tkinter.""" + self.tk.mainloop(threshold) + + def dispatching(self): + """Determine if the Tkinter main loop is running. + + Returns True if the main loop is running. + Returns False if the main loop is not running. + + NOTE: Using update will dispatch events without the main + loop. Dispatching will return False in these cases.""" + return self.tk.dispatching() def quit(self): - """Quit the Tcl interpreter. All widgets will be destroyed.""" + """Signal the Tkinter main loop to stop dispatching. + + The main loop will exit AFTER the current Tcl event handler is + finished calling. If quit is called outside the context of a Tcl + event, for example from a thread, the main loop will not exit + until after the NEXT event is dispatched. If no more events are + forthcoming, main loop will keep blocking even if quit has been + called. In that case, have the thread call after(0, quit) instead. + """ self.tk.quit() def _getints(self, string): diff --git a/Lib/tkinter/test/support.py b/Lib/tkinter/test/support.py index 467a0b66c265c0..1220854205917b 100644 --- a/Lib/tkinter/test/support.py +++ b/Lib/tkinter/test/support.py @@ -22,7 +22,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - cls.root.update_idletasks() + cls.root.update() cls.root.destroy() del cls.root tkinter._default_root = None @@ -38,7 +38,7 @@ def tearDown(self): def destroy_default_root(): if getattr(tkinter, '_default_root', None): - tkinter._default_root.update_idletasks() + tkinter._default_root.update() tkinter._default_root.destroy() tkinter._default_root = None diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 1e089747a91ee5..5e71e8fa0c7c92 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -1,5 +1,7 @@ import unittest import tkinter +import threading +import time from test import support from tkinter.test.support import AbstractTkTest @@ -109,7 +111,7 @@ def callback(start=0, step=1): idle1 = root.after_idle(callback) self.assertIn(idle1, root.tk.call('after', 'info')) (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1)) - root.update_idletasks() # Process all pending events. + root.update() # Process all pending events. self.assertEqual(count, 1) with self.assertRaises(tkinter.TclError): root.tk.call(script) @@ -117,7 +119,7 @@ def callback(start=0, step=1): # Set up with callback with args. count = 0 idle1 = root.after_idle(callback, 42, 11) - root.update_idletasks() # Process all pending events. + root.update() # Process all pending events. self.assertEqual(count, 53) # Cancel before called. @@ -192,6 +194,100 @@ def test_clipboard_astral(self): with self.assertRaises(tkinter.TclError): root.clipboard_get() + def test_mainloop_dispatching(self): + # reconstruct default root destroyed by AbstractTkTest + root = tkinter._default_root = self.root + for obj in (root.tk, root, tkinter): + self.assertFalse(obj.dispatching()) + root.after(0, lambda:self.assertTrue(obj.dispatching())) + + # guarantees mainloop end after first call to Tcl_DoOneEvent + root.after(0, root.quit) + + root.mainloop() + self.assertFalse(obj.dispatching()) + + def test_willdispatch(self): + root = self.root + self.assertFalse(root.dispatching()) + with self.assertWarns(DeprecationWarning): + root.tk.willdispatch() + self.assertTrue(root.dispatching()) + # reset dispatching flag + root.after(0,root.quit) + root.mainloop() + self.assertFalse(root.dispatching()) + + def test_thread_must_wait_for_mainloop(self): + # remove test on eventual WaitForMainloop removal + sentinel = object() + thread_properly_raises = sentinel + thread_dispatching_early = sentinel + thread_dispatching_eventually = sentinel + + def target(): + nonlocal thread_dispatching_early + nonlocal thread_properly_raises + nonlocal thread_dispatching_eventually + + try: + thread_dispatching_early = root.dispatching() + root.after(0) # Null op + except RuntimeError as e: + if str(e) == "main thread is not in main loop": + thread_properly_raises=True + else: + thread_properly_raises=False + raise + else: + thread_properly_raises=False + return + finally: + # must guarantee that any reason not to run mainloop + # is flagged in the above try/except/else and will + # keep the main thread from calling root.mainloop() + ready_for_mainloop.set() + + # self.assertTrue(root.dispatching()) but patient + for i in range(1000): + if root.dispatching(): + thread_dispatching_eventually = True + break + time.sleep(0.01) + else: # if not break + thread_dispatching_eventually = False + root.after(0, root.quit) + + root = self.root + + with self.assertWarns(DeprecationWarning): + root.tk.setmainloopwaitattempts(0) + + try: + ready_for_mainloop = threading.Event() + thread = threading.Thread(target=target) + self.assertFalse(root.dispatching()) + thread.start() + ready_for_mainloop.wait() + + # if these fail we don't want to risk starting mainloop + self.assertFalse(thread_dispatching_early is sentinel) + self.assertFalse(thread_dispatching_early) + self.assertFalse(thread_properly_raises is sentinel) + self.assertTrue(thread_properly_raises) + + root.mainloop() + self.assertFalse(root.dispatching()) + thread.join() + finally: + # this global *must* be reset + with self.assertWarns(DeprecationWarning): + root.tk.setmainloopwaitattempts(10) + + self.assertFalse(thread_dispatching_eventually is sentinel) + self.assertTrue(thread_dispatching_eventually) + + tests_gui = (MiscTest, ) diff --git a/Lib/tkinter/test/test_ttk/test_extensions.py b/Lib/tkinter/test/test_ttk/test_extensions.py index a45f882bb00d48..ff0fb8cacae158 100644 --- a/Lib/tkinter/test/test_ttk/test_extensions.py +++ b/Lib/tkinter/test/test_ttk/test_extensions.py @@ -10,7 +10,7 @@ class LabeledScaleTest(AbstractTkTest, unittest.TestCase): def tearDown(self): - self.root.update_idletasks() + self.root.update() super().tearDown() def test_widget_destroy(self): @@ -219,7 +219,7 @@ def test_widget_destroy(self): var = tkinter.StringVar(self.root) optmenu = ttk.OptionMenu(self.root, var) name = var._name - optmenu.update_idletasks() + optmenu.update() optmenu.destroy() self.assertEqual(optmenu.tk.globalgetvar(name), var.get()) del var diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py index 2598bc67652075..4dd09d905aa3c7 100644 --- a/Lib/tkinter/test/test_ttk/test_widgets.py +++ b/Lib/tkinter/test/test_ttk/test_widgets.py @@ -64,7 +64,7 @@ def setUp(self): def test_identify(self): - self.widget.update_idletasks() + self.widget.update() self.assertEqual(self.widget.identify( int(self.widget.winfo_width() / 2), int(self.widget.winfo_height() / 2) @@ -327,7 +327,7 @@ def test_bbox(self): def test_identify(self): self.entry.pack() self.entry.wait_visibility() - self.entry.update_idletasks() + self.entry.update() # bpo-27313: macOS Cocoa widget differs from X, allow either if sys.platform == 'darwin': @@ -439,7 +439,7 @@ def _show_drop_down_listbox(self): width = self.combo.winfo_width() self.combo.event_generate('', x=width - 5, y=5) self.combo.event_generate('', x=width - 5, y=5) - self.combo.update_idletasks() + self.combo.update() def test_virtual_event(self): @@ -1134,7 +1134,7 @@ def _click_increment_arrow(self): y = height//2 - 5 self.spin.event_generate('', x=x, y=y) self.spin.event_generate('', x=x, y=y) - self.spin.update_idletasks() + self.spin.update() def _click_decrement_arrow(self): width = self.spin.winfo_width() @@ -1143,7 +1143,7 @@ def _click_decrement_arrow(self): y = height//2 + 4 self.spin.event_generate('', x=x, y=y) self.spin.event_generate('', x=x, y=y) - self.spin.update_idletasks() + self.spin.update() def test_command(self): success = [] @@ -1159,7 +1159,7 @@ def test_command(self): # testing postcommand removal self.spin['command'] = '' - self.spin.update_idletasks() + self.spin.update() self._click_increment_arrow() self._click_decrement_arrow() self.spin.update() diff --git a/Misc/ACKS b/Misc/ACKS index 641ef0cace00e2..183e19313a44bb 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1563,6 +1563,7 @@ Vlad Shcherbina Justin Sheehy Akash Shende Charlie Shepherd +Richard Sheridan Bruce Sherwood Alexander Shigin Pete Shinners diff --git a/Misc/NEWS.d/next/Library/2020-07-05-15-38-26.bpo-41176.Q5Ua74.rst b/Misc/NEWS.d/next/Library/2020-07-05-15-38-26.bpo-41176.Q5Ua74.rst new file mode 100644 index 00000000000000..5a629bcfe55e36 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-07-05-15-38-26.bpo-41176.Q5Ua74.rst @@ -0,0 +1,6 @@ +Added :meth:`tkinter.dispatching`, :meth:`tkinter.Misc.dispatching`, and :meth:`_tkinter.tkapp.dispatching`. +These methods may be used to query if the :mod:`tkinter` mainloop is running +Added :meth:`_tkinter.tkapp.setmainloopwaitattempts`. +Deprecated :meth:`_tkinter.tkapp.willdispatch`. +Deprecated undocumented :mod:`tkinter` thread mainloop waiting behavior. +Patch by Richard Sheridan \ No newline at end of file diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c index 793c5e71548846..57c50fc6b1378a 100644 --- a/Modules/_tkinter.c +++ b/Modules/_tkinter.c @@ -360,21 +360,35 @@ Sleep(int milli) } #endif /* MS_WINDOWS */ -/* Wait up to 1s for the mainloop to come up. */ +/* Wait for the mainloop to come up. */ +static int mainloopwaitattempts = 10; // 1 second static int WaitForMainloop(TkappObject* self) { int i; - for (i = 0; i < 10; i++) { - if (self->dispatching) - return 1; + int max_attempts = mainloopwaitattempts; + if (self->dispatching) + { + return 1; + } + for (i = 0; i < max_attempts; i++) { Py_BEGIN_ALLOW_THREADS Sleep(100); Py_END_ALLOW_THREADS + if (self->dispatching) + { + return 1; + } } - if (self->dispatching) - return 1; + PyErr_WarnEx(PyExc_DeprecationWarning, + "It seems you are waiting for Tcl events " + "to be dispatched.\n" + "Future behavior will wait indefinitely. Adjust the wait time " + "limit with setmainloopwaitattempts().\n" + "If you want to avoid this wait consider polling " + "dispatching() before issuing commands from a thread.\n", 1 + ); PyErr_SetString(PyExc_RuntimeError, "main thread is not in main loop"); return 0; } @@ -1500,7 +1514,8 @@ Tkapp_Call(PyObject *selfptr, PyObject *args) marshal the parameters to the interpreter thread. */ Tkapp_CallEvent *ev; Tcl_Condition cond = NULL; - PyObject *exc_type, *exc_value, *exc_tb; + PyObject *exc_type, *exc_value, *exc_tb; + /* Remove after WaitForMainloop deprecation */ if (!WaitForMainloop(self)) return NULL; ev = (Tkapp_CallEvent*)attemptckalloc(sizeof(Tkapp_CallEvent)); @@ -1777,6 +1792,8 @@ var_invoke(EventFunc func, PyObject *selfptr, PyObject *args, int flags) /* The current thread is not the interpreter thread. Marshal the call to the interpreter thread, then wait for completion. */ + + /* Remove after WaitForMainloop deprecation*/ if (!WaitForMainloop(self)) return NULL; @@ -2477,6 +2494,7 @@ _tkinter_tkapp_createcommand_impl(TkappObject *self, const char *name, return NULL; } + /* Remove after WaitForMainloop deprecation */ if (self->threaded && self->thread_id != Tcl_GetCurrentThread() && !WaitForMainloop(self)) return NULL; @@ -3022,17 +3040,89 @@ Tkapp_WantObjects(PyObject *self, PyObject *args) /*[clinic input] _tkinter.tkapp.willdispatch +Skips main loop wait polling until next call of mainloop finishes + +DEPRECATED + +call(), var_invoke(), and createcommand() will wait indefinitely for main loop +Sets the internal dispatching flag regardless of mainloop dispatch state +May lead to unexpected hangs or inconsistencies in dispatching + [clinic start generated code]*/ static PyObject * _tkinter_tkapp_willdispatch_impl(TkappObject *self) -/*[clinic end generated code: output=0e3f46d244642155 input=d88f5970843d6dab]*/ +/*[clinic end generated code: output=0e3f46d244642155 input=7b46376275304a4a]*/ { + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "willdispatch() is deprecated; in the future it will have no effect.", 1)) + { + return NULL; + } self->dispatching = 1; Py_RETURN_NONE; } +/*[clinic input] +_tkinter.tkapp.dispatching + +Returns the internal dispatching state + +Returns True if the main loop is running. +Returns False if the main loop is not running. + +NOTE: Using update or dooneevent will dispatch events without the main + loop. Dispatching will return False in these cases. + +[clinic start generated code]*/ + +static PyObject * +_tkinter_tkapp_dispatching_impl(TkappObject *self) +/*[clinic end generated code: output=1b0192766b008005 input=ff427bd1ca93bac4]*/ +{ + return PyBool_FromLong(self->dispatching); +} + +/*[clinic input] +_tkinter.tkapp.setmainloopwaitattempts + + new_val: int + / + +Set number of 100 millisecond mainloop wait attempts. + +These are used for threads that call(), var_invoke(), and createcommand(). + +Current default is 10 for a 1 second wait, but future behavior +will be equivalent to an unlimited number. + +Setting this will trigger a DeprecationWarning. + +[clinic start generated code]*/ + +static PyObject * +_tkinter_tkapp_setmainloopwaitattempts_impl(TkappObject *self, int new_val) +/*[clinic end generated code: output=a0867fb187c8946e input=2b60ed355c8d7fac]*/ +{ + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "In future behavior, threads will wait indefinitely " + "for the main thread to dispatch.\n" + "If you want to avoid delays, consider polling dispatching()" + "before issuing commands from a thread instead.", 1)) + { + return NULL; + } + if (new_val < 0) + { + PyErr_SetString(PyExc_ValueError, + "mainloopwaitattempts must be >= 0"); + return NULL; + } + mainloopwaitattempts = new_val; + Py_RETURN_NONE; +} + /**** Tkapp Type Methods ****/ @@ -3252,6 +3342,8 @@ static PyType_Spec Tktt_Type_spec = { static PyMethodDef Tkapp_methods[] = { _TKINTER_TKAPP_WILLDISPATCH_METHODDEF + _TKINTER_TKAPP_DISPATCHING_METHODDEF + _TKINTER_TKAPP_SETMAINLOOPWAITATTEMPTS_METHODDEF {"wantobjects", Tkapp_WantObjects, METH_VARARGS}, {"call", Tkapp_Call, METH_VARARGS}, _TKINTER_TKAPP_EVAL_METHODDEF diff --git a/Modules/clinic/_tkinter.c.h b/Modules/clinic/_tkinter.c.h index 9718986838fbb3..3a49ae0ace0f2c 100644 --- a/Modules/clinic/_tkinter.c.h +++ b/Modules/clinic/_tkinter.c.h @@ -631,7 +631,14 @@ _tkinter_tkapp_loadtk(TkappObject *self, PyObject *Py_UNUSED(ignored)) PyDoc_STRVAR(_tkinter_tkapp_willdispatch__doc__, "willdispatch($self, /)\n" "--\n" -"\n"); +"\n" +"Skips main loop wait polling until next call of mainloop finishes\n" +"\n" +"DEPRECATED\n" +"\n" +"call(), var_invoke(), and createcommand() will wait indefinitely for main loop\n" +"Sets the internal dispatching flag regardless of mainloop dispatch state\n" +"May lead to unexpected hangs or inconsistencies in dispatching"); #define _TKINTER_TKAPP_WILLDISPATCH_METHODDEF \ {"willdispatch", (PyCFunction)_tkinter_tkapp_willdispatch, METH_NOARGS, _tkinter_tkapp_willdispatch__doc__}, @@ -645,6 +652,65 @@ _tkinter_tkapp_willdispatch(TkappObject *self, PyObject *Py_UNUSED(ignored)) return _tkinter_tkapp_willdispatch_impl(self); } +PyDoc_STRVAR(_tkinter_tkapp_dispatching__doc__, +"dispatching($self, /)\n" +"--\n" +"\n" +"Returns the internal dispatching state\n" +"\n" +"Returns True if the main loop is running.\n" +"Returns False if the main loop is not running.\n" +"\n" +"NOTE: Using update or dooneevent will dispatch events without the main\n" +" loop. Dispatching will return False in these cases."); + +#define _TKINTER_TKAPP_DISPATCHING_METHODDEF \ + {"dispatching", (PyCFunction)_tkinter_tkapp_dispatching, METH_NOARGS, _tkinter_tkapp_dispatching__doc__}, + +static PyObject * +_tkinter_tkapp_dispatching_impl(TkappObject *self); + +static PyObject * +_tkinter_tkapp_dispatching(TkappObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _tkinter_tkapp_dispatching_impl(self); +} + +PyDoc_STRVAR(_tkinter_tkapp_setmainloopwaitattempts__doc__, +"setmainloopwaitattempts($self, new_val, /)\n" +"--\n" +"\n" +"Set number of 100 millisecond mainloop wait attempts.\n" +"\n" +"These are used for threads that call(), var_invoke(), and createcommand().\n" +"\n" +"Current default is 10 for a 1 second wait, but future behavior\n" +"will be equivalent to an unlimited number.\n" +"\n" +"Setting this will trigger a DeprecationWarning."); + +#define _TKINTER_TKAPP_SETMAINLOOPWAITATTEMPTS_METHODDEF \ + {"setmainloopwaitattempts", (PyCFunction)_tkinter_tkapp_setmainloopwaitattempts, METH_O, _tkinter_tkapp_setmainloopwaitattempts__doc__}, + +static PyObject * +_tkinter_tkapp_setmainloopwaitattempts_impl(TkappObject *self, int new_val); + +static PyObject * +_tkinter_tkapp_setmainloopwaitattempts(TkappObject *self, PyObject *arg) +{ + PyObject *return_value = NULL; + int new_val; + + new_val = _PyLong_AsInt(arg); + if (new_val == -1 && PyErr_Occurred()) { + goto exit; + } + return_value = _tkinter_tkapp_setmainloopwaitattempts_impl(self, new_val); + +exit: + return return_value; +} + PyDoc_STRVAR(_tkinter__flatten__doc__, "_flatten($module, item, /)\n" "--\n" @@ -867,4 +933,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=ab311480dd044fe4 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8a843a5f7728537c input=a9049054013a1b77]*/