3

I want to be able to interrupt a long function with cython, using the usual CTRL+C interrupt command. My C++ long function is repeatedly called inside a while loop from Cython code, but I want to be able, during the loop, to send an "interrupt" and block the while loop.

The interrupt also should wait the longFunction() to finish, so that no data are lost or kept in unknown status.

This is one of my first implementation, which obviously doesn't work:

computed=0;

print "Computing long function..."
    while ( computed==0 ):
        try:
            computed = self.thisptr.aLongFunction()
        except (KeyboardInterrupt, SystemExit):
            computed=1
            print '\n! Received keyboard interrupt.\n'
            break;

(p.s. self.thisptr is the pointer to the current class which implements aLongFunction() )

linello
  • 8,451
  • 18
  • 63
  • 109
  • What you want to do is capture `SIGINT`. See http://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python – Tim Nov 12 '12 at 10:33
  • mmh, this solution doesn't work with my function...the loop doesn't stop – linello Nov 12 '12 at 10:45

2 Answers2

0

You should be able to do something like this:

import signal

class Test():
    def __init__(self):
        self.loop_finished = False
        signal.signal(signal.SIGINT, self.abort_loop)

    def abort_loop(self, signal, frame):
        self.loop_finished = True

    def go(self):
        while not self.loop_finished:
            print "Calculating"

            # Do your calculations
            # Once calcations are done, set self.loop_finished to True

        print "Calculating over"

Test().go()

You can also use additional variables to keep track of whether the computation was manually aborted or not.

Tim
  • 11,710
  • 4
  • 42
  • 43
  • I'm having problem in setting attributes for my class, because my class is a cdef class (I'm using Cython) and when I set class attributes in __cinit__ I get that the attribute is read-only – linello Nov 12 '12 at 11:44
  • I've done very little in Cython, I (perhaps foolishly) assumed it would work the same. Hopefully someone else can help you out. – Tim Nov 12 '12 at 11:48
  • A **hack** that *might* work is to set `loop_finished` to `[False]`, in `abort_loop` use `self.loop_finished[0] = True` and in the `while` check the truth value of `loop_finished[0]`. If cython's int are mutable you could simply increment the `loop_finished` by one instead of setting it to `True`. – Bakuriu Nov 12 '12 at 13:56
  • why are you addressing `self.loop_finished[0]`? is not an array or list – linello Nov 12 '12 at 15:05
0

I am not a Python C-Api foo master, however this works, but maybe its not the best way:

cdef extern from "Python.h":
    int PyErr_CheckSignals()

def test_interrupt():
    cdef int i = 0
    while i < 9999999999 and not PyErr_CheckSignals():
        # do fancy stuff.
        i += 1

Of course that is not asynchronous which is maybe possible but I do not know and this breaks the loop for any signal, not just Ctrl+C. Also maybe its better to not check signals every loop iteration, at least if what is done is very cheap.

You could call PyErr_Occurred, etc. if PyErr_CheckSignals did not return 0 and see what kind of Exception got raised to go specifically for KeybordInterrupt or certain kill signals. (Anyway, check the python C-Api for details there...)


If you are calling a cython cdef function inside the while loop you may be able to achieve this also if you add except *:

cdef function(...) except *:
    pass

see also http://docs.cython.org/src/userguide/language_basics.html#error-return-values

seberg
  • 8,785
  • 2
  • 31
  • 30
  • @linello, did you actually see this? I added the except * thing, which I think would be nicer, but I did not try it. I think such a method should work too, but only if its a Cython function maybe. – seberg Nov 16 '12 at 22:16