File indexing completed on 2023-05-30 09:03:12
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2015 Minh Ngo <minh@fedoraproject.org> 0004 SPDX-FileCopyrightText: 2022 Alexander Semke <alexander.semke@web.de> 0005 */ 0006 0007 #include "pythonserver.h" 0008 #include <vector> 0009 #include <cassert> 0010 #include <iostream> 0011 0012 #include <Python.h> 0013 0014 static_assert(PY_MAJOR_VERSION == 3, "This python server works only with Python 3"); 0015 0016 using namespace std; 0017 0018 namespace 0019 { 0020 string pyObjectToQString(PyObject* obj) 0021 { 0022 return string(PyUnicode_AsUTF8(obj)); 0023 } 0024 } 0025 0026 void PythonServer::login() 0027 { 0028 Py_InspectFlag = 1; 0029 Py_Initialize(); 0030 m_pModule = PyImport_AddModule("__main__"); 0031 PyRun_SimpleString("import sys"); 0032 filePath = "python_cantor_worksheet"; 0033 } 0034 0035 void PythonServer::interrupt() 0036 { 0037 PyErr_SetInterrupt(); 0038 } 0039 0040 void PythonServer::runPythonCommand(const string& command) 0041 { 0042 PyObject* py_dict = PyModule_GetDict(m_pModule); 0043 m_error = false; 0044 0045 const char* prepareCommand = 0046 "import sys;\n"\ 0047 "class CatchOutPythonBackend:\n"\ 0048 " def __init__(self, std_stream):\n"\ 0049 " self.value = ''\n"\ 0050 " self.encoding = std_stream.encoding\n"\ 0051 " def flush():\n"\ 0052 " pass\n"\ 0053 " def write(self, txt):\n"\ 0054 " self.value += txt\n"\ 0055 "outputPythonBackend = CatchOutPythonBackend(sys.stdout)\n"\ 0056 "errorPythonBackend = CatchOutPythonBackend(sys.stderr)\n"\ 0057 "sys.stdout = outputPythonBackend\n"\ 0058 "sys.stderr = errorPythonBackend\n"; 0059 PyRun_SimpleString(prepareCommand); 0060 0061 PyObject* compile = Py_CompileString(command.c_str(), filePath.c_str(), Py_single_input); 0062 // There are two reasons for the error: 0063 // 1) This code is not single expression, so we can't compile this with flag Py_single_input 0064 // 2) There are errors in the code 0065 if (PyErr_Occurred()) 0066 { 0067 PyErr_Clear(); 0068 // Try to recompile code as sequence of expressions 0069 compile = Py_CompileString(command.c_str(), filePath.c_str(), Py_file_input); 0070 if (PyErr_Occurred()) 0071 { 0072 // We now know, that we have a syntax error, so print the traceback and exit 0073 m_error = true; 0074 PyErr_PrintEx(0); 0075 return; 0076 } 0077 } 0078 PyEval_EvalCode(compile, py_dict, py_dict); 0079 0080 if (PyErr_Occurred()) 0081 { 0082 m_error = true; 0083 PyErr_PrintEx(0); 0084 } 0085 } 0086 0087 string PythonServer::getError() const 0088 { 0089 PyObject *errorPython = PyObject_GetAttrString(m_pModule, "errorPythonBackend"); 0090 PyObject *error = PyObject_GetAttrString(errorPython, "value"); 0091 0092 return pyObjectToQString(error); 0093 } 0094 0095 string PythonServer::getOutput() const 0096 { 0097 PyObject *outputPython = PyObject_GetAttrString(m_pModule, "outputPythonBackend"); 0098 PyObject *output = PyObject_GetAttrString(outputPython, "value"); 0099 0100 return pyObjectToQString(output); 0101 } 0102 0103 void PythonServer::setFilePath(const string& path, const string& dir) 0104 { 0105 PyRun_SimpleString(("import sys; sys.argv = ['" + path + "']").c_str()); 0106 if (path.length() == 0) // New session, not from file 0107 { 0108 PyRun_SimpleString("import sys; sys.path.insert(0, '')"); 0109 } 0110 else 0111 { 0112 this->filePath = path; 0113 PyRun_SimpleString(("import sys; sys.path.insert(0, '" + dir + "')").c_str()); 0114 PyRun_SimpleString(("__file__ = '"+path+"'").c_str()); 0115 } 0116 } 0117 0118 string PythonServer::variables(bool parseValue) 0119 { 0120 PyRun_SimpleStringFlags( 0121 "try: \n" 0122 " import numpy \n" 0123 " __cantor_numpy_internal__ = numpy.get_printoptions()['threshold'] \n" 0124 " numpy.set_printoptions(threshold=100000000) \n" 0125 "except ModuleNotFoundError: \n" 0126 " pass \n", nullptr 0127 ); 0128 0129 PyObject* py_dict = PyModule_GetDict(m_pModule); 0130 PyRun_SimpleString("__tmp_globals__ = globals()"); 0131 PyObject* globals = PyObject_GetAttrString(m_pModule,"__tmp_globals__"); 0132 PyObject *key, *value; 0133 Py_ssize_t pos = 0; 0134 0135 vector<string> vars; 0136 while (PyDict_Next(globals, &pos, &key, &value)) { 0137 const string& keyString = pyObjectToQString(key); 0138 if (keyString.substr(0, 2) == string("__")) 0139 continue; 0140 0141 if (keyString == string("CatchOutPythonBackend") 0142 || keyString == string("errorPythonBackend") 0143 || keyString == string("outputPythonBackend")) 0144 continue; 0145 0146 if (PyModule_Check(value)) 0147 continue; 0148 0149 if (PyFunction_Check(value)) 0150 continue; 0151 0152 if (PyType_Check(value)) 0153 continue; 0154 0155 string valueString; 0156 string sizeString; 0157 string typeString; 0158 if (parseValue) 0159 { 0160 valueString = pyObjectToQString(PyObject_Repr(value)); 0161 0162 string command = "sys.getsizeof(" + keyString + ")"; 0163 sizeString = pyObjectToQString(PyObject_Repr(PyRun_String(command.c_str(), Py_eval_input, py_dict, py_dict))); 0164 0165 command = "type(" + keyString + ")"; 0166 typeString = pyObjectToQString(PyObject_Repr(PyRun_String(command.c_str(), Py_eval_input, py_dict, py_dict))); 0167 } 0168 0169 vars.push_back(keyString + char(17) + valueString + char(17) + sizeString + char(17) + typeString); 0170 } 0171 0172 PyRun_SimpleStringFlags( 0173 "try: \n" 0174 " import numpy \n" 0175 " numpy.set_printoptions(threshold=__cantor_numpy_internal__) \n" 0176 " del __cantor_numpy_internal__ \n" 0177 "except ModuleNotFoundError: \n" 0178 " pass \n", nullptr 0179 ); 0180 0181 string result; 0182 for (const string& s : vars) 0183 result += s + char(18); 0184 result += char(18); 0185 return result; 0186 } 0187 0188 bool PythonServer::isError() const 0189 { 0190 return m_error; 0191 }