1 #include "Interpreter.h"
2 #include <iostream>
3 #include <map>
4 #include <memory>
5 
6 PyThreadState* Interpreter::MainThreadState = NULL;
7 
Interpreter()8 Interpreter::Interpreter( )
9 {
10     PyEval_AcquireThread( MainThreadState );
11     m_threadState = Py_NewInterpreter( );
12 
13     PyObject *module = PyImport_ImportModule("__main__");
14     loc = glb = PyModule_GetDict(module);
15 
16     PyRun_SimpleString("import sys\n"
17         "import redirector\n"
18         "sys.path.insert(0, \".\")\n" // add current path
19         "sys.stdout = redirector.redirector()\n"
20         "sys.stderr = sys.stdout\n"
21         "import rlcompleter\n"
22         "sys.completer = rlcompleter.Completer()\n"
23     );
24 
25     PyEval_ReleaseThread( m_threadState );
26 }
27 
~Interpreter()28 Interpreter::~Interpreter( )
29 {
30     PyEval_AcquireThread( m_threadState );
31     Py_EndInterpreter( m_threadState );
32     PyEval_ReleaseLock( );
33 }
34 
35 void
test()36 Interpreter::test( )
37 {
38     PyEval_AcquireThread( m_threadState );
39 
40     PyObject* py_result;
41     PyObject* dum;
42     std::string command = "print 'Hello world'\n";
43     py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
44     if ( py_result == 0 )
45     {
46         std::cout << "Huh?\n";
47         PyEval_ReleaseThread( m_threadState );
48         return;
49     }
50     dum = PyEval_EvalCode (py_result, glb, loc);
51     Py_XDECREF (dum);
52     Py_XDECREF (py_result);
53 
54     std::cout << GetResultString( m_threadState );
55     GetResultString( m_threadState ) = "";
56 
57     PyEval_ReleaseThread( m_threadState );
58 }
59 
60 template<typename ... Args>
string_format(const std::string & format,Args...args)61 std::string string_format( const std::string& format, Args ... args )
62 {
63     size_t size = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
64     std::unique_ptr<char[]> buf( new char[ size ] );
65     std::snprintf( buf.get(), size, format.c_str(), args ... );
66     return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
67 }
68 
69 std::string
interpret(const std::string & command,int * errorCode)70 Interpreter::interpret( const std::string& command, int* errorCode )
71 {
72     PyEval_AcquireThread( m_threadState );
73     *errorCode = 0;
74 
75     PyObject* py_result;
76     PyObject* dum;
77     std::string res;
78     py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
79     if ( py_result == 0 )
80     {
81         if ( PyErr_Occurred( ) )
82         {
83             *errorCode = 1;
84             PyErr_Print( );
85             res = GetResultString( m_threadState );
86             GetResultString( m_threadState ) = "";
87         }
88 
89         PyEval_ReleaseThread( m_threadState );
90         return res;
91     }
92     dum = PyEval_EvalCode (py_result, glb, loc);
93     Py_XDECREF (dum);
94     Py_XDECREF (py_result);
95     if ( PyErr_Occurred( ) )
96     {
97         *errorCode = 1;
98         PyErr_Print( );
99     }
100 
101     res = GetResultString( m_threadState );
102     GetResultString( m_threadState ) = "";
103 
104     PyEval_ReleaseThread( m_threadState );
105     return res;
106 }
107 
suggest(const std::string & hint)108 const std::list<std::string>& Interpreter::suggest( const std::string& hint )
109 {
110     PyEval_AcquireThread( m_threadState );
111     m_suggestions.clear();
112     int i = 0;
113     std::string command = string_format("sys.completer.complete('%s', %d)\n", hint.c_str(),i);
114     std::string res;
115     do
116     {
117         PyObject* py_result;
118         PyObject* dum;
119         py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input);
120         dum = PyEval_EvalCode (py_result, glb, loc);
121         Py_XDECREF (dum);
122         Py_XDECREF (py_result);
123         res = GetResultString( m_threadState );
124         GetResultString( m_threadState ) = "";
125         ++i;
126         command = string_format("sys.completer.complete('%s', %d)\n", hint.c_str(),i);
127         if (res.size())
128         {
129             // throw away the newline
130             res = res.substr(1, res.size() - 3);
131             m_suggestions.push_back(res);
132         }
133     }
134     while (res.size());
135 
136     PyEval_ReleaseThread( m_threadState );
137     return m_suggestions;
138 }
139 
140 void
Initialize()141 Interpreter::Initialize( )
142 {
143     PyImport_AppendInittab("redirector", Interpreter::PyInit_redirector);
144     Py_Initialize( );
145     PyEval_InitThreads( );
146     MainThreadState = PyEval_SaveThread( );
147 }
148 
149 void
Finalize()150 Interpreter::Finalize( )
151 {
152     PyEval_RestoreThread( MainThreadState );
153     Py_Finalize( );
154 }
155 
GetResultString(PyThreadState * threadState)156 std::string& Interpreter::GetResultString( PyThreadState* threadState )
157 {
158     static std::map< PyThreadState*, std::string > ResultStrings;
159     if ( !ResultStrings.count( threadState ) )
160     {
161         ResultStrings[ threadState ] = "";
162     }
163     return ResultStrings[ threadState ];
164 }
165 
RedirectorInit(PyObject *,PyObject *)166 PyObject* Interpreter::RedirectorInit(PyObject *, PyObject *)
167 {
168     Py_INCREF(Py_None);
169     return Py_None;
170 }
171 
RedirectorWrite(PyObject *,PyObject * args)172 PyObject* Interpreter::RedirectorWrite(PyObject *, PyObject *args)
173 {
174     char* output;
175     PyObject *selfi;
176 
177     if (!PyArg_ParseTuple(args,"Os",&selfi,&output))
178     {
179         return NULL;
180     }
181 
182     std::string outputString( output );
183     PyThreadState* currentThread = PyThreadState_Get( );
184     std::string& resultString = GetResultString( currentThread );
185     resultString = resultString + outputString;
186     Py_INCREF(Py_None);
187     return Py_None;
188 }
189 
190 PyMethodDef Interpreter::RedirectorMethods[] =
191 {
192     {"__init__", Interpreter::RedirectorInit, METH_VARARGS,
193      "initialize the stdout/err redirector"},
194     {"write", Interpreter::RedirectorWrite, METH_VARARGS,
195      "implement the write method to redirect stdout/err"},
196     {NULL,NULL,0,NULL},
197 };
198 
199 
createClassObject(const char * name,PyMethodDef methods[])200 PyObject *createClassObject(const char *name, PyMethodDef methods[])
201 {
202     PyObject *pClassName = PyUnicode_FromString(name);
203     PyObject *pClassBases = PyTuple_New(0); // An empty tuple for bases is equivalent to `(object,)`
204     PyObject *pClassDic = PyDict_New();
205 
206 
207     PyMethodDef *def;
208     // add methods to class
209     for (def = methods; def->ml_name != NULL; def++)
210     {
211         PyObject *func = PyCFunction_New(def, NULL);
212         PyObject *method = PyInstanceMethod_New(func);
213         PyDict_SetItemString(pClassDic, def->ml_name, method);
214         Py_DECREF(func);
215         Py_DECREF(method);
216     }
217 
218     // pClass = type(pClassName, pClassBases, pClassDic)
219     PyObject *pClass = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, pClassName, pClassBases, pClassDic, NULL);
220 
221     Py_DECREF(pClassName);
222     Py_DECREF(pClassBases);
223     Py_DECREF(pClassDic);
224 
225 
226     return pClass;
227 }
228 
PyInit_redirector(void)229 PyMODINIT_FUNC Interpreter::PyInit_redirector(void)
230 {
231     static struct PyModuleDef moduledef = {
232             PyModuleDef_HEAD_INIT,
233             "redirector",
234             0,
235             -1,
236             0
237     };
238 
239     PyObject *m = PyModule_Create(&moduledef);
240     if (m) {
241         PyObject *fooClass = createClassObject("redirector", RedirectorMethods);
242         PyModule_AddObject(m, "redirector", fooClass);
243         Py_DECREF(fooClass);
244     }
245     return m;
246 }
247