How to write an infinite loop that an exception can't break? - python

Is it possible to write an infinite loop that exceptions can't break out of even when the exceptions happen between executions of the loop body? If so, how? (These are the sort of silly things I think about when I ponder robustness.)
For example, consider the following code:
import time
def slow_true():
print 'waiting to return True...'
time.sleep(1.0)
return True
while slow_true():
try:
print 'in loop body'
raise RuntimeError('testing')
except:
print 'caught exception, continuing...'
continue
I can easily break out of the loop with Ctrl-C while Python is executing slow_true(). Even if I replace while slow_true(): with while True: there is theoretically a small window of time (between executions of the body) where a SIGINT can cause the script to exit.
I realize I can implement a SIGINT handler to effectively disable Ctrl-C, but that's not the point of this question.
I could wrap the loop with another infinite loop and move the try/except out a level like so:
import time
def slow_true():
print 'waiting to return True...'
time.sleep(1.0)
return True
while True:
try:
while slow_true():
print 'in loop body'
raise RuntimeError('testing')
except:
print 'caught exception, restarting...'
continue
break
This would make it much harder to break out of the loop (two exceptions would have to be raised back-to-back at just the right times), but I think it's still theoretically possible.

One definitely not recommended option is to override the excepthook method.
import sys
def exception_hook(exception_type, value, traceback):
your_loop_function()
sys.excepthook = exception_hook
Alternative solution using signals (less bad):
import signal
def interrupt(signal, frame):
your_loop_function()
signal.signal(signal.SIGINT, interrupt)

Related

Is there a way to break out of the time.sleep() method? [duplicate]

I need to break from time.sleep() using ctrl c.
While 1:
time.sleep(60)
In the above code when the control enters time.sleep function an entire 60 seconds needs to elapsed for python to handled the CTRL C
Is there any elegant way to do it. such that I can interrupt even when the control is in time.sleep function
edit
I was testing it on a legacy implementation which uses python 2.2 on windows 2000 which caused all the trouble . If I had used a higher version of python CTRL C would have interrupted the sleep() . I did a quick hack by calling sleep(1) inside a for loop . which temporarily fixed my issue
The correct answer is to use python stdlib's threading.Event
Sure you can tune down your sleep interval so you sleep for very short periods, but what if you actually want to run your loop once every 60s? Then you need to do more work to determine if it's time to run or just keep sleeping. Furthermore, you're still technically blocking but for only a short period of time. Contrast to threading.Event:
from threading import Event
exit = Event()
def main():
while not exit.is_set():
do_my_thing()
exit.wait(60)
print("All done!")
# perform any cleanup here
def quit(signo, _frame):
print("Interrupted by %d, shutting down" % signo)
exit.set()
if __name__ == '__main__':
import signal
for sig in ('TERM', 'HUP', 'INT'):
signal.signal(getattr(signal, 'SIG'+sig), quit);
main()
When the signal handler calls exit.set(), the main thread's wait() call will immediately be interrupted.
Now, you could use an Event to signal that there's more work to do, etc. But in this case it does double duty as a convenient indicator that we want to quit (e.g. the while not exit.is_set() part.)
You also have the option to put any cleanup code after your while loop.
Not sure what the sense of this code is - but if necessary use a shorter sleep() interval and put a for loop around it:
for i in range(60):
sleep(1)
Catching the KeyboardInterrupt exception using try..except is straight-forward
The KeyboardInterrupt exception is raised when a user hits the interrupt key, Ctrl-C. In python this is translated from a SIGINT signal. That means, you can get handle it however you want using the signal module:
import signal
def handler(signum, frame):
print("do whatever, like call thread.interrupt_main()")
signal.signal(signal.SIGINT, handler)
print("Waiting for SIGINT...")
signal.pause()
That way, you can do whatever you want at the receipt of a keyboard interrupt.
The most elegant solution is certainly threading.Event, though if you only need a quick hack, this code works well :
import time
def main():
print("It’s time !")
if __name__ == "__main__":
print("press ctrl-c to stop")
loop_forever = True
while loop_forever:
main()
try:
time.sleep(60)
except KeyboardInterrupt:
loop_forever = False
I tried your code with python versions 2.5, 2.6, 3 under Linux and all throw "KeyboardInterrupt" exception when hitting CTRL-C.
Maybe some exception handling catches the Interrupt or your problem is like this:
Why is KeyboardInterrupt not working in python?
Based on #Andreas Jung answer
for i in range(360):
try:
sleep(1)
except KeyboardInterrupt:
sys.exit(0)
Figured I'd throw this in.
import time
def sleep(seconds):
for i in range(seconds):
try:
time.sleep(1)
except KeyboardInterrupt:
print("Oh! You have sent a Keyboard Interrupt to me.\nBye, Bye")
break
sleep(60)

