separate pytwrpper from predictorWrappery
[trackerpp.git] / src / PyWrapper.cpp
diff --git a/src/PyWrapper.cpp b/src/PyWrapper.cpp
new file mode 100644 (file)
index 0000000..e19fe3d
--- /dev/null
@@ -0,0 +1,113 @@
+#include "PyWrapper.h"
+#include <string>
+#include <mutex>
+#include "Logger.h"
+
+namespace py = boost::python;
+using namespace std;
+using namespace suanzi;
+
+static const std::string TAG = "PyWrapper";
+
+PyWrapperPtr PyWrapper::getInstance(const string& pydir)
+{
+    LOG_DEBUG(TAG, "getInstance");
+    static PyWrapperPtr instance (new PyWrapper(pydir));
+    return instance;
+}
+
+PyWrapper::PyWrapper(const std::string& python_dir)
+{    
+    LOG_DEBUG(TAG, "Init Python interpreter");
+    Py_Initialize();
+    try{
+        main_module = py::import("__main__");
+        main_namespace = main_module.attr("__dict__");
+        py::exec("import sys", main_namespace);
+        std::string cmd = "sys.path.insert(0, '" + python_dir + "')";
+        py::exec(cmd.c_str(), main_namespace);
+        py::exec("import signal", main_namespace);
+        py::exec("signal.signal(signal.SIGINT, signal.SIG_DFL)", main_namespace);
+    } catch (boost::python::error_already_set const &){
+        LOG_ERROR(TAG, "Error in Python: " + parse_python_exception())
+    }
+}
+
+PyWrapper::~PyWrapper()
+{
+    Py_Finalize();
+    LOG_DEBUG(TAG, "DeInit Python interpreter");
+}
+
+py::object PyWrapper::import(const std::string& module)
+{
+    LOG_DEBUG(TAG, "import " + module);
+    py::object o;
+    try{
+        o = py::import(module.c_str());
+    } catch (boost::python::error_already_set const &){
+        LOG_ERROR(TAG, parse_python_exception());
+    }
+    return o;
+}
+
+void PyWrapper::exec(const std::string& cmd)
+{
+    try{
+        py::exec(cmd.c_str());
+    } catch(boost::python::error_already_set const &){
+        LOG_ERROR(TAG, parse_python_exception());
+    }
+}
+
+
+// refer to http://www.sigverse.org/wiki/en/index.php?Import%20and%20use%20of%20user%20defined%20python%20modules
+std::string PyWrapper::parse_python_exception(){  
+    PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL;  
+    // Fetch the exception info from the Python C API  
+    PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr);  
+  
+    // Fallback error  
+    std::string ret("Unfetchable Python error");  
+    // If the fetch got a type pointer, parse the type into the exception string  
+    if(type_ptr != NULL){  
+        py::handle<> h_type(type_ptr);  
+        py::str type_pstr(h_type);  
+        // Extract the string from the boost::python object  
+        py::extract<std::string> e_type_pstr(type_pstr);  
+        // If a valid string extraction is available, use it   
+        //  otherwise use fallback  
+        if(e_type_pstr.check())  
+            ret = e_type_pstr();  
+        else  
+            ret = "Unknown exception type";  
+    }  
+    // Do the same for the exception value (the stringification of the exception)  
+    if(value_ptr != NULL){  
+        py::handle<> h_val(value_ptr);  
+        py::str a(h_val);  
+        py::extract<std::string> returned(a);  
+        if(returned.check())  
+            ret +=  ": " + returned();  
+        else  
+            ret += std::string(": Unparseable Python error: ");  
+    }  
+    // Parse lines from the traceback using the Python traceback module  
+    if(traceback_ptr != NULL){  
+        py::handle<> h_tb(traceback_ptr);  
+        // Load the traceback module and the format_tb function  
+        py::object tb(py::import("traceback"));  
+        py::object fmt_tb(tb.attr("format_tb"));  
+        // Call format_tb to get a list of traceback strings  
+        py::object tb_list(fmt_tb(h_tb));  
+        // Join the traceback strings into a single string  
+        py::object tb_str(py::str("\n").join(tb_list));  
+        // Extract the string, check the extraction, and fallback in necessary  
+        py::extract<std::string> returned(tb_str);  
+        if(returned.check())  
+            ret += ": " + returned();  
+        else  
+            ret += std::string(": Unparseable Python traceback");  
+    }  
+    return ret;  
+}