resume execution after catching with an excepthook?

A

andrea crotti

So I would like to be able to ask for confirmation when I receive a C-c,
and continue if the answer is "N/n".

I'm already using an exception handler set with sys.excepthook, but I
can't make it work with the confirm_exit, because it's going to quit in
any case..

A possible solution would be to do a global "try/except
KeyboardInterrupt", but since I already have an excepthook I wanted to
use this. Any way to make it continue where it was running after the
exception is handled?


def confirm_exit():
while True:
q = raw_input("This will quit the program, are you sure? [y/N]")
if q in ('y', 'Y'):
sys.exit(0)
elif q in ('n', 'N'):
print("Continuing execution")
# just go back to normal execution, is it possible??
break


def _exception_handler(etype, value, tb):
if etype == KeyboardInterrupt:
confirm_exit()
else:
sys.exit(1)


def set_exception_handler():
sys.excepthook = _exception_handler
 
S

Steven D'Aprano

So I would like to be able to ask for confirmation when I receive a C-c,
and continue if the answer is "N/n".

I don't think there is any way to do this directly.

Without a try...except block, execution will cease after an exception is
caught, even when using sys.excepthook. I don't believe that there is any
way to jump back to the line of code that just failed (and why would you,
it will just fail again) or the next line (which will likely fail because
the previous line failed).

I think the only way you can do this is to write your own execution loop:

while True:
try:
run(next_command())
except KeyboardInterrupt:
if confirm_quit():
break


Of course you need to make run() atomic, or use transactions that can be
reverted or backed out of. How plausible this is depends on what you are
trying to do -- Python's Ctrl-C is not really designed to be ignored.

Perhaps a better approach would be to treat Ctrl-C as an unconditional
exit, and periodically poll the keyboard for another key press to use as
a conditional exit. Here's a snippet of platform-specific code to get a
key press:

http://code.activestate.com/recipes/577977

Note however that it blocks if there is no key press waiting.

I suspect that you may need a proper event loop, as provided by GUI
frameworks, or curses.
 
A

andrea crotti

2012/10/25 Steven D'Aprano said:
I don't think there is any way to do this directly.

Without a try...except block, execution will cease after an exception is
caught, even when using sys.excepthook. I don't believe that there is any
way to jump back to the line of code that just failed (and why would you,
it will just fail again) or the next line (which will likely fail because
the previous line failed).

I think the only way you can do this is to write your own execution loop:

while True:
try:
run(next_command())
except KeyboardInterrupt:
if confirm_quit():
break


Of course you need to make run() atomic, or use transactions that can be
reverted or backed out of. How plausible this is depends on what you are
trying to do -- Python's Ctrl-C is not really designed to be ignored.

Perhaps a better approach would be to treat Ctrl-C as an unconditional
exit, and periodically poll the keyboard for another key press to use as
a conditional exit. Here's a snippet of platform-specific code to get a
key press:

http://code.activestate.com/recipes/577977

Note however that it blocks if there is no key press waiting.

I suspect that you may need a proper event loop, as provided by GUI
frameworks, or curses.



Ok thanks, but here the point is not to resume something that is going
to fail again, just to avoid accidental kill of processes that take a
long time. Probably needed only by me in debugging mode, but anyway I
can do the simple try/except then, thanks..
 
C

Chris Angelico

I don't believe that there is any
way to jump back to the line of code that just failed (and why would you,
it will just fail again)

There are several reasons to retry something after an exception,
mainly if some external state gets changed. Virtual memory is usually
implemented using traps, so the OS handles an interrupt by paging
something in from the disk, then retrying the "failing" instruction.
The old-favorite "Abort, retry, ignore[, fail]?" prompt from DOS has
the same notion; you tried to save a file onto a write-protected
floppy disk, an exception is thrown, you handle the exception by
getting the user to unprotect or change disks, and you resume where
you left off. CPU-level interrupts always have a return address for
that exact reason.

Handling Ctrl-C in this way makes a lot of sense. Give the user the
option to try to abort, but then to optionally change his/her mind and
keep going. Or possibly have a third option: break out of the current
operation and go back to some primary loop (throw the exception and
let it be caught at the main loop).

Arguably the problem here is that KeyboardInterrupt is an exception.
Perhaps a more classic signal handling structure should be used: when
signal received, call function. That function then has the power to
raise an exception, which will propagate through whatever code is
currently executing. This sort of thing would have all the usual
dangers of signal handlers, though; you have NO IDEA what state the
program's in, so you have to be ubercareful of what globals you use or
change.

ChrisA
 
H

Hans Mulder

So I would like to be able to ask for confirmation when I receive a C-c,
and continue if the answer is "N/n".

I'm already using an exception handler set with sys.excepthook, but I
can't make it work with the confirm_exit, because it's going to quit in
any case..

A possible solution would be to do a global "try/except
KeyboardInterrupt", but since I already have an excepthook I wanted to
use this. Any way to make it continue where it was running after the
exception is handled?


