1 // Copyright (C) 2006-2019 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"
26 #include "InputPort.hxx"
27 #include "Container.hxx"
29 Py2yacsException::Py2yacsException(const std::string& what)
35 Py2yacsException::~Py2yacsException()throw ()
39 const char * Py2yacsException::what() const throw ()
46 : _python_parser_module("py2yacs"),
47 _python_parser_function("get_properties"),
54 Py2yacs::Py2yacs(const std::string& python_parser_module,
55 const std::string& python_parser_function)
56 : _python_parser_module(python_parser_module),
57 _python_parser_function(python_parser_function),
64 const std::list<std::string>& Py2yacs::getGlobalErrors() const
66 return _global_errors;
69 const std::list<FunctionProperties>& Py2yacs::getFunctionProperties()const
74 // Copy a python list of strings to a c++ list of strings.
75 // Return an error string. An empty string means no error.
77 std::string copyList(PyObject *pyList, std::list<std::string>& cppList)
80 if(!PyList_Check(pyList))
82 error = "Not a python list.\n";
83 //throw Py2yacsException("Not a python list.");
87 Py_ssize_t n = PyList_Size(pyList);
88 for(Py_ssize_t i=0; i<n; i++)
90 PyObject *elem = PyList_GetItem(pyList,i);
91 if(!PyUnicode_Check(elem))
93 std::stringstream message;
94 message << "List element number " << i << " is not a string.\n";
95 error += message.str();
96 // throw Py2yacsException(message.str());
100 cppList.push_back(std::string(PyUnicode_AsUTF8(elem)));
108 std::string getPyErrorText()
110 std::string result="";
111 if (PyErr_Occurred())
113 PyObject *ptype, *pvalue, *ptraceback;
114 PyObject *pystr, *module_name, *pyth_module, *pyth_func;
115 PyErr_Fetch(&ptype, &pvalue, &ptraceback);
116 pystr = PyObject_Str(pvalue);
117 result = std::string(PyUnicode_AsUTF8(pystr));
121 /* See if we can get a full traceback */
124 module_name = PyUnicode_FromString("traceback");
125 pyth_module = PyImport_Import(module_name);
126 Py_DECREF(module_name);
129 pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
130 if (pyth_func && PyCallable_Check(pyth_func))
133 pyList = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
136 int n = PyList_Size(pyList);
137 for(int i=0; i<n; i++)
139 pystr = PyList_GetItem(pyList,i);
140 result += std::string(PyUnicode_AsUTF8(pystr));
145 Py_XDECREF(pyth_func);
146 Py_DECREF(pyth_module);
151 Py_XDECREF(ptraceback);
157 PyObject* checkAndGetAttribute(PyObject *p,
158 const char* attribute,
161 PyObject *pAttribute = PyObject_GetAttrString(p, attribute);
164 error += "Attribute '";
166 error += "' not found in the returned value of the parsing function.\n";
167 error += getPyErrorText();
172 void Py2yacs::load(const std::string& python_code)
174 PyObject *pModule, *pDict, *pFunc;
175 PyObject *pArgs, *pValue;
177 std::string errorMessage="";
178 _python_code = python_code;
180 _global_errors.clear();
183 YACS::ENGINE::AutoGIL agil;
184 pValue = PyUnicode_FromString(_python_parser_module.c_str());
185 pModule = PyImport_Import(pValue);
190 errorMessage = getPyErrorText();
191 errorMessage += "\nFailed to load ";
192 errorMessage += _python_parser_module;
193 errorMessage += ".\n";
197 pFunc = PyObject_GetAttrString(pModule, _python_parser_function.c_str());
199 if (pFunc && PyCallable_Check(pFunc))
201 pArgs = PyTuple_New(1);
202 pValue = PyUnicode_FromString(python_code.c_str());
203 PyTuple_SetItem(pArgs, 0, pValue);
205 pValue = PyObject_CallObject(pFunc, pArgs);
208 errorMessage = getPyErrorText();
211 if (!PyTuple_Check(pValue))
213 errorMessage += "Parsing function should return a tuple of two string lists.\n";
215 Py_ssize_t n = PyTuple_Size(pValue);
218 errorMessage += "Parsing function should return two string lists.\n";
220 PyObject *pyList = PyTuple_GetItem(pValue, 0);
221 if(!PyList_Check(pyList))
223 errorMessage += "The first returned value of the parsing function"
224 " should be a python list.\n";
228 n = PyList_Size(pyList);
229 for(int i=0; i<n; i++)
231 PyObject *fpy = PyList_GetItem(pyList,i);
232 PyObject *pAttribute;
234 if(pAttribute = checkAndGetAttribute(fpy, "name", errorMessage))
236 if(!PyUnicode_Check(pAttribute))
238 errorMessage += "Attribute 'name' should be a string.\n";
239 Py_DECREF(pAttribute);
243 _functions.push_back(FunctionProperties());
244 FunctionProperties& fcpp = _functions.back();
245 fcpp._name=std::string(PyUnicode_AsUTF8(pAttribute));
246 Py_DECREF(pAttribute);
248 if(pAttribute = checkAndGetAttribute(fpy, "inputs", errorMessage))
249 errorMessage += copyList(pAttribute, fcpp._input_ports);
250 Py_XDECREF(pAttribute);
252 if(pAttribute = checkAndGetAttribute(fpy, "outputs", errorMessage))
253 // None value means no return statement in the function
254 if(pAttribute != Py_None)
255 errorMessage += copyList(pAttribute,fcpp._output_ports);
256 Py_XDECREF(pAttribute);
258 if(pAttribute = checkAndGetAttribute(fpy, "errors", errorMessage))
259 errorMessage += copyList(pAttribute, fcpp._errors);
260 Py_XDECREF(pAttribute);
262 if(pAttribute = checkAndGetAttribute(fpy, "imports", errorMessage))
263 errorMessage += copyList(pAttribute, fcpp._imports);
264 Py_XDECREF(pAttribute);
269 errorMessage += copyList(PyTuple_GetItem(pValue, 1), _global_errors);
275 errorMessage = getPyErrorText();
276 errorMessage += "\nCannot find the parsing function '";
277 errorMessage += _python_parser_function;
278 errorMessage += "' in python module '";
279 errorMessage += _python_parser_module;
280 errorMessage += "'.\n";
286 if(!errorMessage.empty())
287 throw Py2yacsException(errorMessage);
291 void Py2yacs::save(const std::string& file_path,
292 const std::string& python_function)const
294 YACS::ENGINE::Proc* schema = createProc(python_function);
295 schema->saveSchema(file_path);
299 YACS::ENGINE::Proc* Py2yacs::createProc(const std::string& python_function)const
301 if(!_global_errors.empty())
303 std::string error_message = "The python script contains errors.\n";
304 std::list<std::string>::const_iterator it;
305 for(it = _global_errors.begin(); it != _global_errors.end(); it++)
306 error_message += (*it) + "\n";
307 throw Py2yacsException(error_message);
310 // find function properties
311 std::list<FunctionProperties>::const_iterator fn_prop = _functions.begin();
312 while(fn_prop != _functions.end() && fn_prop->_name != python_function)
315 if(fn_prop == _functions.end())
317 throw Py2yacsException(std::string("Function not found: ")+python_function);
320 if(!fn_prop->_errors.empty())
322 std::string error_message = "Function contains errors.\n";
323 std::list<std::string>::const_iterator it;
324 for(it = fn_prop->_errors.begin(); it != fn_prop->_errors.end(); it++)
325 error_message += (*it) + "\n";
326 throw Py2yacsException(error_message);
329 // add the call to the function at the end of the script
330 std::stringstream fn_call;
331 fn_call << std::endl;
332 std::list<std::string>::const_iterator it;
334 for(it=fn_prop->_output_ports.begin();
335 it!=fn_prop->_output_ports.end();
343 fn_call << "=" << python_function << "(";
345 for(it = fn_prop->_input_ports.begin();
346 it != fn_prop->_input_ports.end();
354 fn_call << ")" << std::endl;
355 std::string node_body = _python_code + fn_call.str();
357 YACS::ENGINE::Proc* schema;
358 YACS::ENGINE::RuntimeSALOME::setRuntime();
359 YACS::ENGINE::RuntimeSALOME* runtime = YACS::ENGINE::getSALOMERuntime();
361 // build the YACS schema
362 const char * node_name = "default_name";
363 schema = runtime->createProc("Schema");
364 YACS::ENGINE::InlineNode* node = runtime->createScriptNode("", node_name);
365 schema->edAddChild(node);
366 node->setScript(node_body);
367 YACS::ENGINE::TypeCode *tc_double = runtime->getTypeCode("double");
369 for(it = fn_prop->_input_ports.begin();
370 it != fn_prop->_input_ports.end();
373 YACS::ENGINE::InputPort *newport = node->edAddInputPort(*it, tc_double);
374 newport->edInit(0.0);
377 for(it = fn_prop->_output_ports.begin();
378 it != fn_prop->_output_ports.end();
380 node->edAddOutputPort(*it, tc_double);
382 node->setExecutionMode(YACS::ENGINE::InlineNode::REMOTE_STR);
383 YACS::ENGINE::Container* cont=schema->createContainer("Py2YacsContainer");
384 node->setContainer(cont);
389 std::string Py2yacs::getAllErrors()const
391 std::stringstream buffer;
393 if(! _global_errors.empty())
395 buffer << "Global errors:" << std::endl;
396 std::list<std::string>::const_iterator it;
397 for(it=_global_errors.begin(); it!=_global_errors.end(); it++)
399 buffer << *it << std::endl;
401 buffer << "-----------------------------------------" << std::endl;
404 std::list<FunctionProperties>::const_iterator it_fp;
405 for(it_fp=_functions.begin();it_fp!=_functions.end();it_fp++)
407 if(! it_fp->_errors.empty())
409 buffer << "Function " << it_fp->_name << " has errors:" << std::endl;
410 std::list<std::string>::const_iterator it;
411 buffer << "Errors :" ;
412 for(it=it_fp->_errors.begin();it!=it_fp->_errors.end();it++)
413 buffer << *it << std::endl;
414 buffer << "-----------------------------------------" << std::endl;
420 std::string Py2yacs::getFunctionErrors(const std::string& functionName)const
422 std::stringstream buffer;
424 if(! _global_errors.empty())
426 buffer << "Global errors:" << std::endl;
427 std::list<std::string>::const_iterator it;
428 for(it=_global_errors.begin(); it!=_global_errors.end(); it++)
430 buffer << *it << std::endl;
432 buffer << "-----------------------------------------" << std::endl;
435 bool nameFound = false;
436 std::list<FunctionProperties>::const_iterator it_fp;
437 for(it_fp=_functions.begin(); it_fp!=_functions.end() && !nameFound; it_fp++)
439 if(it_fp->_name == functionName)
442 if(! it_fp->_errors.empty())
444 buffer << "Function " << it_fp->_name << " has errors:" << std::endl;
445 std::list<std::string>::const_iterator it;
446 buffer << "Errors :" ;
447 for(it=it_fp->_errors.begin();it!=it_fp->_errors.end();it++)
448 buffer << *it << std::endl;
449 buffer << "-----------------------------------------" << std::endl;
456 buffer << "Function " << functionName << " not found." << std::endl;