connect SIGINT to custom interrupt handler

  • Thread starter Christoph Scheingraber
  • Start date
C

Christoph Scheingraber

Hi,

I am trying to connect SIGINT (^c) to a custom interrupt handler like
this (no threading, just straightforward):



if __name__ == "__main__":
quit = False
def interrupt_handler(signal, frame):
global quit
if not quit:
print "blabla, i'll finish my task and quit kind of message"
print "Press ^C again to interrupt immediately."
else:
sys.exit(2)
quit = True
signal.signal(signal.SIGINT, interrupt_handler)
# main will use the quit flag to determine if it should quit before next
# task
status = main()


This worked fine in some rare lucky cases, but most of the times, the
module I am using (my university's seismology project) catches the SIGINT
and quits:

select.error: (4, 'Interrupted system call')

How can I prevent the imported module's function from catching the
interrupt signal?


Thanks to anyone that takes the time to help me...

Chris
 
N

Nobody

signal.signal(signal.SIGINT, interrupt_handler)
This worked fine in some rare lucky cases, but most of the times, the
module I am using (my university's seismology project) catches the SIGINT
and quits:

select.error: (4, 'Interrupted system call')

After installing the signal handler, call:

signal.siginterrupt(signal.SIGINT, False)

This will cause (most) interrupted system calls to be restarted after the
signal has been handled.
How can I prevent the imported module's function from catching the
interrupt signal?

It isn't catching the signal. Unless you enable restarting of system
calls, an interrupted system call will typically fail with EINTR. Python
typically reports failures via exceptions; failures due to EINTR aren't
handled differently.
 
C

Christoph Scheingraber

I now have signal.siginterrupt(signal.SIGINT, False) in the line
below signal.signal(signal.SIGINT, interrupt_handler)

Unfortunately, pressing ^c still results in the same interrupt error. I
also tried putting signal.siginterrupt into the interrupt_handler
function, which gave an interesting result:
File "/usr/local/bin/obspysod", line 586, in interrupt_handler
signal.siginterrupt(signal.SIGINT, False)
AttributeError: 'int' object has no attribute 'siginterrupt'

Could there be a namespace problem?
 
C

Chris Angelico

I now have signal.siginterrupt(signal.SIGINT, False) in the line
below signal.signal(signal.SIGINT, interrupt_handler)

Unfortunately, pressing ^c still results in the same interrupt error. I
also tried putting signal.siginterrupt into the interrupt_handler
function, which gave an interesting result:
   File "/usr/local/bin/obspysod", line 586, in interrupt_handler
   signal.siginterrupt(signal.SIGINT, False)
   AttributeError: 'int' object has no attribute 'siginterrupt'

Could there be a namespace problem?

def interrupt_handler(signal, frame):

You're using 'signal' as a parameter here. The local int is masking
the global module.

Chris Angelico
 
T

Thomas 'PointedEars' Lahn

Christoph said:
I now have signal.siginterrupt(signal.SIGINT, False) in the line
below signal.signal(signal.SIGINT, interrupt_handler)

Unfortunately, pressing ^c still results in the same interrupt error. I
also tried putting signal.siginterrupt into the interrupt_handler
function, which gave an interesting result:
File "/usr/local/bin/obspysod", line 586, in interrupt_handler
signal.siginterrupt(signal.SIGINT, False)
AttributeError: 'int' object has no attribute 'siginterrupt'

Could there be a namespace problem?

Obviously. `signal' refers to an `int' object, probably by something like

signal = 42

before. E.g. `print' or a debugger will tell you, as you have not showed
the relevant parts of the code.


Please trim your quotes to the relevant minimum; DO NOT top-post.
Also, it is not acceptable behavior to use domain namespaces without
authorization ([email protected] is not a mailbox, yet spam.org is
registered to someone else).
 
C

Christoph Scheingraber

Obviously. `signal' refers to an `int' object, probably by something like

signal = 42

before. E.g. `print' or a debugger will tell you, as you have not showed
the relevant parts of the code.

The problem is that I am running someone else's module which seems to
use signal, I guess that means I have to create a child method?
Is it correct anyway to have

signal.siginterrupt(signal.SIGINT, False)

in my custom interrupt_handler function or should it be outside but
after signal.signal(signal.SIGINT, interrupt_handler)?
Please trim your quotes to the relevant minimum; DO NOT top-post.
Also, it is not acceptable behavior to use domain namespaces without
authorization ([email protected] is not a mailbox, yet spam.org is
registered to someone else).

I am sorry, I changed it to my own domain.
 
N

Nobody

Is it correct anyway to have

signal.siginterrupt(signal.SIGINT, False)

in my custom interrupt_handler function
No.

or should it be outside but after
signal.signal(signal.SIGINT, interrupt_handler)?

Yes.
 
N

Nobody

I now have signal.siginterrupt(signal.SIGINT, False) in the line
below signal.signal(signal.SIGINT, interrupt_handler)

Unfortunately, pressing ^c still results in the same interrupt error.

Sorry; I wasn't paying sufficient attention to the details:

According to Linux' signal(7) manpage, select() is never restarted,
regardless of the siginterrupt() setting.

In general, wait-for-something functions aren't restarted; the caller is
expected to check that the waited-for condition actually happened, so
returning prematurely isn't considered problematic.

EINTR is one of those "special" errors (like EAGAIN) which don't
actually indicate an error. In the context of select(), a return value of
-1 with errno set to EINTR should normally be handled in the same way as a
return value of zero, i.e. "nothing has happened yet, try again".

While the EINTR case isn't identical to the zero-return case, it's much
closer to it than it is to a genuine error. If it's being treated like
genuine errors (i.e. raising an exception), that's a defect in the Python
bindings. In which case, I'd suggest catching the exception and checking
the error code, e.g.:

def myselect(rlist, wlist, xlist, timeout = None):
try:
return select.select(rlist, wlist, xlist, timeout)
except select.error, e:
if e[0] == errno.EINTR:
return 0
raise
 
C

Christoph Scheingraber

Why not just catch KeyboardInterrupt?

Would it be possible to continue my program as nothing had happened in
that case (like I did before, setting a flag to tell main() to finish the
running data download and quit instead of starting the next data download
{it's a for-loop})?

I have tried it, but after catching the KeyboardInterrupt I could only
continue to the next iteration.
 
J

Jean-Paul Calderone

Would it be possible to continue my program as nothing had happened in
that case (like I did before, setting a flag to tell main() to finish the
running data download and quit instead of starting the next data download
{it's a for-loop})?

I have tried it, but after catching the KeyboardInterrupt I could only
continue to the next iteration.

No, since the exception being raised represents a different flow of
control
through the program, one that is mutually exclusive with the flow of
control
which would be involved with continuing the processing in the
"current"
iteration of your loop.

Setting SA_RESTART on SIGINT is probably the right thing to do. It's
not
totally clear to me from the messages in this thread if you managed to
get
that approach working. The most commonly encountered problem with
this
approach is that it means that any blocking (eg I/O) operation in
progress
won't be interrupted and you'll have to wait for it to complete
normally.
In this case, it sounds like this is the behavior you actually want,
though.

Jean-Paul
 
N

Nobody

Setting SA_RESTART on SIGINT is probably the right thing to do. It's not
totally clear to me from the messages in this thread if you managed to get
that approach working.

He didn't; select() isn't SA_RESTART-able.

Unfortunately, the author of select.select() decided to treat EINTR as an
error. It isn't; it's a "third way", neither success nor failure, similar
to EAGAIN. Normally, you would treat EINTR from select() in the same way
as a timeout.

While it's possible to work around this, the interface encourages getting
it wrong.
 
T

Thomas 'PointedEars' Lahn

Christoph said:
The problem is that I am running someone else's module which seems to
use signal, I guess that means I have to create a child method?
Is it correct anyway to have

signal.siginterrupt(signal.SIGINT, False)

in my custom interrupt_handler function

Only if `signal' is not the name of an int argument.
or should it be outside but
after signal.signal(signal.SIGINT, interrupt_handler)?

In the meantime, Chris Angelico has pointed out the cause of the problem.
Please follow his advice, i.e. rename your argument.
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top