1 // Copyright (C) 2006-2016 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 #include "py2yacs.hxx"
22 #include "RuntimeSALOME.hxx"
24 #include "InlineNode.hxx"
25 #include "AutoGIL.hxx"
27 Py2yacsException::Py2yacsException(const std::string& what)
33 Py2yacsException::~Py2yacsException()throw ()
37 const char * Py2yacsException::what() const throw ()
44 : _python_parser_module("py2yacs"),
45 _python_parser_function("get_properties"),
52 Py2yacs::Py2yacs(const std::string& python_parser_module,
53 const std::string& python_parser_function)
54 : _python_parser_module(python_parser_module),
55 _python_parser_function(python_parser_function),
62 const std::list<std::string>& Py2yacs::getGlobalErrors() const
64 return _global_errors;
67 const std::list<FunctionProperties>& Py2yacs::getFunctionProperties()const
72 // Copy a python list of strings to a c++ list of strings.
73 // Return an error string. An empty string means no error.
75 std::string copyList(PyObject *pyList, std::list<std::string>& cppList)
78 if(not PyList_Check(pyList))
80 error = "Not a python list.\n";
81 //throw Py2yacsException("Not a python list.");
85 Py_ssize_t n = PyList_Size(pyList);
86 for(Py_ssize_t i=0; i<n; i++)
88 PyObject *elem = PyList_GetItem(pyList,i);
89 if(not PyUnicode_Check(elem))
91 std::stringstream message;
92 message << "List element number " << i << " is not a string.\n";
93 error += message.str();
94 // throw Py2yacsException(message.str());
98 cppList.push_back(std::string(PyUnicode_AsUTF8(elem)));
106 std::string getPyErrorText()
108 std::string result="";
109 if (PyErr_Occurred())
111 PyObject *ptype, *pvalue, *ptraceback;
112 PyObject *pystr, *module_name, *pyth_module, *pyth_func;
113 PyErr_Fetch(&ptype, &pvalue, &ptraceback);
114 pystr = PyObject_Str(pvalue);
115 result = std::string(PyUnicode_AsUTF8(pystr));
119 /* See if we can get a full traceback */
122 module_name = PyUnicode_FromString("traceback");
123 pyth_module = PyImport_Import(module_name);
124 Py_DECREF(module_name);
127 pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
128 if (pyth_func && PyCallable_Check(pyth_func))
131 pyList = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
134 int n = PyList_Size(pyList);
135 for(int i=0; i<n; i++)
137 pystr = PyList_GetItem(pyList,i);
138 result += std::string(PyUnicode_AsUTF8(pystr));
143 Py_XDECREF(pyth_func);
144 Py_DECREF(pyth_module);
149 Py_XDECREF(ptraceback);
155 PyObject* checkAndGetAttribute(PyObject *p,
156 const char* attribute,
159 PyObject *pAttribute = PyObject_GetAttrString(p, attribute);
162 error += "Attribute '";
164 error += "' not found in the returned value of the parsing function.\n";
165 error += getPyErrorText();
170 void Py2yacs::load(const std::string& python_code)
172 PyObject *pModule, *pDict, *pFunc;
173 PyObject *pArgs, *pValue;
175 std::string errorMessage="";
176 _python_code = python_code;
178 _global_errors.clear();
181 YACS::ENGINE::AutoGIL agil;
182 pValue = PyUnicode_FromString(_python_parser_module.c_str());
183 pModule = PyImport_Import(pValue);
188 errorMessage = getPyErrorText();
189 errorMessage += "\nFailed to load ";
190 errorMessage += _python_parser_module;
191 errorMessage += ".\n";
195 pFunc = PyObject_GetAttrString(pModule, _python_parser_function.c_str());
197 if (pFunc && PyCallable_Check(pFunc))
199 pArgs = PyTuple_New(1);
200 pValue = PyUnicode_FromString(python_code.c_str());
201 PyTuple_SetItem(pArgs, 0, pValue);
203 pValue = PyObject_CallObject(pFunc, pArgs);
206 errorMessage = getPyErrorText();
209 if (not PyTuple_Check(pValue))
211 errorMessage += "Parsing function should return a tuple of two string lists.\n";
213 Py_ssize_t n = PyTuple_Size(pValue);
216 errorMessage += "Parsing function should return two string lists.\n";
218 PyObject *pyList = PyTuple_GetItem(pValue, 0);
219 if(not PyList_Check(pyList))
221 errorMessage += "The first returned value of the parsing function"
222 " should be a python list.\n";
226 n = PyList_Size(pyList);
227 for(int i=0; i<n; i++)
229 PyObject *fpy = PyList_GetItem(pyList,i);
230 PyObject *pAttribute;
232 if(pAttribute = checkAndGetAttribute(fpy, "name", errorMessage))
234 if(not PyUnicode_Check(pAttribute))
236 errorMessage += "Attribute 'name' should be a string.\n";
237 Py_DECREF(pAttribute);
241 _functions.push_back(FunctionProperties());
242 FunctionProperties& fcpp = _functions.back();
243 fcpp._name=std::string(PyUnicode_AsUTF8(pAttribute));
244 Py_DECREF(pAttribute);
246 if(pAttribute = checkAndGetAttribute(fpy, "inputs", errorMessage))
247 errorMessage += copyList(pAttribute, fcpp._input_ports);
248 Py_XDECREF(pAttribute);
250 if(pAttribute = checkAndGetAttribute(fpy, "outputs", errorMessage))
251 // None value means no return statement in the function
252 if(pAttribute != Py_None)
253 errorMessage += copyList(pAttribute,fcpp._output_ports);
254 Py_XDECREF(pAttribute);
256 if(pAttribute = checkAndGetAttribute(fpy, "errors", errorMessage))
257 errorMessage += copyList(pAttribute, fcpp._errors);
258 Py_XDECREF(pAttribute);
260 if(pAttribute = checkAndGetAttribute(fpy, "imports", errorMessage))
261 errorMessage += copyList(pAttribute, fcpp._imports);
262 Py_XDECREF(pAttribute);
267 errorMessage += copyList(PyTuple_GetItem(pValue, 1), _global_errors);
273 errorMessage = getPyErrorText();
274 errorMessage += "\nCannot find the parsing function '";
275 errorMessage += _python_parser_function;
276 errorMessage += "' in python module '";
277 errorMessage += _python_parser_module;
278 errorMessage += "'.\n";
284 if(not errorMessage.empty())
285 throw Py2yacsException(errorMessage);
289 void Py2yacs::save(const std::string& file_path,
290 const std::string& python_function)const
292 YACS::ENGINE::Proc* schema = createProc(python_function);
293 schema->saveSchema(file_path);
297 YACS::ENGINE::Proc* Py2yacs::createProc(const std::string& python_function)const
299 if(not _global_errors.empty())
301 std::string error_message = "The python script contains errors.\n";
302 std::list<std::string>::const_iterator it;
303 for(it = _global_errors.begin(); it != _global_errors.end(); it++)
304 error_message += (*it) + "\n";
305 throw Py2yacsException(error_message);
308 // find function properties
309 std::list<FunctionProperties>::const_iterator fn_prop = _functions.begin();
310 while(fn_prop != _functions.end() and fn_prop->_name != python_function)
313 if(fn_prop == _functions.end())
315 throw Py2yacsException(std::string("Function not found: ")+python_function);
318 if(not fn_prop->_errors.empty())
320 std::string error_message = "Function contains errors.\n";
321 std::list<std::string>::const_iterator it;
322 for(it = fn_prop->_errors.begin(); it != fn_prop->_errors.end(); it++)
323 error_message += (*it) + "\n";
324 throw Py2yacsException(error_message);
327 // add the call to the function at the end of the script
328 std::stringstream fn_call;
329 fn_call << std::endl;
330 std::list<std::string>::const_iterator it;
332 for(it=fn_prop->_output_ports.begin();
333 it!=fn_prop->_output_ports.end();
341 fn_call << "=" << python_function << "(";
343 for(it = fn_prop->_input_ports.begin();
344 it != fn_prop->_input_ports.end();
352 fn_call << ")" << std::endl;
353 std::string node_body = _python_code + fn_call.str();
355 YACS::ENGINE::Proc* schema;
356 YACS::ENGINE::RuntimeSALOME::setRuntime();
357 YACS::ENGINE::RuntimeSALOME* runtime = YACS::ENGINE::getSALOMERuntime();
359 // build the YACS schema
360 const char * node_name = "default_name";
361 schema = runtime->createProc("Schema");
362 YACS::ENGINE::InlineNode* node = runtime->createScriptNode("", node_name);
363 schema->edAddChild(node);
364 node->setScript(node_body);
365 YACS::ENGINE::TypeCode *tc_double = runtime->getTypeCode("double");
367 for(it = fn_prop->_input_ports.begin();
368 it != fn_prop->_input_ports.end();
370 node->edAddInputPort(*it, tc_double);
372 for(it = fn_prop->_output_ports.begin();
373 it != fn_prop->_output_ports.end();
375 node->edAddOutputPort(*it, tc_double);