Salome HOME
Merge branch 'V9_5_BR'
[modules/shaper.git] / src / InitializationPlugin / InitializationPlugin_PyInterp.cpp
1 // Copyright (C) 2014-2020  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include <InitializationPlugin_PyInterp.h>
21
22 #include <string>
23 #include <stdexcept>
24 #include <clocale>
25
26 InitializationPlugin_PyInterp::InitializationPlugin_PyInterp()
27 : PyInterp_Interp()
28 {
29 }
30
31 InitializationPlugin_PyInterp::~InitializationPlugin_PyInterp()
32 {
33 }
34
35 const char* aSearchCode =
36   "import ast\n"
37   "class FindName(ast.NodeVisitor):\n"
38   "    def __init__(self, name):\n"
39   "        self.name = name\n"
40   "    def visit_Name(self, node):\n"
41   "        if node.id == self.name:\n"
42   "            positions.append((node.lineno, node.col_offset))\n"
43   "FindName(name).visit(ast.parse(expression))";
44
45 // make the expression be correct for the python interpreter even for the
46 // beta=alfa*2 expressions
47 static std::string adjustExpression(const std::string& theExpression) {
48   std::string anExpression = theExpression;
49   if (!anExpression.empty() && anExpression.back() == '=') {
50     anExpression = anExpression.substr(0, anExpression.length() - 1);
51   }
52   return anExpression;
53 }
54
55 std::list<std::pair<int, int> >
56 InitializationPlugin_PyInterp::positions(const std::string& theExpression,
57                                      const std::string& theName)
58 {
59   PyLockWrapper lck; // Acquire GIL until the end of the method
60
61   std::list<std::pair<int, int> > aResult;
62
63   // prepare a context
64   PyObject* aContext = PyDict_New();
65   PyDict_SetItemString(aContext, "__builtins__", PyEval_GetBuiltins());
66
67   std::string anExpression = adjustExpression(theExpression);
68   // extend aContext with variables
69   PyDict_SetItemString(aContext, "expression", PyUnicode_FromString(anExpression.c_str()));
70   PyDict_SetItemString(aContext, "name", PyUnicode_FromString(theName.c_str()));
71   PyDict_SetItemString(aContext, "positions", Py_BuildValue("[]"));
72
73   // run the search code
74   PyObject* aExecResult = PyRun_String(aSearchCode, Py_file_input, aContext, aContext);
75   Py_XDECREF(aExecResult);
76
77   // receive results from context
78   PyObject* aPositions = PyDict_GetItemString(aContext, "positions");
79   for (int anIndex = 0; anIndex < PyList_Size(aPositions); ++anIndex) {
80     PyObject* aPosition = PyList_GetItem(aPositions, anIndex);
81     PyObject* aLineNo = PyTuple_GetItem(aPosition, 0);
82     PyObject* aColOffset = PyTuple_GetItem(aPosition, 1);
83
84     aResult.push_back(
85         std::pair<int, int>((int)PyLong_AsLong(aLineNo),
86                             (int)PyLong_AsLong(aColOffset)));
87   }
88
89   // TODO(spo): after this refCount of the variable is not 0. Is there memory leak?
90   Py_DECREF(aContext);
91
92   return aResult;
93 }
94
95
96 std::list<std::string> InitializationPlugin_PyInterp::compile(const std::string& theExpression)
97 {
98   PyLockWrapper lck; // Acquire GIL until the end of the method
99   std::list<std::string> aResult;
100   PyObject *aCodeopModule = PyImport_AddModule("codeop");
101   if(!aCodeopModule) { // Fatal error. No way to go on.
102     PyErr_Print();
103     return aResult;
104   }
105   // support "variable_name=" expression as "variable_name"
106   std::string anExpression = adjustExpression(theExpression);
107
108   PyObject *aCodePyObj =
109     PyObject_CallMethod(aCodeopModule, (char*)"compile_command", (char*)"(s)",
110                         anExpression.c_str());
111
112   if(!aCodePyObj || aCodePyObj == Py_None || !PyCode_Check(aCodePyObj)) {
113     Py_XDECREF(aCodePyObj);
114     return aResult;
115   }
116
117   PyCodeObject* aCodeObj = (PyCodeObject*) aCodePyObj;
118   std::string aCodeName(PyBytes_AsString(aCodeObj->co_code));
119   // co_names should be tuple, but can be changed in modern versions of python (>2.7.3)
120   if(!PyTuple_Check(aCodeObj->co_names)) {
121     return aResult;
122   }
123
124   size_t params_size = PyTuple_Size(aCodeObj->co_names);
125   if (params_size > 0) {
126     for (size_t i = 0; i < params_size; i++) {
127       PyObject* aParamObj = PyTuple_GetItem(aCodeObj->co_names, i);
128       PyObject* aParamObjStr = PyObject_Str(aParamObj);
129       std::string aParamName(PyUnicode_AsUTF8(aParamObjStr));
130       aResult.push_back(aParamName);
131       Py_XDECREF(aParamObjStr);
132     }
133   }
134   Py_XDECREF(aCodeObj);
135   return aResult;
136 }
137
138 void InitializationPlugin_PyInterp::extendLocalContext(const std::list<std::string>& theParameters)
139 {
140   PyLockWrapper lck; // Acquire GIL until the end of the method
141   if (theParameters.empty())
142     return;
143   std::list<std::string>::const_iterator it = theParameters.begin();
144   for ( ; it != theParameters.cend(); it++) {
145     std::string aParamValue = *it;
146     simpleRun(aParamValue.c_str(), false);
147   }
148 }
149
150 void InitializationPlugin_PyInterp::clearLocalContext()
151 {
152   PyLockWrapper lck;
153   PyDict_Clear(_local_context);
154 }
155
156 double InitializationPlugin_PyInterp::evaluate(const std::string& theExpression,
157                                                std::string& theError)
158 {
159   // support "variable_name=" expression as "variable_name"
160   std::string anExpression = adjustExpression(theExpression);
161
162   PyLockWrapper lck; // Acquire GIL until the end of the method
163   PyCompilerFlags aFlags = {CO_FUTURE_DIVISION};
164   aFlags.cf_flags = CO_FUTURE_DIVISION;
165   PyCodeObject* anExprCode = (PyCodeObject *) Py_CompileStringFlags(anExpression.c_str(),
166                                 "<string>", Py_eval_input, &aFlags);
167   if(!anExprCode) {
168     theError = errorMessage();
169     Py_XDECREF(anExprCode);
170     return 0.;
171   }
172
173   PyObject* anEvalResult = PyEval_EvalCode((PyObject *)anExprCode, _global_context, _local_context);
174   if(!anEvalResult) {
175     theError = errorMessage();
176     Py_XDECREF(anExprCode);
177     Py_XDECREF(anEvalResult);
178     return 0.;
179   }
180
181   PyObject* anEvalStrObj = PyObject_Str(anEvalResult);
182   std::string anEvalStr(PyUnicode_AsUTF8(anEvalStrObj));
183   Py_XDECREF(anExprCode);
184   Py_XDECREF(anEvalResult);
185   Py_XDECREF(anEvalStrObj);
186   double result = 0.;
187   try {
188     // set locale due to the #2485
189     std::string aCurLocale = std::setlocale(LC_NUMERIC, 0);
190     std::setlocale(LC_NUMERIC, "C");
191     result = std::stod(anEvalStr);
192     std::setlocale(LC_NUMERIC, aCurLocale.c_str());
193   }
194   catch (const std::invalid_argument&) {
195     theError = "Unable to eval " + anEvalStr;
196   }
197
198   return result;
199 }
200
201 std::string InitializationPlugin_PyInterp::errorMessage()
202 {
203   std::string aPyError;
204   if (PyErr_Occurred()) {
205     PyObject *pstr, *ptype, *pvalue, *ptraceback;
206     PyErr_Fetch(&ptype, &pvalue, &ptraceback);
207     PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
208     pstr = PyObject_Str(pvalue);
209     aPyError = std::string(PyUnicode_AsUTF8(pstr));
210     Py_XDECREF(pstr);
211     Py_XDECREF(ptype);
212     Py_XDECREF(pvalue);
213     Py_XDECREF(ptraceback);
214   }
215   return aPyError;
216 }
217
218 bool InitializationPlugin_PyInterp::initContext()
219 {
220   PyObject *m = PyImport_AddModule("__main__");  // interpreter main module (module context)
221   if(!m){
222     PyErr_Print();
223     return false;
224   }
225   _global_context = PyModule_GetDict(m);          // get interpreter global variable context
226   Py_INCREF(_global_context);
227   _local_context = PyDict_New();
228   Py_INCREF(_local_context);
229
230   // to avoid "help()" hang in the python console
231   const static char* aHelpTxt = "def help(): print(\"Available modules:\\n"
232     "  salome.shaper.model : higher level access to features and data model\\n"
233     "  BuildAPI            : Build plugin features allowing to build shapes\\n"
234     "  ConfigAPI           : configuration management: preferences and XML properties\\n"
235     "  ConstructionAPI     : Construction plugin for auxiliary features creation\\n"
236     "  EventsAPI           : application events receiving and emitting manager\\n"
237     "  ExchangeAPI         : Exchange plugin with import/export features\\n"
238     "  FeaturesAPI         : Features plugin with general 3D features\\n"
239     "  GeomAlgoAPI         : geometrical algorithms\\n"
240     "  GeomAPI             : geometrical data structures\\n"
241     "  GeomDataAPI         : specific geometrical data structures stored in the data model\\n"
242     "  ModelAPI            : general low-level interface to access data model\\n"
243     "  ModelHighAPI        : general high-level interface to access data model\\n"
244     "  ParametersAPI       : Parameters plugin for parameters feature management\\n"
245     "  PartSetAPI          : PartSet plugin for management Parts features\\n"
246     "  SketchAPI           : Sketch plugin with all sketch features\")";
247
248   PyRun_SimpleString(aHelpTxt);
249
250   return PyRun_SimpleString("from math import *") == 0;
251 }
252
253 void InitializationPlugin_PyInterp::closeContext()
254 {
255   Py_XDECREF(_local_context);
256   PyInterp_Interp::closeContext();
257 }
258
259 bool InitializationPlugin_PyInterp::runString(std::string theString)
260 {
261   PyLockWrapper lck; // Acquire GIL until the end of the method
262   return PyRun_SimpleString(theString.c_str());
263 }