Salome HOME
updated copyright message
[modules/shaper.git] / src / InitializationPlugin / InitializationPlugin_PyInterp.cpp
index f9391699e678ea33e355227616a0764b4c70fe38..a9c57539ca98608eaa9046dde78b61d5fd7c9775 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017  CEA/DEN, EDF R&D
+// Copyright (C) 2014-2023  CEA/DEN, EDF R&D
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
 //
 // You should have received a copy of the GNU Lesser General Public
 // License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 //
-// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
 
-/*
- * InitializationPlugin_PyInterp.cpp
- *
- *  Created on: Apr 2, 2015
- *      Author: sbh
- */
-
 #include <InitializationPlugin_PyInterp.h>
 
+#include <Locale_Convert.h>
+
 #include <string>
 #include <stdexcept>
+#include <clocale>
+#include <Python.h>
 
 InitializationPlugin_PyInterp::InitializationPlugin_PyInterp()
 : PyInterp_Interp()
@@ -48,9 +45,19 @@ const char* aSearchCode =
   "            positions.append((node.lineno, node.col_offset))\n"
   "FindName(name).visit(ast.parse(expression))";
 
+// make the expression be correct for the python interpreter even for the
+// beta=alfa*2 expressions
+static std::wstring adjustExpression(const std::wstring& theExpression) {
+  std::wstring anExpression = theExpression;
+  if (!anExpression.empty() && anExpression.back() == L'=') {
+    anExpression = anExpression.substr(0, anExpression.length() - 1);
+  }
+  return anExpression;
+}
+
 std::list<std::pair<int, int> >
