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(!PyList_Check(pyList))
80 error = "Not a python list.\n";
81 //throw Py2yacsException("Not a python list.");
85 int n = PyList_Size(pyList);
86 for(int i=0; i<n; i++)
88 PyObject *elem = PyList_GetItem(pyList,i);
89 if(!PyString_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 const char * portName = PyString_AsString(elem);
99 cppList.push_back(portName);
107 std::string getPyErrorText()
109 std::string result="";
110 if (PyErr_Occurred())
112 PyObject *ptype, *pvalue, *ptraceback;
113 PyObject *pystr, *module_name, *pyth_module, *pyth_func;
114 PyErr_Fetch(&ptype, &pvalue, &ptraceback);
115 pystr = PyObject_Str(pvalue);
116 result = PyString_AsString(pystr);
120 /* See if we can get a full traceback */
123 module_name = PyString_FromString("traceback");
124 pyth_module = PyImport_Import(module_name);
125 Py_DECREF(module_name);
128 pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
129 if (pyth_func && PyCallable_Check(pyth_func))
132 pyList = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
135 int n = PyList_Size(pyList);
136 for(int i=0; i<n; i++)
138 pystr = PyList_GetItem(pyList,i);
139 result += PyString_AsString(pystr);
144 Py_XDECREF(pyth_func);
145 Py_DECREF(pyth_module);
150 Py_XDECREF(ptraceback);
156 PyObject* checkAndGetAttribute(PyObject *p,
157 const char* attribute,
160 PyObject *pAttribute = PyObject_GetAttrString(p, attribute);
163 error += "Attribute '";
165 error += "' not found in the returned value of the parsing function.\n";
166 error += getPyErrorText();
171 void Py2yacs::load(const std::string& python_code)
173 PyObject *pModule, *pDict, *pFunc;
174 PyObject *pArgs, *pValue;
176 std::string errorMessage="";
177 _python_code = python_code;
179 _global_errors.clear();
182 YACS::ENGINE::AutoGIL agil;
183 pValue = PyString_FromString(_python_parser_module.c_str());
184 pModule = PyImport_Import(pValue);
189 errorMessage = getPyErrorText();
190 errorMessage += "\nFailed to load ";
191 errorMessage += _python_parser_module;
192 errorMessage += ".\n";
196 pFunc = PyObject_GetAttrString(pModule, _python_parser_function.c_str());
198 if (pFunc && PyCallable_Check(pFunc))
200 pArgs = PyTuple_New(1);
201 pValue = PyString_FromString(python_code.c_str());
202 PyTuple_SetItem(pArgs, 0, pValue);
204 pValue = PyObject_CallObject(pFunc, pArgs);
207 errorMessage = getPyErrorText();
210 if (!PyTuple_Check(pValue))
212 errorMessage += "Parsing function should return a tuple of two string lists.\n";
214 int n = PyTuple_Size(pValue);
217 errorMessage += "Parsing function should return two string lists.\n";
219 PyObject *pyList = PyTuple_GetItem(pValue, 0);
220 if(!PyList_Check(pyList))
222 errorMessage += "The first returned value of the parsing function"
223 " should be a python list.\n";
227 n = PyList_Size(pyList);
228 for(int i=0; i<n; i++)
230 PyObject *fpy = PyList_GetItem(pyList,i);
231 PyObject *pAttribute;
233 if(pAttribute = checkAndGetAttribute(fpy, "name", errorMessage))
235 if(!PyString_Check(pAttribute))
237 errorMessage += "Attribute 'name' should be a string.\n";
238 Py_DECREF(pAttribute);
242 _functions.push_back(FunctionProperties());
243 FunctionProperties& fcpp = _functions.back();
244 fcpp._name=PyString_AsString(pAttribute);
245 Py_DECREF(pAttribute);
247 if(pAttribute = checkAndGetAttribute(fpy, "inputs", errorMessage))
248 errorMessage += copyList(pAttribute, fcpp._input_ports);
249 Py_XDECREF(pAttribute);
251 if(pAttribute = checkAndGetAttribute(fpy, "outputs", errorMessage))
252 // None value means no return statement in the function
253 if(pAttribute != Py_None)
254 errorMessage += copyList(pAttribute,fcpp._output_ports);
255 Py_XDECREF(pAttribute);
257 if(pAttribute = checkAndGetAttribute(fpy, "errors", errorMessage))
258 errorMessage += copyList(pAttribute, fcpp._errors);
259 Py_XDECREF(pAttribute);
261 if(pAttribute = checkAndGetAttribute(fpy, "imports", errorMessage))
262 errorMessage += copyList(pAttribute, fcpp._imports);
263 Py_XDECREF(pAttribute);
268 errorMessage += copyList(PyTuple_GetItem(pValue, 1), _global_errors);
274 errorMessage = getPyErrorText();
275 errorMessage += "\nCannot find the parsing function '";
276 errorMessage += _python_parser_function;
277 errorMessage += "' in python module '";
278 errorMessage += _python_parser_module;
279 errorMessage += "'.\n";
285 if(!errorMessage.empty())
286 throw Py2yacsException(errorMessage);
290 void Py2yacs::save(const std::string& file_path,
291 const std::string& python_function)const
293 YACS::ENGINE::Proc* schema = createProc(python_function);
294 schema->saveSchema(file_path);
298 YACS::ENGINE::Proc* Py2yacs::createProc(const std::string& python_function)const
300 if(!_global_errors.empty())
302 std::string error_message = "The python script contains errors.\n";
303 std::list<std::string>::const_iterator it;
304 for(it = _global_errors.begin(); it != _global_errors.end(); it++)
305 error_message += (*it) + "\n";
306 throw Py2yacsException(error_message);
309 // find function properties
310 std::list<FunctionProperties>::const_iterator fn_prop = _functions.begin();
311 while(fn_prop != _functions.end() && fn_prop->_name != python_function)
314 if(fn_prop == _functions.end())
316 throw Py2yacsException(std::string("Function not found: ")+python_function);
319 if(!fn_prop->_errors.empty())
321 std::string error_message = "Function contains errors.\n";
322 std::list<std::string>::const_iterator it;
323 for(it = fn_prop->_errors.begin(); it != fn_prop->_errors.end(); it++)
324 error_message += (*it) + "\n";
325 throw Py2yacsException(error_message);
328 // add the call to the function at the end of the script
329 std::stringstream fn_call;
330 fn_call << std::endl;
331 std::list<std::string>::const_iterator it;
333 for(it=fn_prop->_output_ports.begin();
334 it!=fn_prop->_output_ports.end();
342 fn_call << "=" << python_function << "(";
344 for(it = fn_prop->_input_ports.begin();
345 it != fn_prop->_input_ports.end();
353 fn_call << ")" << std::endl;
354 std::string node_body = _python_code + fn_call.str();
356 YACS::ENGINE::Proc* schema;
357 YACS::ENGINE::RuntimeSALOME::setRuntime();
358 YACS::ENGINE::RuntimeSALOME* runtime = YACS::ENGINE::getSALOMERuntime();
360 // build the YACS schema
361 const char * node_name = "default_name";
362 schema = runtime->createProc("Schema");
363 YACS::ENGINE::InlineNode* node = runtime->createScriptNode("", node_name);
364 schema->edAddChild(node);
365 node->setScript(node_body);
366 YACS::ENGINE::TypeCode *tc_double = runtime->getTypeCode("double");
368 for(it = fn_prop->_input_ports.begin();
369 it != fn_prop->_input_ports.end();
371 node->edAddInputPort(*it, tc_double);
373 for(it = fn_prop->_output_ports.begin();
374 it != fn_prop->_output_ports.end();
376 node->edAddOutputPort(*it, tc_double);