Account Options

  1. Sign in
Google Groups Home
« Groups Home
KeyboardInterrupt eats my error and then won't be caught
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  7 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Philip Semanchuk  
View profile  
 More options Jun 18 2009, 11:19 pm
Newsgroups: comp.lang.python
From: Philip Semanchuk <phi...@semanchuk.com>
Date: Thu, 18 Jun 2009 23:19:09 -0400
Local: Thurs, Jun 18 2009 11:19 pm
Subject: KeyboardInterrupt eats my error and then won't be caught
Hi all,
I need help understanding how Python deals with Ctrl-C.

A user has reported a bug in my posix_ipc module. When a Python app is  
waiting to acquire an IPC semaphore and the user hits Ctrl-C, my code  
should return a custom error indicating that the semaphore wait was  
interrupted by a signal.

However, the caller never sees the error I set. Instead they get a  
KeyboardInterrupt that refuses to be caught by try/except. Here's a  
sample program that demonstrates the problem when run from the command  
line:

# -----------------------------------
import posix_ipc

sem = posix_ipc.Semaphore(None, posix_ipc.O_CREX)

try:
    sem.acquire()   # User hits Ctrl + C while this is waiting
except:
    print "********* I caught it!"

sem.close()
sem.unlink()
# -----------------------------------

I expected that code to raise a posix_ipc.Error with the text, "The  
wait was interrupted by a signal" which would then be trapped by the  
except statement which would print the "I caught it!" message.

Instead a KeyboardInterrupt error is propagated up to the interpreter  
and the process is killed as if the try/except wasn't there at all.

I have verified that the C function sem_wait() returns -1 (failure),  
that errno is set to EINTR and that my detects that properly. So far,  
so good. PyErr_Occurred() returns NULL at that point. So my code calls  
PyErr_SetString() to set a custom error for the caller and returns  
NULL. It's apparently at some point after that that the  
KeyboardInterrupt error is being set.

If I substitute my sysv_ipc module for posix_ipc (very similar to  
posix_ipc but uses Sys V semaphores instead of POSIX), I get the same  
behavior.

I see this w/Python 2.5 under OS X and also w/Python 2.5 under Ubuntu  
8.0.4.

If anyone wants to look at my C code, the relevant case statement is  
on line 555 of posix_ipc_module.c.

http://semanchuk.com/philip/posix_ipc/
http://semanchuk.com/philip/sysv_ipc/

Any suggestions would be appreciated.

Thanks
Philip


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
greg  
View profile  
 More options Jun 20 2009, 12:09 am
Newsgroups: comp.lang.python
From: greg <g...@cosc.canterbury.ac.nz>
Date: Sat, 20 Jun 2009 16:09:08 +1200
Local: Sat, Jun 20 2009 12:09 am
Subject: Re: KeyboardInterrupt eats my error and then won't be caught

Philip Semanchuk wrote:
> try:
>    sem.acquire()   # User hits Ctrl + C while this is waiting
> except:
>    print "********* I caught it!"
> Instead a KeyboardInterrupt error is propagated up to the interpreter  
> and the process is killed as if the try/except wasn't there at all.

Not sure exactly what's happening, but I think I can guess.
Python installs a signal handler for Ctrl-C that sets a
flag in the interpreter. Every so many bytecodes executed,
the flag is checked and KeyboardInterrupt raised if it's
set.

So there can be a short delay between the Ctrl-C signal
being received and KeyboardInterrupt being raised, and it
seems that this delay results in it happening after the
try-except has exited.

You could try using signal.signal() to install a handler
for Ctrl-C that does nothing in a section around the
sem.acquire call(). That should prevent the KeyboardInterrupt
flag from being set, but the signal will still be occurring
at the Unix level, so the system call will get interrupted.

--
Greg


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Piet van Oostrum  
View profile  
 More options Jun 20 2009, 4:19 am
Newsgroups: comp.lang.python
From: Piet van Oostrum <p...@cs.uu.nl>
Date: Sat, 20 Jun 2009 10:19:28 +0200
Local: Sat, Jun 20 2009 4:19 am
Subject: Re: KeyboardInterrupt eats my error and then won't be caught

I think you are approaching the cause of the problem.
Your answer triggered the following thought in my head:

