From: Ovidiu Mircescu Date: Fri, 27 Jan 2017 13:58:59 +0000 (+0100) Subject: Add py2yacs X-Git-Tag: V8_3_0a2^2 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fomu%2Fpy2yacs;p=modules%2Fyacs.git Add py2yacs py2yacs library can build a yacs schema out of a python script. The schema contains a single python node which executes a function from the python script. Input and output ports are deducted from the parameters and the returns of the chosen function. The type of ports are restricted to 'double'. --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 064805212..af8928c9f 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,7 +29,7 @@ SET(SUBDIRS_COMMON # KERNEL ## IF(SALOME_YACS_USE_KERNEL) - SET(SUBDIRS_KERNEL runtime yacsloader evalyfx) + SET(SUBDIRS_KERNEL runtime yacsloader evalyfx py2yacs) ENDIF(SALOME_YACS_USE_KERNEL) ## diff --git a/src/CTestTestfileInstall.cmake b/src/CTestTestfileInstall.cmake index 71e7e4c8b..4d5dd69d2 100644 --- a/src/CTestTestfileInstall.cmake +++ b/src/CTestTestfileInstall.cmake @@ -33,4 +33,5 @@ SUBDIRS( pmml yacsloader yacsloader_swig + py2yacs ) diff --git a/src/py2yacs/CMakeLists.txt b/src/py2yacs/CMakeLists.txt new file mode 100644 index 000000000..d5c4ce3ba --- /dev/null +++ b/src/py2yacs/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (C) 2012-2016 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +INCLUDE_DIRECTORIES( + ${PYTHON_INCLUDE_DIR} + ${SALOME_INCL_PATH} + ${PROJECT_SOURCE_DIR}/src/bases + ${PROJECT_SOURCE_DIR}/src/engine + ${PROJECT_SOURCE_DIR}/src/runtime + ${PROJECT_SOURCE_DIR} + ) + +ADD_DEFINITIONS( + ${PYTHON_DEFINITIONS} + ) + +IF(SALOME_BUILD_TESTS) + ADD_SUBDIRECTORY(Test) +ENDIF(SALOME_BUILD_TESTS) + +SET(_link_LIBRARIES + ${PYTHON_LIBRARIES} + YACSRuntimeSALOME + ) + +SET(_py2yacs_sources + py2yacs.cxx + ) + +SET(_py2yacs_headers + py2yacs.hxx + ) + +ADD_LIBRARY(py2yacslib ${_py2yacs_sources}) +TARGET_LINK_LIBRARIES(py2yacslib ${_link_LIBRARIES}) + +INSTALL(TARGETS py2yacslib EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS}) +INSTALL(FILES ${_py2yacs_headers} DESTINATION ${SALOME_INSTALL_HEADERS}) + +SET( _py_SCRIPTS + py2yacs.py + ) + +SALOME_INSTALL_SCRIPTS("${_py_SCRIPTS}" ${SALOME_INSTALL_PYTHON}) diff --git a/src/py2yacs/Test/CMakeLists.txt b/src/py2yacs/Test/CMakeLists.txt new file mode 100644 index 000000000..ad8303cf8 --- /dev/null +++ b/src/py2yacs/Test/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2012-2016 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +INCLUDE_DIRECTORIES( + ${CPPUNIT_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +SET(_link_LIBRARIES + ${CPPUNIT_LIBRARIES} + py2yacslib +) + +SET(Py2yacsTest_SOURCES + TestPy2yacs.cxx + Py2yacsTest.cxx + Py2yacsTest.hxx +) + +ADD_EXECUTABLE(TestPy2yacs ${Py2yacsTest_SOURCES}) +TARGET_LINK_LIBRARIES(TestPy2yacs ${_link_LIBRARIES}) + +ADD_EXECUTABLE(test_py2yacs test_py2yacs.cxx) +TARGET_LINK_LIBRARIES(test_py2yacs py2yacslib) + +# For salome test +IF(NOT WIN32) + ADD_TEST(TestPy2yacs TestPy2yacs) + SET_TESTS_PROPERTIES(TestPy2yacs PROPERTIES + ENVIRONMENT "PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_CURRENT_SOURCE_DIR}/..:$ENV{PYTHONPATH}" + ) + + SET(LOCAL_TEST_DIR ${SALOME_YACS_INSTALL_TEST}/py2yacs) + SET(LOCAL_TEST_FILES + bad_parsers.py + err_py2yacs_invalid.py + ) + INSTALL(FILES ${LOCAL_TEST_FILES} + DESTINATION ${LOCAL_TEST_DIR}) + INSTALL(FILES CTestTestfileInstall.cmake + DESTINATION ${LOCAL_TEST_DIR} + RENAME CTestTestfile.cmake) + INSTALL(TARGETS TestPy2yacs test_py2yacs + DESTINATION ${LOCAL_TEST_DIR}) + +ENDIF() \ No newline at end of file diff --git a/src/py2yacs/Test/CTestTestfileInstall.cmake b/src/py2yacs/Test/CTestTestfileInstall.cmake new file mode 100644 index 000000000..808eefd43 --- /dev/null +++ b/src/py2yacs/Test/CTestTestfileInstall.cmake @@ -0,0 +1,28 @@ +# Copyright (C) 2012-2016 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +IF(NOT WIN32) + SET(TEST_NAME ${COMPONENT_NAME}_Py2YacsTest) + # No need of a salome session for this test + ADD_TEST(${TEST_NAME} TestPy2yacs) + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES + LABELS "${COMPONENT_NAME}" + ) + +ENDIF() diff --git a/src/py2yacs/Test/Py2yacsTest.cxx b/src/py2yacs/Test/Py2yacsTest.cxx new file mode 100644 index 000000000..8572bd3ff --- /dev/null +++ b/src/py2yacs/Test/Py2yacsTest.cxx @@ -0,0 +1,344 @@ +// Copyright (C) 2006-2016 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#include +#include +#include +#include + +#include "Py2yacsTest.hxx" +#include "py2yacs.hxx" +#include "Proc.hxx" +#include "Executor.hxx" + +#include "RuntimeSALOME.hxx" +#include "PythonPorts.hxx" +#include "InputPort.hxx" +//#include "parsers.hxx" + +void Py2yacsTest::setUp() +{ + YACS::ENGINE::RuntimeSALOME::setRuntime(); + //YACS::ENGINE::getRuntime()->init(); +} + +void Py2yacsTest::tearDown() +{ + //YACS::ENGINE::getRuntime()->fini(); +} + +static +void verifyCorrectPycode(const char * code_py, const char * function_name, + int nbInputPorts, const char** input_ports, + int nbOutputPorts, const char** output_ports) +{ + std::cerr << std::endl; + std::cerr << "-----------------------------------------------" << std::endl; + std::cerr << code_py << std::endl; + std::cerr << "-----------------------------------------------" << std::endl; + std::cerr << "Function :" << function_name << std::endl; + Py2yacs parser; + try + { + parser.load(code_py); + } + catch(Py2yacsException& e) + { + CPPUNIT_FAIL(e.what()); + } + catch(...) + { + CPPUNIT_FAIL("Unknown exception"); + } + YACS::ENGINE::Proc* p = parser.createProc(function_name); + CPPUNIT_ASSERT( p != NULL); + YACS::ENGINE::Node* n = p->getChildByShortName("default_name"); + CPPUNIT_ASSERT( n != NULL); + CPPUNIT_ASSERT( n->getNumberOfInputPorts() == nbInputPorts); + for(int i = 0; i < nbInputPorts; i++) + CPPUNIT_ASSERT( n->getInputPort(input_ports[i]) != NULL); + CPPUNIT_ASSERT( n->getNumberOfOutputPorts() == nbOutputPorts); + for(int i = 0; i < nbOutputPorts; i++) + CPPUNIT_ASSERT( n->getOutputPort(output_ports[i]) != NULL); + delete p; +} + +static +void verifyWrongPycode(const char* code_py, const char* function_name, + const char* error_message) +{ + Py2yacs parser; + try + { + parser.load(code_py); + YACS::ENGINE::Proc* p = parser.createProc(function_name); + CPPUNIT_FAIL("Exception expected and no exception occured."); + } + catch(Py2yacsException& e) + { + std::string what = e.what(); + std::cerr << std::endl; + std::cerr << "-----------------------------------------------" << std::endl; + std::cerr << code_py << std::endl; + std::cerr << "-----------------------------------------------" << std::endl; + std::cerr << "Function :" << function_name << std::endl; + std::cerr << "==========EXCEPTION TEXT=======================" << std::endl; + std::cerr << what << std::endl; + std::cerr << "===============================================" << std::endl; + CPPUNIT_ASSERT(what.find(error_message) != std::string::npos); + } +} + +static +void verifyWrongParser(const char* parser_module, const char* parser_function, + const char* error_message) +{ + const char* code_py = "def f():\n" + " return\n"; + Py2yacs parser(parser_module, parser_function); + try + { + parser.load(code_py); + CPPUNIT_FAIL("Exception expected and no exception occured."); + } + catch(Py2yacsException& e) + { + std::string what = e.what(); + std::cerr << std::endl; + std::cerr << "==========EXCEPTION TEXT=======================" << std::endl; + std::cerr << what << std::endl; + std::cerr << "===============================================" << std::endl; + CPPUNIT_ASSERT(what.find(error_message) != std::string::npos); + } +} + +void Py2yacsTest::t1() +{ + const char * code_py = "def f1(a, b, c):\n" + " y = a*b + c\n" + " return y\n"; + const char* input_ports[] = {"a", "b", "c"}; + const char* output_ports[] = {"y"}; + verifyCorrectPycode(code_py, "f1", 3, input_ports, 1, output_ports); +} + +void Py2yacsTest::t2() +{ + const char * code_py = "def f1(a, b, c):\n" + " x = a + b + c\n" + " y = a*b + c\n" + " z = a*b*c\n" + " return x, y, z\n"; + const char* input_ports[] = {"a", "b", "c"}; + const char* output_ports[] = {"x", "y", "z"}; + verifyCorrectPycode(code_py, "f1", 3, input_ports, 3, output_ports); +} + +void Py2yacsTest::t3() +{ + const char * code_py = "def f1(a, b, c):\n" + " print a\n" + " print b\n" + " print c\n"; + const char* input_ports[] = {"a", "b", "c"}; + const char* output_ports[] = {}; + verifyCorrectPycode(code_py, "f1", 3, input_ports, 0, output_ports); +} + +void Py2yacsTest::t4() +{ + const char * code_py = "def f1():\n" + " print 'toto'\n" + " return\n"; + const char* input_ports[] = {"a", "b", "c"}; + const char* output_ports[] = {}; + verifyCorrectPycode(code_py, "f1", 0, input_ports, 0, output_ports); +} + +void Py2yacsTest::t5() +{ + const char * code_py = "def f1(a):\n" + " x = -a\n" + " if a > 0:\n" + " return a\n" + " else:\n" + " return x\n" + "class ignoremoi:\n" + " def ignoreF(t):\n" + " return t+1\n" + "def f2(v):\n" + " ret = f1(v)\n" + " return ret\n"; + const char* input_ports[] = {"v"}; + const char* output_ports[] = {"ret"}; + verifyCorrectPycode(code_py, "f2", 1, input_ports, 1, output_ports); +} + +void Py2yacsTest::no_multiple_returns() +{ + const char * code_py = "def f(a):\n" + " x = -a\n" + " if a > 0:\n" + " return a\n" + " else:\n" + " return x\n"; + verifyWrongPycode(code_py, "f", "multiple returns."); +} + +void Py2yacsTest::unaccepted_statement() +{ + const char * code_py = "def f(a):\n" + " return a\n" + "x=5\n"; + verifyWrongPycode(code_py, "f", "not accepted statement"); +} + +void Py2yacsTest::unaccepted_statement2() +{ + const char * code_py = "def f(a):\n" + " return a\n" + "if __name__ == '__main__':" + " print 'toto'\n"; + verifyWrongPycode(code_py, "f", "not accepted statement"); +} + +void Py2yacsTest::unaccepted_return() +{ + const char * code_py = "def f(a):\n" + " return 7\n"; + verifyWrongPycode(code_py, "f", "invalid type returned"); +} + +void Py2yacsTest::unaccepted_return2() +{ + const char * code_py = "def f1(a):\n" + " return 7\n" + "def f2(x):\n" + " return f1(x)\n"; + verifyWrongPycode(code_py, "f2", "invalid type returned"); +} + +void Py2yacsTest::syntaxError() +{ + const char * code_py = "bla bla bla\n" + " return f1(x)\n"; + verifyWrongPycode(code_py, "f2", "SyntaxError"); +} + +void Py2yacsTest::badFunctionName() +{ + const char * code_py = "def f1(a):\n" + " x=7\n" + " return x\n" + "def f2(x):\n" + " y=8\n" + " return y\n"; + verifyWrongPycode(code_py, "nonexistant", "Function not found:"); +} + +void Py2yacsTest::schemaExec() +{ + const char * code_py = "def f(a):\n" + " x = a\n" + " return x\n"; + Py2yacs parser; + try + { + parser.load(code_py); + } + catch(Py2yacsException& e) + { + CPPUNIT_FAIL(e.what()); + } + catch(...) + { + CPPUNIT_FAIL("Unknown exception"); + } + YACS::ENGINE::Proc* p = parser.createProc("f"); + CPPUNIT_ASSERT( p != NULL); + YACS::ENGINE::Node* n = p->getChildByShortName("default_name"); + CPPUNIT_ASSERT( n != NULL); + CPPUNIT_ASSERT( n->getInputPort("a") != NULL); + CPPUNIT_ASSERT( n->getOutputPort("x") != NULL); + // run the schema + n->getInputPort("a")->edInit(42.); + YACS::ENGINE::Executor executor; + executor.RunW(p, 0); + // verify the output port value + YACS::ENGINE::OutputPyPort* var = dynamic_cast(n->getOutputPort("x")); + CPPUNIT_ASSERT(var); + PyObject* pyVal = var->get(); + CPPUNIT_ASSERT(pyVal); + CPPUNIT_ASSERT(PyFloat_Check(pyVal)); + CPPUNIT_ASSERT_DOUBLES_EQUAL(42., PyFloat_AsDouble(pyVal), 1.E-12); +} + +// Test the behaviour when there is an error in the python parser +void Py2yacsTest::parserErrors() +{ + verifyWrongParser("bad_parsers", "p1", "argument"); + verifyWrongParser("bad_parsers", "p2", "Attribute 'name' not found"); + verifyWrongParser("bad_parsers", "p3", "should be a python list"); + verifyWrongParser("bad_parsers", "p4", "Traceback"); + verifyWrongParser("bad_parsers", "f", "Cannot find the parsing function"); + verifyWrongParser("err_py2yacs_invalid", "get_properties", "invalid syntax"); + verifyWrongParser("no_file", "f", "Failed to load"); + verifyWrongParser("bad_parsers", "p5", " "); + verifyWrongParser("bad_parsers", "p6", " "); + verifyWrongParser("bad_parsers", "p7", " "); + verifyWrongParser("bad_parsers", "p8", "Attribute 'name' should be a string."); + verifyWrongParser("bad_parsers", "p9", " "); + verifyWrongParser("bad_parsers", "p10", " "); +} + +void Py2yacsTest::globalVerification() +{ + std::ifstream file_stream("exemple_py2yacs.py"); + if(!file_stream) + return; + + std::stringstream buffer; + buffer << file_stream.rdbuf(); + Py2yacs parser; + parser.load(buffer.str()); + std::list::const_iterator it_fp; + const std::list& functions = parser.getFunctionProperties(); + for(it_fp=functions.begin();it_fp!=functions.end();it_fp++) + { + std::cerr << "Function :" << it_fp->_name << std::endl; + std::list::const_iterator it; + std::cerr << "Input ports :" ; + for(it=it_fp->_input_ports.begin();it!=it_fp->_input_ports.end();it++) + std::cerr << *it << ","; + std::cerr << std::endl; + std::cerr << "Output ports :" ; + for(it=it_fp->_output_ports.begin();it!=it_fp->_output_ports.end();it++) + std::cerr << *it << ","; + std::cerr << std::endl; + std::cerr << "Imports :" ; + for(it=it_fp->_imports.begin();it!=it_fp->_imports.end();it++) + std::cerr << *it << ","; + std::cerr << std::endl; + std::cerr << "Errors :" ; + for(it=it_fp->_errors.begin();it!=it_fp->_errors.end();it++) + std::cerr << *it << std::endl; + std::cerr << std::endl; + std::cerr << "-----------------------------------------" << std::endl; + } + +} \ No newline at end of file diff --git a/src/py2yacs/Test/Py2yacsTest.hxx b/src/py2yacs/Test/Py2yacsTest.hxx new file mode 100644 index 000000000..e328c88ba --- /dev/null +++ b/src/py2yacs/Test/Py2yacsTest.hxx @@ -0,0 +1,65 @@ +// Copyright (C) 2006-2016 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#ifndef _Py2yacsTest_hxx_ +#define _Py2yacsTest_hxx_ + +#include + +class Py2yacsTest: public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(Py2yacsTest); + CPPUNIT_TEST(t1); + CPPUNIT_TEST(t2); + CPPUNIT_TEST(t3); + CPPUNIT_TEST(t4); + CPPUNIT_TEST(t5); + CPPUNIT_TEST(no_multiple_returns); + CPPUNIT_TEST(unaccepted_statement); + CPPUNIT_TEST(unaccepted_statement2); + CPPUNIT_TEST(unaccepted_return); + CPPUNIT_TEST(unaccepted_return2); + CPPUNIT_TEST(syntaxError); + CPPUNIT_TEST(badFunctionName); + CPPUNIT_TEST(schemaExec); + CPPUNIT_TEST(parserErrors); + CPPUNIT_TEST(globalVerification); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp(); + void tearDown(); + + void t1(); + void t2(); + void t3(); + void t4(); + void t5(); + void no_multiple_returns(); + void unaccepted_statement(); + void unaccepted_statement2(); + void unaccepted_return(); + void unaccepted_return2(); + void syntaxError(); + void badFunctionName(); + void schemaExec(); + void parserErrors(); + void globalVerification(); +}; + +#endif \ No newline at end of file diff --git a/src/py2yacs/Test/TestPy2yacs.cxx b/src/py2yacs/Test/TestPy2yacs.cxx new file mode 100644 index 000000000..6b2028966 --- /dev/null +++ b/src/py2yacs/Test/TestPy2yacs.cxx @@ -0,0 +1,24 @@ +// Copyright (C) 2006-2016 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#include "Py2yacsTest.hxx" +#define UNIT_TEST_HEADER " --- TEST Py2yacsTest" + +CPPUNIT_TEST_SUITE_REGISTRATION( Py2yacsTest ); + +#include "BasicMainTest.hxx" diff --git a/src/py2yacs/Test/bad_parsers.py b/src/py2yacs/Test/bad_parsers.py new file mode 100644 index 000000000..ed18c1a55 --- /dev/null +++ b/src/py2yacs/Test/bad_parsers.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +# +# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# +def p1(): + return ["a"], ["b"] + +def p2(x): + return ["a"], ["b"] + +def p3(x): + return 5, 6 + +def p4(a): + x= a / 0 + return ["a"], ["b"] + +class FunctionProperties: + def __init__(self, function_name): + self.name = function_name + self.inputs=[] + self.outputs=None + self.errors=[] + self.imports=[] + pass + +def p5(f): + fp = FunctionProperties("boo") + fp.inputs=["a", 5] + return [fp], ["a", "b", "c"] + +def p6(f): + fp = FunctionProperties("boo") + fp.outputs=[7, 5] + return [fp], ["a", "b", "c"] + +def p7(f): + fp = FunctionProperties("boo") + fp.errors=[7, 5] + return [fp], ["a", "b", "c"] + +def p8(f): + fp = FunctionProperties("boo") + fp.name=[5] + return [fp], ["a", "b", "c"] + +def p9(f): + fp = FunctionProperties("boo") + return [fp], "a" + +def p10(f): + fp = FunctionProperties("boo") + return [fp], ["a", fp, "c"] + diff --git a/src/py2yacs/Test/err_py2yacs_invalid.py b/src/py2yacs/Test/err_py2yacs_invalid.py new file mode 100644 index 000000000..046bdf6e2 --- /dev/null +++ b/src/py2yacs/Test/err_py2yacs_invalid.py @@ -0,0 +1,5 @@ +invalid python file +{ +} +x += 4; +return \ No newline at end of file diff --git a/src/py2yacs/Test/test_py2yacs.cxx b/src/py2yacs/Test/test_py2yacs.cxx new file mode 100644 index 000000000..08aae0d38 --- /dev/null +++ b/src/py2yacs/Test/test_py2yacs.cxx @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include "py2yacs.hxx" +#include "RuntimeSALOME.hxx" + +/* +#include "Proc.hxx" +#include "Node.hxx" +#include "CORBAPorts.hxx" +#include "InputPort.hxx" +#include "Executor.hxx" +*/ + +int main(int argc, char *argv[]) +{ + if (argc != 4) + { + std::cerr << "Usage: " << argv[0] << " pythonfile funcname outfile" << std::endl; + return 1; + } + YACS::ENGINE::RuntimeSALOME::setRuntime(); + //Py_Initialize(); + Py2yacs parser; + std::ifstream file_stream(argv[1]); + if(!file_stream) + { + std::cerr << "Error when opening file " << argv[1] + << ": " << strerror(errno) + << std::endl; + return 1; + } + std::stringstream buffer; + buffer << file_stream.rdbuf(); + try + { + parser.load(buffer.str()); + parser.save(argv[3], argv[2]); + + /* + YACS::ENGINE::Proc* p = parser.createProc(argv[2]); + //p->setInPortValue("Schema.default_name", "a", "42."); + YACS::ENGINE::Node* n = p->getChildByShortName("default_name"); + //YACS::ENGINE::Node* n = p->nodeMap["default_name"]; + if(!n) + std::cerr << "Node not found." << std::endl; + else + n->getInputPort("a")->edInit(42.); + p->saveSchema(argv[3]); + + YACS::ENGINE::Executor executor; + executor.RunW(p, 0); + n->getOutputPort("x")->dump(); + */ + } + catch(Py2yacsException& e) + { + std::cerr << e.what() << std::endl; + } + YACS::ENGINE::getRuntime()->fini(); + //Py_Finalize(); +} \ No newline at end of file diff --git a/src/py2yacs/py2yacs.cxx b/src/py2yacs/py2yacs.cxx new file mode 100644 index 000000000..9e3644006 --- /dev/null +++ b/src/py2yacs/py2yacs.cxx @@ -0,0 +1,379 @@ +// Copyright (C) 2006-2016 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#include +#include +#include "py2yacs.hxx" +#include "RuntimeSALOME.hxx" +#include "Proc.hxx" +#include "InlineNode.hxx" +#include "AutoGIL.hxx" + +Py2yacsException::Py2yacsException(const std::string& what) +: std::exception(), + _what(what) +{ +} + +Py2yacsException::~Py2yacsException()throw () +{ +} + +const char * Py2yacsException::what() const throw () +{ + return _what.c_str(); +} + + +Py2yacs::Py2yacs() +: _python_parser_module("py2yacs"), + _python_parser_function("get_properties"), + _functions(), + _global_errors(), + _python_code() +{ +} + +Py2yacs::Py2yacs(const std::string& python_parser_module, + const std::string& python_parser_function) +: _python_parser_module(python_parser_module), + _python_parser_function(python_parser_function), + _functions(), + _global_errors(), + _python_code() +{ +} + +const std::list& Py2yacs::getGlobalErrors() const +{ + return _global_errors; +} + +const std::list& Py2yacs::getFunctionProperties()const +{ + return _functions; +} + +// Copy a python list of strings to a c++ list of strings. +// Return an error string. An empty string means no error. +static +std::string copyList(PyObject *pyList, std::list& cppList) +{ + std::string error; + if(not PyList_Check(pyList)) + { + error = "Not a python list.\n"; + //throw Py2yacsException("Not a python list."); + } + else + { + int n = PyList_Size(pyList); + for(int i=0; isaveSchema(file_path); + delete schema; +} + +YACS::ENGINE::Proc* Py2yacs::createProc(const std::string& python_function)const +{ + if(not _global_errors.empty()) + { + std::string error_message = "The python script contains errors.\n"; + std::list::const_iterator it; + for(it = _global_errors.begin(); it != _global_errors.end(); it++) + error_message += (*it) + "\n"; + throw Py2yacsException(error_message); + } + + // find function properties + std::list::const_iterator fn_prop = _functions.begin(); + while(fn_prop != _functions.end() and fn_prop->_name != python_function) + fn_prop++; + + if(fn_prop == _functions.end()) + { + throw Py2yacsException(std::string("Function not found: ")+python_function); + } + + if(not fn_prop->_errors.empty()) + { + std::string error_message = "Function contains errors.\n"; + std::list::const_iterator it; + for(it = fn_prop->_errors.begin(); it != fn_prop->_errors.end(); it++) + error_message += (*it) + "\n"; + throw Py2yacsException(error_message); + } + + // add the call to the function at the end of the script + std::stringstream fn_call; + fn_call << std::endl; + std::list::const_iterator it; + bool first = true; + for(it=fn_prop->_output_ports.begin(); + it!=fn_prop->_output_ports.end(); + it++) + { + if (not first) + fn_call << ","; + first = false; + fn_call << *it; + } + fn_call << "=" << python_function << "("; + first = true; + for(it = fn_prop->_input_ports.begin(); + it != fn_prop->_input_ports.end(); + it++) + { + if (not first) + fn_call << ","; + first = false; + fn_call << *it; + } + fn_call << ")" << std::endl; + std::string node_body = _python_code + fn_call.str(); + + YACS::ENGINE::Proc* schema; + YACS::ENGINE::RuntimeSALOME::setRuntime(); + YACS::ENGINE::RuntimeSALOME* runtime = YACS::ENGINE::getSALOMERuntime(); + + // build the YACS schema + const char * node_name = "default_name"; + schema = runtime->createProc("Schema"); + YACS::ENGINE::InlineNode* node = runtime->createScriptNode("", node_name); + schema->edAddChild(node); + node->setScript(node_body); + YACS::ENGINE::TypeCode *tc_double = runtime->getTypeCode("double"); + + for(it = fn_prop->_input_ports.begin(); + it != fn_prop->_input_ports.end(); + it++) + node->edAddInputPort(*it, tc_double); + + for(it = fn_prop->_output_ports.begin(); + it != fn_prop->_output_ports.end(); + it++) + node->edAddOutputPort(*it, tc_double); + + return schema; +} \ No newline at end of file diff --git a/src/py2yacs/py2yacs.hxx b/src/py2yacs/py2yacs.hxx new file mode 100644 index 000000000..b0ca9c409 --- /dev/null +++ b/src/py2yacs/py2yacs.hxx @@ -0,0 +1,107 @@ +// Copyright (C) 2006-2016 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +#ifndef _PY2YACS_H_ +#define _PY2YACS_H_ + +#include +#include +#include + +class Py2yacsException: std::exception +{ + public: + Py2yacsException(const std::string& what); + virtual ~Py2yacsException()throw (); + virtual const char *what() const throw (); + private: + std::string _what; +}; + +namespace YACS +{ + namespace ENGINE + { + class Proc; + }; +}; + +struct FunctionProperties +{ + public: + std::string _name; + std::list _input_ports; + std::list _output_ports; + std::list _errors; + std::list _imports; +}; + +/*! \brief Converter of a python script to a yacs schema. + * This class converts a string containing a python script to a yacs schema + * containing a python script node. + */ +class Py2yacs +{ + public: + Py2yacs(); + + /*! + * This constructor can be used if you want to define your own python parser. + * The parser function should be a python function and return a tuple of + * two lists. + * The first list contains the properties of all the functions in the script + * and the second one contains global errors. + * \param python_parser_module: name of the parser module + * \param python_parser_function: name of the parser function + */ + Py2yacs(const std::string& python_parser_module, + const std::string& python_parser_function); + + /*! + * \param python_code: contains the python code that will be converted + * to a yacs schema. + */ + void load(const std::string& python_code); + + /*! + * \param file_path: path to the xml file where to save the yacs schema. + * \param python_function: function defined in the python code that will be + * called in the yacs node. + */ + void save(const std::string& file_path, + const std::string& python_function)const; + + /*! + * A new schema is created. + * \param python_function: function defined in the python code that will be + * called in the yacs node. + */ + YACS::ENGINE::Proc* createProc(const std::string& python_function)const; + + const std::list& getGlobalErrors() const; + const std::list& getFunctionProperties()const; + + private: + std::string _python_parser_module; + std::string _python_parser_function; + std::list _functions; + std::list _global_errors; + std::string _python_code; +}; + +#endif //_PY2YACS_H_ \ No newline at end of file diff --git a/src/py2yacs/py2yacs.py b/src/py2yacs/py2yacs.py new file mode 100644 index 000000000..84ac997f0 --- /dev/null +++ b/src/py2yacs/py2yacs.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# -*- coding: utf-8 *- +# Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +# +# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# +import ast + +class FunctionProperties: + def __init__(self, function_name): + self.name = function_name + self.inputs=[] + self.outputs=None + self.errors=[] + self.imports=[] + pass + def __str__(self): + result = "Function:" + self.name + "\n" + result+= " Inputs:" + str(self.inputs) + "\n" + result+= " Outputs:"+ str(self.outputs) + "\n" + result+= " Errors:" + str(self.errors) + "\n" + result+= " Imports:"+ str(self.imports) + "\n" + return result + +class v(ast.NodeVisitor): + def visit_Module(self, node): + #print type(node).__name__, ":" + accepted_tokens = ["Import", "ImportFrom", "FunctionDef", "ClassDef"] + #print "module body:" + self.global_errors=[] + for e in node.body: + type_name = type(e).__name__ + if type_name not in accepted_tokens: + error="py2yacs error at line %s: not accepted statement '%s'." % ( + e.lineno, type_name) + self.global_errors.append(error) + #print type_name + #print "------------------------------------------------------------------" + self.functions=[] + self.lastfn="" + self.infunc=False + self.inargs=False + self.generic_visit(node) + pass + def visit_FunctionDef(self, node): + #print type(node).__name__, ":", node.name + if not self.infunc: + self.lastfn = FunctionProperties(node.name) + self.functions.append(self.lastfn) + self.infunc=True + # + self.generic_visit(node) + # + self.lastfn = None + self.infunc=False + pass + def visit_arguments(self, node): + #print type(node).__name__, ":" + self.inargs=True + self.generic_visit(node) + self.inargs=False + pass + def visit_Name(self, node): + if self.inargs : + #print type(node).__name__, ":", node.id + self.lastname=node.id + self.generic_visit(node) + pass + def visit_Param(self, node): + #print type(node).__name__, ":", self.lastname + self.lastfn.inputs.append(self.lastname) + pass + def visit_Return(self, node): + #print type(node).__name__, ":", node.value + if self.lastfn.outputs is not None : + error="py2yacs error at line %s: multiple returns." % node.lineno + self.lastfn.errors.append(error) + return + self.lastfn.outputs = [] + if node.value is None : + pass + elif 'Tuple' == type(node.value).__name__ : + for e in node.value.elts: + if 'Name' == type(e).__name__ : + self.lastfn.outputs.append(e.id) + else : + error="py2yacs error at line %s: invalid type returned '%s'." % ( + node.lineno, type(e).__name__) + self.lastfn.errors.append(error) + else: + if 'Name' == type(node.value).__name__ : + self.lastfn.outputs.append(node.value.id) + else : + error="py2yacs error at line %s: invalid type returned '%s'." %( + node.lineno, type(node.value).__name__) + self.lastfn.errors.append(error) + pass + pass + pass + + def visit_ClassDef(self, node): + # just ignore classes + pass + + def visit_Import(self, node): + if self.infunc: + for n in node.names: + self.lastfn.imports.append(n.name) + def visit_ImportFrom(self, node): + if self.infunc: + m=node.module + for n in node.names: + self.lastfn.imports.append(m+"."+n.name) + +class vtest(ast.NodeVisitor): + def generic_visit(self, node): + #print type(node).__name__ + ast.NodeVisitor.generic_visit(self, node) + +def create_yacs_schema(text, fn_name, fn_args, fn_returns, file_name): + import pilot + import SALOMERuntime + #import loader + SALOMERuntime.RuntimeSALOME_setRuntime() + runtime = pilot.getRuntime() + schema = runtime.createProc("schema") + node = runtime.createFuncNode("", "default_name") + schema.edAddChild(node) + fncall = "\n%s=%s(%s)\n"%(",".join(fn_returns), + fn_name, + ",".join(fn_args)) + node.setScript(text+fncall) + node.setFname(fn_name) + td=schema.getTypeCode("double") + for p in fn_args: + node.edAddInputPort(p, td) + for p in fn_returns: + node.edAddOutputPort(p, td) + schema.saveSchema(file_name) + +def get_properties(text_file): + bt=ast.parse(text_file) + w=v() + w.visit(bt) + return w.functions, w.global_errors + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser(description="Generate a YACS schema from a python file containing a function to run.") + parser.add_argument("file", help='Path to the python file') + parser.add_argument("-o","--output", + help='Path to the output file (yacs_schema.xml by default)', + default='yacs_schema.xml') + parser.add_argument("-d","--def_name", + help='Name of the function to call in the yacs node (_exec by default)', + default='_exec') + args = parser.parse_args() + with open(args.file, 'r') as f: + text_file = f.read() + #bt=ast.parse(text_file) + #w=vtest() + #w=v() + #w.visit(bt) + #print "global errors:", w.global_errors + #for f in w.functions: + # print f + + fn_name = args.def_name + functions,errors = get_properties(text_file) + print "global errors:", errors + for f in functions: + print f + + fn_properties = next((f for f in functions if f.name == fn_name), None) + if fn_properties is not None : + if not fn_properties.errors : + create_yacs_schema(text_file, fn_name, + fn_properties.inputs, fn_properties.outputs, + args.output) + else: + print "\n".join(fn_properties.errors) + else: + print "Function not found:", fn_name + \ No newline at end of file