Salome HOME
Use containers with yacsdecorator.
authorOvidiu Mircescu <ovidiu.mircescu@edf.fr>
Wed, 2 Sep 2020 15:06:13 +0000 (17:06 +0200)
committerOvidiu Mircescu <ovidiu.mircescu@edf.fr>
Wed, 2 Sep 2020 15:06:13 +0000 (17:06 +0200)
src/py2yacs/Test/testDeco.py
src/py2yacs/yacsbuild.py
src/py2yacs/yacsdecorator.py

index dc68a935d0e7433330a7e4f0148ea4cb98a91d92..796e9576452633b0899900f7bbe8770c44558a9d 100755 (executable)
@@ -99,6 +99,63 @@ class TestDeco(unittest.TestCase):
       obtained = proc.getChildByName("output_doublefr_0").getOutputPort("r_0_0").getPyObj()
       self.assertEqual(expected, obtained)
 
+    def test_t4(self):
+      """
+      Using specific containers.
+      """
+      import yacsdecorator
+      cm = yacsdecorator.ContainerManager()
+      cm.addContainer("c1", 1, False)
+      cm.addContainer("c2", 4, True)
+      cm.addContainer(yacsdecorator.ContainerManager.defaultContainerName, 1, False)
+      cont_file = os.path.join(dir_test, "containers_t4.json")
+      cm.saveFile(cont_file)
+      script = """import yacsdecorator
+@yacsdecorator.leaf("c1")
+def f_c1(x,y):
+  s = x + y
+  return s
+
+@yacsdecorator.leaf("c2")
+def f_c2(x,y):
+  p = x * y
+  return p
+
+@yacsdecorator.leaf
+def f_def(x,y):
+  d = x - y
+  return d
+
+@yacsdecorator.bloc
+def main():
+  s1 = f_c1(3,4)
+  p1 = f_c2(5,6)
+  r = f_def(p1, s1)
+"""
+      script_file = os.path.join(dir_test, "script_t4.py")
+      with open(script_file, "w") as f:
+        f.write(script)
+      yacs_build_command = "yacsbuild.py"
+      main_function_name = "main"
+      yacs_schema_file = os.path.join(dir_test, "schema_t4.xml")
+      subprocess.run([yacs_build_command,
+                      script_file, main_function_name, yacs_schema_file,
+                      "-c", cont_file])
+      l = loader.YACSLoader()
+      ex = pilot.ExecutorSwig()
+      proc = l.load(yacs_schema_file)
+      ex.RunW(proc,0)
+      self.assertEqual(proc.getState(),pilot.DONE)
+      c1 = proc.getChildByName("f_c1_0").getContainer()
+      self.assertFalse(c1.isUsingPythonCache())
+      self.assertEqual(c1.getProperty("nb_parallel_procs"), "1")
+      c2 = proc.getChildByName("f_c2_0").getContainer()
+      self.assertTrue(c2.isUsingPythonCache())
+      self.assertEqual(c2.getProperty("nb_parallel_procs"), "4")
+      c3 = proc.getChildByName("f_def_0").getContainer()
+      self.assertFalse(c3.isUsingPythonCache())
+      self.assertEqual(c3.getProperty("nb_parallel_procs"), "1")
+
 if __name__ == '__main__':
   file_test = os.path.join(dir_test,"UnitTestsResult")
   with open(file_test, 'a') as f:
index 61aaaf6b721191ded137bac591c5e56d3b4c0568..92a3fdbb2ea880e960cb729c8ae3a296602917c8 100755 (executable)
@@ -26,10 +26,15 @@ if __name__ == '__main__':
   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.')
+  parser.add_argument("-c", "--containers",
+                      help="File of containers.",
+                      default=None)
   args = parser.parse_args()
   import yacsdecorator
   yacsdecorator.activateYacsMode()
+  if not args.containers is None :
+    yacsdecorator.loadContainers(args.containers)
   import yacstools
   fn = yacstools.getFunction(args.path, args.mainbloc)
   fn()
-  yacsdecorator.finalize(args.yacsfile)
+  yacsdecorator.export(args.yacsfile)
index 462981e6b50efa327f9a297da2d94612d687631b..a0580311d4cd71df82aebda52d731f6d121bbff5 100644 (file)
@@ -18,6 +18,7 @@
 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 #
 import sys
+import json
 
 # this is a pointer to the module object instance itself.
 this_module = sys.modules[__name__]
@@ -28,11 +29,12 @@ class OutputPort:
     self.yacs_port = yacs_port
 
 class LeafNodeType:
-  def __init__(self, path, fn_name, inputs, outputs):
+  def __init__(self, path, fn_name, inputs, outputs, container_name):
     self.path = path
     self.fn_name = fn_name
     self.inputs = inputs
     self.outputs = outputs
+    self.container_name = container_name
     self.number = 0
 
   def newName(self):
@@ -48,90 +50,72 @@ class LeafNodeType:
     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
+class ContainerProperties():
+  def __init__(self, name, nb_cores, use_cache):
+    self.name = name
+    self.nb_cores = nb_cores
+    self.use_cache = use_cache
 