There are actually two exceptions occurring: One is the Ctrl-C, which as
you correctly say, will probably delayed until there is a `check' in the
interpreter (see also David Beazley's wonderful presentation on the
GIL). The other one is the exception that is generated in the IPC code
by returning a NULL. This one should caught by the except clause.

As the call to sem.acquire releases and reacquires the GIL, I think
signal processing will be done immediately. This causes the
KeyboardInterrupt exception to occur immediately i.e. to interrupt the
handling of the other exception.

>g> You could try using signal.signal() to install a handler
>g> for Ctrl-C that does nothing in a section around the
>g> sem.acquire call(). That should prevent the KeyboardInterrupt
>g> flag from being set, but the signal will still be occurring
>g> at the Unix level, so the system call will get interrupted.

Your suggestion seems to work:

import posix_ipc
import signal

sem = posix_ipc.Semaphore(None, posix_ipc.O_CREX)
signal.signal(signal.SIGINT, lambda sig, frame: None)
status = []
try:
    status.append("Trying")
    sem.acquire()   # User hits Ctrl + C while this is waiting
    status.append("Acquired")
except:
    status.append("I caught it!")
print status

sem.close()
sem.unlink()

prints: ['Trying', 'I caught it!']

I also tried some other variants, catching the KeyboardInterrupt at
various places:

This one prints: ['Trying', 'Keyboard Interrupt']
This suggests to me that the first exception handling is aborted by the
Ctrl-C handling.

import posix_ipc

sem = posix_ipc.Semaphore(None, posix_ipc.O_CREX)

status = []
try:
    try:
        status.append("Trying")
        sem.acquire()   # User hits Ctrl + C while this is waiting
        status.append("Acquired")
    except:
        status.append("I caught it!")
except KeyboardInterrupt:
    status.append("Keyboard Interrupt")
print status

sem.close()
sem.unlink()

And this one prints: ['Trying', 'I caught it!']

import posix_ipc

sem = posix_ipc.Semaphore(None, posix_ipc.O_CREX)

status = []
try:
    status.append("Trying")
    try:
        sem.acquire()   # User hits Ctrl + C while this is waiting
        status.append("Acquired")
    except KeyboardInterrupt:
        status.append("Interrupt")
except:
    status.append("I caught it!")
print status

sem.close()
sem.unlink()

I was actually a bit surprised that the addition of the try/except
KeyboardInterrupt helps solve the problem but that apparently the
exception handler is not executed.

Folding the two try's into one with two except clauses will not help as
there are indeed two exceptions to be handled.

I also added traceback printout in the outer exception handler and it
points to the sem.acquire line.

My conclusion is that if there are two exceptions at the same time, the
inner exception handler is interrupted by the other exception even
before the except clause can be entered. And only the outer one is
really executed. This explains the behaviour that the OP described.

I think you can only have two exceptions at the same time if at least
one of them is a signal.
--
Piet van Oostrum <p...@cs.uu.nl>
URL: http://pietvanoostrum.com [PGP 8DAE142BE17999C4]
Private email: p...@vanoostrum.org


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Piet van Oostrum  
View profile  
 More options Jun 20 2009, 7:41 am
Newsgroups: comp.lang.python
From: Piet van Oostrum <p...@cs.uu.nl>
Date: Sat, 20 Jun 2009 13:41:36 +0200
Local: Sat, Jun 20 2009 7:41 am
Subject: Re: KeyboardInterrupt eats my error and then won't be caught
After my previous experiment I was curious how this works with
input(). 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.
--
Piet van Oostrum <p...@cs.uu.nl>
URL: http://pietvanoostrum.com [PGP 8DAE142BE17999C4]
Private email: p...@vanoostrum.org


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Philip Semanchuk  
View profile  
 More options Jun 20 2009, 4:30 pm
Newsgroups: comp.lang.python
From: Philip Semanchuk <phi...@semanchuk.com>
Date: Sat, 20 Jun 2009 16:30:58 -0400
Local: Sat, Jun 20 2009 4:30 pm
Subject: Re: KeyboardInterrupt eats my error and then won't be caught

On Jun 20, 2009, at 7:41 AM, Piet van Oostrum wrote:

Thank you Greg and especially Piet for going to the trouble of  
installing posix_ipc and experimenting with it.

Piet, your research into raw_input() gave me a solution.

In my C code, I added a call to PyErr_CheckSignals() as the first line  
inside the "case EINTR". Apparently calling PyErr_CheckSignals() sets  
the Python error indicator -- PyErr_Occurred() returns true and the  
error is PyExc_KeyboardInterrupt. I'm able to clear that error and  
return my own.

Prior to adding the call to PyErr_CheckSignals(), PyErr_Occurred()  
returned false, so my code had no error to clear.

Best of all, PyErr_CheckSignals() doesn't interfere with a Python-
level signal handler if one is set.

This worked under OS X and Linux.

I'll have to think some more about exactly how I want my module to  
report the Ctrl-C, but at least now I have the tools to control the  
situation.

Thanks again for your invaluable assistance. If I'm ever in NL or NZ  
I'll buy you a beer. =)

Cheers
Philip


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
greg  
View profile  
 More options Jun 20 2009, 10:21 pm
Newsgroups: comp.lang.python
From: greg <g...@cosc.canterbury.ac.nz>
Date: Sun, 21 Jun 2009 14:21:23 +1200
Local: Sat, Jun 20 2009 10:21 pm
Subject: Re: KeyboardInterrupt eats my error and then won't be caught

Philip Semanchuk wrote:
> Best of all, PyErr_CheckSignals() doesn't interfere with a Python- level
> signal handler if one is set.

Ah, I hadn't realised that you were doing this in C
code, and I was trying to think of a Python-level
solution.

For C code, the solution you give sounds like a
good one.

My only misgiving is that the user might expect to
get a KeyboardInterrupt in response to Ctrl-C, so
it might be better to just let it propagate instead
of turning it into a different exception.

--
Greg


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Philip Semanchuk  
View profile  
 More options Jun 21 2009, 10:49 am
Newsgroups: comp.lang.python
From: Philip Semanchuk <phi...@semanchuk.com>
Date: Sun, 21 Jun 2009 10:49:42 -0400
Local: Sun, Jun 21 2009 10:49 am
Subject: Re: KeyboardInterrupt eats my error and then won't be caught

On Jun 20, 2009, at 10:21 PM, greg wrote:

I completely agree. The simple solution (for me) is to handle all  
return codes of EINTR the same way which is to raise posix_ipc.Error  
with the message "The wait was interrupted by a signal". But that  
loses information. KeyboardInterrupt is very specific while  
posix_ipc.Error is generic and even parsing the message doesn't tell  
much more.

When my C code sees EINTR, I will probably raise KeyboardError when I  
see that and add a specific posix_ipc.SignalError to raise in other  
EINTR circumstances.

Thanks,
Philip


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »