11450


Python C/C++ APIで下記のようにforで0から引数としてとった整数までを表示させる物を書いているのですが、Ctrl-
Cを押しても割り込んで処理を停止させることができません。  
9999999まで表示した後でKeyboardInterruptが出てしまいます。  
どうすればいいのでしょうか?  
ちなみにコンパイルは`python3 setup.py build_ext -i`でやっています。

環境はOS X 10.10, Python3.4.3です

[code]

    
    #include <iostream>
    #include <Python.h>
    
    static PyObject*
    for_int(PyObject *self, PyObject *args)
    {
        int x;
    
        if (!PyArg_ParseTuple(args, "i", &x))
            return NULL;
        for (int i=0; i<x; i++) {
            std::cout << i << "\n";
        }
        Py_INCREF(Py_None);
        return Py_None;
    
    }
    
    static PyMethodDef For_INT_Methods[] = {
        {"for_int", for_int, METH_VARARGS,
         "fast increment print"}, 
        {NULL, NULL, 0 , NULL}
    };
    
    static struct PyModuleDef for_int_module = {
        PyModuleDef_HEAD_INIT,
        "for_int",
        "spam_doc_string",
        -1,
        For_INT_Methods
    };
    
    PyMODINIT_FUNC
    PyInit_for_int(void)
    {
        PyObject *m;
    
        m = PyModule_Create(&for_int_module);
        if (m == NULL)
            return NULL;
        Py_INCREF(PyExc_KeyboardInterrupt);
        PyModule_AddObject(m, "InterruptError", PyExc_KeyboardInterrupt);
        return m;
    }
    
    
    int
    main(int argc, char *argv[])
    {
        PyImport_AppendInittab("for_int", PyInit_for_int);
        Py_Initialize();
        PyImport_ImportModule("for_int");
    }
    
    
[/code]

コンパイルするためのsetup.py

[code]

    
    from distutils.core import setup, Extension
    module = Extension('for_int', ['for.cpp'])
    setup(name='for',
        version='1.0',
        ext_modules=[module],
    )
    
    
    
[/code]

**追記**  
forの部分でPyErr_CheckSignals()を呼び出すようにしました(code2)  
しかし、Ctrl-Cを押しても処理は止まりませんでした。

code2

[code]

    
    for (int i=0; i<x; i++) {
            PyErr_CheckSignals(); # 追加
            std::cout << i << "\n";
        }
    
    
[/code]


CC BY-SA 3.0
2015-06-14T02:19:34.860
11320
2015-06-17T17:13:37.073
2015-06-17T11:17:43.973
5246
5246
question
python c++ c
Python C/C++ APIでKeyboardInterruptで処理を停止できない


[例外処理 —— Python 3.4.3ドキュメント](http://docs.python.jp/3/c-api/exceptions.html)
によると (強調は引用者による)、

>
> ある関数が呼び出した関数がいくつか失敗したために、その関数が失敗しなければならないとき、一般的にエラーインジケータを設定しません。呼び出した関数がすでに設定しています。
> **エラーを処理して例外をクリアするか、あるいは(オブジェクト参照またはメモリ割り当てのような)それが持つどんなリソースも取り除いた後に戻るかのどちらか一方を行う責任があります。エラーを処理する準備をしていなければ、普通に続けるべきではありません。**
> エラーのために戻る場合は、エラーが設定されていると呼び出し元に知らせることが大切です。エラーが処理されていない場合または丁寧に伝えられている場合には、
> Python/C APIのさらなる呼び出しは意図した通りには動かない可能性があり、不可解な形で失敗するかもしれません。

(最後の一文、「エラーが処理されていなかったり、注意深く伝搬されていなかったりした場合には」の誤訳だと思う -- 閑話休題)

とのことなので、エラーを検知した場合、それを関数内で処理し切ってしまうか、あるいは処理を中止してエラーを伝搬させるかを、関数自身の責任で行わなければなりません。  
シグナル発生時に中断したいならば、@masm さんがコメントで指摘されているように、 `PyErr_CheckSignals()`
でシグナル発生をチェックした上で、発生していたらそこで中断するようにします。  
`for_int()` の定義で、 `PyArg_ParseTuple()` が失敗したら `NULL`
を返すようになっていますが、これと同様、`PyErr_CheckSignals()` がシグナルの発生を検知したら、`NULL`
を返して終了するようにします。必要に応じてリソースの解放を行う必要もありますが、今回は不要ですね。

[code]

    static PyObject*
    for_int(PyObject *self, PyObject *args)
    {
        int x;
    
        if (!PyArg_ParseTuple(args, "i", &x))
            return NULL;
        for (int i=0; i<x; i++) {
            // シグナルの発生を調べ、発生していたら中断する。
            if (PyErr_CheckSignals() != 0) {
                return NULL;
            }
            std::cout << i << "\n";
        }
        Py_INCREF(Py_None);
        return Py_None;
    
    }
    
[/code]


CC BY-SA 3.0
2015-06-17T17:13:37.073
11450
2015-06-17T17:13:37.073
7831
11320
answer