3

I have a problem trying to abort a Python script, for example:

#!/usr/bin/python

import hello

hello.draw()

exit()

where draw() is a C function. In this function there is a loop to print some text. I tried CTRL-C to break out of it but it doesn't work. Important thing is that C code cannot be modified!

Any ideas?

4 Answers4

2

I put together an example of how you might do this. It lets to augment the function and escape from it without needing to modify it. For example, given the function foo (which was inline in test.h for my testing):

void foo() {
  while(1); // never returns
}

You can play a game with signals and siglongjmp. It should be noted however that you need to be very careful to ensure you only call async safe functions from within foo, or alternatively mask the signals before and unmask them after any unsafe calls.

We can then wrap the function and use %exception to inject some extra code to allow the signal to backout of the function:

%module test

%{
#include <setjmp.h>
#include <signal.h>

static sigjmp_buf timeout;

static void backout(int sig) {
  siglongjmp(timeout, sig);
}

#include "test.h"
%}

%include <exception.i>

%exception {
  if (!sigsetjmp(timeout, 1)) {
    signal(SIGALRM,backout); // Check return?
    $action
  }
  else {
    // raise a Python exception
    SWIG_exception(SWIG_RuntimeError, "Timeout in $decl");
  }
}

void foo();

%exception;

Which allows us to write:

import signal
import test

signal.alarm(5)
test.foo()

Which works as hoped, given the caveats about async-safe functions. I used SIGALRM here because it allowed me to keep it all self-contained within one Python file. You could use another thread/process and other signals if you preferred.

If you change SIGALRM in my example to SIGINT (and don't raise a SIGALRM either obviously) it'll work with Ctrl+C - the reason it doesn't is because of how the default Python signal handlers work. The default handler Python uses simply sets a flag and processes the signal after the underlying call has returned control to Python, which neatly side-steps the whole async-safety issue.

Community
  • 1
  • 1
Flexo
  • 87,323
  • 22
  • 191
  • 272
  • This is really neat. I spent quite some time working with a thread waiting for signals using sigwait. I had to go back to old-school sigsetjmp. Wasn't aware about the %exception construct using SWIG. Pretty cool – Jens Munk Jul 19 '15 at 09:17
0

You will need to give your C function a way to abort it - for example, it could poll a global status flag in its draw loop.

Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122
  • oh yes, you could do that too. – Alexander Gessler Aug 27 '12 at 16:09
  • seriously? so you actually want to run c code that just loops for ever printing hello world? – Joran Beasley Aug 27 '12 at 16:43
  • 1
    It is only example. I try to use some functions from one bigger application and it read and print some information about network(IP, port, protocol, ...). This file have 50MB. So, printing and reading will end but there is some situation when I need to stop it. – user1628147 Aug 27 '12 at 16:54
0

One other possibility is to stop the process and then kill it. In the bash command line (Linux/MacOS), that would look like this:

Let's say you launch your script...

$ python3 example.py

...then type control+Z to stop the process. Look for the number in square brackets, in this case, 1.

[1]+  Stopped                 python3 example.py

Now you can kill that process using the kill command, followed by the number shown in square brackets.

$ kill %1
[1]+  Terminated: 15          python3 example.py
reynoldsnlp
  • 1,072
  • 1
  • 18
  • 45
0

Here's an alternative approach to siglongjmp for C++, but could also be easily adapted to C without the lambda:

%exception {
    signal(SIGINT, [](int signum){
        PyErr_SetNone(PyExc_KeyboardInterrupt);
        Py_Finalize();
        exit(1);
    });
    $action
};
Kexanone
  • 111
  • 4