Closed fijam closed 3 years ago
This is a dupe of https://github.com/chriskiehl/Gooey/issues/232. Closing in favor of that issue.
Good news is that I'm actually working on this feature right now ^_^ It's a bit trickier than expected thanks to Windows being Windows, but it should be ready to go soon.
You'll be able to pass in an arbitrary signal to control what happens when you click the stop button. For instance,
@Gooey(terminate_using=signal.CTRL_C_EVENT)
Which could be caught in your app as a KeyboardInterrupt
.
That's great news. Thanks!
Hey @fijam , I've added the ability to customize the stop strategy to the release branch. This'll let you catch when Gooey is doing a force stop. Wanna give it try and let me know if you hit any issues? Getting the signals sorted out on Windows took a fair bit of fiddling, so I'd love to hear if you run into any problems.
Branch: https://github.com/chriskiehl/Gooey/tree/1.0.9-release
You can checkout the docs here: https://github.com/chriskiehl/Gooey/blob/1.0.9-release/docs/Gracefully-Stopping.md
tl;dr: you can now pass a signal
to the gooey decorator.
import signal
@Gooey(shutdown_signal=signal.CTRL_C_EVENT)
def main():
...
Hi, I tested it quickly and while it seems to work in the sense that except clause is executed (yay!), pressing Yes on the Stop Task modal immediately closes the Gooey window and the except clause continues to run in the background. Is this the intended behavior or a problem between keyboard and chair? It would be nice to communicate to the user that the program is cleaning up (it can take a few minutes for me). Can the window be kept up until the process actually exits?
My environment is Python (3.8.10) on Windows 10 (19043.1110)
Thanks for testing!
Arg.. these are the Windows quirks I mentioned which is making this tricky... Signals get sent to the whole process group, which I thought I was able to ignore in Gooey land, but I guess it's unreliable. So, it's definitely unexpected that Gooey is itself is terminating and not the desired behavior.
Are you seeing the problem when you send the CTRL_C_EVENT
or for all events (CTRL_C_EVENT
, CTRL_BREAK_EVENT
, SIGTERM
)?
One option is to change how Gooey spawns the process such that a new ProcessGroup is made. However, what sucks is that, for whatever reason, you can only send CTRL_BREAK
signals under that setup, which cannot be caught in the child process via the nicer KeyboardInterrupt
exception and instead require a signal handler to be added.
I tested with a handler and CTRL_BREAK_EVENT
and looks like it works! Cheers.
It's too bad that you can't handle that with KeyboardInterrupt
then but I guess you can't have your cake and eat it too.
I have also noticed that the exception handler does not work in an application built with PyInstaller (4.5) which may be a whole 'nother can of worms.
Ok! I think I've got this a little more pinned down. All signals should work as expected, and none of them should cause the parent Gooey process to inadvertently close. It took trawling through the Python issue tracker to finally figure out that both the Python docs and Microsoft's docs are either outright incorrect, or missing very important info with regards to how signals get handled in Windows. TL;DR: I think we're in good shape (at least on Windows).
Release branch has the latest fixes.
Thanks again for testing! It's super helpful having additional feedback for fiddly OS stuff like this! ^_^
@Duke-Pickle @MrSassyBritches (for visibility, since there was past interest in this issue)
Hi again, I re-tested the updated 1.0.9 release branch and now CTRL_C_EVENT / KeyboardInterrupt works as expected. Nice!
I have also been fumbling around some more with pyinstaller and discovered that interrupt handling only works if the app is built in --onedir
mode with console=True
(https://gist.github.com/fijam/d5ef98f365d2ba6c4a3f2984eebc5a61). Looks like a pyinstaller bug.
By the way, I see a similar behavior when packaging with cx_freeze: an interrupt works well if there is a console window alongside Gooey, but fails without console shown (base = "Win32GUI"
). In the second case, clicking Stop once does nothing, and a second click kills the process without raising an exception.
Hi @chriskiehl I was wondering if this still holds true for a function being called from another script? I'm using the decorator
@Gooey(shutdown_signal=signal.CTRL_C_EVENT)
main():
...
When Gooey parses the arguments, those are sent to another function, and within that function if a user presses stop, I'd like to be able to cut out early, but then still save some results. When doing as @fijam using the combination of CTRL_C_EVENT / KeyboardInterrupt
, when pressing stop, I get the normal error of data corruption, and nothing after the except clause (within in the function) runs. I'm guessing the except clauses needs to be on the outside of the function being called?
I'm not sure if this is within the scope of the project, but would it be possible to raise an exception when the Stop button is clicked (much like you would have a KeyboardInterrupt when a user interrupts the program with Ctrl-C) so you can clean up?