Salome HOME
Fix pipe to avoid moving of the path, if it is passed through the first face.
[modules/shaper.git] / src / InitializationPlugin / InitializationPlugin_PyInterp.cpp
1 // Copyright (C) 2014-2017  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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include <InitializationPlugin_PyInterp.h>
22
23 #include <string>
24 #include <stdexcept>
25 #include <clocale>
26
27 InitializationPlugin_PyInterp::InitializationPlugin_PyInterp()
28 : PyInterp_Interp()
29 {
30 }
31
32 InitializationPlugin_PyInterp::~InitializationPlugin_PyInterp()
33 {
34 }
35
36 const char* aSearchCode =
37   "import ast\n"
38   "class FindName(ast.NodeVisitor):\n"
39   "    def __init__(self, name):\n"
40   "        self.name = name\n"
41   "    def visit_Name(self, node):\n"
42   "        if node.id == self.name:\n"
43   "            positions.append((node.lineno, node.col_offset))\n"
44   "FindName(name).visit(ast.parse(expression))";
45
46 // make the expression be correct for the python interpreter even for the
47 // beta=alfa*2 expressions
48 static std::string adjustExpression(const std::string& theExpression) {
49   std::string anExpression = theExpression;
50   if (!anExpression.empty() && anExpression.back() == '=') {
51     anExpression = anExpression.substr(0, anExpression.length() - 1);
52   }
53   return anExpression;
54 }
55
56 std::list<std::pair<int, int> >
57 InitializationPlugin_PyInterp::positions(const std::string& theExpression,
58                                      const std::string& theName)
59 {
60   PyLockWrapper lck; // Acquire GIL until the end of the method
61
62   std::list<std::pair<int, int> > aResult;
63
64   // prepare a context
65   PyObject* aContext = PyDict_New();
66   PyDict_SetItemString(aContext, "__builtins__", PyEval_GetBuiltins());
67
68   std::string anExpression = adjustExpression(theExpression);
69   // extend aContext with variables
70   PyDict_SetItemString(aContext, "expression", PyUnicode_FromString(anExpression.c_str()));
71   PyDict_SetItemString(aContext, "name", PyUnicode_FromString(theName.c_str()));
72   PyDict_SetItemString(aContext, "positions", Py_BuildValue("[]"));
73
74   // run the search code
75   PyObject* aExecResult = PyRun_String(aSearchCode, Py_file_input, aContext, aContext);
76   Py_XDECREF(aExecResult);
77
78   // receive results from context
79   PyObject* aPositions = PyDict_GetItemString(aContext, "positions");
80   for (int anIndex = 0; anIndex < PyList_Size(aPositions); ++anIndex) {
81     PyObject* aPosition = PyList_GetItem(aPositions, anIndex);
82     PyObject* aLineNo = PyTuple_GetItem(aPosition, 0);
83     PyObject* aColOffset = PyTuple_GetItem(aPosition, 1);
84
85     aResult.push_back(
86         std::pair<int, int>((int)PyLong_AsLong(aLineNo),
87                             (int)PyLong_AsLong(aColOffset)));
88   }
89
90   // TODO(spo): after this refCount of the variable is not 0. Is there memory leak?
91   Py_DECREF(aContext);
92
93   return aResult;
94 }
95
96
97 std::list<std::string> InitializationPlugin_PyInterp::compile(const std::string& theExpression)
98 {
99   PyLockWrapper lck; // Acquire GIL until the end of the method
100   std::list<std::string> aResult;
101   PyObject *aCodeopModule = PyImport_AddModule("codeop");
102   if(!aCodeopModule) { // Fatal error. No way to go on.
103     PyErr_Print();
104     return aResult;
105   }
106   // support "variable_name=" expression as "variable_name"
107   std::string anExpression = adjustExpression(theExpression);
108
109   PyObject *aCodePyObj =
110     PyObject_CallMethod(aCodeopModule, (char*)"compile_command", (char*)"(s)",
111                         anExpression.c_str());
112
113   if(!aCodePyObj || aCodePyObj == Py_None || !PyCode_Check(aCodePyObj)) {
114     Py_XDECREF(aCodePyObj);
115     return aResult;
116   }
117
118   PyCodeObject* aCodeObj = (PyCodeObject*) aCodePyObj;
119   std::string aCodeName(PyBytes_AsString(aCodeObj->co_code));
120   // co_names should be tuple, but can be changed in modern versions of python (>2.7.3)
121   if(!PyTuple_Check(aCodeObj->co_names)) {
122     return aResult;
123   }
124
125   size_t params_size = PyTuple_Size(aCodeObj->co_names);
126   if (params_size > 0) {
127     for (size_t i = 0; i < params_size; i++) {
128       PyObject* aParamObj = PyTuple_GetItem(aCodeObj->co_names, i);
129       PyObject* aParamObjStr = PyObject_Str(aParamObj);
130       std::string aParamName(PyUnicode_AsUTF8(aParamObjStr));
131       aResult.push_back(aParamName);
132       Py_XDECREF(aParamObjStr);
133     }
134   }
135   Py_XDECREF(aCodeObj);
136   return aResult;
137 }
138
139 void InitializationPlugin_PyInterp::extendLocalContext(const std::list<std::string>& theParameters)
140 {
141   PyLockWrapper lck; // Acquire GIL until the end of the method
142   if (theParameters.empty())
143     return;
144   std::list<std::string>::const_iterator it = theParameters.begin();
145   for ( ; it != theParameters.cend(); it++) {
146     std::string aParamValue = *it;
147     simpleRun(aParamValue.c_str(), false);
148   }
149 }
150
151 void InitializationPlugin_PyInterp::clearLocalContext()
152 {
153   PyLockWrapper lck;
154   PyDict_Clear(_local_context);
155 }
156
157 double InitializationPlugin_PyInterp::evaluate(const std::string& theExpression,
158                                                std::string& theError)
159 {
160   // support "variable_name=" expression as "variable_name"
161   std::string anExpression = adjustExpression(theExpression);
162
163   PyLockWrapper lck; // Acquire GIL until the end of the method
164   PyCompilerFlags aFlags = {CO_FUTURE_DIVISION};
165   aFlags.cf_flags = CO_FUTURE_DIVISION;
166   PyCodeObject* anExprCode = (PyCodeObject *) Py_CompileStringFlags(anExpression.c_str(),
167                                 "<string>", Py_eval_input, &aFlags);
168   if(!anExprCode) {
169     theError = errorMessage();
170     Py_XDECREF(anExprCode);
171     return 0.;
172   }
173
174   PyObject* anEvalResult = PyEval_EvalCode((PyObject *)anExprCode, _global_context, _local_context);
175   if(!anEvalResult) {
176     theError = errorMessage();
177     Py_XDECREF(anExprCode);
178     Py_XDECREF(anEvalResult);
179     return 0.;
180   }
181
182   PyObject* anEvalStrObj = PyObject_Str(anEvalResult);
183   std::string anEvalStr(PyUnicode_AsUTF8(anEvalStrObj));
184   Py_XDECREF(anExprCode);
185   Py_XDECREF(anEvalResult);
186   Py_XDECREF(anEvalStrObj);
187   double result = 0.;
188   try {
189     // set locale due to the #2485
190     std::string aCurLocale = std::setlocale(LC_NUMERIC, 0);
191     std::setlocale(LC_NUMERIC, "C");
192     result = std::stod(anEvalStr);
193     std::setlocale(LC_NUMERIC, aCurLocale.c_str());
194   }
195   catch (const std::invalid_argument&) {
196     theError = "Unable to eval " + anEvalStr;
197   }
198
199   return result;
200 }
201
202 std::string InitializationPlugin_PyInterp::errorMessage()
203 {
204   std::string aPyError;
205   if (PyErr_Occurred()) {
206     PyObject *pstr, *ptype, *pvalue, *ptraceback;
207     PyErr_Fetch(&ptype, &pvalue, &ptraceback);
208     PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
209     pstr = PyObject_Str(pvalue);
210     aPyError = std::string(PyUnicode_AsUTF8(pstr));
211     Py_XDECREF(pstr);
212     Py_XDECREF(ptype);
213     Py_XDECREF(pvalue);
214     Py_XDECREF(ptraceback);
215   }
216   return aPyError;
217 }
218
219 bool InitializationPlugin_PyInterp::initContext()
220 {
221   PyObject *m = PyImport_AddModule("__main__");  // interpreter main module (module context)
222   if(!m){
223     PyErr_Print();
224     return false;
225   }
226   _global_context = PyModule_GetDict(m);          // get interpreter global variable context
227   Py_INCREF(_global_context);
228   _local_context = PyDict_New();
229   Py_INCREF(_local_context);
230
231   return PyRun_SimpleString("from math import *") == 0;
232 }
233
234 void InitializationPlugin_PyInterp::closeContext()
235 {
236   Py_XDECREF(_local_context);
237   PyInterp_Interp::closeContext();
238 }