e19fe3d32196dedf35be44cff4c81d88bf73f082
[trackerpp.git] / src / PyWrapper.cpp
1 #include "PyWrapper.h"
2 #include <string>
3 #include <mutex>
4 #include "Logger.h"
5
6 namespace py = boost::python;
7 using namespace std;
8 using namespace suanzi;
9
10 static const std::string TAG = "PyWrapper";
11
12 PyWrapperPtr PyWrapper::getInstance(const string& pydir)
13 {
14     LOG_DEBUG(TAG, "getInstance");
15     static PyWrapperPtr instance (new PyWrapper(pydir));
16     return instance;
17 }
18
19 PyWrapper::PyWrapper(const std::string& python_dir)
20 {    
21     LOG_DEBUG(TAG, "Init Python interpreter");
22     Py_Initialize();
23     try{
24         main_module = py::import("__main__");
25         main_namespace = main_module.attr("__dict__");
26         py::exec("import sys", main_namespace);
27         std::string cmd = "sys.path.insert(0, '" + python_dir + "')";
28         py::exec(cmd.c_str(), main_namespace);
29         py::exec("import signal", main_namespace);
30         py::exec("signal.signal(signal.SIGINT, signal.SIG_DFL)", main_namespace);
31     } catch (boost::python::error_already_set const &){
32         LOG_ERROR(TAG, "Error in Python: " + parse_python_exception())
33     }
34 }
35
36 PyWrapper::~PyWrapper()
37 {
38     Py_Finalize();
39     LOG_DEBUG(TAG, "DeInit Python interpreter");
40 }
41
42 py::object PyWrapper::import(const std::string& module)
43 {
44     LOG_DEBUG(TAG, "import " + module);
45     py::object o;
46     try{
47         o = py::import(module.c_str());
48     } catch (boost::python::error_already_set const &){
49         LOG_ERROR(TAG, parse_python_exception());
50     }
51     return o;
52 }
53
54 void PyWrapper::exec(const std::string& cmd)
55 {
56     try{
57         py::exec(cmd.c_str());
58     } catch(boost::python::error_already_set const &){
59         LOG_ERROR(TAG, parse_python_exception());
60     }
61 }
62
63
64 // refer to http://www.sigverse.org/wiki/en/index.php?Import%20and%20use%20of%20user%20defined%20python%20modules
65 std::string PyWrapper::parse_python_exception(){  
66     PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL;  
67     // Fetch the exception info from the Python C API  
68     PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr);  
69   
70     // Fallback error  
71     std::string ret("Unfetchable Python error");  
72     // If the fetch got a type pointer, parse the type into the exception string  
73     if(type_ptr != NULL){  
74         py::handle<> h_type(type_ptr);  
75         py::str type_pstr(h_type);  
76         // Extract the string from the boost::python object  
77         py::extract<std::string> e_type_pstr(type_pstr);  
78         // If a valid string extraction is available, use it   
79         //  otherwise use fallback  
80         if(e_type_pstr.check())  
81             ret = e_type_pstr();  
82         else  
83             ret = "Unknown exception type";  
84     }  
85     // Do the same for the exception value (the stringification of the exception)  
86     if(value_ptr != NULL){  
87         py::handle<> h_val(value_ptr);  
88         py::str a(h_val);  
89         py::extract<std::string> returned(a);  
90         if(returned.check())  
91             ret +=  ": " + returned();  
92         else  
93             ret += std::string(": Unparseable Python error: ");  
94     }  
95     // Parse lines from the traceback using the Python traceback module  
96     if(traceback_ptr != NULL){  
97         py::handle<> h_tb(traceback_ptr);  
98         // Load the traceback module and the format_tb function  
99         py::object tb(py::import("traceback"));  
100         py::object fmt_tb(tb.attr("format_tb"));  
101         // Call format_tb to get a list of traceback strings  
102         py::object tb_list(fmt_tb(h_tb));  
103         // Join the traceback strings into a single string  
104         py::object tb_str(py::str("\n").join(tb_list));  
105         // Extract the string, check the extraction, and fallback in necessary  
106         py::extract<std::string> returned(tb_str);  
107         if(returned.check())  
108             ret += ": " + returned();  
109         else  
110             ret += std::string(": Unparseable Python traceback");  
111     }  
112     return ret;  
113 }