11#define PY_SSIZE_T_CLEAN
16#include <PythonAPI/extensions/IOCatcher.h>
19#include <QApplication>
25 bool printPromt, QObject* parent) :
28 printPrompt_(printPromt),
29 outputChannel_(outputChannel),
30 errorChannel_(errorChannel)
40 PyImport_AppendInittab(
"IOCatcher", &PyInit_IOCatcher);
44 if (Py_IsInitialized() ==
false)
46 errorChannel_->write(QStringLiteral(
"Could not initialize Python interpreter."));
50 threadState_ = Py_NewInterpreter();
52 outputChannel_->write(QStringLiteral(
"Python ") + QString(Py_GetVersion()) + QString(
"\n"));
54 PyObject *pyModule = PyImport_ImportModule(
"__main__");
55 localContext_ = PyModule_GetDict(pyModule);
56 globalContext_ = localContext_;
58 if (redirectIO(interactive) ==
false)
65 PyEval_ReleaseThread(threadState_);
76 PyEval_AcquireThread(threadState_);
93 Q_ASSERT_X(Py_IsInitialized(),
"Python interpreter",
"Trying to execute without initializing.");
95 if (command.empty() && inputBuffer_.empty())
101 inputBuffer_.append(command);
102 inputBuffer_.push_back(
'\n');
104 auto inputSize = inputBuffer_.length();
105 bool endMultiline = runMultiline_ && inputSize >= 2 && inputBuffer_.at(inputSize - 2) ==
'\n';
108 PyEval_AcquireThread(threadState_);
110 PyObject* src = Py_CompileString(inputBuffer_.c_str(),
"<stdin>", Py_single_input);
116 if (runMultiline_ ==
false || endMultiline)
118 PyObject* dum = PyEval_EvalCode(src, globalContext_, localContext_);
122 if (PyErr_Occurred())
127 runMultiline_ =
false;
128 inputBuffer_.clear();
132 runMultiline_ =
true;
136 else if (PyErr_ExceptionMatches(PyExc_SyntaxError))
138 PyObject* exc =
nullptr;
139 PyObject* val =
nullptr;
140 PyObject* trb =
nullptr;
141 PyObject* obj =
nullptr;
146 PyErr_Fetch(&exc, &val, &trb);
148 if (PyArg_ParseTuple(val,
"sO", &msg, &obj) && !strcmp(msg,
"unexpected EOF while parsing") &&
149 endMultiline ==
false)
155 runMultiline_ =
true;
159 PyErr_Restore(exc, val, trb);
162 runMultiline_ =
false;
163 inputBuffer_.clear();
169 runMultiline_ =
false;
170 inputBuffer_.clear();
173 PyEval_ReleaseThread(threadState_);
184 PyEval_AcquireThread(threadState_);
185 PyObject* result = PyRun_String(
string.toStdString().c_str(), Py_file_input, globalContext_, localContext_);
187 if (result ==
nullptr)
196 PyEval_ReleaseThread(threadState_);
207 Q_ASSERT_X(Py_IsInitialized(),
"Python interpreter",
"Trying to execute file without initializing.");
209 QFile scriptFile(filePath);
210 if (scriptFile.open(QIODevice::ReadOnly | QIODevice::Text))
212 int fd = scriptFile.handle();
213 FILE* f = fdopen(dup(fd),
"rb");
215 PyEval_AcquireThread(threadState_);
216 retVal = PyRun_SimpleFile(f, scriptFile.fileName().toLocal8Bit());
217 PyEval_ReleaseThread(threadState_);
233bool PythonInterpreter::redirectIO(
bool interative)
235 PyObject* IOCatcherName = PyUnicode_FromString(
"IOCatcher");
236 if (IOCatcherName == NULL)
241 PyObject* IOCatcherModule = PyImport_Import(IOCatcherName);
242 Py_DECREF(IOCatcherName);
243 if (IOCatcherModule == NULL)
248 PyObject* dict = PyModule_GetDict(IOCatcherModule);
249 Py_DECREF(IOCatcherModule);
254 errorChannel_->write(QStringLiteral(
"Fails to get the output dictionary.\n"));
258 PyObject* python_class = PyDict_GetItemString(dict,
"OutputCatcher");
259 if (python_class == NULL)
262 errorChannel_->write(QStringLiteral(
"Fails to get the output Python class.\n"));
266 PyObject* outCatcher =
nullptr;
267 PyObject* errCatcher =
nullptr;
270 if (PyCallable_Check(python_class))
272 outCatcher = PyObject_CallObject(python_class,
nullptr);
273 errCatcher = PyObject_CallObject(python_class,
nullptr);
277 outputChannel_->write(QStringLiteral(
"Cannot instantiate the Python class for output"));
281 ((OutputCatcherObject*)outCatcher)->channel = outputChannel_;
282 if (PySys_SetObject(
"stdout", outCatcher) < 0)
287 ((OutputCatcherObject*)errCatcher)->channel = errorChannel_;
288 if (PySys_SetObject(
"stderr", errCatcher) < 0)
293 if (interative ==
false)
295 PyObject* input_python_class = PyDict_GetItemString(dict,
"InputCatcher");
297 if (input_python_class == NULL)
300 errorChannel_->write(QStringLiteral(
"Fails to get the input Python class.\n"));
304 PyObject* inputBuffer;
307 if (PyCallable_Check(input_python_class))
309 inputBuffer = PyObject_CallObject(input_python_class,
nullptr);
312 if (PySys_SetObject(
"stdin", inputBuffer) < 0)
324bool PythonInterpreter::setAPI()
326 PyObject* emptyList = PyList_New(0);
327 PyObject* apiModule = PyImport_ImportModuleEx(
"pythonAPI", globalContext_, localContext_, emptyList);
328 Py_XDECREF(emptyList);
330 if (apiModule == NULL)
332 errorChannel_->write(QStringLiteral(
"Could not import Kactus2 PythonAPI.\n"));
337 PyObject* dict = PyModule_GetDict(apiModule);
341 errorChannel_->write(QStringLiteral(
"Could not import Kactus2 PythonAPI.\n"));
345 PyObject* python_class = PyDict_GetItemString(dict,
"PythonAPI");
347 if (python_class == NULL) {
349 errorChannel_->write(QStringLiteral(
"Could not import Kactus2 PythonAPI.\n"));
354 if (PyCallable_Check(python_class))
356 PyObject* apiObject = PyObject_CallObject(python_class,
nullptr);
357 PyDict_SetItemString(localContext_,
"kactus2", apiObject);
367void PythonInterpreter::printPrompt()
const
369 if (printPrompt_ ==
false)
376 outputChannel_->write(
"... ");
380 outputChannel_->write(
">>> ");
PythonInterpreter(WriteChannel *outputChannel, WriteChannel *errorChannel, bool printPromt=true, QObject *parent=nullptr)
bool initialize(bool interactive=true)
int runFile(QString const &filePath)
void execute(std::string const &line)
void executeString(QString const &string)
virtual void write(QString const &command) override final
WriteChannel()=default
The constructor.