--- /dev/null
+#!/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())
--- /dev/null
+#!/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)
+
+