Catch all exceptions in a class

I'm wondering if anybody would have an idea to catch all exceptions in a running thread. My program is started as follow, by a service
def main():
global RUNNING
signal.signal(signal.SIGINT, stopHandler)
signal.signal(signal.SIGTERM, stopHandler)
projectAlice = ProjectAlice()
try:
while RUNNING:
time.sleep(0.1)
except KeyboardInterrupt:
pass
finally:
projectAlice.onStop()
_logger.info('Project Alice stopped, see you soon!')
So a CTRL-C or a signal can stop it. ProjectAlice runs forever and answers to mqtt topics that are sent by Snips. It uses paho-mqtt with loop_forever. As it's pretty large, errors can occur, even though they shouldn't. I cover as many as I can, but today, as an exemple, google-translate started to throw out errors, because it can't use the service anymore (free...). Unhandled errors.... So the thread crashes and ProjectAlice is left as is. I would like to, as it's possible per exemple in Java, to super catch all exceptions and work further from there
Here's a simple solution to override the python exception hook, thus enabling you to handle uncaught exceptions:
import sys
def my_custom_exception_hook(exctype, value, tb):
print('Yo, do stuff here, handle specific exceptions and raise others or whatever')
and before your actual code starts do:
sys.excepthook = my_custom_exception_hook
A simple except Exception: will catch all exceptions except KeyboardInterrupt and SystemExit within the same thread.
You'll have to have the try: except ...: block within the code that is run in the thread to catch exceptions occurring in the thread.

Ask input once then wait for response

I'm a total newbie and I have question about asking about input while the loop is working.
Lets pretend that i have simple loop.
x = 1
y = 1
while x == 1:
y += 1
print(y)
And now i want user input to stop this script but only if he types cancel and the loop is supposed to run while python is waiting for input.
As I mentioned in the comments you can achieve "running the loop while waiting for input" using threads from the threading module.
The idea is to have two threads that will run in parallel (ie at the same time) and each of them will do its own thing. The first one will do only one thing : wait for input. The second one will do the work that you would have put in the loop, and only check at the start of each loop if it should stop or not according to the information it gets from the first thread.
The following code illustrate this (note this requires python3):
from threading import Thread
import time
class Input_thread(Thread):
def __init__(self):
Thread.__init__(self)
self.keep_working = True
def run(self):
while True:
a = input("Type *cancel* and press Enter at anytime to cancel \n")
print("You typed "+a)
if a == "cancel":
self.keep_working = False
return
else:
pass
class Work_thread(Thread):
def __init__(self, other_thread):
Thread.__init__(self)
self.other_thread = other_thread
def run(self):
while True:
if self.other_thread.keep_working is True:
print("I'm working")
time.sleep(2)
else :
print("I'm done")
return
# Creating threads
input_thread = Input_thread()
work_thread = Work_thread(input_thread)
# Lanching threads
input_thread.start()
work_thread.start()
# Waiting for threads to end
input_thread.join()
work_thread.join()
As you can see using threading isn't trivial and requires some knowledge about classes.
A way of achieving something similar in a slightly simpler way would be to use the python's exception called KeyboardInterrupt. If you are not familiar with exceptions : there are python's way of handling errors in your code, that means if at some point in your code Python finds a line it can't run, it will raise an exception, and if nothing was planned to deal with that error (aka if you don't catch the exception with the try/except syntax), python stops running and display that exception and the traceback in the terminal window.
Now the thing is when you press Ctrl-c (same as the copy shortcut) in the terminal window while your program runs, it will automaticaly raise an exception called KeyboardInterupt in your program, and you can catch it to use it as way to send cancel to your program.
See that code for an example of how to do that :
import time
y=1
try:
while True:
y+=1
print(y)
time.sleep(1)
except KeyboardInterrupt:
print("User pressed Ctrl-c, I will now exit gracefully")
print("Done")

