Unable to KeyboardInterrupt while loop - python

I have a python script that runs on an infinite loops. In the loop multiprocess and async functions happen so normally I catch KeyboardInterrupt to properly kill all the processes.
Using similar code somehow on one of the loops I am unable to catch the KeyboardInterrupt the loop just keeps going.
logic goes like that:
try:
while True:
do stuff
except (KeyboardInterrupt, SystemExit):
exit cleanly
Normally I would suspect a blanket try ... except somewhere in the children functions but I went over the whole code base and while there is a lot of error catching everything is specific.
Is there a way to trace errors and somehow figure out where the KeyboardInterrupt is caught ?
Thank you
****** Edit after some debugging...
So I disabled the code part by part until I cornered the bug:
Somewhere in the code I was calling a method that was missing self and was not marked as #staticmethod.
Changing that fixed my issue.

This works for me.
try:
while True:
print(1)
except KeyboardInterrupt:
raise
EDIT:
Actually read your question, I won't be able to tell you why it's not raising an error without seeing the rest or more of the code.

Related

Do something specific if python scripts exits because of error

My script sometimes errors which is fine, but I have to manually restart the script.
Anyone knows how to make it so that it actually works infinitely even if it crashed 50 times, currently what I have only works for 1 crash.
try:
while True:
do_main_logic()
except:
continue
I have tried many scripts, but I am expecting it to just continue no matter what.
Wrap only do_main_logic() in a try-except block, not the full loop.
while True:
try:
do_main_logic()
except:
pass
Caveat: Catching bare exceptions is frowned upon for good reason. It would be better if you could specify the type(s) of exceptions you expect. To cite the Programming Recommendations in the Style Guide for Python Code:
When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause.
A bare except: clause will catch SystemExit and KeyboardInterrupt exceptions, making it harder to interrupt a program with Control-C, and can disguise other problems. If you want to catch all exceptions that signal program errors, use except Exception: (bare except is equivalent to except BaseException:).
You want to do it forever ?
Just add a while :)
while True:
try:
while True:
do_main_logic()
except:
continue

Is it possible to create never crashing python code with exception handling?

I wonder if the below python code (specifically http server) ever crashes? Assuming that there is no grammer error in any of the library code(already compiled), what I think that handling the exceptions in a while loop should be sufficient for this code not to crash anytime. I tried the below code for a while and never crashed, but I wonder if theoretically or practically possible for this program to crash?
while True:
try:
server = HTTPServer(('', PORT_NUMBER), myHandler)
server.serve_forever()
except:
try:
server.socket.close()
except:
pass
The actual reason I am asking this question that I don't want to deal with UNIX staff to watch the process and restart it if it crashes. Is the above solution sufficient?
Thanks.
If "except" block has worng code, it can crash cause of it. I mean, something like that:
# path/to/py3
FOO = [1,2,3]
try:
# index out of bound, try block has error, so it goes ahead and executes except-block
print(FOO[4])
except:
# if there is some kind of error, like Syntax error, program can crash
print "Index out of bound!"
print("2"+2)
print(FOO["BAR"])
but if exception block has the correct logic too, then programm should work without crashing
Like Klaus D. already mentioned in his comment, there can be cases where the socket close code in your except block crashes. You could optionally also throw a try except around that as well...
Another option is to use something like this (no UNIX involved):
http://supervisord.org/
It's easy to run and will automatically restart your program if it crashes.

Is there anything in Python like a try-finally except it still raises exceptions normally?