-InitializationPlugin_PyInterp::positions(const std::string& theExpression,
-                                     const std::string& theName)
+InitializationPlugin_PyInterp::positions(const std::wstring& theExpression,
+                                         const std::wstring& theName)
 {
   PyLockWrapper lck; // Acquire GIL until the end of the method
 
@@ -58,12 +65,13 @@ InitializationPlugin_PyInterp::positions(const std::string& theExpression,
 
   // prepare a context
   PyObject* aContext = PyDict_New();
-  PyObject* aBuiltinModule = PyImport_AddModule("__builtin__");
-  PyDict_SetItemString(aContext, "__builtins__", aBuiltinModule);
+  PyDict_SetItemString(aContext, "__builtins__", PyEval_GetBuiltins());
 
+  std::wstring anExpression = adjustExpression(theExpression);
   // extend aContext with variables
-  PyDict_SetItemString(aContext, "expression", PyString_FromString(theExpression.c_str()));
-  PyDict_SetItemString(aContext, "name", PyString_FromString(theName.c_str()));
+  PyDict_SetItemString(aContext, "expression",
+      PyUnicode_FromWideChar(anExpression.c_str(), anExpression.size()));
+  PyDict_SetItemString(aContext, "name", PyUnicode_FromWideChar(theName.c_str(), theName.size()));
   PyDict_SetItemString(aContext, "positions", Py_BuildValue("[]"));
 
   // run the search code
@@ -78,8 +86,8 @@ InitializationPlugin_PyInterp::positions(const std::string& theExpression,
     PyObject* aColOffset = PyTuple_GetItem(aPosition, 1);
 
     aResult.push_back(
-        std::pair<int, int>((int)PyInt_AsLong(aLineNo),
-                            (int)PyInt_AsLong(aColOffset)));
+        std::pair<int, int>((int)PyLong_AsLong(aLineNo),
+                            (int)PyLong_AsLong(aColOffset)));
   }
 
   // TODO(spo): after this refCount of the variable is not 0. Is there memory leak?
@@ -89,19 +97,21 @@ InitializationPlugin_PyInterp::positions(const std::string& theExpression,
 }
 
 
-std::list<std::string> InitializationPlugin_PyInterp::compile(const std::string& theExpression)
+std::list<std::wstring> InitializationPlugin_PyInterp::compile(const std::wstring& theExpression)
 {
   PyLockWrapper lck; // Acquire GIL until the end of the method
-  std::list<std::string> aResult;
+  std::list<std::wstring> aResult;
   PyObject *aCodeopModule = PyImport_AddModule("codeop");
   if(!aCodeopModule) { // Fatal error. No way to go on.
     PyErr_Print();
     return aResult;
   }
+  // support "variable_name=" expression as "variable_name"
+  std::wstring anExpression = adjustExpression(theExpression);
 
   PyObject *aCodePyObj =
     PyObject_CallMethod(aCodeopModule, (char*)"compile_command", (char*)"(s)",
-                        theExpression.c_str());
+                        Locale::Convert::toString(anExpression).c_str());
 
   if(!aCodePyObj || aCodePyObj == Py_None || !PyCode_Check(aCodePyObj)) {
     Py_XDECREF(aCodePyObj);
@@ -109,17 +119,23 @@ std::list<std::string> InitializationPlugin_PyInterp::compile(const std::string&
   }
 
   PyCodeObject* aCodeObj = (PyCodeObject*) aCodePyObj;
-  std::string aCodeName(PyString_AsString(aCodeObj->co_code));
+#if PY_VERSION_HEX >= 0x030B0000
+  std::string aCodeName(PyBytes_AsString(PyObject_GetAttrString((PyObject *)(aCodeObj), "co_code")));
+#else
+  std::string aCodeName(PyBytes_AsString(aCodeObj->co_code));
+#endif
   // co_names should be tuple, but can be changed in modern versions of python (>2.7.3)
-  if(!PyTuple_Check(aCodeObj->co_names))
+  if(!PyTuple_Check(aCodeObj->co_names)) {
     return aResult;
+  }
 
   size_t params_size = PyTuple_Size(aCodeObj->co_names);
   if (params_size > 0) {
     for (size_t i = 0; i < params_size; i++) {
       PyObject* aParamObj = PyTuple_GetItem(aCodeObj->co_names, i);
       PyObject* aParamObjStr = PyObject_Str(aParamObj);
-      std::string aParamName(PyString_AsString(aParamObjStr));
+      Py_ssize_t aSize;
+      std::wstring aParamName(PyUnicode_AsWideCharString(aParamObjStr, &aSize));
       aResult.push_back(aParamName);
       Py_XDECREF(aParamObjStr);
     }
@@ -128,14 +144,14 @@ std::list<std::string> InitializationPlugin_PyInterp::compile(const std::string&
   return aResult;
 }
 
-void InitializationPlugin_PyInterp::extendLocalContext(const std::list<std::string>& theParameters)
+void InitializationPlugin_PyInterp::extendLocalContext(const std::list<std::wstring>& theParameters)
 {
   PyLockWrapper lck; // Acquire GIL until the end of the method
   if (theParameters.empty())
     return;
-  std::list<std::string>::const_iterator it = theParameters.begin();
+  std::list<std::wstring>::const_iterator it = theParameters.begin();
   for ( ; it != theParameters.cend(); it++) {
-    std::string aParamValue = *it;
+    std::string aParamValue = Locale::Convert::toString(*it);
     simpleRun(aParamValue.c_str(), false);
   }
 }
@@ -146,13 +162,17 @@ void InitializationPlugin_PyInterp::clearLocalContext()
   PyDict_Clear(_local_context);
 }
 
-double InitializationPlugin_PyInterp::evaluate(const std::string& theExpression,
+double InitializationPlugin_PyInterp::evaluate(const std::wstring& theExpression,
                                                std::string& theError)
 {
+  // support "variable_name=" expression as "variable_name"
+  std::wstring anExpression = adjustExpression(theExpression);
+
   PyLockWrapper lck; // Acquire GIL until the end of the method
   PyCompilerFlags aFlags = {CO_FUTURE_DIVISION};
   aFlags.cf_flags = CO_FUTURE_DIVISION;
-  PyCodeObject* anExprCode = (PyCodeObject *) Py_CompileStringFlags(theExpression.c_str(),
+  PyCodeObject* anExprCode = (PyCodeObject *) Py_CompileStringFlags(
+                                Locale::Convert::toString(anExpression).c_str(),
                                 "<string>", Py_eval_input, &aFlags);
   if(!anExprCode) {
     theError = errorMessage();
@@ -160,7 +180,7 @@ double InitializationPlugin_PyInterp::evaluate(const std::string& theExpression,
     return 0.;
   }
 
-  PyObject* anEvalResult = PyEval_EvalCode(anExprCode, _global_context, _local_context);
+  PyObject* anEvalResult = PyEval_EvalCode((PyObject *)anExprCode, _global_context, _local_context);
   if(!anEvalResult) {
     theError = errorMessage();
     Py_XDECREF(anExprCode);
@@ -169,14 +189,19 @@ double InitializationPlugin_PyInterp::evaluate(const std::string& theExpression,
   }
 
   PyObject* anEvalStrObj = PyObject_Str(anEvalResult);
-  std::string anEvalStr(PyString_AsString(anEvalStrObj));
+  std::string anEvalStr(PyUnicode_AsUTF8(anEvalStrObj));
   Py_XDECREF(anExprCode);
   Py_XDECREF(anEvalResult);
   Py_XDECREF(anEvalStrObj);
   double result = 0.;
   try {
+    // set locale due to the #2485
+    std::string aCurLocale = std::setlocale(LC_NUMERIC, 0);
+    std::setlocale(LC_NUMERIC, "C");
     result = std::stod(anEvalStr);
-  } catch (const std::invalid_argument&) {
+    std::setlocale(LC_NUMERIC, aCurLocale.c_str());
+  }
+  catch (const std::invalid_argument&) {
     theError = "Unable to eval " + anEvalStr;
   }
 
@@ -191,7 +216,7 @@ std::string InitializationPlugin_PyInterp::errorMessage()
     PyErr_Fetch(&ptype, &pvalue, &ptraceback);
     PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
     pstr = PyObject_Str(pvalue);
-    aPyError = std::string(PyString_AsString(pstr));
+    aPyError = std::string(PyUnicode_AsUTF8(pstr));
     Py_XDECREF(pstr);
     Py_XDECREF(ptype);
     Py_XDECREF(pvalue);
@@ -212,6 +237,26 @@ bool InitializationPlugin_PyInterp::initContext()
   _local_context = PyDict_New();
   Py_INCREF(_local_context);
 
+  // to avoid "help()" hang in the python console
+  const static char* aHelpTxt = "def help(): print(\"Available modules:\\n"
+    "  salome.shaper.model : higher level access to features and data model\\n"
+    "  BuildAPI            : Build plugin features allowing to build shapes\\n"
+    "  ConfigAPI           : configuration management: preferences and XML properties\\n"
+    "  ConstructionAPI     : Construction plugin for auxiliary features creation\\n"
+    "  EventsAPI           : application events receiving and emitting manager\\n"
+    "  ExchangeAPI         : Exchange plugin with import/export features\\n"
+    "  FeaturesAPI         : Features plugin with general 3D features\\n"
+    "  GeomAlgoAPI         : geometrical algorithms\\n"
+    "  GeomAPI             : geometrical data structures\\n"
+    "  GeomDataAPI         : specific geometrical data structures stored in the data model\\n"
+    "  ModelAPI            : general low-level interface to access data model\\n"
+    "  ModelHighAPI        : general high-level interface to access data model\\n"
+    "  ParametersAPI       : Parameters plugin for parameters feature management\\n"
+    "  PartSetAPI          : PartSet plugin for management Parts features\\n"
+    "  SketchAPI           : Sketch plugin with all sketch features\")";
+
+  PyRun_SimpleString(aHelpTxt);
+
   return PyRun_SimpleString("from math import *") == 0;
 }
 
@@ -220,3 +265,9 @@ void InitializationPlugin_PyInterp::closeContext()
   Py_XDECREF(_local_context);
   PyInterp_Interp::closeContext();
 }
+
+bool InitializationPlugin_PyInterp::runString(std::string theString)
+{
+  PyLockWrapper lck; // Acquire GIL until the end of the method
+  return PyRun_SimpleString(theString.c_str());
+}