| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # test_tkinter_vwait_mainloop_stdin.py | ||
| import unittest | ||
| import subprocess | ||
| import sys | ||
| import time | ||
| from test import support | ||
| from test.support import import_helper | ||
|
|
||
| tkinter = import_helper.import_module("tkinter") | ||
|
|
||
|
|
||
| @unittest.skipUnless(support.has_subprocess_support, "test requires subprocess") | ||
| @unittest.skipIf(sys.platform == "win32", "test is not supported on Windows") | ||
| class TkVwaitMainloopStdinTest(unittest.TestCase): | ||
|
|
||
| def run_child(self): | ||
| # Full Python script to execute in the child | ||
| code = r""" | ||
| import tkinter as tk | ||
| import time | ||
|
|
||
| interp = tk.Tcl() | ||
|
|
||
| def do_vwait(): | ||
| start_wall = time.time() | ||
| start_cpu = time.process_time() | ||
| interp.eval("vwait myvar") | ||
| end_cpu = time.process_time() | ||
| end_wall = time.time() | ||
| cpu_frac = (end_cpu - start_cpu) / (end_wall - start_wall) | ||
| print(cpu_frac) | ||
|
|
||
| # Schedule vwait and release | ||
| interp.after(100, do_vwait) | ||
| interp.after(500, lambda: interp.setvar("myvar", "done")) | ||
| """ | ||
|
|
||
| # Start child in interactive mode, but use -c to execute code immediately | ||
| proc = subprocess.Popen( | ||
| [sys.executable, "-i", "-c", code], | ||
| stdin=subprocess.PIPE, | ||
| stdout=subprocess.PIPE, | ||
| stderr=subprocess.PIPE, | ||
| ) | ||
| return proc | ||
|
|
||
| def test_vwait_stdin_busy_loop(self): | ||
| proc = self.run_child() | ||
|
|
||
| # Wait until vwait likely started | ||
| time.sleep(0.15) | ||
|
|
||
| # Send input to stdin to trigger the bug | ||
| proc.stdin.write(b"\n") | ||
|
|
||
| # Ensure child exits cleanly | ||
| proc.stdin.write(b"exit()\n") | ||
|
|
||
| stdout, stderr = proc.communicate() | ||
| out = stdout.decode("utf-8", errors="replace") | ||
| err = stderr.decode("utf-8", errors="replace") | ||
|
|
||
| if proc.returncode != 0: | ||
| self.fail(f"Child exited with {proc.returncode}\nSTDOUT:\n{out}\nSTDERR:\n{err}") | ||
|
|
||
| # Extract CPU fraction printed by child | ||
| cpu_frac = float(out) | ||
|
|
||
| # Fail if CPU fraction is too high (indicative of busy-loop) | ||
| self.assertLess(cpu_frac, 0.5, | ||
| f"CPU usage too high during vwait with stdin input (fraction={cpu_frac:.2f})") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| Prevents tkinter ending up in a busy loop when ``vwait`` (and other Tcl commands | ||
| that run their own tight event loop) are used with Python in interactive mode, | ||
| and the user types any input on stdin while ``vwait`` is still running. Patch | ||
| by Michiel de Hoon. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| Expand Up | @@ -3333,7 +3333,9 @@ static int stdin_ready = 0; | |||||
| static void | ||||||
| MyFileProc(void *clientData, int mask) | ||||||
| { | ||||||
| int *tfile = clientData; | ||||||
| stdin_ready = 1; | ||||||
| Tcl_DeleteFileHandler(*tfile); | ||||||
| } | ||||||
| #endif | ||||||
|
|
||||||
| Expand All | @@ -3350,7 +3352,7 @@ EventHook(void) | |||||
| errorInCmd = 0; | ||||||
| #ifndef MS_WINDOWS | ||||||
| tfile = fileno(stdin); | ||||||
| Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, NULL); | ||||||
| Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, &tfile); | ||||||
|
Comment thread
Copy link
Copy Markdown
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality
Suggested change
Sorry, something went wrong.
All reactions
|
||||||
| #endif | ||||||
| while (!errorInCmd && !stdin_ready) { | ||||||
| int result; | ||||||
| Expand All | @@ -3375,14 +3377,14 @@ EventHook(void) | |||||
| if (result < 0) | ||||||
| break; | ||||||
| } | ||||||
| #ifndef MS_WINDOWS | ||||||
| Tcl_DeleteFileHandler(tfile); | ||||||
| #endif | ||||||
| if (errorInCmd) { | ||||||
| errorInCmd = 0; | ||||||
| PyErr_SetRaisedException(excInCmd); | ||||||
| excInCmd = NULL; | ||||||
| PyErr_Print(); | ||||||
| #ifndef MS_WINDOWS | ||||||
| if (!stdin_ready) Tcl_DeleteFileHandler(tfile); | ||||||
| #endif | ||||||
| } | ||||||
| PyEval_SaveThread(); | ||||||
| return 0; | ||||||
| Expand Down | ||||||
| Back | FazBrowse Home | New Git URL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualitySorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low QualityThanks for looking into this. This is an important point.
The while-loop in EventHook can only be exited if either stdin_ready is 1 or errorInCmd is 1.
stdin_ready is set to 1 only in MyFileProc, which also calls Tcl_DeleteFileHandler. As the file handler is deleted, there won't be a dangling pointer.
But I was missing a call to Tcl_DeleteFileHandler for the case in which errorInCmd is 1, and MyFileProc was never called. Then then file handler will stay alive and will keep a dangling pointer to tfile after EventHook exits.
I fixed this by adding a call to Tcl_DeleteFileHandler to the if (errorInCmd) {... to ensure that the file handler is deleted before EventHook exits, and no pointer to tfile remains,
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.