├── Make.rules ├── part3 ├── conf_file.1.conf ├── conf_file.2.conf ├── Makefile ├── config_parser.h ├── config_parser.cpp ├── parse_config.cpp └── handle_error.cpp ├── part4 ├── conf_file.1.conf ├── conf_file.2.conf ├── handle_error_option1.h ├── Makefile ├── handle_error.h ├── config_parser.h ├── parse_config.cpp ├── config_parser.cpp ├── handle_error.cpp └── handle_error_option1.cpp ├── part1 ├── Makefile └── hello_world.cpp ├── part2 ├── Makefile └── handle_error.cpp ├── part5 ├── Makefile ├── python_thread_lock.h ├── handle_error.h ├── two_threads.cpp └── handle_error.cpp ├── README └── Make.defaults /Make.rules: -------------------------------------------------------------------------------- 1 | %.o: %.cpp 2 | $(CPP) $(INCDIR) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ 3 | -------------------------------------------------------------------------------- /part3/conf_file.1.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | Directory = /path/to/something/super/important 3 | -------------------------------------------------------------------------------- /part4/conf_file.1.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | Directory = /path/to/something/super/important 3 | -------------------------------------------------------------------------------- /part3/conf_file.2.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | Directory = /a/different/path/ 3 | 4 | [Auth] 5 | Username = Manh4ttan 6 | Password = cl4mCh0wderzzz 7 | -------------------------------------------------------------------------------- /part4/conf_file.2.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | Directory = /a/different/path/ 3 | 4 | [Auth] 5 | Username = Manh4ttan 6 | Password = cl4mCh0wderzzz 7 | -------------------------------------------------------------------------------- /part4/handle_error_option1.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = boost::python; 5 | 6 | std::string parse_python_exception(); 7 | -------------------------------------------------------------------------------- /part1/Makefile: -------------------------------------------------------------------------------- 1 | include ../Make.defaults 2 | include ../Make.rules 3 | 4 | HWOBJS = hello_world.o 5 | 6 | all: helloworld 7 | 8 | helloworld: $(HWOBJS) $(BOOST_PYTHON_LIB) 9 | $(LD) -o hello_world $^ $(LDFLAGS) -lpython2.6 10 | 11 | clean: 12 | rm -rf $(HWOBJS) hello_world 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /part2/Makefile: -------------------------------------------------------------------------------- 1 | include ../Make.defaults 2 | include ../Make.rules 3 | 4 | OBJS = handle_error.o 5 | 6 | all: handle_error 7 | 8 | handle_error: $(OBJS) $(BOOST_PYTHON_LIB) 9 | $(LD) -o handle_error $^ $(LDFLAGS) -lpython2.6 10 | 11 | clean: 12 | rm -rf $(OBJS) handle_error 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /part5/Makefile: -------------------------------------------------------------------------------- 1 | include ../Make.defaults 2 | include ../Make.rules 3 | 4 | OBJS = handle_error.o two_threads.o 5 | 6 | all: parse_config 7 | 8 | parse_config: $(OBJS) $(BOOST_PYTHON_LIB) 9 | $(LD) -o two_threads $^ $(LDFLAGS) -lpython2.6 10 | 11 | clean: 12 | rm -rf $(OBJS) two_threads 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /part3/Makefile: -------------------------------------------------------------------------------- 1 | include ../Make.defaults 2 | include ../Make.rules 3 | 4 | OBJS = handle_error.o parse_config.o config_parser.o 5 | 6 | all: parse_config 7 | 8 | parse_config: $(OBJS) $(BOOST_PYTHON_LIB) 9 | $(LD) -o parse_config $^ $(LDFLAGS) -lpython2.6 10 | 11 | clean: 12 | rm -rf $(OBJS) parse_config 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /part4/Makefile: -------------------------------------------------------------------------------- 1 | include ../Make.defaults 2 | include ../Make.rules 3 | 4 | OBJS = handle_error.o parse_config.o config_parser.o 5 | 6 | all: parse_config 7 | 8 | parse_config: $(OBJS) $(BOOST_PYTHON_LIB) 9 | $(LD) -o parse_config $^ $(LDFLAGS) -lpython2.6 10 | 11 | clean: 12 | rm -rf $(OBJS) parse_config 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This repo contains the code from my "Embedding Python in C++ Applications with boost::python" tutorial on my blog at http://thejosephturner.com. 2 | 3 | In a Linux build environment, all you should have to do is change the paths in the top-level Make.defaults file. You may also need to change the Python include directory if things don't seem to be working. 4 | -------------------------------------------------------------------------------- /part5/python_thread_lock.h: -------------------------------------------------------------------------------- 1 | #ifndef PYTHON_THREAD_LOCK_H 2 | #define PYTHON_THREAD_LOCK_H 3 | 4 | #include 5 | 6 | class PythonThreadLock{ 7 | private: 8 | PyGILState_STATE gstate_; 9 | public: 10 | PythonThreadLock(){ 11 | gstate_ = PyGILState_Ensure(); 12 | } 13 | ~PythonThreadLock(){ 14 | PyGILState_Release(gstate_); 15 | } 16 | }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /part3/config_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_PARSER_H 2 | #define CONFIG_PARSER_H 3 | 4 | #include 5 | 6 | class ConfigParser{ 7 | private: 8 | boost::python::object conf_parser_; 9 | public: 10 | ConfigParser(); 11 | 12 | bool parse_file(const std::string &filename); 13 | std::string get(const std::string &attr, 14 | const std::string §ion = "DEFAULT"); 15 | void set(const std::string &attr, 16 | const std::string &value, 17 | const std::string §ion = "DEFAULT"); 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /part4/handle_error.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = boost::python; 5 | 6 | std::string parse_python_exception(); 7 | 8 | template 9 | return_type call_python_func(boost::function to_call, const std::string &error_pre){ 10 | // Prefix the error string 11 | std::string error_str(error_pre); 12 | 13 | try{ 14 | return to_call(); 15 | }catch(boost::python::error_already_set const &){ 16 | // Handle the error here, repackage as a C++ error 17 | error_str = error_str + parse_python_exception(); 18 | throw std::runtime_error(error_str); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /part5/handle_error.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = boost::python; 5 | 6 | std::string parse_python_exception(); 7 | 8 | template 9 | return_type call_python_func(boost::function to_call, const std::string &error_pre){ 10 | // Prefix the error string 11 | std::string error_str(error_pre); 12 | 13 | try{ 14 | return to_call(); 15 | }catch(boost::python::error_already_set const &){ 16 | // Handle the error here, repackage as a C++ error 17 | error_str = error_str + parse_python_exception(); 18 | throw std::runtime_error(error_str); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Make.defaults: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CPP = g++ 3 | AS = as 4 | LD = g++ 5 | AR = ar 6 | RANLIB = ranlib 7 | OBJCOPY = objcopy 8 | 9 | # external libraries and headers 10 | 11 | # change the next line to point to the location of your boost installation 12 | EXTERN_DIR ?= /path/to/boost 13 | EXTERN_LIBDIR = $(EXTERN_DIR)/lib 14 | EXTERN_INCDIR = $(EXTERN_DIR)/include 15 | EXTERN_BINDIR = $(EXTERN_DIR)/bin 16 | 17 | BOOST_PYTHON_LIB = $(EXTERN_LIBDIR)/libboost_python.a 18 | 19 | INCDIRS = . 20 | INCDIRS += $(EXTERN_INCDIR) 21 | # you may also need to change this directory if you want to pin to a specific 22 | # python version 23 | INCDIRS += /usr/include/python2.6 24 | INCDIR = $(foreach dir, $(INCDIRS), -I$(dir)) 25 | -------------------------------------------------------------------------------- /part4/config_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_PARSER_H 2 | #define CONFIG_PARSER_H 3 | 4 | #include 5 | 6 | class ConfigParser{ 7 | private: 8 | boost::python::object conf_parser_; 9 | 10 | void init_py(); 11 | bool parse_file_py(const std::string &filename); 12 | std::string get_py(const std::string &attr, 13 | const std::string §ion = "DEFAULT"); 14 | void set_py(const std::string &attr, 15 | const std::string &value, 16 | const std::string §ion = "DEFAULT"); 17 | public: 18 | ConfigParser(); 19 | 20 | bool parse_file(const std::string &filename); 21 | std::string get(const std::string &attr, 22 | const std::string §ion = "DEFAULT"); 23 | void set(const std::string &attr, 24 | const std::string &value, 25 | const std::string §ion = "DEFAULT"); 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /part3/config_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "config_parser.h" 2 | 3 | namespace py = boost::python; 4 | 5 | ConfigParser::ConfigParser(){ 6 | // load the main namespace 7 | py::object mm = py::import("__main__"); 8 | // load the dictionary for the namespace 9 | py::object mn = mm.attr("__dict__"); 10 | // import the ConfigParser module into the namespace 11 | py::exec("import ConfigParser", mn); 12 | // create the locally-held RawConfigParser object 13 | conf_parser_ = py::eval("ConfigParser.RawConfigParser()", mn); 14 | } 15 | 16 | bool ConfigParser::parse_file(const std::string &filename){ 17 | // return true if we have successfully parsed the file 18 | return py::len(conf_parser_.attr("read")(filename)) == 1; 19 | } 20 | 21 | std::string ConfigParser::get(const std::string &attr, const std::string §ion){ 22 | // extract the string value of the attribute within the given section 23 | return py::extract(conf_parser_.attr("get")(section, attr)); 24 | } 25 | 26 | void ConfigParser::set(const std::string &attr, const std::string &value, const std::string §ion){ 27 | // set the string value of the attribute within the given section 28 | conf_parser_.attr("set")(section, attr, value); 29 | } 30 | -------------------------------------------------------------------------------- /part4/parse_config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "config_parser.h" 5 | 6 | namespace py = boost::python; 7 | using namespace std; 8 | 9 | std::string parse_python_exception(); 10 | 11 | int main(){ 12 | Py_Initialize(); 13 | try{ 14 | ConfigParser parser; 15 | // Parse the first file 16 | parser.parse_file("conf_file.1.conf"); 17 | // Read a value out of the first file 18 | cout << "Directory (file 1): " << parser.get("Directory", "DEFAULT") << endl; 19 | // Parse the second file 20 | parser.parse_file("conf_file.2.conf"); 21 | // Read the (overridden) same value 22 | cout << "Directory (file 2): " << parser.get("Directory", "DEFAULT") << endl; 23 | // Read some values out of non-DEFAULT sections 24 | cout << "Username: " << parser.get("Username", "Auth") << endl; 25 | cout << "Password: " << parser.get("Password", "Auth") << endl; 26 | // Set a value and re-read 27 | parser.set("Directory", "values can be arbitrary strings", "DEFAULT"); 28 | cout << "Directory (force set by application): " << parser.get("Directory") << endl; 29 | 30 | // Will raise a NoOption exception 31 | cout << "Proxy host: " << parser.get("ProxyHost", "Network") << endl; 32 | }catch(exception &e){ 33 | // The python error string will be in the thrown exception 34 | cout << "Here is the error, from a C++ exception: " << e.what() << endl; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /part3/parse_config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "config_parser.h" 5 | 6 | namespace py = boost::python; 7 | using namespace std; 8 | 9 | std::string parse_python_exception(); 10 | 11 | int main(){ 12 | Py_Initialize(); 13 | try{ 14 | ConfigParser parser; 15 | // Parse the first file 16 | parser.parse_file("conf_file.1.conf"); 17 | // Read a value out of the first file 18 | cout << "Directory (file 1): " << parser.get("Directory", "DEFAULT") << endl; 19 | // Parse the second file 20 | parser.parse_file("conf_file.2.conf"); 21 | // Read the (overridden) same value 22 | cout << "Directory (file 2): " << parser.get("Directory", "DEFAULT") << endl; 23 | // Read some values out of non-DEFAULT sections 24 | cout << "Username: " << parser.get("Username", "Auth") << endl; 25 | cout << "Password: " << parser.get("Password", "Auth") << endl; 26 | // Set a value and re-read 27 | parser.set("Directory", "values can be arbitrary strings", "DEFAULT"); 28 | cout << "Directory (force set by application): " << parser.get("Directory") << endl; 29 | // Will raise a NoOption exception 30 | // cout << "Proxy host: " << parser.get("ProxyHost", "Network") << endl; 31 | }catch(boost::python::error_already_set const &){ 32 | // Parse and output the exception 33 | string perror_str = parse_python_exception(); 34 | cout << "Error during configuration parsing: " << perror_str << endl; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /part5/two_threads.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "handle_error.h" 7 | #include "python_thread_lock.h" 8 | 9 | namespace py = boost::python; 10 | using namespace std; 11 | 12 | void py_run(){ 13 | // Acquire the thread lock 14 | PythonThreadLock p; 15 | // Do the init 16 | py::object mm = py::import("__main__"); 17 | py::object mn = mm.attr("__dict__"); 18 | // Release the thread lock, and acquire it on each iteration instead 19 | for(int i = 0; i < 5; i++){ 20 | py::exec("print 'Hey I am a thread!'", mn); 21 | sleep(1); 22 | } 23 | } 24 | 25 | // Brainless python thread function 26 | void *do_python(void *arg){ 27 | try{ 28 | call_python_func(py_run, "Error during thread execution: "); 29 | }catch(exception &e){ 30 | // As before, any python error string will be in the thrown exception 31 | cout << e.what() << endl; 32 | } 33 | return NULL; 34 | } 35 | 36 | // Initialization steps for multithreaded embedded Python 37 | void init_threading(){ 38 | // Initialize the GIL 39 | PyEval_InitThreads(); 40 | // InitThreads aquires the GIL, so delete the thread state and release the 41 | // lock to start from scratch 42 | PyThreadState* tcur = PyThreadState_Get(); 43 | PyThreadState_Swap(NULL); 44 | PyThreadState_Clear(tcur); 45 | PyThreadState_Delete(tcur); 46 | PyEval_ReleaseLock(); 47 | } 48 | 49 | int main(){ 50 | // This is basically the simplest pthread introduction, with Python to boot 51 | Py_Initialize(); 52 | init_threading(); 53 | // Pthread stuff 54 | pthread_t *threads; 55 | pthread_attr_t pthread_custom_attr; 56 | threads = new pthread_t[4 * sizeof(pthread_t)]; 57 | pthread_attr_init(&pthread_custom_attr); 58 | 59 | // Spawn 4 threads 60 | for(int i = 0; i < 4; i++){ 61 | pthread_create(&threads[i], &pthread_custom_attr, do_python, NULL); 62 | } 63 | // Wait for the threads to finish 64 | for (int i = 0; i < 4; i++){ 65 | pthread_join(threads[i], NULL); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /part4/config_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "config_parser.h" 2 | #include "handle_error.h" 3 | 4 | using namespace std; 5 | namespace py = boost::python; 6 | 7 | ConfigParser::ConfigParser(){ 8 | call_python_func(boost::bind(&ConfigParser::init_py, this), 9 | "Error during configuration parser init: "); 10 | } 11 | 12 | void ConfigParser::init_py(){ 13 | // load the main namespace 14 | py::object mm = py::import("__main__"); 15 | // load the dictionary for the namespace 16 | py::object mn = mm.attr("__dict__"); 17 | // import the ConfigParser module into the namespace 18 | py::exec("import ConfigParser", mn); 19 | // create the locally-held RawConfigParser object 20 | conf_parser_ = py::eval("ConfigParser.RawConfigParser()", mn); 21 | } 22 | 23 | bool ConfigParser::parse_file_py(const string &filename){ 24 | // return true if we have successfully parsed the file 25 | return py::len(conf_parser_.attr("read")(filename)) == 1; 26 | } 27 | 28 | bool ConfigParser::parse_file(const string &filename){ 29 | return call_python_func(boost::bind(&ConfigParser::parse_file_py, this, filename), 30 | "Error during configuration file parsing: "); 31 | } 32 | 33 | string ConfigParser::get_py(const string &attr, const string §ion){ 34 | // extract the string value of the attribute within the given section 35 | return py::extract(conf_parser_.attr("get")(section, attr)); 36 | } 37 | 38 | string ConfigParser::get(const string &attr, const string §ion){ 39 | return call_python_func(boost::bind(&ConfigParser::get_py, this, attr, section), 40 | "Error getting configuration option: "); 41 | } 42 | 43 | void ConfigParser::set_py(const string &attr, const string &value, const string §ion){ 44 | // set the string value of the attribute within the given section 45 | conf_parser_.attr("set")(section, attr, value); 46 | } 47 | 48 | void ConfigParser::set(const string &attr, const string &value, const string §ion){ 49 | call_python_func(boost::bind(&ConfigParser::set_py, this, attr, value, section), 50 | "Error setting configuration option: "); 51 | } 52 | -------------------------------------------------------------------------------- /part1/hello_world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace py = boost::python; 6 | 7 | int main(){ 8 | // Must be called before any boost::python functions 9 | Py_Initialize(); 10 | // import the main module 11 | py::object main_module = py::import("__main__"); 12 | // load the dictionary object out of the main module 13 | py::object main_namespace = main_module.attr("__dict__"); 14 | // run simple code within the main namespace using the boost::python::exec 15 | // function 16 | py::exec("print 'Hello, world'", main_namespace); 17 | // any valid Python will execute 18 | py::exec("print 'Hello, world'[3:5]", main_namespace); 19 | // it is as if you opened a new Python file and wrote the commands 20 | // all of the normal functionality is available 21 | py::exec("print '.'.join(['1','2','3'])", main_namespace); 22 | 23 | // of course, you can also import functionality from the standard library 24 | py::exec("import random", main_namespace); 25 | // boost::python::eval will return the result of an expression 26 | // as a boost::python::object 27 | py::object rand = py::eval("random.random()", main_namespace); 28 | // the boost::python::extract function can then convert to C++ data types 29 | // (only as appropriate, of course) 30 | std::cout << py::extract(rand) << std::endl; 31 | 32 | // or, if you'd prefer, you can extract the functions themselves into objects 33 | // then call them as you wish from C++. this is akin to what we did at the top 34 | // of this file. you can't execute code with the exec function, though, without 35 | // extracting the namespace object as we did with __main__ above. 36 | // this method ultimately provides a much cleaner way of interacting with Python 37 | // objects over their lifetimes 38 | 39 | // import the random module 40 | py::object rand_mod = py::import("random"); 41 | // extract the random function from the random module (random.random()) 42 | py::object rand_func = rand_mod.attr("random"); 43 | // call the function and extract the result 44 | // [sidenote: this sort of syntax is what makes boost::python so valuable] 45 | py::object rand2 = rand_func(); 46 | std::cout << py::extract(rand2) << std::endl; 47 | } 48 | -------------------------------------------------------------------------------- /part3/handle_error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = boost::python; 5 | 6 | std::string parse_python_exception(); 7 | 8 | // Parses the value of the active python exception 9 | // NOTE SHOULD NOT BE CALLED IF NO EXCEPTION 10 | std::string parse_python_exception(){ 11 | PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL; 12 | // Fetch the exception info from the Python C API 13 | PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr); 14 | 15 | // Fallback error 16 | std::string ret("Unfetchable Python error"); 17 | // If the fetch got a type pointer, parse the type into the exception string 18 | if(type_ptr != NULL){ 19 | py::handle<> h_type(type_ptr); 20 | py::str type_pstr(h_type); 21 | // Extract the string from the boost::python object 22 | py::extract e_type_pstr(type_pstr); 23 | // If a valid string extraction is available, use it 24 | // otherwise use fallback 25 | if(e_type_pstr.check()) 26 | ret = e_type_pstr(); 27 | else 28 | ret = "Unknown exception type"; 29 | } 30 | // Do the same for the exception value (the stringification of the exception) 31 | if(value_ptr != NULL){ 32 | py::handle<> h_val(value_ptr); 33 | py::str a(h_val); 34 | py::extract returned(a); 35 | if(returned.check()) 36 | ret += ": " + returned(); 37 | else 38 | ret += std::string(": Unparseable Python error: "); 39 | } 40 | // Parse lines from the traceback using the Python traceback module 41 | if(traceback_ptr != NULL){ 42 | py::handle<> h_tb(traceback_ptr); 43 | // Load the traceback module and the format_tb function 44 | py::object tb(py::import("traceback")); 45 | py::object fmt_tb(tb.attr("format_tb")); 46 | // Call format_tb to get a list of traceback strings 47 | py::object tb_list(fmt_tb(h_tb)); 48 | // Join the traceback strings into a single string 49 | py::object tb_str(py::str("\n").join(tb_list)); 50 | // Extract the string, check the extraction, and fallback in necessary 51 | py::extract returned(tb_str); 52 | if(returned.check()) 53 | ret += ": " + returned(); 54 | else 55 | ret += std::string(": Unparseable Python traceback"); 56 | } 57 | return ret; 58 | } 59 | -------------------------------------------------------------------------------- /part4/handle_error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = boost::python; 5 | 6 | std::string parse_python_exception(); 7 | 8 | // Parses the value of the active python exception 9 | // NOTE SHOULD NOT BE CALLED IF NO EXCEPTION 10 | std::string parse_python_exception(){ 11 | PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL; 12 | // Fetch the exception info from the Python C API 13 | PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr); 14 | 15 | // Fallback error 16 | std::string ret("Unfetchable Python error"); 17 | // If the fetch got a type pointer, parse the type into the exception string 18 | if(type_ptr != NULL){ 19 | py::handle<> h_type(type_ptr); 20 | py::str type_pstr(h_type); 21 | // Extract the string from the boost::python object 22 | py::extract e_type_pstr(type_pstr); 23 | // If a valid string extraction is available, use it 24 | // otherwise use fallback 25 | if(e_type_pstr.check()) 26 | ret = e_type_pstr(); 27 | else 28 | ret = "Unknown exception type"; 29 | } 30 | // Do the same for the exception value (the stringification of the exception) 31 | if(value_ptr != NULL){ 32 | py::handle<> h_val(value_ptr); 33 | py::str a(h_val); 34 | py::extract returned(a); 35 | if(returned.check()) 36 | ret += ": " + returned(); 37 | else 38 | ret += std::string(": Unparseable Python error: "); 39 | } 40 | // Parse lines from the traceback using the Python traceback module 41 | if(traceback_ptr != NULL){ 42 | py::handle<> h_tb(traceback_ptr); 43 | // Load the traceback module and the format_tb function 44 | py::object tb(py::import("traceback")); 45 | py::object fmt_tb(tb.attr("format_tb")); 46 | // Call format_tb to get a list of traceback strings 47 | py::object tb_list(fmt_tb(h_tb)); 48 | // Join the traceback strings into a single string 49 | py::object tb_str(py::str("\n").join(tb_list)); 50 | // Extract the string, check the extraction, and fallback in necessary 51 | py::extract returned(tb_str); 52 | if(returned.check()) 53 | ret += ": " + returned(); 54 | else 55 | ret += std::string(": Unparseable Python traceback"); 56 | } 57 | return ret; 58 | } 59 | -------------------------------------------------------------------------------- /part5/handle_error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = boost::python; 5 | 6 | std::string parse_python_exception(); 7 | 8 | // Parses the value of the active python exception 9 | // NOTE SHOULD NOT BE CALLED IF NO EXCEPTION 10 | std::string parse_python_exception(){ 11 | PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL; 12 | // Fetch the exception info from the Python C API 13 | PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr); 14 | 15 | // Fallback error 16 | std::string ret("Unfetchable Python error"); 17 | // If the fetch got a type pointer, parse the type into the exception string 18 | if(type_ptr != NULL){ 19 | py::handle<> h_type(type_ptr); 20 | py::str type_pstr(h_type); 21 | // Extract the string from the boost::python object 22 | py::extract e_type_pstr(type_pstr); 23 | // If a valid string extraction is available, use it 24 | // otherwise use fallback 25 | if(e_type_pstr.check()) 26 | ret = e_type_pstr(); 27 | else 28 | ret = "Unknown exception type"; 29 | } 30 | // Do the same for the exception value (the stringification of the exception) 31 | if(value_ptr != NULL){ 32 | py::handle<> h_val(value_ptr); 33 | py::str a(h_val); 34 | py::extract returned(a); 35 | if(returned.check()) 36 | ret += ": " + returned(); 37 | else 38 | ret += std::string(": Unparseable Python error: "); 39 | } 40 | // Parse lines from the traceback using the Python traceback module 41 | if(traceback_ptr != NULL){ 42 | py::handle<> h_tb(traceback_ptr); 43 | // Load the traceback module and the format_tb function 44 | py::object tb(py::import("traceback")); 45 | py::object fmt_tb(tb.attr("format_tb")); 46 | // Call format_tb to get a list of traceback strings 47 | py::object tb_list(fmt_tb(h_tb)); 48 | // Join the traceback strings into a single string 49 | py::object tb_str(py::str("\n").join(tb_list)); 50 | // Extract the string, check the extraction, and fallback in necessary 51 | py::extract returned(tb_str); 52 | if(returned.check()) 53 | ret += ": " + returned(); 54 | else 55 | ret += std::string(": Unparseable Python traceback"); 56 | } 57 | return ret; 58 | } 59 | -------------------------------------------------------------------------------- /part4/handle_error_option1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace py = boost::python; 5 | 6 | std::string parse_python_exception(); 7 | 8 | // Parses the value of the active python exception 9 | // NOTE SHOULD NOT BE CALLED IF NO EXCEPTION 10 | std::string parse_python_exception(){ 11 | PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL; 12 | // Fetch the exception info from the Python C API 13 | PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr); 14 | 15 | // Fallback error 16 | std::string ret("Unfetchable Python error"); 17 | // If the fetch got a type pointer, parse the type into the exception string 18 | if(type_ptr != NULL){ 19 | py::handle<> h_type(type_ptr); 20 | py::str type_pstr(h_type); 21 | // Extract the string from the boost::python object 22 | py::extract e_type_pstr(type_pstr); 23 | // If a valid string extraction is available, use it 24 | // otherwise use fallback 25 | if(e_type_pstr.check()) 26 | ret = e_type_pstr(); 27 | else 28 | ret = "Unknown exception type"; 29 | } 30 | // Do the same for the exception value (the stringification of the exception) 31 | if(value_ptr != NULL){ 32 | py::handle<> h_val(value_ptr); 33 | py::str a(h_val); 34 | py::extract returned(a); 35 | if(returned.check()) 36 | ret += ": " + returned(); 37 | else 38 | ret += std::string(": Unparseable Python error: "); 39 | } 40 | // Parse lines from the traceback using the Python traceback module 41 | if(traceback_ptr != NULL){ 42 | py::handle<> h_tb(traceback_ptr); 43 | // Load the traceback module and the format_tb function 44 | py::object tb(py::import("traceback")); 45 | py::object fmt_tb(tb.attr("format_tb")); 46 | // Call format_tb to get a list of traceback strings 47 | py::object tb_list(fmt_tb(h_tb)); 48 | // Join the traceback strings into a single string 49 | py::object tb_str(py::str("\n").join(tb_list)); 50 | // Extract the string, check the extraction, and fallback in necessary 51 | py::extract returned(tb_str); 52 | if(returned.check()) 53 | ret += ": " + returned(); 54 | else 55 | ret += std::string(": Unparseable Python traceback"); 56 | } 57 | return ret; 58 | } 59 | -------------------------------------------------------------------------------- /part2/handle_error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace py = boost::python; 6 | 7 | std::string parse_python_exception(); 8 | 9 | int main(){ 10 | try{ 11 | Py_Initialize(); 12 | py::object rand_mod = py::import("fake_module"); 13 | }catch(boost::python::error_already_set const &){ 14 | // Parse and output the exception 15 | std::string perror_str = parse_python_exception(); 16 | std::cout << "Error in Python: " << perror_str << std::endl; 17 | } 18 | } 19 | 20 | // Parses the value of the active python exception 21 | // NOTE SHOULD NOT BE CALLED IF NO EXCEPTION 22 | std::string parse_python_exception(){ 23 | PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL; 24 | // Fetch the exception info from the Python C API 25 | PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr); 26 | 27 | // Fallback error 28 | std::string ret("Unfetchable Python error"); 29 | // If the fetch got a type pointer, parse the type into the exception string 30 | if(type_ptr != NULL){ 31 | py::handle<> h_type(type_ptr); 32 | py::str type_pstr(h_type); 33 | // Extract the string from the boost::python object 34 | py::extract e_type_pstr(type_pstr); 35 | // If a valid string extraction is available, use it 36 | // otherwise use fallback 37 | if(e_type_pstr.check()) 38 | ret = e_type_pstr(); 39 | else 40 | ret = "Unknown exception type"; 41 | } 42 | // Do the same for the exception value (the stringification of the exception) 43 | if(value_ptr != NULL){ 44 | py::handle<> h_val(value_ptr); 45 | py::str a(h_val); 46 | py::extract returned(a); 47 | if(returned.check()) 48 | ret += ": " + returned(); 49 | else 50 | ret += std::string(": Unparseable Python error: "); 51 | } 52 | // Parse lines from the traceback using the Python traceback module 53 | if(traceback_ptr != NULL){ 54 | py::handle<> h_tb(traceback_ptr); 55 | // Load the traceback module and the format_tb function 56 | py::object tb(py::import("traceback")); 57 | py::object fmt_tb(tb.attr("format_tb")); 58 | // Call format_tb to get a list of traceback strings 59 | py::object tb_list(fmt_tb(h_tb)); 60 | // Join the traceback strings into a single string 61 | py::object tb_str(py::str("\n").join(tb_list)); 62 | // Extract the string, check the extraction, and fallback in necessary 63 | py::extract returned(tb_str); 64 | if(returned.check()) 65 | ret += ": " + returned(); 66 | else 67 | ret += std::string(": Unparseable Python traceback"); 68 | } 69 | return ret; 70 | } 71 | --------------------------------------------------------------------------------