Suppose I want to run some function in Python, ensuring that whether it fails or not some cleanup code is always run. Something like this:
try: some_function()
finally: cleanup()
Ok, simple enough. But hold up! If any exceptions occur in the try block, they'll be suppressed. So really this construct did more than I wanted. All I really wanted to do was make sure some cleanup code runs after the function whether it finishes successfully or not. I still want any exceptions which occur in my function to happen normally. Perhaps that would look something like this:
do: some_function()
finally: cleanup()
Of course, that isn't real Python code. The actual way which I've found to do this is like follows:
try: some_function()
except Exception as error: raise error
finally: cleanup()
Eww, gross. I'm adding an extra line to re-throw an exception which I wanted to just happen normally in the first place. And additionally the stack trace now has an extra line in it showing the except Exception as error: raise error bit. This seems less than ideal to me, but it also seems to be the only way to accomplish what I'm trying to do.
So is this really the way I'm supposed to go about this?
If yes, then I have a further question: Why doesn't Python have a dedicated construct for simply ensuring some block of code runs whether some other block succeeds or not?
As far as my small mind is concerned, this whole idea has little to do with exception handling, since I don't actually want to keep exceptions from occurring where they normally would in the stack trace. Therefore, forcing people to use a try-except-finally construct seems just weird to me.
Python does!
try:
1/0
finally:
print("Hello, world!")
print("This will not print.")
Alright so as #user2357112 pointed out it looks like I somehow had the wild misconception that the try part of a try-except-finally construct is what catches exceptions. If anyone else gets confused similarly... it's the except bit that does the catching. Pretty obvious after some thinking, but hey everyone has brain farts sometimes.

Python 2.7 - weird behaviour on finally clause

