From 3b1e552d7293fe951444ce161ab1dc6a2ac3778a Mon Sep 17 00:00:00 2001 From: Ovidiu Mircescu Date: Tue, 1 Sep 2020 15:33:21 +0200 Subject: [PATCH] Yacs decorator Build a yacs schema out of a decorated python script. --- src/py2yacs/CMakeLists.txt | 7 + src/py2yacs/Test/CMakeLists.txt | 11 +- src/py2yacs/Test/CTestTestfileInstall.cmake | 5 + src/py2yacs/Test/formule.py | 30 ++ src/py2yacs/Test/testDeco.py | 108 ++++++ src/py2yacs/Test/testforeach.py | 70 ++++ src/py2yacs/py2yacs.py | 29 +- src/py2yacs/yacsbuild.py | 35 ++ src/py2yacs/yacsdecorator.py | 367 ++++++++++++++++++++ src/py2yacs/yacstools.py | 28 ++ 10 files changed, 686 insertions(+), 4 deletions(-) create mode 100755 src/py2yacs/Test/formule.py create mode 100755 src/py2yacs/Test/testDeco.py create mode 100755 src/py2yacs/Test/testforeach.py create mode 100755 src/py2yacs/yacsbuild.py create mode 100644 src/py2yacs/yacsdecorator.py create mode 100644 src/py2yacs/yacstools.py diff --git a/src/py2yacs/CMakeLists.txt b/src/py2yacs/CMakeLists.txt index c66943ca4..487c7a441 100644 --- a/src/py2yacs/CMakeLists.txt +++ b/src/py2yacs/CMakeLists.txt @@ -57,6 +57,13 @@ INSTALL(FILES ${_py2yacs_headers} DESTINATION ${SALOME_INSTALL_HEADERS}) SET( _py_SCRIPTS py2yacs.py + yacsdecorator.py + yacstools.py + ) + +SET(_py_PROGS + yacsbuild.py ) SALOME_INSTALL_SCRIPTS("${_py_SCRIPTS}" ${SALOME_INSTALL_PYTHON}) +SALOME_INSTALL_SCRIPTS("${_py_PROGS}" ${SALOME_INSTALL_SCRIPT_SCRIPTS}) diff --git a/src/py2yacs/Test/CMakeLists.txt b/src/py2yacs/Test/CMakeLists.txt index 56cc8c068..1ee8cc9bf 100644 --- a/src/py2yacs/Test/CMakeLists.txt +++ b/src/py2yacs/Test/CMakeLists.txt @@ -42,7 +42,7 @@ TARGET_LINK_LIBRARIES(test_py2yacs py2yacslib) # For salome test IF(NOT WIN32) # This test needs a running salome session - # TODO: Run the test in a salome session + # TODO: Run the test in a salome session with make test #ADD_TEST(TestPy2yacs TestPy2yacs) #SET_TESTS_PROPERTIES(TestPy2yacs PROPERTIES # ENVIRONMENT "PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_CURRENT_SOURCE_DIR}/..:$ENV{PYTHONPATH}" @@ -52,13 +52,20 @@ IF(NOT WIN32) SET(LOCAL_TEST_FILES bad_parsers.py err_py2yacs_invalid.py + testforeach.py + formule.py + ) + SET(LOCAL_TEST_SCRIPTS + testDeco.py ) INSTALL(FILES ${LOCAL_TEST_FILES} DESTINATION ${LOCAL_TEST_DIR}) + INSTALL(PROGRAMS ${LOCAL_TEST_SCRIPTS} + 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 +ENDIF() diff --git a/src/py2yacs/Test/CTestTestfileInstall.cmake b/src/py2yacs/Test/CTestTestfileInstall.cmake index 9187bfae3..6ccef91de 100644 --- a/src/py2yacs/Test/CTestTestfileInstall.cmake +++ b/src/py2yacs/Test/CTestTestfileInstall.cmake @@ -25,4 +25,9 @@ IF(NOT WIN32) LABELS "${COMPONENT_NAME}" ) + SET(TEST_NAME ${COMPONENT_NAME}_PyDecorator) + ADD_TEST(${TEST_NAME} ${SALOME_TEST_DRIVER} ${TIMEOUT} testDeco.py) + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES + LABELS "${COMPONENT_NAME}" + ) ENDIF() diff --git a/src/py2yacs/Test/formule.py b/src/py2yacs/Test/formule.py new file mode 100755 index 000000000..eba73c621 --- /dev/null +++ b/src/py2yacs/Test/formule.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +import yacsdecorator + +@yacsdecorator.leaf +def f1(x,y): + r = x+y + return r + +@yacsdecorator.leaf +def f2(a): + r = a + 2 + return r + +@yacsdecorator.leaf +def f3(x, y): + s = x+y + p = x*y + return s,p + +@yacsdecorator.bloc +def b1(): + x = f1(x=3,y=4) + a,b = f3(x, 2) + f2(x) + r = f1(a,b) + return r + +if __name__ == '__main__': + r = b1() + print("result:", r) diff --git a/src/py2yacs/Test/testDeco.py b/src/py2yacs/Test/testDeco.py new file mode 100755 index 000000000..dc68a935d --- /dev/null +++ b/src/py2yacs/Test/testDeco.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# Copyright (C) 2006-2020 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 +# + +import unittest +import tempfile +import os +import subprocess + +import SALOMERuntime +import loader +import pilot + +dir_test = tempfile.mkdtemp(suffix=".yacstest") + +class TestDeco(unittest.TestCase): + + def setUp(self): + SALOMERuntime.RuntimeSALOME_setRuntime() + + def test_t1(self): + """ + Schema: + jdd -> foreach -> post + > f2 + / + f1 -> f3 -> f1 + """ + import testforeach + expected_1, expected_2 = testforeach.main() + yacs_schema_file = os.path.join(dir_test, "schema_t1.xml") + yacs_build_command = "yacsbuild.py" + test_script = "testforeach.py" + main_function_name = "main" + subprocess.run([yacs_build_command, + test_script, main_function_name, yacs_schema_file]) + l = loader.YACSLoader() + ex = pilot.ExecutorSwig() + proc = l.load(yacs_schema_file) + ex.RunW(proc,0) + obtained_1 = proc.getChildByName("post_0").getOutputPort("s").getPyObj() + obtained_2 = proc.getChildByName("f1_1").getOutputPort("r").getPyObj() + self.assertEqual(expected_1, obtained_1) + self.assertEqual(expected_2, obtained_2) + + def test_t2(self): + """ + Foreach initialized by value. + """ + import testforeach + expected_1, expected_2 = testforeach.mainbloc() + yacs_schema_file = os.path.join(dir_test, "schema_t2.xml") + yacs_build_command = "yacsbuild.py" + test_script = "testforeach.py" + main_function_name = "mainbloc" + subprocess.run([yacs_build_command, + test_script, main_function_name, yacs_schema_file]) + l = loader.YACSLoader() + ex = pilot.ExecutorSwig() + proc = l.load(yacs_schema_file) + ex.RunW(proc,0) + obtained_1 = proc.getChildByName("output_fr_0").getOutputPort("s_0").getPyObj() + obtained_2 = proc.getChildByName("output_fr_0").getOutputPort("p_1").getPyObj() + self.assertEqual(expected_1, obtained_1) + self.assertEqual(expected_2, obtained_2) + + def test_t3(self): + """ + Foreach on 2 levels. + """ + import testforeach + expected = testforeach.maindoublefr() + yacs_schema_file = os.path.join(dir_test, "schema_t3.xml") + yacs_build_command = "yacsbuild.py" + test_script = "testforeach.py" + main_function_name = "maindoublefr" + subprocess.run([yacs_build_command, + test_script, main_function_name, yacs_schema_file]) + l = loader.YACSLoader() + ex = pilot.ExecutorSwig() + proc = l.load(yacs_schema_file) + ex.RunW(proc,0) + obtained = proc.getChildByName("output_doublefr_0").getOutputPort("r_0_0").getPyObj() + self.assertEqual(expected, obtained) + +if __name__ == '__main__': + file_test = os.path.join(dir_test,"UnitTestsResult") + with open(file_test, 'a') as f: + f.write(" --- TEST src/py2yacs: testDeco.py\n") + suite = unittest.makeSuite(TestDeco) + result=unittest.TextTestRunner(f, descriptions=1, verbosity=1).run(suite) + sys.exit(not result.wasSuccessful()) diff --git a/src/py2yacs/Test/testforeach.py b/src/py2yacs/Test/testforeach.py new file mode 100755 index 000000000..5df0dfec5 --- /dev/null +++ b/src/py2yacs/Test/testforeach.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import yacsdecorator +import formule + +@yacsdecorator.leaf +def f1(x,y): + r = x+y + return r + +@yacsdecorator.leaf +def f2(a): + r = a + 2 + return r + +@yacsdecorator.leaf +def f3(x, y): + s = x+y + p = x*y + return s,p + +@yacsdecorator.leaf +def jdd(): + r = list(range(10)) + return r + +@yacsdecorator.foreach +def fr(v): + a,b = f3(v, 2) + return a,b + +@yacsdecorator.foreach +def fr2(v): + r = f2(v) + return r + +@yacsdecorator.foreach +def doublefr(v): + return fr2(v) + +@yacsdecorator.leaf +def post(t): + s = 0 + for e in t: + s += e + return s + +@yacsdecorator.bloc +def mainbloc(): + return fr(range(10)) + +@yacsdecorator.bloc +def maindoublefr(): + vals = [ list(range(x)) for x in range(10)] + return doublefr(vals) + +@yacsdecorator.bloc +def main(): + vals = jdd() + result = fr2(vals) + r1 = post(result) + x = formule.f1(x=3,y=4) + a,b = formule.f3(x, 2) + formule.f2(x) + r2 = formule.f1(a,b) + return r1,r2 + +if __name__ == '__main__': + v1, v2 = main() + print("v1:", v1) + print("v2:", v2) diff --git a/src/py2yacs/py2yacs.py b/src/py2yacs/py2yacs.py index 364bd8d6a..3ee6251ae 100644 --- a/src/py2yacs/py2yacs.py +++ b/src/py2yacs/py2yacs.py @@ -36,7 +36,7 @@ class FunctionProperties: result+= " Imports:"+ str(self.imports) + "\n" return result -class v(ast.NodeVisitor): +class VisitAST(ast.NodeVisitor): def visit_Module(self, node): accepted_tokens = ["Import", "ImportFrom", "FunctionDef", "ClassDef"] self.global_errors=[] @@ -140,10 +140,35 @@ def get_properties(text_file): except SyntaxError as err: import traceback return [], ["".join(traceback.format_exception_only(SyntaxError,err))] - w=v() + w=VisitAST() w.visit(bt) return w.functions, w.global_errors +def function_properties(python_path, fn_name): + """ + python_path : path to a python file + fn_name : name of a function in the file + return : properties of the function. see class FunctionProperties + """ + with open(python_path, 'r') as f: + text_file = f.read() + functions,errors = get_properties(text_file) + result = [fn for fn in functions if fn.name == fn_name] + if len(result) < 1: + raise Exception("Function not found: {}".format(fn_name)) + result = result[0] + error_string = "" + #if len(errors) > 0: + #error_string += "Global errors in file {}\n".format(python_path) + #error_string += '\n'.join(errors) + #raise Exception(error_string) + if len(result.errors) > 0: + error_string += "Errors when parsing function {}\n".format(fn_name) + error_string += '\n'.join(result.errors) + raise Exception(error_string) + return result + + def main(python_path, yacs_path, function_name="_exec"): with open(python_path, 'r') as f: text_file = f.read() diff --git a/src/py2yacs/yacsbuild.py b/src/py2yacs/yacsbuild.py new file mode 100755 index 000000000..61aaaf6b7 --- /dev/null +++ b/src/py2yacs/yacsbuild.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# Copyright (C) 2006-2020 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 __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser( + description="Build a YACS schema out of a decorated python script.") + parser.add_argument("path", help='Path to the script.') + parser.add_argument("mainbloc", + help='Name of the function containing the main bloc of the schema.') + parser.add_argument("yacsfile", help='Path to the output yacs file.') + args = parser.parse_args() + import yacsdecorator + yacsdecorator.activateYacsMode() + import yacstools + fn = yacstools.getFunction(args.path, args.mainbloc) + fn() + yacsdecorator.finalize(args.yacsfile) diff --git a/src/py2yacs/yacsdecorator.py b/src/py2yacs/yacsdecorator.py new file mode 100644 index 000000000..462981e6b --- /dev/null +++ b/src/py2yacs/yacsdecorator.py @@ -0,0 +1,367 @@ +#!/usr/bin/env python3 +# Copyright (C) 2006-2020 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 +# +import sys + +# this is a pointer to the module object instance itself. +this_module = sys.modules[__name__] + +class OutputPort: + def __init__(self, yacs_node, yacs_port): + self.yacs_node = yacs_node + self.yacs_port = yacs_port + +class LeafNodeType: + def __init__(self, path, fn_name, inputs, outputs): + self.path = path + self.fn_name = fn_name + self.inputs = inputs + self.outputs = outputs + self.number = 0 + + def newName(self): + name = self.fn_name + "_" + str(self.number) + self.number += 1 + return name + + def createNewNode(self, inputs): + """ + inputs : dict {input_name:value} + """ + generator = getGenerator() + output_ports = generator.createScriptNode(self, inputs) + return output_ports + +def leaf(f): + """ + Decorator for python scripts. + """ + if this_module._exec_mode == this_module._default_mode: + return f + co = f.__code__ + import py2yacs + props = py2yacs.function_properties(co.co_filename, co.co_name) + nodeType = LeafNodeType(co.co_filename, co.co_name, + props.inputs, props.outputs) + def my_func(*args, **kwargs): + if len(args) + len(kwargs) != len(nodeType.inputs): + mes = "Wrong number of arguments when calling function '{}'.\n".format( + nodeType.fn_name) + mes += " {} arguments expected and {} arguments found.\n".format( + len(nodeType.inputs), len(args) + len(kwargs)) + raise Exception(mes) + idx = 0 + args_dic = {} + for a in args: + args_dic[nodeType.inputs[idx]] = a + idx += 1 + for k,v in kwargs.items(): + args_dic[k] = v + if len(args_dic) != len(nodeType.inputs): + mes="Wrong arguments when calling function {}.\n".format(nodeType.fn_name) + raise Exception(mes) + return nodeType.createNewNode(args_dic) + return my_func + +def bloc(f): + """ + Decorator for blocs. + """ + #co = f.__code__ + #print("bloc :", co.co_name) + #print(" file:", co.co_filename) + #print(" line:", co.co_firstlineno) + #print(" args:", co.co_varnames) + return f + +def foreach(f): + """ + Decorator to generate foreach blocs + """ + if this_module._exec_mode == this_module._default_mode: + return default_foreach(f) + elif this_module._exec_mode == this_module._yacs_mode: + return yacs_foreach(f) + +def default_foreach(f): + def my_func(lst): + result = [] + for e in lst: + result.append(f(e)) + t_result = result + if len(result) > 0 : + if type(result[0]) is tuple: + # transform the list of tuples in a tuple of lists + l_result = [] + for e in result[0]: + l_result.append([]) + for t in result: + idx = 0 + for e in t: + l_result[idx].append(e) + idx += 1 + t_result = tuple(l_result) + return t_result + return my_func + +def yacs_foreach(f): + #co = f.__code__ + #import yacsvisit + #props = yacsvisit.main(co.co_filename, co.co_name) + def my_func(input_list): + fn_name = f.__code__.co_name + generator = getGenerator() + sample_port = generator.beginForeach(fn_name, input_list) + output_list = f(sample_port) + output_list = generator.endForeach(output_list) + return output_list + return my_func + +class SchemaGenerator(): + """ + Link to Salome for YACS schema generation. + """ + def __init__(self): + import SALOMERuntime + SALOMERuntime.RuntimeSALOME.setRuntime() + self.runtime = SALOMERuntime.getSALOMERuntime() + self.proc = self.runtime.createProc("GeneratedSchema") + self.proc.setProperty("executor","workloadmanager") + self.containers = {} + self.pyobjtype = self.runtime.getTypeCode("pyobj") + self.seqpyobjtype = self.runtime.getTypeCode("seqpyobj") + self.bloc_stack = [self.proc] + self.name_index = 0 # used to ensure unique names + + def newName(self, name): + new_name = name + "_" + str(self.name_index) + self.name_index += 1 + return new_name + + def getContextName(self): + context_name = "" + if len(self.bloc_stack) > 1: + # We are in a block + block_path = ".".join([ b.getName() for b in self.bloc_stack[1:] ]) + context_name = block_path + "." + return context_name + + def getContainer(self, container_type): + """ + A new container may be created if it does not already exist for this type. + """ + if container_type not in self.containers: + cont=self.proc.createContainer(container_type,"Salome") + #cont.setProperty("nb_proc_per_node","0") + cont.setProperty("type","multi") + cont.usePythonCache(False) + cont.attachOnCloning() + self.containers[container_type] = cont + return self.containers[container_type] + + def createScript(self, file_path, function_name, inputs, outputs): + import inspect + stack = inspect.stack() + stack_info = "Call stack\n" + # skip the first 4 levels in the stack + for level in stack[4:-1] : + info = inspect.getframeinfo(level[0]) + stack_info += "file: {}, line: {}, function: {}, context: {}\n".format( + info.filename, info.lineno, info.function, info.code_context) + + if len(outputs) == 0: + result = "" + elif len(outputs) == 1: + result = "{} = ".format(outputs[0]) + else: + result = ",".join(outputs) + result += " = " + + if len(inputs) == 0: + params = "" + elif len(inputs) == 1: + params = "{} ".format(inputs[0]) + else: + params = ",".join(inputs) + + script = """''' +{call_stack} +''' +import yacstools +study_function = yacstools.getFunction("{file_path}", "{function_name}") +{result}study_function({parameters}) +""".format(call_stack=stack_info, + file_path=file_path, + function_name=function_name, + result=result, + parameters=params) + return script + + def createScriptNode(self, leaf, input_values): + node_name = leaf.newName() + file_path = leaf.path + function_name = leaf.fn_name + inputs = leaf.inputs # names + outputs = leaf.outputs # names + script = self.createScript(file_path, function_name, inputs, outputs) + container = self.getContainer("generic_cont") + new_node = self.runtime.createScriptNode("Salome", node_name) + new_node.setContainer(container) + new_node.setExecutionMode("remote") + new_node.setScript(script) + self.bloc_stack[-1].edAddChild(new_node) + # create ports + for p in inputs: + new_node.edAddInputPort(p, self.pyobjtype) + output_obj_list = [] + for p in outputs: + port = new_node.edAddOutputPort(p, self.pyobjtype) + output_obj_list.append(OutputPort(new_node, port)) + # create links + for k,v in input_values.items(): + input_port = new_node.getInputPort(k) + if isinstance(v, OutputPort): + self.proc.edAddLink(v.yacs_port, input_port) + self.addCFLink(v.yacs_node, new_node) + #self.proc.edAddCFLink(v.yacs_node, new_node) + else: + input_port.edInitPy(v) + # return output ports + result = None + if len(output_obj_list) == 1 : + result = output_obj_list[0] + elif len(output_obj_list) > 1 : + result = tuple(output_obj_list) + return result + + def beginForeach(self, fn_name, input_values): + foreach_name = self.newName(fn_name) + new_foreach = self.runtime.createForEachLoopDyn(foreach_name, + self.pyobjtype) + #new_foreach = self.runtime.createForEachLoop(foreach_name, self.pyobjtype) + #new_foreach.edGetNbOfBranchesPort().edInitInt(1) + self.bloc_stack[-1].edAddChild(new_foreach) + bloc_name = "bloc_"+foreach_name + new_block = self.runtime.createBloc(bloc_name) + new_foreach.edAddChild(new_block) + sample_port = new_foreach.edGetSamplePort() + input_list_port = new_foreach.edGetSeqOfSamplesPort() + if isinstance(input_values, OutputPort): + # we need a conversion node pyobj -> seqpyobj + conversion_node = self.runtime.createScriptNode("Salome", + "input_"+foreach_name) + port_name = "val" + input_port = conversion_node.edAddInputPort(port_name, self.pyobjtype) + output_port = conversion_node.edAddOutputPort(port_name, + self.seqpyobjtype) + conversion_node.setExecutionMode("local") # no need for container + # no script, the same variable for input and output + conversion_node.setScript("") + self.bloc_stack[-1].edAddChild(conversion_node) + self.proc.edAddLink(input_values.yacs_port, input_port) + self.addCFLink(input_values.yacs_node, conversion_node) + self.proc.edAddLink(output_port, input_list_port) + # No need to look for ancestors. Both nodes are on the same level. + self.proc.edAddCFLink(conversion_node, new_foreach) + else: + input_list_port.edInitPy(list(input_values)) + self.bloc_stack.append(new_foreach) + self.bloc_stack.append(new_block) + return OutputPort(new_foreach, sample_port) + + def endForeach(self, outputs): + self.bloc_stack.pop() # remove the block + for_each_node = self.bloc_stack.pop() # remove the foreach + converted_ret = None + if outputs is not None: + # We need a conversion node seqpyobj -> pyobj + if type(outputs) is tuple: + list_out = list(outputs) + else: + list_out = [outputs] + conversion_node_name = "output_" + for_each_node.getName() + conversion_node = self.runtime.createScriptNode("Salome", + conversion_node_name) + conversion_node.setExecutionMode("local") # no need for container + conversion_node.setScript("") + self.bloc_stack[-1].edAddChild(conversion_node) + list_ret = [] + idx_name = 0 # for unique port names + for port in list_out : + if isinstance(port, OutputPort): + port_name = port.yacs_port.getName() + "_" + str(idx_name) + idx_name += 1 + input_port = conversion_node.edAddInputPort(port_name, + self.seqpyobjtype) + output_port = conversion_node.edAddOutputPort(port_name, + self.pyobjtype) + self.proc.edAddLink(port.yacs_port, input_port) + list_ret.append(OutputPort(conversion_node, output_port)) + else: + list_ret.append(port) + self.proc.edAddCFLink(for_each_node, conversion_node) + if len(list_ret) > 1 : + converted_ret = tuple(list_ret) + else: + converted_ret = list_ret[0] + return converted_ret + + def dump(self, file_path): + self.proc.saveSchema(file_path) + + def addCFLink(self, node_from, node_to): + commonAncestor = self.proc.getLowestCommonAncestor(node_from, node_to) + if node_from.getName() != commonAncestor.getName() : + link_from = node_from + while link_from.getFather().getName() != commonAncestor.getName() : + link_from = link_from.getFather() + link_to = node_to + while link_to.getFather().getName() != commonAncestor.getName() : + link_to = link_to.getFather() + self.proc.edAddCFLink(link_from, link_to) + else: + # from node is ancestor of to node. No CF link needed. + pass + +_generator = None + +_default_mode = "Default" +_yacs_mode = "YACS" +_exec_mode = _default_mode + +def getGenerator(): + """ + Get the singleton object. + """ + if this_module._generator is None: + if this_module._exec_mode == this_module._yacs_mode: + this_module._generator = SchemaGenerator() + return this_module._generator + +def activateYacsMode(): + this_module._exec_mode = this_module._yacs_mode + +def activateDefaultMode(): + this_module._exec_mode = this_module._default_mode + +def finalize(path): + if this_module._exec_mode == this_module._yacs_mode : + getGenerator().dump(path) + + diff --git a/src/py2yacs/yacstools.py b/src/py2yacs/yacstools.py new file mode 100644 index 000000000..13b639c3f --- /dev/null +++ b/src/py2yacs/yacstools.py @@ -0,0 +1,28 @@ +# Copyright (C) 2006-2020 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 +# +def getFunction(script_path, function_name): + import os + import sys + import importlib + pathname, filename = os.path.split(script_path) + sys.path.append(os.path.abspath(pathname)) + modname = os.path.splitext(filename)[0] + module = importlib.import_module(modname) + return getattr(module, function_name) + -- 2.30.2