Reputation: 3359
When I hit ctrl-c the prompt pops up on the screen and I input no because I want to keep running the program but the program exits anyway. Where am I going wrong? Is this functionality even possible?
from functools import wraps
import sys
class CleanExit(object):
def __init__(self, *args, **kw):
# You can supply an optional function
# to be called when the KeyboardInterrupt
# takes place.
# If the function doesn't require any arguments:
# @CleanExit(handler=func)
# If the function requires arguments:
# @CleanExit('foo', handler=handle)
self.kw = kw
self.args = args
self.handler = kw.get('handler')
def __call__(self, original_func):
decorator_self = self
@wraps(original_func)
def wrappee(*args, **kwargs):
try:
original_func(*args,**kwargs)
except KeyboardInterrupt:
if self.handler:
self.handler(*self.args)
else:
sys.exit(0)
return wrappee
def handle():
answer = raw_input("Are you sure you want to exit?").lower().strip()
if 'y' in answer:
sys.exit(0)
@CleanExit(handler=handle)
def f():
while 1: pass
f()
Upvotes: 0
Views: 68
Reputation: 2545
Your problem is that you're not doing anything to continue the function after handling it - so your code handles the interrupt, and then exits anyway. You can recursively re-enter wrappee if the handler exits successfully like this:
def __call__(self, original_func):
decorator_self = self
@wraps(original_func)
def wrappee(*args, **kwargs):
try:
original_func(*args,**kwargs)
except KeyboardInterrupt:
if self.handler:
self.handler(*self.args)
wrappee(*args, **kwargs)
else:
sys.exit(0)
return wrappee
Now this should work. Note that this is a little bit naughty, as Python can't optimise tail calls, so if you KeyboardInterrupt
more often than sys.getrecursionlimit()
, Python will run out of stack frames and crash.
EDIT: That was silly - having thought about it, this function is so trivial to derecurse by hand it probably doesn't even count.
def __call__(self, original_func):
decorator_self = self
@wraps(original_func)
def wrappee(*args, **kwargs):
while True:
try:
original_func(*args,**kwargs)
except KeyboardInterrupt:
if self.handler:
self.handler(*self.args)
else:
sys.exit(0)
return wrappee
should also work just fine.
Upvotes: 2