Salome HOME
updated copyright message
[modules/gui.git] / tools / PyInterp / src / PyInterp_Interp.cxx
index 27703c2a010cf94a6e927a1659d28893d35eb055..815a847a6b906b11e87b6e433c1e66413bf19909 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2023  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -35,7 +35,8 @@
 #include <sstream>
 #include <algorithm>
 
-#include <QRegExp>
+#include <QRegularExpression>
+#include <QStringList>
 
 #define TOP_HISTORY_PY   "--- top of history ---"
 #define BEGIN_HISTORY_PY "--- begin of history ---"
@@ -55,8 +56,7 @@ static PyObject*
 PyStdOut_write(PyStdOut *self, PyObject *args)
 {
   char *c;
-  int l;
-  if (!PyArg_ParseTuple(args, "t#:write",&c, &l))
+  if (!PyArg_ParseTuple(args, "s",&c))
     return NULL;
   if(self->_cb==NULL) {
     if ( self->_iscerr )
@@ -72,22 +72,29 @@ PyStdOut_write(PyStdOut *self, PyObject *args)
 }
 
 static PyObject*
-PyStdOut_flush(PyStdOut *self)
+PyStdOut_flush(PyStdOut * /*self*/, PyObject * /*args*/)
 {
   Py_INCREF(Py_None);
   return Py_None;
 }
 
+static PyObject*
+PyStdOut_isatty(PyStdOut * /*self*/, PyObject */*args*/)
+{
+  return Py_False;
+}
+
 static PyMethodDef PyStdOut_methods[] = {
   {"write",  (PyCFunction)PyStdOut_write,  METH_VARARGS, PyDoc_STR("write(string) -> None")},
   {"flush",  (PyCFunction)PyStdOut_flush,  METH_NOARGS,  PyDoc_STR("flush() -> None")},
-  {NULL,    NULL}   /* sentinel */
+  {"isatty", (PyCFunction)PyStdOut_isatty, METH_NOARGS,  PyDoc_STR("isatty() -> bool")},
+  {NULL,     (PyCFunction)NULL,            0,            NULL}   /* sentinel */
 };
 
 static PyMemberDef PyStdOut_memberlist[] = {
   {(char*)"softspace", T_INT,  offsetof(PyStdOut, softspace), 0,
    (char*)"flag indicating that a space needs to be printed; used by print"},
-  {NULL} /* Sentinel */
+  {NULL, 0, 0, 0, NULL} /* Sentinel */
 };
 
 static PyTypeObject PyStdOut_Type = {
@@ -136,6 +143,20 @@ static PyTypeObject PyStdOut_Type = {
   0,                            /*tp_new*/
   0,                            /*tp_free*/
   0,                            /*tp_is_gc*/
+  0,                            /*tp_bases*/
+  0,                            /*tp_mro*/
+  0,                            /*tp_cache*/
+  0,                            /*tp_subclasses*/
+  0,                            /*tp_weaklist*/
+  0,                            /*tp_del*/
+  0,                            /*tp_version_tag*/
+  0,                            /*tp_finalize*/
+#if PY_VERSION_HEX >= 0x03080000
+  0,                            /*tp_vectorcall*/
+#if PY_VERSION_HEX < 0x03090000
+  0,                            /*tp_print*/
+#endif
+#endif
 };
 
 #define PyStdOut_Check(v)  ((v)->ob_type == &PyStdOut_Type)
@@ -164,10 +185,10 @@ char* PyInterp_Interp::_argv[] = {(char*)""};
   \brief Basic constructor.
 
   After construction the interpreter instance successor classes
-  must call virtual method initalize().
+  must call virtual method initialize().
 */
 PyInterp_Interp::PyInterp_Interp():
-  _vout(0), _verr(0), _local_context(0), _global_context(0), _initialized(false)
+  _vout(0), _verr(0), _global_context(0), _local_context(0), _initialized(false)
 {
 }
 
@@ -182,7 +203,7 @@ PyInterp_Interp::~PyInterp_Interp()
 /*!
   \brief Initialize embedded interpreter.
 
-  This method shoud be called after construction of the interpreter.
+  This method should be called after construction of the interpreter.
   The method initialize() calls virtuals methods
   - initPython()  to initialize global Python interpreter
   - initContext() to initialize interpreter internal context
@@ -243,18 +264,28 @@ void PyInterp_Interp::initPython()
   if (!Py_IsInitialized()){
     // Python is not initialized
     wchar_t **changed_argv = new wchar_t*[_argc]; // Setting arguments
-    size_t mbslen;
     for (int i = 0; i < _argc; i++)
     {
       changed_argv[i] = Py_DecodeLocale(_argv[i], NULL);
     }
-   
+
+#if PY_VERSION_HEX < 0x03080000
     Py_SetProgramName(changed_argv[0]);
     Py_Initialize(); // Initialize the interpreter
     PySys_SetArgv(_argc, changed_argv);
-
+#else
+    PyConfig config;
+    PyConfig_InitPythonConfig(&config);
+    PyStatus status = PyConfig_SetString(&config, &config.program_name, changed_argv[0]);
+    status = PyConfig_SetArgv(&config, _argc, changed_argv);
+    status = Py_InitializeFromConfig(&config);
+    PyConfig_Clear(&config);
+#endif
+
+#if PY_VERSION_HEX < 0x03070000
     PyEval_InitThreads(); // Create (and acquire) the Python global interpreter lock (GIL)
-    PyEval_ReleaseLock();
+#endif
+    PyEval_SaveThread(); // release safely GIL
   }
 }
 
@@ -319,7 +350,7 @@ void PyInterp_Interp::closeContext()
 /*!
   \brief Compile Python command and evaluate it in the
          python dictionary contexts if possible. This is not thread-safe.
-         This is the caller's responsability to make this thread-safe.
+         This is the caller's responsibility to make this thread-safe.
   \internal
   \param command Python command string
   \return -1 on fatal error, 1 if command is incomplete and 0
@@ -388,9 +419,9 @@ std::string
 __join(const std::vector<std::string>& v, int begin=0, int end=-1)
 {
   if (end == -1)
-    end = v.size();
+    end = (int)v.size(); //!< TODO: conversion from size_t to int
   std::stringstream ss;
-  for (size_t i = begin; i < end; ++i) {
+  for (int i = begin; i < end; ++i) {
     if (i != begin)
       ss << ",";
     ss << v[i];
@@ -411,7 +442,7 @@ __getArgsList(std::string argsString)
   bool containsList = (argsString.find('[') != std::string::npos);
   if (containsList) {
     std::vector<int> listBeginIndices, listEndIndices;
-    for (int pos = 0; pos < x.size(); ++pos) {
+    for (int pos = 0; pos < (int)x.size(); ++pos) {
       if (x[pos][0] == '[')
         listBeginIndices.push_back(pos);
       else if (x[pos][x[pos].size()-1] == ']')
@@ -419,7 +450,7 @@ __getArgsList(std::string argsString)
     }
     std::vector<std::string> extractedArgs;
     int start = 0;
-    for (int pos = 0; pos < listBeginIndices.size(); ++pos) {
+    for (int pos = 0; pos < (int)listBeginIndices.size(); ++pos) {
       int lbeg = listBeginIndices[pos];
       int lend = listEndIndices[pos];
       if (lbeg > start)
@@ -428,8 +459,8 @@ __getArgsList(std::string argsString)
       extractedArgs.push_back(__join(x, lbeg, lend+1));
       start = lend+1;
     }
-    if (start < x.size())
-      for (int k = start; k < x.size(); ++k)
+    if (start < (int)x.size())
+      for (int k = start; k < (int)x.size(); ++k)
         extractedArgs.push_back(x[k]);
     return extractedArgs;
   }
@@ -443,7 +474,8 @@ __getArgsList(std::string argsString)
          python dictionary context if possible. Command might correspond to
          the execution of a script with optional arguments.
          In this case, command is:
-         execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
+           exec(open(r"/absolute/path/to/script.py", "rb").read(), args=(arg1,...,argn))
+        and args parameter is optional one. This parameter is specified as a tuple of strings.
   \internal
   \param command Python command string
   \param context Python context (dictionary)
@@ -453,36 +485,27 @@ __getArgsList(std::string argsString)
 static int compile_command(const char *command, PyObject * global_ctxt, PyObject * local_ctxt)
 {
   // First guess if command is execution of a script with args, or a simple Python command
-  std::string singleCommand = command;
-  std::string commandArgs = "";
-
-  QRegExp rx("execfile\\s*\\(.*(args:.*)\"\\s*\\)");
-  if (rx.indexIn(command) != -1) {
-    commandArgs = rx.cap(1).remove(0,5).toStdString(); // arguments of command
-    singleCommand = rx.cap().remove(rx.cap(1)).remove(" ").toStdString(); // command for execution without arguments
+  QString singleCommand = command;
+  QString commandArgs = "";
+
+  QRegularExpression rx("exec\\s*\\(.*open\\s*\\(\\s*(.*)\\s*\\)\\s*\\.\\s*read\\s*\\(\\)(\\s*,\\s*args\\s*=\\s*\\(.*\\))\\s*\\)");
+  QRegularExpressionMatch match = rx.match(command);
+  if (match.hasMatch()) {
+    commandArgs = match.captured(2).remove(0, match.captured(2).indexOf("(")); // arguments of command
+    commandArgs.insert(commandArgs.indexOf('(')+1, match.captured(1).split(",")[0].trimmed() + ","); // prepend arguments list by the script file itself
+    singleCommand = singleCommand.remove(match.capturedStart(2), match.captured(2).size()); // command for execution without arguments
   }
 
-  if (commandArgs.empty()) {
-    // process command: expression
-    // process command: execfile(r"/absolute/path/to/script.py") (no args)
-    return run_command(singleCommand.c_str(), global_ctxt, local_ctxt);
+  if (commandArgs.isEmpty()) {
+    return run_command(singleCommand.toStdString().c_str(), global_ctxt, local_ctxt);
   }
   else {
-    // process command: execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
-    std::string script = singleCommand.substr(11); // remove leading execfile(r"
-    script = script.substr(0, script.length()-2); // remove trailing ")
-    std::vector<std::string> argList = __getArgsList(commandArgs);
-
-    std::string preCommandBegin = "import sys; save_argv = sys.argv; sys.argv=[";
-    std::string preCommandEnd = "];";
-    std::string completeCommand = preCommandBegin+"\""+script+"\",";
-    for (std::vector<std::string>::iterator itr = argList.begin(); itr != argList.end(); ++itr) {
-      if (itr != argList.begin())
-        completeCommand += ",";
-      completeCommand = completeCommand + "\"" + *itr + "\"";
-    }
-    completeCommand = completeCommand+preCommandEnd+singleCommand+";sys.argv=save_argv";
-    return run_command(completeCommand.c_str(), global_ctxt, local_ctxt);
+    ///////////////std::vector<std::string> argList = __getArgsList(commandArgs);
+    QString preCommandBegin = "import sys; save_argv = sys.argv; sys.argv=list(";
+    QString preCommandEnd = ");";
+    QString postCommand = ";sys.argv=save_argv";
+    QString completeCommand = preCommandBegin+commandArgs+preCommandEnd+singleCommand.trimmed()+postCommand;
+    return run_command(completeCommand.toStdString().c_str(), global_ctxt, local_ctxt);
   }
 }
 
@@ -501,7 +524,7 @@ int PyInterp_Interp::run(const char *command)
 }
 
 /**
- * Called before a command is run (when calling run() method). Not thread-safe. Caller's responsability
+ * Called before a command is run (when calling run() method). Not thread-safe. Caller's responsibility
  * to acquire GIL if needed.
  */
 int PyInterp_Interp::beforeRun()
@@ -510,7 +533,7 @@ int PyInterp_Interp::beforeRun()
 }
 
 /**
- * Called after a command is run (when calling run() method). Not thread-safe. Caller's responsability
+ * Called after a command is run (when calling run() method). Not thread-safe. Caller's responsibility
  * to acquire GIL if needed.
  */
 int PyInterp_Interp::afterRun()
@@ -519,7 +542,7 @@ int PyInterp_Interp::afterRun()
 }
 
 /*!
-  \brief Run Python command (used internally). Not thread-safe. GIL acquisition is caller's responsability.
+  \brief Run Python command (used internally). Not thread-safe. GIL acquisition is caller's responsibility.
   \param command Python command
   \param addToHistory if \c true (default), the command is added to the commands history
   \return command status