5

I want to try capturing PyCharm's stop signal (when stop is pressed) in a try block, but I cannot figure out what this signal is or how to capture it in code. JetBrains doesn't provide insight into this in their documentation.

I've tried catching it as BaseException but it does not seem to be an exception at all.

Is this programmatically capturable at all?

bad_coder
  • 11,289
  • 20
  • 44
  • 72
Jack Avante
  • 1,405
  • 1
  • 15
  • 32
  • Did you try using the top of [the hierarchy](https://docs.python.org/3/library/exceptions.html#exception-hierarchy)? – jonrsharpe Oct 11 '21 at 12:48
  • Just have, it is unfortunately not even BaseException – Jack Avante Oct 11 '21 at 14:04
  • Unfortunately this is not possible. Pycharm sends a SIGKILL and you have no way of intercepting it... If you need to catch it I can only suggest running your script from the terminal with something like `python my script.py` and then a try-except block can catch the `KeyboardInterrupt` raised when you do a Ctrl-C in the terminal. – Matteo Zanoni Oct 11 '21 at 14:46

2 Answers2

5

You can catch the exception when you press the stop button and log or examine the traceback. However when you run the debugger pydev.py what happens is your are launching a client/server process that will execute your program as a client. Notice the console when you launch the debugger:

C:\path_to_your_python_binary\python.exe "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py" --multiproc --qt-support=auto --client 127.0.0.1 --port 51438 --file C:/path_to/your_module.py C:\path_to_working_directory
Connected to pydev debugger (build 212.5080.64)

By pressing stop you are actually terminating the pydev.py server process, so the debugger won't stay frozen on your modules except clause as usual. You can verify it by pressing the stop button in the print statement of the following snippet and printing or logging the traceback (example code adapted from this answer).

try:
    print("Press stop button here.")
except BaseException as err:
    raise Exception('Smelly socks').with_traceback(err.__traceback__)

See the screenshot:

enter image description here

The stacktrace will give:

Traceback (most recent call last):
  File "C:/path_to/your_module.py", line 2, in <module>
    print("Press stop button here.")
  File "C:/path_to/your_module.py", line 2, in <module>
    print("Press stop button here.")
  File "_pydevd_bundle\pydevd_cython_win32_39_64.pyx", line 1589, in _pydevd_bundle.pydevd_cython_win32_39_64.ThreadTracer.__call__
  File "_pydevd_bundle\pydevd_cython_win32_39_64.pyx", line 929, in _pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch
  File "_pydevd_bundle\pydevd_cython_win32_39_64.pyx", line 920, in _pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch
  File "_pydevd_bundle\pydevd_cython_win32_39_64.pyx", line 317, in _pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py", line 1147, in do_wait_suspend
    self._do_wait_suspend(thread, frame, event, arg, suspend_type, from_this_thread)
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py", line 1162, in _do_wait_suspend
    time.sleep(0.01)
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py", line 2173, in <module>
    main()
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py", line 2164, in main
    globals = debugger.run(setup['file'], None, None, is_module)
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py", line 1476, in run
    return self._exec(is_module, entry_point_fn, module_name, file, globals, locals)
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py", line 1483, in _exec
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "C:/path_to/your_module.py", line 4, in <module>
    raise Exception('Smelly socks').with_traceback(err.__traceback__)
  File "C:/path_to/your_module.py ", line 2, in <module>
    print("Press stop button here.")
  File "C:/path_to/your_module.py ", line 2, in <module>
    print("Press stop button here.")
  File "_pydevd_bundle\pydevd_cython_win32_39_64.pyx", line 1589, in _pydevd_bundle.pydevd_cython_win32_39_64.ThreadTracer.__call__
  File "_pydevd_bundle\pydevd_cython_win32_39_64.pyx", line 929, in _pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch
  File "_pydevd_bundle\pydevd_cython_win32_39_64.pyx", line 920, in _pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.trace_dispatch
  File "_pydevd_bundle\pydevd_cython_win32_39_64.pyx", line 317, in _pydevd_bundle.pydevd_cython_win32_39_64.PyDBFrame.do_wait_suspend
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py", line 1147, in do_wait_suspend
    self._do_wait_suspend(thread, frame, event, arg, suspend_type, from_this_thread)
  File "C:\Program Files\JetBrains\PyCharm 2019.3.2\plugins\python\helpers\pydev\pydevd.py", line 1162, in _do_wait_suspend
    time.sleep(0.01)
Exception: Smelly socks

Process finished with exit code 1

What the stacktrace shows is the exception thrown when you press the stop button is a KeyboardInterrupt but you could also write the catch clause using an exception that's higher in the Exception Hierarchy (e.g. BaseException) the results would be the same.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
  • So when OP said it wasn't caught by `BaseException`, you think that's incorrect? – nanotek Oct 11 '21 at 15:11
  • @nanotek the code in the answer shows the result of catching `BaseException`, if you change the catch to `KeyboardInterrupt` you get the same result. What I think the OP meant was the debugger doesn't stop as it usually would in the `catch` block that can be unexpected. – bad_coder Oct 11 '21 at 15:14
  • Thanks for your answers! Your answer clarifies what may be happening, but the one by @nanotek gives a simple solution. Not sure which to mark as a solution in this case. I should specify that I wasn't actually using debug, just plain run. – Jack Avante Oct 11 '21 at 16:40
3

I wasn't able to replicate the other answers as the stop button being sent as a keyboard interrupt. I do believe it's possible for the stop button to be implemented differently on different versions of PyCharm and OS (I'm on Linux where a different answer seems to be Windows, but I'm not positive on many aspects here)

It seems to me that a kill signal is being sent, and it doesn't seem like catching that as an exception works (for me). However, I was able to somewhat catch a kill signal by referencing this post that talks about catching kill signals in Python and killing gracefully. Below is the code I used. When I press the stop button I see Hello world, but I do NOT see foobar.

Also, the debugger is NOT able to be caught for me by breakpoints in handler_stop_signals by doing this, but I do see the text. So I'm not sure if this is actually going to answer your question or not, based on your needs. Also note I would never actually write code like this (using globals), but it was the simplest answer I was able to come up with.

import signal
import time

run = True

def handler_stop_signals(signum, frame):
    global run
    print("Hello world")
    run = False

signal.signal(signal.SIGINT, handler_stop_signals)
signal.signal(signal.SIGTERM, handler_stop_signals)

while run:
    try:
        time.sleep(20) # do stuff including other IO stuff
    except: BaseException as e:
        print('foobar')
bad_coder
  • 11,289
  • 20
  • 44
  • 72
nanotek
  • 2,959
  • 15
  • 25