Pythonプログラマは、特定のエラー処理が要求される場合だけ 例外を扱う必要がある。扱われなかった例外は自動的に呼び出し側に、 そして呼び出し側の呼び出し側に、などと最上位のインタープリタ に到達するまで伝播する。インタープリタではエラーが スタックトレースバックを伴なってユーザに報告される。
しかしながら、Cプログラマにとっては、エラー検査は常に明示的で
ある必要がある。
関数の説明の中で明示的に主張されない限り、
Python C/API中の全ての関数は例外を起こすことができる。
一般に、ある関数がエラーに遭遇したとき、関数は例外を設定して、
所有するオブジェクトへの参照を捨てて、エラーの指標を返す
-- 指標は通常はNULLもしくは-1である。
いくつかの関数は、ブール型の真偽の結果を、エラーを示す偽の
値で返す。明示的なエラーの指標を返さなかったり曖昧な戻り値を持ち、
PyErr_Occurred()
例外の状態はスレッドごとの記憶域(これはスレッド化されていない
アプリケーションで大域的な記憶域を使うことと同じである)で
維持される。
スレッドは2つの状態のうちの1つでいることができる: 例外が
起こったか、そうでないかである。
これを調べるために、関数PyErr_Occurred()を
使うことができる: その関数は例外が起こったときに、例外の
型オブジェクトへの借りものの参照を、そうでなければNULLを返す。
例外の状態を設定するいくつかの関数がある:
PyErr_SetString()
完全な例外の状態は3つのオブジェクト(それら全てはNULLであり得る)
から成る: 例外の型、対応する例外の値、そしてトレースバックである。
これらはPythonの
sys.exc_type、sys.exc_value、
sys.exc_tracebackと同じ意味を持つ。
しかしながら、それらは同じではない: そのPythonのオブジェクトは
Pythonのtry ... except文で扱われた
最後の例外を表わし、一方C言語水準での例外の状態は、
例外がPythonバイトコードのインタープリタのメインループに到達する
まで、C言語関数の間で渡されている間存在するだけである。
このメインループでは、その例外をsys.exc_typeとその仲間に
移す処理を行う。
Python 1.5から始まった、Pythonのコードから例外状態に
アクセスするためのより好ましくスレッドで安全な方法は、関数
一般原則として、ある仕事を行うために別の関数を呼ぶ関数は、 呼び出した関数が例外を起こしたかどうか調べて、もしそうであれば、 その関数の呼び出し側に例外の状態を渡すべきである。 それが所有するオブジェクトへのどんな参照も捨てて、エラーの指標を 返すべきである。しかし、別の例外を設定すべきではない -- それはちょうど起きた例外を上書きして、エラーの正確な原因についての 重要な情報を失うだろう。
例外を検出してそれらを渡す単純な例題が、
上記の例題sum_sequence()
def incr_item(dict, key):
try:
item = dict[key]
except KeyError:
item = 0
return item + 1
ここに対応するC言語のコードを、きちんとした形で示す。
int incr_item(PyObject *dict, PyObject *key)
{
/* Objects all initialized to NULL for Py_XDECREF */
PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;
int rv = -1; /* Return value initialized to -1 (failure) */
item = PyObject_GetItem(dict, key);
if (item == NULL) {
/* Handle KeyError only: */
if (!PyErr_ExceptionMatches(PyExc_KeyError)) goto error;
/* Clear the error and use zero: */
PyErr_Clear();
item = PyInt_FromLong(0L);
if (item == NULL) goto error;
}
const_one = PyInt_FromLong(1L);
if (const_one == NULL) goto error;
incremented_item = PyNumber_Add(item, const_one);
if (incremented_item == NULL) goto error;
if (PyObject_SetItem(dict, key, incremented_item) < 0) goto error;
rv = 0; /* Success */
/* Continue with cleanup code */
error:
/* Cleanup code, shared by success and failure path */
/* Use Py_XDECREF() to ignore NULL references */
Py_XDECREF(item);
Py_XDECREF(const_one);
Py_XDECREF(incremented_item);
return rv; /* -1 for error, 0 for success */
}
この例はC言語でのgoto文の推奨される使い方を示している。
それは特定の例外を扱うための
PyErr_ExceptionMatches()-1 (失敗)に初期化され、
最後の呼び出しが成功してから成功に設定されるだけである。