Warning, file /education/cantor/src/backends/python/pythonserver.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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 }