I've run into a very strange problem, that is making me wonder if I understand exception handling at all.
I have a code (that I'll post at the end) that looks more or less like this:
try:
doSomething()
finally:
print 'bye'
The code in the finally clause is not being executed when I exit my program via ctrl+c.
To make matters worse, now consider the following:
try:
doSomething()
except: # this could be replaced by except Exception, it doesn't matter
print 'something'
finally:
print 'bye'
Now the code in the except clause is not executed.. but the code in the finally clause is!
I realize this has to be the fault of the code executed by doSomething(). But my question is, how is it even possible? I thought we could be 100% confident that finally clauses always got executed.
Here goes the real code. It's running on a raspberry pi 3. It's an adaptation of the code found here.
import RPi.GPIO as GPIO, time
GPIO.setmode(GPIO.BCM)
# Define function to measure charge time
def RCtime (PiPin):
# Discharge capacitor
GPIO.setup(PiPin, GPIO.OUT)
GPIO.output(PiPin, GPIO.LOW)
time.sleep(.1)
time1 = time.time()
GPIO.setup(PiPin, GPIO.IN)
if (GPIO.input(PiPin) == GPIO.LOW):
GPIO.wait_for_edge(PiPin, GPIO.RISING, timeout=1000)
time_elap = time.time()-time1
return time_elap*1e3
# Main program loop
try:
while True:
print RCtime(4) # Measure timing using GPIO4
except Exception:
print '---------got ya-----------------'
finally:
print '---Finaly---'
GPIO.cleanup() # this ensures a clean exit
To be more specific, the behaviour depicted appears when the program is waiting at the GPIO.wait_for_edge(PiPin, GPIO.RISING, timeout=1000) line.
If your code does not handle the KeyboardInterrupt exception, the system has to do it: which means it kills the code. Instead, if you have an exception handler, your code takes over, leaves the loop and then executes the finally block.
Well, this seems to be a bug related to the way RPi.GPIO handles signals (such as KeyboardInterrupt). This module invokes some functions written in c. The function is sort of catching the KeyboardInterrupt and throwing an exception, but it doesn't do it properly. Instead of getting only one exception, the two exceptions get "stacked". Because of that, the first thing that run after an exception in my code is going to be terminated by the second exception. If I don't include an except block, the finally block gets executed after the first exception, and is terminated by the second exception. If I include an except block, it tries to run after the first exception, it fails because of the second exception, and then it goes to the finally block.
I only found one forum where people deal with a similar problem.
After my previous experiment I was curious how this works with
input() [https://svn.python.org/projects/python/trunk/Parser/myreadline.c]. I replaced the sem.acquire() with raw_input() and ran the same
tests. Now the inner exception is really taken so it works like the OP
expected. The exception, however is KeyboardInterrupt, not the special
exception from the IPC module.
So I looked in the source code how they did it:
The code is in Parser/myreadline.c.
This code for input in function calls PyErr_CheckSignals() and
PyOS_InterruptOccurred() for a proper handling of the interrupt. So it
seems the OP should do something similar. Onl;y to deliver the custom
error you will have to do some other stuff. I don't know what but maybe
calling PyErr_SetString is sufficient as it might overwrite the
KeyboardInterrupt stuff.

Python3: purge exception chain?

I'm trying to raise an exception within an except: block but the interpreter tries to be helpful and prints stack traces 'by force'. Is it possible to avoid this?
A little bit of background information:
I'm toying with urwid, a TUI library for python. The user interface is started by calling urwid.MainLoop.run() and ended by raising urwid.ExitMainLoop(). So far this works fine but what happens when another exception is raised? E.g. when I'm catching KeyboardInterrupt (the urwid MainLoop does not), I do some cleanup and want to end the user interface - by raising the appropriate exception. But this results in a screen full of stack traces.
Some little research showed python3 remembers chained exceptions and one can explicitly raise an exception with a 'cause': raise B() from A(). I learned a few ways to change or append data regarding the raised exceptions but I found no way to 'disable' this feature. I'd like to avoid the printing of stack traces and lines like The above exception was the direct cause of... and just raise the interface-ending exception within an except: block like I would outside of one.
Is this possible or am I doing something fundamentally wrong?
Edit:
Here's an example resembling my current architecture, resulting in the same problem:
#!/usr/bin/env python3
import time
class Exit_Main_Loop(Exception):
pass
# UI main loop
def main_loop():
try:
while True:
time.sleep(0.1)
except Exit_Main_Loop as e:
print('Exit_Main_Loop')
# do some UI-related clean up
# my main script
try:
main_loop()
except KeyboardInterrupt as e:
print('KeyboardInterrupt')
# do some clean up
raise Exit_Main_Loop() # signal the UI to terminate
Unfortunately I can't change main_loop to except KeyboardInterrupt as well. Is there a pattern to solve this?
I still don't quite understand your explanation, but from the code:
try:
main_loop()
except KeyboardInterrupt as e:
print('KeyboardInterrupt')
# do some clean up
raise Exit_Main_Loop() # signal the UI to terminate
There is no way that main_loop could ever see the Exit_Main_Loop() exception. By the time you get to the KeyboardInterrupt handle, main_loop is guaranteed to have already finished (in this case, because of an unhandled KeyboardInterrupt), so its exception handler is no longer active.
So, what happens is that you raise a new exception that nobody catches. And when an exception gets to the top of your code without being handled, Python handles it automatically by printing a traceback and quitting.
If you want to convert one type of exception into another so main_loop can handle it, you have to do that somewhere inside the try block.
You say:
Unfortunately I can't change main_loop to except KeyboardInterrupt as well.
If that's true, there's no real answer to your problem… but I'm not sure there's a problem in the first place, other than the one you created. Just remove the Exit_Main_Loop() from your code, and isn't it already doing what you wanted? If you're just trying to prevent Python from printing a traceback and exiting, this will take care of it for you.
If there really is a problem—e.g., the main_loop code has some cleanup code that you need to get executed no matter what, and it's not getting executed because it doesn't handle KeyboardInterrupt—there are two ways you could work around this.
First, as the signal docs explain:
The signal.signal() function allows to define custom handlers to be executed when a signal is received. A small number of default handlers are installed: … SIGINT is translated into a KeyboardInterrupt exception.
So, all you have to do is replace the default handler with a different one:
def handle_sigint(signum, frame):
raise ExitMainLoop()
signal.signal(signal.SIGINT, handle_sigint)
Just do this before you start main_loop, and you should be fine. Keep in mind that there are some limitations with threaded programs, and with Windows, but if none of those limitations apply, you're golden; a ctrl-C will trigger an ExitMainLoop exception instead of a KeyboardInterrupt, so the main loop will handle it. (You may want to also add an except ExitMainLoop: block in your wrapper code, in case there's an exception outside of main_loop. However, you could easily write a contextmanager that sets and restores the signal around the call to main_loop, so there isn't any outside code that could possibly raise it.)
Alternatively, even if you can't edit the main_loop source code, you can always monkeypatch it at runtime. Without knowing what the code looks like, it's impossible to explain exactly how to do this, but there's almost always a way to do it.

Categories