Salome HOME
Add py2yacs omu/py2yacs
authorOvidiu Mircescu <ovidiu.mircescu@edf.fr>
Fri, 27 Jan 2017 13:58:59 +0000 (14:58 +0100)
committerOvidiu Mircescu <ovidiu.mircescu@edf.fr>
Fri, 27 Jan 2017 13:58:59 +0000 (14:58 +0100)
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'.

14 files changed:
src/CMakeLists.txt
src/CTestTestfileInstall.cmake
src/py2yacs/CMakeLists.txt [new file with mode: 0644]
src/py2yacs/Test/CMakeLists.txt [new file with mode: 0644]
src/py2yacs/Test/CTestTestfileInstall.cmake [new file with mode: 0644]
src/py2yacs/Test/Py2yacsTest.cxx [new file with mode: 0644]
src/py2yacs/Test/Py2yacsTest.hxx [new file with mode: 0644]
src/py2yacs/Test/TestPy2yacs.cxx [new file with mode: 0644]
src/py2yacs/Test/bad_parsers.py [new file with mode: 0644]
src/py2yacs/Test/err_py2yacs_invalid.py [new file with mode: 0644]
src/py2yacs/Test/test_py2yacs.cxx [new file with mode: 0644]
src/py2yacs/py2yacs.cxx [new file with mode: 0644]
src/py2yacs/py2yacs.hxx [new file with mode: 0644]
src/py2yacs/py2yacs.py [new file with mode: 0644]

index 064805212e5407d8cdda0c71837a46bb570d7331..af8928c9fb58d7e84be992132b89f0f2406d45e5 100755 (executable)
@@ -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)
 
 ##