Python Threaded Timer Returning Random Errors

I have a python thread that runs every 20 seconds. The code is:
import threading
def work():
Try:
#code here
except (SystemExit, KeyboardInterrupt):
raise
except Exception, e:
logger.error('error somewhere',exc_info=True)
threading.Timer(20, work).start ();
It usually runs completely fine. Once in a while, it'll return an error that doesnt make much sense. The errors are the same two errors. The first one might be legitimate, but the errors after that definitely aren't. Then after that, it returns that same error every time it runs the thread. If I kill the process and start over, then it runs cleanly. I have absolutely no idea what going on here. Help please.
As currently defined in your question, you are most likely exceeding your maximum recursion depth. I can't be certain because you have omitted any opportunities for flow control that may be evident in your try block. Furthermore, everytime your code fails to execute, the general catch for exceptions will log the exception and then bump you into a new timer with a new logger (assume you are declaring that in the try block). I think you probably meant to do the following:
import threading
import time
def work():
try:
#code here
pass
except (SystemExit, KeyboardInterrupt):
raise
except Exception, e:
logger.error('error somewhere',exc_info=True)
t = threading.Timer(20, work)
t.start()
i = 0
while True:
time.sleep(1)
i+=1
if i >1000:
break
t.cancel()
If this is in fact the case, the reason your code was not working is that when you call your work function the first time, it processes and then right at the end, starts another work function in a new timer. This happens add infinitum until the stack fills up, python coughs, and gets angry that you have recursed (called a function from within itself) too many times.
My code fix pulls the timer outside of the function so we create a single timer, which calls the work function once every 20 seconds.
Because threading.timers run in separate threads, we also need to wait around in the main thread. To do this, I added a simple while loop that will run for 1000 seconds and then close the timer and exit. If we didn't wait around in the main loop, it would call your timer and then close out immediately causing python to clean up the timer before it executed even once.

Looping until a completely reliable exception

Right now I'm doing this:
try:
while True:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
I repeatedly call client.recv_into until it raises an exception. Now, I know eventually, without a doubt, it will raise an exception...
Is there a cleaner way to do this? Is there some sort of loop-until-exception construct or a common way to format this?
There are two ways to do this:
Like you did
try:
while True:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
except YourException:
pass
or
while True:
try:
s = client.recv_into( buff, buff_size )
roll += buff.decode()
except YourException:
break
Personally, I would prefer the second solution as the break keyword makes clear what is happening in case of the exception.
Secondly, you should only catch the exception you're awaiting (in my example YourException). Otherwise IOError, KeyboardInterrupt, SystemExit etc. will also be caught, hiding "real errors" and potentially blocking your program from exiting properly.
That appears to be the (pythonic) way to do it. Indeed, the Python itertools page gives the following recipe for iter_except (calls a function func until the desired exception occurs):
def iter_except(func, exception, first=None):
try:
if first is not None:
yield first()
while 1:
yield func()
except exception:
pass
which is almost exactly what you're done here (although you probably do want to add a except [ExceptionName] line in your try loop, as Nils mentioned).

Categories

Resources