Salome HOME
[PY3] Remove obsolete imports
[tools/yacsgen.git] / module_generator / pycompo.py
1 # Copyright (C) 2009-2016  EDF R&D
2 #
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20 """
21   Module that defines PYComponent for SALOME components implemented in Python
22 """
23 import os
24 from module_generator.gener import Component, Invalid
25 from module_generator.pyth_tmpl import pyinitService, pyService, pyCompoEXE, pyCompo, cmake_src_compo_py
26 import textwrap
27
28 def indent(text, prefix='    '):
29   """Indent text by prepending a given prefix to each line."""
30   if not text: return ''
31   lines = text.split('\n')
32   lines = list(map(lambda line, prefix=prefix: prefix + line, lines))
33   if lines: lines[-1] = lines[-1].rstrip()
34   return '\n'.join(lines)
35
36 class PYComponent(Component):
37   """
38    A :class:`PYComponent` instance represents a Python SALOME component with services given as a list of :class:`Service`
39    instances with the parameter *services*.
40
41    :param name: gives the name of the component.
42    :type name: str
43    :param services: the list of services (:class:`Service`) of the component.
44    :param kind: If it is given and has the value "exe", the component will be built as a standalone
45       component (python executable). The default is to build the component as a python module.
46    :param sources: gives all the external Python source files to add in the component directory (list of paths).
47    :param python_path: If it is given (as a list of paths), all the paths are added to the python path (sys.path).
48    :param compodefs: can be used to add extra definition code in the component for example when using a base class
49       to define the component class by deriving it (see *inheritedclass* parameter)
50    :param inheritedclass: can be used to define a base class for the component. The base class can be defined in external
51       source or with the *compodefs* parameter. The value of the *inheritedclass* parameter is the name of the base class.
52    :param idls: can be used to add extra idl CORBA interfaces. This parameter must gives a list of idl file names that are
53       added into the generated module (idl directory) and compiled with the generated idl of the module.
54    :param interfacedefs: can be used to add idl definitions (or includes of idl files) into the generated idl of the module.
55    :param inheritedinterface: can be used to make the component inherit an extra idl interface that has been included through
56       the *idls* and *interfacedefs* parameters. See the pygui1 example for how to use these last parameters.
57
58    For example, the following call defines a Python component named "mycompo" with one service s1 (it must have been defined before)::
59
60       >>> c1 = module_generator.PYComponent('mycompo', services=[s1,],
61                                                        python_path="apath")
62
63   """
64   def __init__(self, name, services=None, kind="lib", sources=None, python_path=None,
65                      compodefs="", inheritedclass="", idls=None, interfacedefs="", inheritedinterface=""):
66     """initialise component attributes"""
67     self.python_path = python_path or []
68     Component.__init__(self, name, services, impl="PY", kind=kind, sources=sources,
69                              inheritedclass=inheritedclass, compodefs=compodefs,
70                              idls=idls,interfacedefs=interfacedefs,inheritedinterface=inheritedinterface)
71
72   def validate(self):
73     """validate component attributes"""
74     Component.validate(self)
75     kinds = ("lib","exe")
76     if self.kind not in kinds:
77       raise Invalid("kind must be one of %s for component %s" % (kinds,self.name))
78
79   def libraryName(self):
80     """ Name of the target library
81         No library for a python component
82     """
83     return ""
84     
85   def makeCompo(self, gen):
86     """generate component sources as a dictionary containing
87        file names (key) and file content (values)
88     """
89     pyfile = ""
90     file_content = ""
91     if self.kind == "lib":
92       pyfile = self.name  + ".py"
93       file_content = self.makepy(gen)
94     elif self.kind == "exe":
95       pyfile = self.name + ".exe"
96       file_content = self.makepyexe(gen)
97     else :
98       raise Invalid("Invalid kind ()%s for component %s" % (
99                                     self.kind, self.name))
100     
101     sources = pyfile + "".join(["\n  " + os.path.basename(x) for x in self.sources])
102     cmake_content = cmake_src_compo_py.substitute(sources=sources)
103     
104     return {"CMakeLists.txt":cmake_content,
105             pyfile:file_content
106            }
107
108   def makepy(self, gen):
109     """generate standard SALOME component source (python module)"""
110     services = []
111     inits = []
112     defs = []
113     for serv in self.services:
114       defs.append(serv.defs)
115       params = []
116       pyparams = []
117       for name, typ in serv.inport:
118         if typ=="file":continue #files are not passed through service interface
119         params.append(name)
120         if typ == "pyobj":
121           pyparams.append("      %s=cPickle.loads(%s)" %(name, name))
122       inparams = ",".join(params)
123       convertinparams = '\n'.join(pyparams)
124
125       params = []
126       pyparams = []
127       for name, typ in serv.outport:
128         if typ=="file":continue #files are not passed through service interface
129         params.append(name)
130         if typ == "pyobj":
131           pyparams.append("      %s=cPickle.dumps(%s,-1)" %(name, name))
132       outparams = ",".join(params)
133       convertoutparams = '\n'.join(pyparams)
134       #dedent and indent the body
135       body=textwrap.dedent(serv.body)
136       body=indent(body,' '*6)
137
138       service = pyService.substitute(component=self.name, service=serv.name, inparams=inparams,
139                                      outparams=outparams, body= body,
140                                      convertinparams=convertinparams,
141                                      convertoutparams=convertoutparams)
142       streams = []
143       for name, typ, dep in serv.instream:
144         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","IN","%s")'% (name, typ, dep))
145       instream = "\n".join(streams)
146       streams = []
147       for name, typ, dep in serv.outstream:
148         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","OUT","%s")'% (name, typ, dep))
149       outstream = "\n".join(streams)
150
151       init = pyinitService.substitute(component=self.name, service=serv.name,
152                                       instream=instream, outstream=outstream)
153       services.append(service)
154       inits.append(init)
155
156     python_path = ",".join([repr(p) for p in self.python_path])
157
158     inheritedclass=self.inheritedclass
159     callconstructor=""
160     if self.inheritedclass:
161       inheritedclass= self.inheritedclass + ","
162       callconstructor="""
163     if hasattr(%s,"__init__"):
164       %s.__init__(self)""" % (self.inheritedclass,self.inheritedclass)
165
166     return pyCompo.substitute(component=self.name, module=gen.module.name,
167                               servicesdef="\n".join(defs), servicesimpl="\n".join(services),
168                               initservice='\n'.join(inits),
169                               python_path=python_path,inheritedclass=inheritedclass,
170                               compodefs=self.compodefs, callconstructor=callconstructor)
171
172   def makepyexe(self, gen):
173     """generate standalone component source (python executable)"""
174     services = []
175     inits = []
176     defs = []
177     for serv in self.services:
178       defs.append(serv.defs)
179       params = []
180       pyparams = []
181       for name, typ in serv.inport:
182         if typ=="file":continue #files are not passed through service interface
183         params.append(name)
184         if typ == "pyobj":
185           pyparams.append("      %s=cPickle.loads(%s)" %(name, name))
186       inparams = ",".join(params)
187       convertinparams = '\n'.join(pyparams)
188
189       params = []
190       pyparams = []
191       for name, typ in serv.outport:
192         if typ=="file":continue #files are not passed through service interface
193         params.append(name)
194         if typ == "pyobj":
195           pyparams.append("      %s=cPickle.dumps(%s,-1)" %(name, name))
196       outparams = ",".join(params)
197       convertoutparams = '\n'.join(pyparams)
198       #dedent and indent the body
199       body=textwrap.dedent(serv.body)
200       body=indent(body,' '*6)
201       service = pyService.substitute(component=self.name, service=serv.name,
202                                      inparams=inparams, outparams=outparams,
203                                      body=body,
204                                      convertinparams=convertinparams,
205                                      convertoutparams=convertoutparams,
206                                     )
207       streams = []
208       for name, typ, dep in serv.instream:
209         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","IN","%s")'% (name, typ, dep))
210       instream = "\n".join(streams)
211       streams = []
212       for name, typ, dep in serv.outstream:
213         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","OUT","%s")'% (name, typ, dep))
214       outstream = "\n".join(streams)
215
216       init = pyinitService.substitute(component=self.name, service=serv.name,
217                                       instream=instream, outstream=outstream)
218       services.append(service)
219       inits.append(init)
220
221     python_path = ",".join([repr(p) for p in self.python_path])
222
223     inheritedclass=self.inheritedclass
224     callconstructor=""
225     if self.inheritedclass:
226       inheritedclass= self.inheritedclass + ","
227       callconstructor="""
228     if hasattr(%s,"__init__"):
229       %s.__init__(self)""" % (self.inheritedclass,self.inheritedclass)
230
231     return pyCompoEXE.substitute(component=self.name, module=gen.module.name,
232                                  servicesdef="\n".join(defs),
233                                  servicesimpl="\n".join(services),
234                                  initservice='\n'.join(inits),
235                                  python_path=python_path,inheritedclass=inheritedclass,
236                                  compodefs=self.compodefs, callconstructor=callconstructor)
237