| #include "Interpreter.h" | |
| #include <iostream> | |
| #include <map> | |
| #include <memory> | |
| PyThreadState* Interpreter::MainThreadState = NULL; | |
| Interpreter::Interpreter( ) | |
| { | |
| PyEval_AcquireThread( MainThreadState ); | |
| m_threadState = Py_NewInterpreter( ); | |
| PyObject *module = PyImport_ImportModule("__main__"); | |
| loc = glb = PyModule_GetDict(module); | |
| PyRun_SimpleString("import sys\n" | |
| "import redirector\n" | |
| "sys.path.insert(0, \".\")\n" // add current path | |
| "sys.stdout = redirector.redirector()\n" | |
| "sys.stderr = sys.stdout\n" | |
| "import rlcompleter\n" | |
| "sys.completer = rlcompleter.Completer()\n" | |
| ); | |
| PyEval_ReleaseThread( m_threadState ); | |
| } | |
| Interpreter::~Interpreter( ) | |
| { | |
| PyEval_AcquireThread( m_threadState ); | |
| Py_EndInterpreter( m_threadState ); | |
| PyEval_ReleaseLock( ); | |
| } | |
| void | |
| Interpreter::test( ) | |
| { | |
| PyEval_AcquireThread( m_threadState ); | |
| PyObject* py_result; | |
| PyObject* dum; | |
| std::string command = "print 'Hello world'\n"; | |
| py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input); | |
| if ( py_result == 0 ) | |
| { | |
| std::cout << "Huh?\n"; | |
| PyEval_ReleaseThread( m_threadState ); | |
| return; | |
| } | |
| dum = PyEval_EvalCode (py_result, glb, loc); | |
| Py_XDECREF (dum); | |
| Py_XDECREF (py_result); | |
| std::cout << GetResultString( m_threadState ); | |
| GetResultString( m_threadState ) = ""; | |
| PyEval_ReleaseThread( m_threadState ); | |
| } | |
| template<typename ... Args> | |
| std::string string_format( const std::string& format, Args ... args ) | |
| { | |
| size_t size = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' | |
| std::unique_ptr<char[]> buf( new char[ size ] ); | |
| std::snprintf( buf.get(), size, format.c_str(), args ... ); | |
| return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside | |
| } | |
| std::string | |
| Interpreter::interpret( const std::string& command, int* errorCode ) | |
| { | |
| PyEval_AcquireThread( m_threadState ); | |
| *errorCode = 0; | |
| PyObject* py_result; | |
| PyObject* dum; | |
| std::string res; | |
| py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input); | |
| if ( py_result == 0 ) | |
| { | |
| if ( PyErr_Occurred( ) ) | |
| { | |
| *errorCode = 1; | |
| PyErr_Print( ); | |
| res = GetResultString( m_threadState ); | |
| GetResultString( m_threadState ) = ""; | |
| } | |
| PyEval_ReleaseThread( m_threadState ); | |
| return res; | |
| } | |
| dum = PyEval_EvalCode (py_result, glb, loc); | |
| Py_XDECREF (dum); | |
| Py_XDECREF (py_result); | |
| if ( PyErr_Occurred( ) ) | |
| { | |
| *errorCode = 1; | |
| PyErr_Print( ); | |
| } | |
| res = GetResultString( m_threadState ); | |
| GetResultString( m_threadState ) = ""; | |
| PyEval_ReleaseThread( m_threadState ); | |
| return res; | |
| } | |
| const std::list<std::string>& Interpreter::suggest( const std::string& hint ) | |
| { | |
| PyEval_AcquireThread( m_threadState ); | |
| m_suggestions.clear(); | |
| int i = 0; | |
| std::string command = string_format("sys.completer.complete('%s', %d)\n", hint.c_str(),i); | |
| std::string res; | |
| do | |
| { | |
| PyObject* py_result; | |
| PyObject* dum; | |
| py_result = Py_CompileString(command.c_str(), "<stdin>", Py_single_input); | |
| dum = PyEval_EvalCode (py_result, glb, loc); | |
| Py_XDECREF (dum); | |
| Py_XDECREF (py_result); | |
| res = GetResultString( m_threadState ); | |
| GetResultString( m_threadState ) = ""; | |
| ++i; | |
| command = string_format("sys.completer.complete('%s', %d)\n", hint.c_str(),i); | |
| if (res.size()) | |
| { | |
| // throw away the newline | |
| res = res.substr(1, res.size() - 3); | |
| m_suggestions.push_back(res); | |
| } | |
| } | |
| while (res.size()); | |
| PyEval_ReleaseThread( m_threadState ); | |
| return m_suggestions; | |
| } | |
| void | |
| Interpreter::Initialize( ) | |
| { | |
| PyImport_AppendInittab("redirector", Interpreter::PyInit_redirector); | |
| Py_Initialize( ); | |
| PyEval_InitThreads( ); | |
| MainThreadState = PyEval_SaveThread( ); | |
| } | |
| void | |
| Interpreter::Finalize( ) | |
| { | |
| PyEval_RestoreThread( MainThreadState ); | |
| Py_Finalize( ); | |
| } | |
| std::string& Interpreter::GetResultString( PyThreadState* threadState ) | |
| { | |
| static std::map< PyThreadState*, std::string > ResultStrings; | |
| if ( !ResultStrings.count( threadState ) ) | |
| { | |
| ResultStrings[ threadState ] = ""; | |
| } | |
| return ResultStrings[ threadState ]; | |
| } | |
| PyObject* Interpreter::RedirectorInit(PyObject *, PyObject *) | |
| { | |
| Py_INCREF(Py_None); | |
| return Py_None; | |
| } | |
| PyObject* Interpreter::RedirectorWrite(PyObject *, PyObject *args) | |
| { | |
| char* output; | |
| PyObject *selfi; | |
| if (!PyArg_ParseTuple(args,"Os",&selfi,&output)) | |
| { | |
| return NULL; | |
| } | |
| std::string outputString( output ); | |
| PyThreadState* currentThread = PyThreadState_Get( ); | |
| std::string& resultString = GetResultString( currentThread ); | |
| resultString = resultString + outputString; | |
| Py_INCREF(Py_None); | |
| return Py_None; | |
| } | |
| PyMethodDef Interpreter::RedirectorMethods[] = | |
| { | |
| {"__init__", Interpreter::RedirectorInit, METH_VARARGS, | |
| "initialize the stdout/err redirector"}, | |
| {"write", Interpreter::RedirectorWrite, METH_VARARGS, | |
| "implement the write method to redirect stdout/err"}, | |
| {NULL,NULL,0,NULL}, | |
| }; | |
| PyObject *createClassObject(const char *name, PyMethodDef methods[]) | |
| { | |
| PyObject *pClassName = PyUnicode_FromString(name); | |
| PyObject *pClassBases = PyTuple_New(0); // An empty tuple for bases is equivalent to `(object,)` | |
| PyObject *pClassDic = PyDict_New(); | |
| PyMethodDef *def; | |
| // add methods to class | |
| for (def = methods; def->ml_name != NULL; def++) | |
| { | |
| PyObject *func = PyCFunction_New(def, NULL); | |
| PyObject *method = PyInstanceMethod_New(func); | |
| PyDict_SetItemString(pClassDic, def->ml_name, method); | |
| Py_DECREF(func); | |
| Py_DECREF(method); | |
| } | |
| // pClass = type(pClassName, pClassBases, pClassDic) | |
| PyObject *pClass = PyObject_CallFunctionObjArgs((PyObject *)&PyType_Type, pClassName, pClassBases, pClassDic, NULL); | |
| Py_DECREF(pClassName); | |
| Py_DECREF(pClassBases); | |
| Py_DECREF(pClassDic); | |
| return pClass; | |
| } | |
| PyMODINIT_FUNC Interpreter::PyInit_redirector(void) | |
| { | |
| static struct PyModuleDef moduledef = { | |
| PyModuleDef_HEAD_INIT, | |
| "redirector", | |
| 0, | |
| -1, | |
| 0 | |
| }; | |
| PyObject *m = PyModule_Create(&moduledef); | |
| if (m) { | |
| PyObject *fooClass = createClassObject("redirector", RedirectorMethods); | |
| PyModule_AddObject(m, "redirector", fooClass); | |
| Py_DECREF(fooClass); | |
| } | |
| return m; | |
| } |