2 # Copyright (C) 2006-2020 CEA/DEN, EDF R&D
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 # this is a pointer to the module object instance itself.
23 this_module = sys.modules[__name__]
26 def __init__(self, yacs_node, yacs_port):
27 self.yacs_node = yacs_node
28 self.yacs_port = yacs_port
31 def __init__(self, path, fn_name, inputs, outputs):
33 self.fn_name = fn_name
35 self.outputs = outputs
39 name = self.fn_name + "_" + str(self.number)
43 def createNewNode(self, inputs):
45 inputs : dict {input_name:value}
47 generator = getGenerator()
48 output_ports = generator.createScriptNode(self, inputs)
53 Decorator for python scripts.
55 if this_module._exec_mode == this_module._default_mode:
59 props = py2yacs.function_properties(co.co_filename, co.co_name)
60 nodeType = LeafNodeType(co.co_filename, co.co_name,
61 props.inputs, props.outputs)
62 def my_func(*args, **kwargs):
63 if len(args) + len(kwargs) != len(nodeType.inputs):
64 mes = "Wrong number of arguments when calling function '{}'.\n".format(
66 mes += " {} arguments expected and {} arguments found.\n".format(
67 len(nodeType.inputs), len(args) + len(kwargs))
72 args_dic[nodeType.inputs[idx]] = a
74 for k,v in kwargs.items():
76 if len(args_dic) != len(nodeType.inputs):
77 mes="Wrong arguments when calling function {}.\n".format(nodeType.fn_name)
79 return nodeType.createNewNode(args_dic)
87 #print("bloc :", co.co_name)
88 #print(" file:", co.co_filename)
89 #print(" line:", co.co_firstlineno)
90 #print(" args:", co.co_varnames)
95 Decorator to generate foreach blocs
97 if this_module._exec_mode == this_module._default_mode:
98 return default_foreach(f)
99 elif this_module._exec_mode == this_module._yacs_mode:
100 return yacs_foreach(f)
102 def default_foreach(f):
109 if type(result[0]) is tuple:
110 # transform the list of tuples in a tuple of lists
117 l_result[idx].append(e)
119 t_result = tuple(l_result)
126 #props = yacsvisit.main(co.co_filename, co.co_name)
127 def my_func(input_list):
128 fn_name = f.__code__.co_name
129 generator = getGenerator()
130 sample_port = generator.beginForeach(fn_name, input_list)
131 output_list = f(sample_port)
132 output_list = generator.endForeach(output_list)
136 class SchemaGenerator():
138 Link to Salome for YACS schema generation.
142 SALOMERuntime.RuntimeSALOME.setRuntime()
143 self.runtime = SALOMERuntime.getSALOMERuntime()
144 self.proc = self.runtime.createProc("GeneratedSchema")
145 self.proc.setProperty("executor","workloadmanager")
147 self.pyobjtype = self.runtime.getTypeCode("pyobj")
148 self.seqpyobjtype = self.runtime.getTypeCode("seqpyobj")
149 self.bloc_stack = [self.proc]
150 self.name_index = 0 # used to ensure unique names
152 def newName(self, name):
153 new_name = name + "_" + str(self.name_index)
157 def getContextName(self):
159 if len(self.bloc_stack) > 1:
161 block_path = ".".join([ b.getName() for b in self.bloc_stack[1:] ])
162 context_name = block_path + "."
165 def getContainer(self, container_type):
167 A new container may be created if it does not already exist for this type.
169 if container_type not in self.containers:
170 cont=self.proc.createContainer(container_type,"Salome")
171 #cont.setProperty("nb_proc_per_node","0")
172 cont.setProperty("type","multi")
173 cont.usePythonCache(False)
174 cont.attachOnCloning()
175 self.containers[container_type] = cont
176 return self.containers[container_type]
178 def createScript(self, file_path, function_name, inputs, outputs):
180 stack = inspect.stack()
181 stack_info = "Call stack\n"
182 # skip the first 4 levels in the stack
183 for level in stack[4:-1] :
184 info = inspect.getframeinfo(level[0])
185 stack_info += "file: {}, line: {}, function: {}, context: {}\n".format(
186 info.filename, info.lineno, info.function, info.code_context)
188 if len(outputs) == 0:
190 elif len(outputs) == 1:
191 result = "{} = ".format(outputs[0])
193 result = ",".join(outputs)
198 elif len(inputs) == 1:
199 params = "{} ".format(inputs[0])
201 params = ",".join(inputs)
207 study_function = yacstools.getFunction("{file_path}", "{function_name}")
208 {result}study_function({parameters})
209 """.format(call_stack=stack_info,
211 function_name=function_name,
216 def createScriptNode(self, leaf, input_values):
217 node_name = leaf.newName()
218 file_path = leaf.path
219 function_name = leaf.fn_name
220 inputs = leaf.inputs # names
221 outputs = leaf.outputs # names
222 script = self.createScript(file_path, function_name, inputs, outputs)
223 container = self.getContainer("generic_cont")
224 new_node = self.runtime.createScriptNode("Salome", node_name)
225 new_node.setContainer(container)
226 new_node.setExecutionMode("remote")
227 new_node.setScript(script)
228 self.bloc_stack[-1].edAddChild(new_node)
231 new_node.edAddInputPort(p, self.pyobjtype)
234 port = new_node.edAddOutputPort(p, self.pyobjtype)
235 output_obj_list.append(OutputPort(new_node, port))
237 for k,v in input_values.items():
238 input_port = new_node.getInputPort(k)
239 if isinstance(v, OutputPort):
240 self.proc.edAddLink(v.yacs_port, input_port)
241 self.addCFLink(v.yacs_node, new_node)
242 #self.proc.edAddCFLink(v.yacs_node, new_node)
244 input_port.edInitPy(v)
245 # return output ports
247 if len(output_obj_list) == 1 :
248 result = output_obj_list[0]
249 elif len(output_obj_list) > 1 :
250 result = tuple(output_obj_list)
253 def beginForeach(self, fn_name, input_values):
254 foreach_name = self.newName(fn_name)
255 new_foreach = self.runtime.createForEachLoopDyn(foreach_name,
257 #new_foreach = self.runtime.createForEachLoop(foreach_name, self.pyobjtype)
258 #new_foreach.edGetNbOfBranchesPort().edInitInt(1)
259 self.bloc_stack[-1].edAddChild(new_foreach)
260 bloc_name = "bloc_"+foreach_name
261 new_block = self.runtime.createBloc(bloc_name)
262 new_foreach.edAddChild(new_block)
263 sample_port = new_foreach.edGetSamplePort()
264 input_list_port = new_foreach.edGetSeqOfSamplesPort()
265 if isinstance(input_values, OutputPort):
266 # we need a conversion node pyobj -> seqpyobj
267 conversion_node = self.runtime.createScriptNode("Salome",
268 "input_"+foreach_name)
270 input_port = conversion_node.edAddInputPort(port_name, self.pyobjtype)
271 output_port = conversion_node.edAddOutputPort(port_name,
273 conversion_node.setExecutionMode("local") # no need for container
274 # no script, the same variable for input and output
275 conversion_node.setScript("")
276 self.bloc_stack[-1].edAddChild(conversion_node)
277 self.proc.edAddLink(input_values.yacs_port, input_port)
278 self.addCFLink(input_values.yacs_node, conversion_node)
279 self.proc.edAddLink(output_port, input_list_port)
280 # No need to look for ancestors. Both nodes are on the same level.
281 self.proc.edAddCFLink(conversion_node, new_foreach)
283 input_list_port.edInitPy(list(input_values))
284 self.bloc_stack.append(new_foreach)
285 self.bloc_stack.append(new_block)
286 return OutputPort(new_foreach, sample_port)
288 def endForeach(self, outputs):
289 self.bloc_stack.pop() # remove the block
290 for_each_node = self.bloc_stack.pop() # remove the foreach
292 if outputs is not None:
293 # We need a conversion node seqpyobj -> pyobj
294 if type(outputs) is tuple:
295 list_out = list(outputs)
298 conversion_node_name = "output_" + for_each_node.getName()
299 conversion_node = self.runtime.createScriptNode("Salome",
300 conversion_node_name)
301 conversion_node.setExecutionMode("local") # no need for container
302 conversion_node.setScript("")
303 self.bloc_stack[-1].edAddChild(conversion_node)
305 idx_name = 0 # for unique port names
306 for port in list_out :
307 if isinstance(port, OutputPort):
308 port_name = port.yacs_port.getName() + "_" + str(idx_name)
310 input_port = conversion_node.edAddInputPort(port_name,
312 output_port = conversion_node.edAddOutputPort(port_name,
314 self.proc.edAddLink(port.yacs_port, input_port)
315 list_ret.append(OutputPort(conversion_node, output_port))
317 list_ret.append(port)
318 self.proc.edAddCFLink(for_each_node, conversion_node)
319 if len(list_ret) > 1 :
320 converted_ret = tuple(list_ret)
322 converted_ret = list_ret[0]
325 def dump(self, file_path):
326 self.proc.saveSchema(file_path)
328 def addCFLink(self, node_from, node_to):
329 commonAncestor = self.proc.getLowestCommonAncestor(node_from, node_to)
330 if node_from.getName() != commonAncestor.getName() :
331 link_from = node_from
332 while link_from.getFather().getName() != commonAncestor.getName() :
333 link_from = link_from.getFather()
335 while link_to.getFather().getName() != commonAncestor.getName() :
336 link_to = link_to.getFather()
337 self.proc.edAddCFLink(link_from, link_to)
339 # from node is ancestor of to node. No CF link needed.
344 _default_mode = "Default"
346 _exec_mode = _default_mode
350 Get the singleton object.
352 if this_module._generator is None:
353 if this_module._exec_mode == this_module._yacs_mode:
354 this_module._generator = SchemaGenerator()
355 return this_module._generator
357 def activateYacsMode():
358 this_module._exec_mode = this_module._yacs_mode
360 def activateDefaultMode():
361 this_module._exec_mode = this_module._default_mode
364 if this_module._exec_mode == this_module._yacs_mode :
365 getGenerator().dump(path)