index 71e7e4c8b7f725bde7362914c06d57ef24fc277d..4d5dd69d23bb8f32dd6035e4d06b6b43dcd488bf 100644 (file)
@@ -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 (file)
index 0000000..d5c4ce3
--- /dev/null
@@ -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 (file)
index 0000000..ad8303c
--- /dev/null
@@ -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 (file)
index 0000000..808eefd
--- /dev/null
@@ -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 (file)
index 0000000..8572bd3
--- /dev/null
@@ -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 <Python.h>
+#include <fstream>
+#include <string>
+#include <sstream>
+
+#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<YACS::ENGINE::OutputPyPort*>(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<FunctionProperties>::const_iterator it_fp;
+  const std::list<FunctionProperties>& functions = parser.getFunctionProperties();
+  for(it_fp=functions.begin();it_fp!=functions.end();it_fp++)
+  {
+    std::cerr << "Function :" << it_fp->_name << std::endl;
+    std::list<std::string>::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 (file)
index 0000000..e328c88
--- /dev/null
@@ -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 <cppunit/extensions/HelperMacros.h>
+
+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 (file)
index 0000000..6b20289
--- /dev/null
@@ -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 (file)
index 0000000..ed18c1a
--- /dev/null
@@ -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 (file)
index 0000000..046bdf6
--- /dev/null
@@ -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 (file)
index 0000000..08aae0d
--- /dev/null
@@ -0,0 +1,63 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <cerrno>
+#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 (file)
index 0000000..9e36440
--- /dev/null
@@ -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 <Python.h>
+#include <sstream>
+#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<std::string>& Py2yacs::getGlobalErrors() const
+{
+  return _global_errors;
+}
+
+const std::list<FunctionProperties>& 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<std::string>& 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; i<n; i++)
+    {
+      PyObject *elem = PyList_GetItem(pyList,i);
+      if(not PyString_Check(elem))
+      {
+        std::stringstream message;
+        message << "List element number " << i << " is not a string.\n";
+        error += message.str();
+        // throw Py2yacsException(message.str());
+      }
+      else
+      {
+        const char * portName = PyString_AsString(elem);
+        cppList.push_back(portName);
+      }
+    }
+  }
+  return error;
+}
+
+static
+std::string getPyErrorText()
+{
+  std::string result="";
+  if (PyErr_Occurred())
+  {
+    PyObject *ptype, *pvalue, *ptraceback;
+    PyObject *pystr, *module_name, *pyth_module, *pyth_func;
+    PyErr_Fetch(&ptype, &pvalue, &ptraceback);
+    pystr = PyObject_Str(pvalue);
+    result = PyString_AsString(pystr);
+    result += "\n";
+    Py_DECREF(pystr);
+    
+    /* See if we can get a full traceback */
+    if(ptraceback)
+    {
+      module_name = PyString_FromString("traceback");
+      pyth_module = PyImport_Import(module_name);
+      Py_DECREF(module_name);
+      if (pyth_module)
+      {
+        pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
+        if (pyth_func && PyCallable_Check(pyth_func))
+        {
+          PyObject *pyList;
+          pyList = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
+          if(pyList)
+          {
+            int n = PyList_Size(pyList);
+            for(int i=0; i<n; i++)
+            {
+              pystr = PyList_GetItem(pyList,i);
+              result += PyString_AsString(pystr);
+            }
+            Py_DECREF(pyList);
+          }
+        }
+        Py_XDECREF(pyth_func);
+        Py_DECREF(pyth_module);
+      }
+    }
+    Py_XDECREF(ptype);
+    Py_XDECREF(pvalue);
+    Py_XDECREF(ptraceback);
+  }
+  return result;
+}
+
+static
+PyObject* checkAndGetAttribute(PyObject *p,
+                               const char* attribute,
+                               std::string& error)
+{
+  PyObject *pAttribute = PyObject_GetAttrString(p, attribute);
+  if(not pAttribute)
+  {
+    error += "Attribute '";
+    error += attribute;
+    error += "' not found in the returned value of the parsing function.\n";
+    error += getPyErrorText();
+  }
+  return pAttribute;
+}
+
+void Py2yacs::load(const std::string& python_code)
+{
+    PyObject *pModule, *pDict, *pFunc;
+    PyObject *pArgs, *pValue;
+    int i;
+    std::string errorMessage="";
+    _python_code = python_code;
+    _functions.clear();
+    _global_errors.clear();
+    
+    // Py_Initialize();
+    YACS::ENGINE::AutoGIL agil;
+    pValue = PyString_FromString(_python_parser_module.c_str());
+    pModule = PyImport_Import(pValue);
+    Py_DECREF(pValue);
+
+    if (not pModule)
+    {
+      errorMessage  = getPyErrorText();
+      errorMessage += "\nFailed to load ";
+      errorMessage += _python_parser_module;
+      errorMessage += ".\n";
+    }
+    else
+    {
+      pFunc = PyObject_GetAttrString(pModule, _python_parser_function.c_str());
+
+      if (pFunc && PyCallable_Check(pFunc))
+      {
+        pArgs = PyTuple_New(1);
+        pValue = PyString_FromString(python_code.c_str());
+        PyTuple_SetItem(pArgs, 0, pValue);
+        
+        pValue = PyObject_CallObject(pFunc, pArgs);
+        Py_DECREF(pArgs);
+        if (not pValue)
+            errorMessage = getPyErrorText();
+        else
+        {
+          if (not PyTuple_Check(pValue))
+          {
+            errorMessage += "Parsing function should return a tuple of two string lists.\n";
+          }
+          int n = PyTuple_Size(pValue);
+          if(n != 2)
+          {
+            errorMessage += "Parsing function should return two string lists.\n";
+          }
+          PyObject *pyList = PyTuple_GetItem(pValue, 0);
+          if(not PyList_Check(pyList))
+          {
+            errorMessage += "The first returned value of the parsing function"
+                            " should be a python list.\n";
+          }
+          else
+          {
+            n = PyList_Size(pyList);
+            for(int i=0; i<n; i++)
+            {
+              PyObject *fpy = PyList_GetItem(pyList,i);
+              PyObject *pAttribute;
+              
+              if(pAttribute = checkAndGetAttribute(fpy, "name", errorMessage))
+              {
+                if(not PyString_Check(pAttribute))
+                {
+                  errorMessage += "Attribute 'name' should be a string.\n";
+                  Py_DECREF(pAttribute);
+                }
+                else
+                {
+                  _functions.push_back(FunctionProperties());
+                  FunctionProperties& fcpp = _functions.back();
+                  fcpp._name=PyString_AsString(pAttribute);
+                  Py_DECREF(pAttribute);
+                  
+                  if(pAttribute = checkAndGetAttribute(fpy, "inputs", errorMessage))
+                    errorMessage += copyList(pAttribute, fcpp._input_ports);
+                  Py_XDECREF(pAttribute);
+                  
+                  if(pAttribute = checkAndGetAttribute(fpy, "outputs", errorMessage))
+                    // None value means no return statement in the function
+                    if(pAttribute != Py_None)
+                      errorMessage += copyList(pAttribute,fcpp._output_ports);
+                  Py_XDECREF(pAttribute);
+                    
+                  if(pAttribute = checkAndGetAttribute(fpy, "errors", errorMessage))
+                    errorMessage += copyList(pAttribute, fcpp._errors);
+                  Py_XDECREF(pAttribute);
+                  
+                  if(pAttribute = checkAndGetAttribute(fpy, "imports", errorMessage))
+                    errorMessage += copyList(pAttribute, fcpp._imports);
+                  Py_XDECREF(pAttribute);
+                }
+              }
+            }
+          }
+          errorMessage += copyList(PyTuple_GetItem(pValue, 1), _global_errors);
+          Py_DECREF(pValue);
+        }
+      }
+      else
+      {
+        errorMessage  = getPyErrorText();
+        errorMessage += "\nCannot find the parsing function '";
+        errorMessage += _python_parser_function;
+        errorMessage += "' in python module '";
+        errorMessage += _python_parser_module;
+        errorMessage += "'.\n";
+      }
+      Py_XDECREF(pFunc);
+      Py_DECREF(pModule);
+    }
+    
+    if(not errorMessage.empty())
+      throw Py2yacsException(errorMessage);
+    // Py_Finalize();
+}
+
+void Py2yacs::save(const std::string& file_path,
+                   const std::string& python_function)const
+{
+  YACS::ENGINE::Proc* schema = createProc(python_function);
+  schema->saveSchema(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<std::string>::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<FunctionProperties>::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<std::string>::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<std::string>::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 (file)
index 0000000..b0ca9c4
--- /dev/null
@@ -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 <string>
+#include <list>
+#include <exception>
+
+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<std::string> _input_ports;
+    std::list<std::string> _output_ports;
+    std::list<std::string> _errors;
+    std::list<std::string> _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<std::string>& getGlobalErrors() const;
+    const std::list<FunctionProperties>& getFunctionProperties()const;
+
+  private:
+    std::string _python_parser_module;
+    std::string _python_parser_function;
+    std::list<FunctionProperties> _functions;
+    std::list<std::string> _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 (file)
index 0000000..84ac997
--- /dev/null
@@ -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