-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 jsonContainerEncoder(obj):
+  if isinstance(obj, ContainerProperties) :
+    return {
+            "name": obj.name,
+            "nb_cores": obj.nb_cores,
+            "use_cache": obj.use_cache }
+  else:
+    raise TypeError("Cannot serialize object "+str(obj))
 
-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 jsonContainerDecoder(dct):
+  if "name" in dct and "nb_cores" in dct and "use_cache" in dct :
+    return ContainerProperties(dct["name"], dct["nb_cores"], dct["use_cache"])
+  return dct
 
-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
+class ContainerManager():
+  defaultContainerName = "default_container"
+  def __init__(self):
+    self._containers = []
+    self._defaultContainer = ContainerProperties(
+                                ContainerManager.defaultContainerName, 0, False)
+    self._containers.append(self._defaultContainer)
 
-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
+  def setDefaultContainer(self, nb_cores, use_cache):
+    self._defaultContainer.nb_cores = nb_cores
+    self._defaultContainer.use_cache = use_cache
+
+  def loadFile(self, file_path):
+    with open(file_path, 'r') as json_file:
+      self._containers = json.load(json_file, object_hook=jsonContainerDecoder)
+    try:
+      self._defaultContainer = next(cont for cont in self._containers
+                          if cont.name == ContainerManager.defaultContainerName)
+    except StopIteration:
+      self._defaultContainer = ContainerProperties(
+                                ContainerManager.defaultContainerName, 0, False)
+      self._containers.append(self._defaultContainer)
+
+  def saveFile(self, file_path):
+    with open(file_path, 'w') as json_file:
+      json.dump(self._containers, json_file,
+                indent=2, default=jsonContainerEncoder)
+
+  def addContainer(self, name, nb_cores, use_cache):
+    try:
+      # if the name already exists
+      obj = next(cont for cont in self._containers if cont.name == name)
+      obj.nb_cores = nb_cores
+      obj.use_cache = use_cache
+    except StopIteration:
+      # new container
+      self._containers.append(ContainerProperties(name, nb_cores, use_cache))
+
+  def getContainer(self, name):
+    ret = self._defaultContainer
+    try:
+      ret = next(cont for cont in self._containers if cont.name == name)
+    except StopIteration:
+      # not found
+      pass
+    return ret
 
 class SchemaGenerator():
   """
@@ -148,6 +132,7 @@ class SchemaGenerator():
     self.seqpyobjtype = self.runtime.getTypeCode("seqpyobj")
     self.bloc_stack = [self.proc]
     self.name_index = 0 # used to ensure unique names
+    self.container_manager = ContainerManager()
 
   def newName(self, name):
     new_name = name + "_" + str(self.name_index)
@@ -166,11 +151,12 @@ class SchemaGenerator():
     """
     A new container may be created if it does not already exist for this type.
     """
+    container_properties = self.container_manager.getContainer(container_type)
     if container_type not in self.containers:
-      cont=self.proc.createContainer(container_type,"Salome")
-      #cont.setProperty("nb_proc_per_node","0")
+      cont=self.proc.createContainer(container_properties.name,"Salome")
+      cont.setProperty("nb_parallel_procs", str(container_properties.nb_cores))
       cont.setProperty("type","multi")
-      cont.usePythonCache(False)
+      cont.usePythonCache(container_properties.use_cache)
       cont.attachOnCloning()
       self.containers[container_type] = cont
     return self.containers[container_type]
@@ -220,7 +206,7 @@ study_function = yacstools.getFunction("{file_path}", "{function_name}")
     inputs = leaf.inputs # names
     outputs = leaf.outputs # names
     script = self.createScript(file_path, function_name, inputs, outputs)
-    container = self.getContainer("generic_cont")
+    container = self.getContainer(leaf.container_name)
     new_node = self.runtime.createScriptNode("Salome", node_name)
     new_node.setContainer(container)
     new_node.setExecutionMode("remote")
@@ -254,8 +240,6 @@ study_function = yacstools.getFunction("{file_path}", "{function_name}")
     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)
@@ -345,6 +329,8 @@ _default_mode = "Default"
 _yacs_mode = "YACS"
 _exec_mode = _default_mode
 
+# Public functions
+
 def getGenerator():
   """
   Get the singleton object.
@@ -360,8 +346,111 @@ def activateYacsMode():
 def activateDefaultMode():
   this_module._exec_mode = this_module._default_mode
 
-def finalize(path):
+def loadContainers(file_path):
+  getGenerator().container_manager.loadFile(file_path)
+
+def export(path):
   if this_module._exec_mode == this_module._yacs_mode :
     getGenerator().dump(path)
 
+# Decorators
+class LeafDecorator():
+  def __init__(self, container_name):
+    self.container_name = container_name
+
+  def __call__(self, f):
+    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, self.container_name)
+    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 leaf(arg):
+  """
+  Decorator for python scripts.
+  """
+  if callable(arg):
+    # decorator used without parameters. arg is the function
+    container = ContainerManager.defaultContainerName
+    ret = (LeafDecorator(container))(arg)
+  else:
+    # decorator used with parameter. arg is the container name
+    ret = LeafDecorator(arg)
+  return ret
+
+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 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
+
+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)
+