Salome HOME
Merge branch V7_3_1_BR
[tools/yacsgen.git] / module_generator / pycompo.py
1 # Copyright (C) 2009-2014  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 gener import Component, Invalid
25 from pyth_tmpl import pyinitService, pyService, pyCompoEXE, pyCompo
26 import textwrap
27 from string import split,rstrip,join
28
29 def indent(text, prefix='    '):
30   """Indent text by prepending a given prefix to each line."""
31   if not text: return ''
32   lines = split(text, '\n')
33   lines = map(lambda line, prefix=prefix: prefix + line, lines)
34   if lines: lines[-1] = rstrip(lines[-1])
35   return join(lines, '\n')
36
37 class PYComponent(Component):
38   """
39    A :class:`PYComponent` instance represents a Python SALOME component with services given as a list of :class:`Service`
40    instances with the parameter *services*.
41
42    :param name: gives the name of the component.
43    :type name: str
44    :param services: the list of services (:class:`Service`) of the component.
45    :param kind: If it is given and has the value "exe", the component will be built as a standalone
46       component (python executable). The default is to build the component as a python module.
47    :param sources: gives all the external Python source files to add in the component directory (list of paths).
48    :param python_path: If it is given (as a list of paths), all the paths are added to the python path (sys.path).
49    :param compodefs: can be used to add extra definition code in the component for example when using a base class
50       to define the component class by deriving it (see *inheritedclass* parameter)
51    :param inheritedclass: can be used to define a base class for the component. The base class can be defined in external
52       source or with the *compodefs* parameter. The value of the *inheritedclass* parameter is the name of the base class.
53    :param idls: can be used to add extra idl CORBA interfaces. This parameter must gives a list of idl file names that are
54       added into the generated module (idl directory) and compiled with the generated idl of the module.
55    :param interfacedefs: can be used to add idl definitions (or includes of idl files) into the generated idl of the module.
56    :param inheritedinterface: can be used to make the component inherit an extra idl interface that has been included through
57       the *idls* and *interfacedefs* parameters. See the pygui1 example for how to use these last parameters.
58
59    For example, the following call defines a Python component named "mycompo" with one service s1 (it must have been defined before)::
60
61       >>> c1 = module_generator.PYComponent('mycompo', services=[s1,],
62                                                        python_path="apath")
63
64   """
65   def __init__(self, name, services=None, kind="lib", sources=None, python_path=None,
66                      compodefs="", inheritedclass="", idls=None, interfacedefs="", inheritedinterface=""):
67     """initialise component attributes"""
68     self.python_path = python_path or []
69     Component.__init__(self, name, services, impl="PY", kind=kind, sources=sources,
70                              inheritedclass=inheritedclass, compodefs=compodefs,
71                              idls=idls,interfacedefs=interfacedefs,inheritedinterface=inheritedinterface)
72
73   def validate(self):
74     """validate component attributes"""
75     Component.validate(self)
76     kinds = ("lib","exe")
77     if self.kind not in kinds:
78       raise Invalid("kind must be one of %s for component %s" % (kinds,self.name))
79
80   def makeCompo(self, gen):
81     """generate component sources as a dictionary containing
82        file names (key) and file content (values)
83     """
84     pyfile = "%s.py" % self.name
85     sources = " ".join(map(os.path.basename,self.sources))
86     if self.kind == "lib":
87       return {"Makefile.am":gen.makeMakefile(self.getMakefileItems(gen)),
88               pyfile:self.makepy(gen)
89              }
90     if self.kind == "exe":
91       return {"Makefile.am":gen.makeMakefile(self.getMakefileItems(gen)),
92               self.name+".exe":self.makepyexe(gen),
93              }
94
95   def getMakefileItems(self,gen):
96     makefileItems={"header":"include $(top_srcdir)/adm_local/make_common_starter.am"}
97     if self.kind == "lib":
98       makefileItems["salomepython_PYTHON"]=[self.name+".py"]+self.sources
99     if self.kind == "exe":
100       makefileItems["salomepython_PYTHON"]=self.sources
101       makefileItems["dist_salomescript_SCRIPTS"]=[self.name+".exe"]
102     return makefileItems
103
104   def makepy(self, gen):
105     """generate standard SALOME component source (python module)"""
106     services = []
107     inits = []
108     defs = []
109     for serv in self.services:
110       defs.append(serv.defs)
111       params = []
112       pyparams = []
113       for name, typ in serv.inport:
114         if typ=="file":continue #files are not passed through service interface
115         params.append(name)
116         if typ == "pyobj":
117           pyparams.append("      %s=cPickle.loads(%s)" %(name, name))
118       inparams = ",".join(params)
119       convertinparams = '\n'.join(pyparams)
120
121       params = []
122       pyparams = []
123       for name, typ in serv.outport:
124         if typ=="file":continue #files are not passed through service interface
125         params.append(name)
126         if typ == "pyobj":
127           pyparams.append("      %s=cPickle.dumps(%s,-1)" %(name, name))
128       outparams = ",".join(params)
129       convertoutparams = '\n'.join(pyparams)
130       #dedent and indent the body
131       body=textwrap.dedent(serv.body)
132       body=indent(body,' '*6)
133
134       service = pyService.substitute(component=self.name, service=serv.name, inparams=inparams,
135                                      outparams=outparams, body= body,
136                                      convertinparams=convertinparams,
137                                      convertoutparams=convertoutparams)
138       streams = []
139       for name, typ, dep in serv.instream:
140         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","IN","%s")'% (name, typ, dep))
141       instream = "\n".join(streams)
142       streams = []
143       for name, typ, dep in serv.outstream:
144         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","OUT","%s")'% (name, typ, dep))
145       outstream = "\n".join(streams)
146
147       init = pyinitService.substitute(component=self.name, service=serv.name,
148                                       instream=instream, outstream=outstream)
149       services.append(service)
150       inits.append(init)
151
152     python_path = ",".join([repr(p) for p in self.python_path])
153
154     inheritedclass=self.inheritedclass
155     callconstructor=""
156     if self.inheritedclass:
157       inheritedclass= self.inheritedclass + ","
158       callconstructor="""
159     if hasattr(%s,"__init__"):
160       %s.__init__(self)""" % (self.inheritedclass,self.inheritedclass)
161
162     return pyCompo.substitute(component=self.name, module=gen.module.name,
163                               servicesdef="\n".join(defs), servicesimpl="\n".join(services),
164                               initservice='\n'.join(inits),
165                               python_path=python_path,inheritedclass=inheritedclass,
166                               compodefs=self.compodefs, callconstructor=callconstructor)
167
168   def makepyexe(self, gen):
169     """generate standalone component source (python executable)"""
170     services = []
171     inits = []
172     defs = []
173     for serv in self.services:
174       defs.append(serv.defs)
175       params = []
176       pyparams = []
177       for name, typ in serv.inport:
178         if typ=="file":continue #files are not passed through service interface
179         params.append(name)
180         if typ == "pyobj":
181           pyparams.append("      %s=cPickle.loads(%s)" %(name, name))
182       inparams = ",".join(params)
183       convertinparams = '\n'.join(pyparams)
184
185       params = []
186       pyparams = []
187       for name, typ in serv.outport:
188         if typ=="file":continue #files are not passed through service interface
189         params.append(name)
190         if typ == "pyobj":
191           pyparams.append("      %s=cPickle.dumps(%s,-1)" %(name, name))
192       outparams = ",".join(params)
193       convertoutparams = '\n'.join(pyparams)
194       #dedent and indent the body
195       body=textwrap.dedent(serv.body)
196       body=indent(body,' '*6)
197       service = pyService.substitute(component=self.name, service=serv.name,
198                                      inparams=inparams, outparams=outparams,
199                                      body=body,
200                                      convertinparams=convertinparams,
201                                      convertoutparams=convertoutparams,
202                                     )
203       streams = []
204       for name, typ, dep in serv.instream:
205         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","IN","%s")'% (name, typ, dep))
206       instream = "\n".join(streams)
207       streams = []
208       for name, typ, dep in serv.outstream:
209         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","OUT","%s")'% (name, typ, dep))
210       outstream = "\n".join(streams)
211
212       init = pyinitService.substitute(component=self.name, service=serv.name,
213                                       instream=instream, outstream=outstream)
214       services.append(service)
215       inits.append(init)
216
217     python_path = ",".join([repr(p) for p in self.python_path])
218
219     inheritedclass=self.inheritedclass
220     callconstructor=""
221     if self.inheritedclass:
222       inheritedclass= self.inheritedclass + ","
223       callconstructor="""
224     if hasattr(%s,"__init__"):
225       %s.__init__(self)""" % (self.inheritedclass,self.inheritedclass)
226
227     return pyCompoEXE.substitute(component=self.name, module=gen.module.name,
228                                  servicesdef="\n".join(defs),
229                                  servicesimpl="\n".join(services),
230                                  initservice='\n'.join(inits),
231                                  python_path=python_path,inheritedclass=inheritedclass,
232                                  compodefs=self.compodefs, callconstructor=callconstructor)
233