You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

91 lines
3.4 KiB

  1. """
  2. Utilities for patching Windows so that CTRL-C signals
  3. can be received by process groups.
  4. The best resource for understanding why this is required is the Python
  5. Issue here: https://bugs.python.org/issue13368
  6. **The official docs from both Python and Microsoft cannot be trusted due to inaccuracies**
  7. The two sources directly conflict with regards to what signals are possible on Windows
  8. under which circumstances.
  9. Python's docs:
  10. https://bugs.python.org/issue13368
  11. On Windows, SIGTERM is an alias for terminate(). CTRL_C_EVENT and
  12. CTRL_BREAK_EVENT can be sent to processes started with a creationflags
  13. parameter which includes CREATE_NEW_PROCESS_GROUP.
  14. Microsoft's docs:
  15. https://docs.microsoft.com/en-us/windows/console/generateconsolectrlevent
  16. Generates a CTRL+C signal. This signal cannot be generated for process groups.
  17. Another piece of the puzzle:
  18. https://docs.microsoft.com/en-us/windows/console/handlerroutine#remarks
  19. Each console process has its own list of HandlerRoutine functions. Initially,
  20. this list contains only a default handler function that calls ExitProcess. A
  21. console process adds or removes additional handler functions by calling the
  22. SetConsoleCtrlHandler function, which does not affect the list of handler
  23. functions for other processes.
  24. The most important line here is: "Initially, this list contains only the default
  25. handler function that calls exit Process"
  26. So, despite what Microsoft's docs for ctrlcevent state, it IS possible to send
  27. the ctrl+c signal to process groups **IFF** the leader for that process group has
  28. the appropriate handler installed.
  29. We can solve this transparently in Gooey land by installing the handler on behalf
  30. of the user when required.
  31. """
  32. import sys
  33. import signal
  34. from textwrap import dedent
  35. def requires_special_handler(platform, requested_signal):
  36. """
  37. Checks whether we need to attach additional handlers
  38. to this process to deal with ctrl-C signals
  39. """
  40. return platform.startswith("win") and requested_signal == signal.CTRL_C_EVENT
  41. def install_handler():
  42. """
  43. Adds a Ctrl+C 'handler routine'. This allows the CTRL-C
  44. signal to be received even when the process was created in
  45. a new process group.
  46. See module docstring for additional info.
  47. """
  48. assert sys.platform.startswith("win")
  49. import ctypes
  50. help_msg = dedent('''
  51. Please open an issue here: https://github.com/chriskiehl/Gooey/issues
  52. GOOD NEWS: THERE IS A WORK AROUND:
  53. Gooey only needs to attach special Windows handlers for the CTRL-C event. Consider using
  54. the `CTRL_BREAK_EVENT` or `SIGTERM` instead to get past this error.
  55. See the Graceful Shutdown docs for information on how to catch alternative signals.
  56. https://github.com/chriskiehl/Gooey/tree/master/docs
  57. ''')
  58. try:
  59. kernel32 = ctypes.WinDLL('kernel32')
  60. if kernel32.SetConsoleCtrlHandler(None, 0) == 0:
  61. raise Exception(dedent('''
  62. Gooey was unable to install the handler required for processing
  63. CTRL-C signals. This is a **very** unexpected failure.
  64. ''') + help_msg)
  65. except OSError as e:
  66. raise OSError(dedent('''
  67. Gooey failed while trying to find the kernel32 module.
  68. Gooey requires this module in order to attach handlers for CTRL-C signal.
  69. Not being able to find this is **very** unexpected.
  70. ''') + help_msg)