def confirm_exit():
while True:
q = raw_input("This will quit the program, are you sure? [y/N]")
if q in ('y', 'Y'):
sys.exit(0)
elif q in ('n', 'N'):
print("Continuing execution")
# just go back to normal execution, is it possible??
break


def _exception_handler(etype, value, tb):
if etype == KeyboardInterrupt:
confirm_exit()
else:
sys.exit(1)


def set_exception_handler():
sys.excepthook = _exception_handler

I think the trick is to not use an except hook, but trap the
interrupt on a lower level.

This seems to work; I'm not sure how robust it is:

import signal

def handler(signum, frame):
while True:
q = raw_input("This will quit the program, are you sure? [y/N]")
if q[:1] in "yY":
raise KeyboardInterrupt
elif q[:1] in "nN":
print("Continuing execution")
# just go back to normal execution
return

signal.signal(signal.SIGINT, handler)


If you're debugging this on a Unix platform, it may help to know
that you can also kill a process with control-\

Hope this helps,

-- HansM
 
S

Steven D'Aprano

There are several reasons to retry something after an exception,

I'm sure there are, but you're taking my point out of context.

Andrea described his problem as *continuing*, not *re-trying*. I
understand that re-trying operations is useful:

while True:
try:
some_operation()
except SomeException:
if not retry():
break # or raise an exception, or return


but I wouldn't describe that as "continuing", as Andrea did. I understand
that as:

try:
operation(1)
operation(2)
operation(3)
operation(4)
# and so forth...
except SomeException:
if retry():
# Magically jump back to the operation that was
# active when the exception occurred.
magic_happens_here()


If you could guarantee that each operation(N) was atomic ("all or
nothing" -- it either succeeds, or has no effect) then such a feature
would be useful. But as far as I know, you can't jump back into a try
block from the except block, and even if you could, what's to stop the
operation from failing again and again and again?

In Andrea's case, the failure he is worried about is "oops, I hit Ctrl-C
when I actually wanted to not hit Ctrl-C", so presumably the failure
wouldn't reoccur if you could jump backwards. But in any case, I can see
no obvious way to make it work.

The python debugger pdb has a "jump" command that allows you to step
backwards and re-execute code under certain conditions, so perhaps it is
not quite impossible.

(I'm tempted to reply that the actual solution to this problem of
accidentally hitting Ctrl-C is "well don't do that then".)
 
C

Chris Angelico

This seems to work; I'm not sure how robust it is:

import signal

def handler(signum, frame):
while True:
q = raw_input("This will quit the program, are you sure? [y/N]")
if q[:1] in "yY":
raise KeyboardInterrupt
elif q[:1] in "nN":
print("Continuing execution")
# just go back to normal execution
return

signal.signal(signal.SIGINT, handler)

Yes, that's what I was talking about. You do have to be fairly careful
what you do (for instance, what should happen if the user hits Ctrl-C
during handler()? Default is that it'll raise KeyboardInterrupt
unconditionally), but you have perfect flexibility.

ChrisA
 
P

Prasad, Ramit

andrea said:
2012/10/25 Steven D'Aprano said:
On Wed, 24 Oct 2012 13:51:30 +0100, andrea crotti wrote:

[snip]
Without a try...except block, execution will cease after an exception is
caught, even when using sys.excepthook. I don't believe that there is any
way to jump back to the line of code that just failed (and why would you,
it will just fail again) or the next line (which will likely fail because
the previous line failed).

I think the only way you can do this is to write your own execution loop:

while True:
try:
run(next_command())
except KeyboardInterrupt:
if confirm_quit():
break


Of course you need to make run() atomic, or use transactions that can be
reverted or backed out of. How plausible this is depends on what you are
trying to do -- Python's Ctrl-C is not really designed to be ignored.

Perhaps a better approach would be to treat Ctrl-C as an unconditional
exit, and periodically poll the keyboard for another key press to use as
a conditional exit. Here's a snippet of platform-specific code to get a
key press:

http://code.activestate.com/recipes/577977

Note however that it blocks if there is no key press waiting.

I suspect that you may need a proper event loop, as provided by GUI
frameworks, or curses.

Ok thanks, but here the point is not to resume something that is going
to fail again, just to avoid accidental kill of processes that take a
long time. Probably needed only by me in debugging mode, but anyway I
can do the simple try/except then, thanks..

On the other hand, if you store state externally (pickle?) maybe
you can just restart at the last "check point". That way even if
the program dies you can recover on the next run.

Ramit Prasad


This email is confidential and subject to important disclaimers and
conditions including on offers for thepurchase or sale of
securities, accuracy and completeness of information, viruses,
confidentiality, legal privilege, and legal entity disclaimers,
available at http://www.jpmorgan.com/pages/disclosures/email.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,052
Latest member
LucyCarper

Latest Threads

Top