1 # Copyright (C) 2009-2014 EDF R&D
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.
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.
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
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 Module that defines PYComponent for SALOME components implemented in Python
24 from gener import Component, Invalid
25 from pyth_tmpl import pyinitService, pyService, pyCompoEXE, pyCompo
27 from string import split,rstrip,join
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')
37 class PYComponent(Component):
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*.
42 :param name: gives the name of the component.
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.
59 For example, the following call defines a Python component named "mycompo" with one service s1 (it must have been defined before)::
61 >>> c1 = module_generator.PYComponent('mycompo', services=[s1,],
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)
74 """validate component attributes"""
75 Component.validate(self)
77 if self.kind not in kinds:
78 raise Invalid("kind must be one of %s for component %s" % (kinds,self.name))
80 def makeCompo(self, gen):
81 """generate component sources as a dictionary containing
82 file names (key) and file content (values)
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)
90 if self.kind == "exe":
91 return {"Makefile.am":gen.makeMakefile(self.getMakefileItems(gen)),
92 self.name+".exe":self.makepyexe(gen),
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"]
104 def makepy(self, gen):
105 """generate standard SALOME component source (python module)"""
109 for serv in self.services:
110 defs.append(serv.defs)
113 for name, typ in serv.inport:
114 if typ=="file":continue #files are not passed through service interface
117 pyparams.append(" %s=cPickle.loads(%s)" %(name, name))
118 inparams = ",".join(params)
119 convertinparams = '\n'.join(pyparams)
123 for name, typ in serv.outport:
124 if typ=="file":continue #files are not passed through service interface
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)
134 service = pyService.substitute(component=self.name, service=serv.name, inparams=inparams,
135 outparams=outparams, body= body,
136 convertinparams=convertinparams,
137 convertoutparams=convertoutparams)
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)
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)
147 init = pyinitService.substitute(component=self.name, service=serv.name,
148 instream=instream, outstream=outstream)
149 services.append(service)
152 python_path = ",".join([repr(p) for p in self.python_path])
154 inheritedclass=self.inheritedclass
156 if self.inheritedclass:
157 inheritedclass= self.inheritedclass + ","
159 if hasattr(%s,"__init__"):
160 %s.__init__(self)""" % (self.inheritedclass,self.inheritedclass)
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)
168 def makepyexe(self, gen):
169 """generate standalone component source (python executable)"""
173 for serv in self.services:
174 defs.append(serv.defs)
177 for name, typ in serv.inport:
178 if typ=="file":continue #files are not passed through service interface
181 pyparams.append(" %s=cPickle.loads(%s)" %(name, name))
182 inparams = ",".join(params)
183 convertinparams = '\n'.join(pyparams)
187 for name, typ in serv.outport:
188 if typ=="file":continue #files are not passed through service interface
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,
200 convertinparams=convertinparams,
201 convertoutparams=convertoutparams,
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)
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)
212 init = pyinitService.substitute(component=self.name, service=serv.name,
213 instream=instream, outstream=outstream)
214 services.append(service)
217 python_path = ",".join([repr(p) for p in self.python_path])
219 inheritedclass=self.inheritedclass
221 if self.inheritedclass:
222 inheritedclass= self.inheritedclass + ","
224 if hasattr(%s,"__init__"):
225 %s.__init__(self)""" % (self.inheritedclass,self.inheritedclass)
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)