How do I propagate C++ exceptions to Python in a SWIG wrapper library?

Viewed 11666

I'm writing a SWIG wrapper around a custom C++ library which defines its own C++ exception types. The library's exception types are richer and more specific than standard exceptions. (For example, one class represents parse errors and has a collection of line numbers.) How do I propagate those exceptions back to Python while preserving the type of the exception?

6 Answers

example.h

struct MyBaseException : public std::runtime_error {
  MyBaseException(const std::string& msg)
    : std::runtime_error{msg} {}
};

struct MyDerivedException : public MyBaseException {
  MyDerivedException(const std::string& msg)
    : MyBaseException{msg} {}
};

void foo() { throw MyBaseException{"my base exception"}; }
void bar() { throw MyDerivedException{"my derived exception"}; }
void baz() { throw std::runtime_error{"runtime error"}; }
void qux() { throw 0; }

example.i

%module example
%{
#include "example.h"
%}

%include <std_string.i>
%include <exception.i>

%exception {
  try {
    $action
  } catch (const MyDerivedException& e) {
     PyErr_SetString(SWIG_Python_ExceptionType(SWIGTYPE_p_MyDerivedException), e.what());
     SWIG_fail;
  } catch (const MyBaseException& e) {
     PyErr_SetString(SWIG_Python_ExceptionType(SWIGTYPE_p_MyBaseException), e.what());
     SWIG_fail;
  } catch(const std::exception& e) {
    SWIG_exception(SWIG_RuntimeError, e.what());
  } catch(...) {
    SWIG_exception(SWIG_UnknownError, "");
  }
}

%exceptionclass MyBaseException;

%include "example.h"

test.py

import example

try:
  example.foo()
except example.MyBaseException as e:
  print(e)

try:
  example.bar()
except example.MyDerivedException as e:
  print(e)

try:
  example.baz()
except RuntimeError as e:
  print(e)

try:
  example.qux()
except:
  print("unknown error")

python3 test.py

my base exception
my derived exception
runtime error
unknown error
Related