C 언어로 작성된 Python 확장 모듈에서도 일반적인 Python 코드와 마찬가지로 예외를 발생시킬 수 있습니다. C API 수준에서는 PyErr_SetString()이나 PyErr_Format() 같은 함수로 예외를 설정한 뒤 NULL을 반환하면 되며, 경우에 따라서는 C API 함수 자체가 내부적으로 예외를 설정하기도 합니다.
예외를 발생시키는 C 확장 예제
다음은 의도적으로 잘못된 형식 문자를 Py_BuildValue()에 전달해 예외를 유발하는 hello.c 예제입니다.
#include <Python.h>
static PyObject* helloworld(PyObject* self) {
// 't'는 유효하지 않은 형식 문자이므로 예외가 발생함
return Py_BuildValue("t");
}
static PyMethodDef myMethods[] = {
{ "helloworld", helloworld, METH_NOARGS, "예외 발생 테스트 함수" },
{ NULL, NULL, 0, NULL }
};
static struct PyModuleDef helloworldmodule = {
PyModuleDef_HEAD_INIT,
"helloworld",
"C 확장 예외 테스트 모듈",
-1,
myMethods
};
PyMODINIT_FUNC PyInit_helloworld(void) {
return PyModule_Create(&helloworldmodule);
}
Py_BuildValue()는 지원하지 않는 형식 문자를 만나면 스스로 예외를 설정하고 NULL을 반환합니다. 이렇게 설정된 예외는 이 함수를 호출한 Python 코드 쪽으로 그대로 전달됩니다.
setup.py 스크립트 작성
위 모듈을 빌드하고 설치하려면 다음과 같은 setup.py 스크립트가 필요합니다.
from distutils.core import setup, Extension
setup(name='helloworld', version='1.0', \
ext_modules=[Extension('helloworld', ['hello.c'])])
확장 모듈 설치
다음 명령을 실행해 확장 모듈을 빌드하고 설치합니다.
$ python setup.py install
Python에서 예외 처리 확인
설치가 완료되면 Python 스크립트(test.py)에서 해당 확장 모듈을 임포트하고, try-except 블록으로 발생한 예외를 잡아낼 수 있습니다.
#test.py
import helloworld
try:
print(helloworld.helloworld())
except Exception as e:
print(str(e))
이 스크립트를 실행하면 다음과 같은 결과가 출력됩니다.
bad format char passed to Py_BuildValue
이처럼 C 확장 내부에서 설정된 예외 메시지가 Python의 except 블록에서 정상적으로 처리되는 것을 확인할 수 있습니다. 나아가 PyErr_NewException()을 사용하면 새로운 예외 타입을 직접 정의해 모듈 외부에 노출할 수도 있으므로, 더 세밀한 오류 처리가 필요한 경우 활용해 보시기 바랍니다.