Salome HOME
Merge branch 'Pre_2.8.0_development'
[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
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 std::list<std::pair<int, int> >
46 InitializationPlugin_PyInterp::positions(const std::string& theExpression,
47                                      const std::string& theName)
48 {
49   PyLockWrapper lck; // Acquire GIL until the end of the method
50
51   std::list<std::pair<int, int> > aResult;
52
53   // prepare a context
54   PyObject* aContext = PyDict_New();
55   PyObject* aBuiltinModule = PyImport_AddModule("__builtin__");
56   PyDict_SetItemString(aContext, "__builtins__", aBuiltinModule);
57
58   // extend aContext with variables
59   PyDict_SetItemString(aContext, "expression", PyString_FromString(theExpression.c_str()));
60   PyDict_SetItemString(aContext, "name", PyString_FromString(theName.c_str()));
61   PyDict_SetItemString(aContext, "positions", Py_BuildValue("[]"));
62
63   // run the search code
64   PyObject* aExecResult = PyRun_String(aSearchCode, Py_file_input, aContext, aContext);
65   Py_XDECREF(aExecResult);
66
67   // receive results from context
68   PyObject* aPositions = PyDict_GetItemString(aContext, "positions");
69   for (int anIndex = 0; anIndex < PyList_Size(aPositions); ++anIndex) {
70     PyObject* aPosition = PyList_GetItem(aPositions, anIndex);
71     PyObject* aLineNo = PyTuple_GetItem(aPosition, 0);
72     PyObject* aColOffset = PyTuple_GetItem(aPosition, 1);
73
74     aResult.push_back(
75         std::pair<int, int>((int)PyInt_AsLong(aLineNo),
76                             (int)PyInt_AsLong(aColOffset)));
77   }
78
79   // TODO(spo): after this refCount of the variable is not 0. Is there memory leak?
80   Py_DECREF(aContext);
81
82   return aResult;
83 }
84
85
86 std::list<std::string> InitializationPlugin_PyInterp::compile(const std::string& theExpression)
87 {
88   PyLockWrapper lck; // Acquire GIL until the end of the method
89   std::list<std::string> aResult;
90   PyObject *aCodeopModule = PyImport_AddModule("codeop");
91   if(!aCodeopModule) { // Fatal error. No way to go on.
92     PyErr_Print();
93     return aResult;
94   }
95
96   PyObject *aCodePyObj =
97     PyObject_CallMethod(aCodeopModule, (char*)"compile_command", (char*)"(s)",
98                         theExpression.c_str());
99
100   if(!aCodePyObj || aCodePyObj == Py_None || !PyCode_Check(aCodePyObj)) {
101     Py_XDECREF(aCodePyObj);
102     return aResult;
103   }
104
105   PyCodeObject* aCodeObj = (PyCodeObject*) aCodePyObj;
106   std::string aCodeName(PyString_AsString(aCodeObj->co_code));
107   // co_names should be tuple, but can be changed in modern versions of python (>2.7.3)
108   if(!PyTuple_Check(aCodeObj->co_names))
109     return aResult;
110
111   size_t params_size = PyTuple_Size(aCodeObj->co_names);
112   if (params_size > 0) {
113     for (size_t i = 0; i < params_size; i++) {
114       PyObject* aParamObj = PyTuple_GetItem(aCodeObj->co_names, i);
115       PyObject* aParamObjStr = PyObject_Str(aParamObj);
116       std::string aParamName(PyString_AsString(aParamObjStr));
117       aResult.push_back(aParamName);
118       Py_XDECREF(aParamObjStr);
119     }
120   }
121   Py_XDECREF(aCodeObj);
122   return aResult;
123 }
124
125 void InitializationPlugin_PyInterp::extendLocalContext(const std::list<std::string>& theParameters)
126 {
127   PyLockWrapper lck; // Acquire GIL until the end of the method
128   if (theParameters.empty())
129     return;
130   std::list<std::string>::const_iterator it = theParameters.begin();
131   for ( ; it != theParameters.cend(); it++) {
132     std::string aParamValue = *it;
133     simpleRun(aParamValue.c_str(), false);
134   }
135 }
136
137 void InitializationPlugin_PyInterp::clearLocalContext()
138 {
139   PyLockWrapper lck;
140   PyDict_Clear(_local_context);
141 }
142
143 double InitializationPlugin_PyInterp::evaluate(const std::string& theExpression,
144                                                std::string& theError)
145 {
146   PyLockWrapper lck; // Acquire GIL until the end of the method
147   PyCompilerFlags aFlags = {CO_FUTURE_DIVISION};
148   aFlags.cf_flags = CO_FUTURE_DIVISION;
149   PyCodeObject* anExprCode = (PyCodeObject *) Py_CompileStringFlags(theExpression.c_str(),
150                                 "<string>", Py_eval_input, &aFlags);
151   if(!anExprCode) {
152     theError = errorMessage();
153     Py_XDECREF(anExprCode);
154     return 0.;
155   }
156
157   PyObject* anEvalResult = PyEval_EvalCode(anExprCode, _global_context, _local_context);
158   if(!anEvalResult) {
159     theError = errorMessage();
160     Py_XDECREF(anExprCode);
161     Py_XDECREF(anEvalResult);
162     return 0.;
163   }
164
165   PyObject* anEvalStrObj = PyObject_Str(anEvalResult);
166   std::string anEvalStr(PyString_AsString(anEvalStrObj));
167   Py_XDECREF(anExprCode);
168   Py_XDECREF(anEvalResult);
169   Py_XDECREF(anEvalStrObj);
170   double result = 0.;
171   try {
172     result = std::stod(anEvalStr);
173   } catch (const std::invalid_argument&) {
174     theError = "Unable to eval " + anEvalStr;
175   }
176
177   return result;
178 }
179
180 std::string InitializationPlugin_PyInterp::errorMessage()
181 {
182   std::string aPyError;
183   if (PyErr_Occurred()) {
184     PyObject *pstr, *ptype, *pvalue, *ptraceback;
185     PyErr_Fetch(&ptype, &pvalue, &ptraceback);
186     PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
187     pstr = PyObject_Str(pvalue);
188     aPyError = std::string(PyString_AsString(pstr));
189     Py_XDECREF(pstr);
190     Py_XDECREF(ptype);
191     Py_XDECREF(pvalue);
192     Py_XDECREF(ptraceback);
193   }
194   return aPyError;
195 }
196
197 bool InitializationPlugin_PyInterp::initContext()
198 {
199   PyObject *m = PyImport_AddModule("__main__");  // interpreter main module (module context)
200   if(!m){
201     PyErr_Print();
202     return false;
203   }
204   _global_context = PyModule_GetDict(m);          // get interpreter global variable context
205   Py_INCREF(_global_context);
206   _local_context = PyDict_New();
207   Py_INCREF(_local_context);
208
209   return PyRun_SimpleString("from math import *") == 0;
210 }
211
212 void InitializationPlugin_PyInterp::closeContext()
213 {
214   Py_XDECREF(_local_context);
215   PyInterp_Interp::closeContext();
216 }