comparison src/bridge/bridge_contructor.py @ 265:b5f1f3dc9ac6

bridge: automatic bridge generator, first draft
author Goffi <goffi@goffi.org>
date Mon, 24 Jan 2011 01:22:00 +0100
parents
children c4b84a2d2ad1
comparison
equal deleted inserted replaced
264:27bc5d7732a3 265:b5f1f3dc9ac6
1 #!/usr/bin/python
2 #-*- coding: utf-8 -*-
3
4 """
5 SAT: a jabber client
6 Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org)
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """
21
22
23 #consts
24 NAME = u"bridge_constructor"
25 VERSION="0.1.0"
26 ABOUT = NAME+u""" v%s (c) Jérôme Poisson (aka Goffi) 2011
27
28 ---
29 """+NAME+u""" Copyright (C) 2011 Jérôme Poisson (aka Goffi)
30 This program comes with ABSOLUTELY NO WARRANTY;
31 This is free software, and you are welcome to redistribute it
32 under certain conditions.
33 ---
34
35 This script construct a SàT bridge using the given protocol
36 """
37 MANAGED_PROTOCOLES=['dbus']
38 DEFAULT_PROTOCOLE='dbus'
39
40 import sys
41 import os
42 from os import path
43 from optparse import OptionParser
44 from ConfigParser import RawConfigParser as Parser
45 from ConfigParser import NoOptionError
46
47
48 class ParseError(Exception):
49 #Used when the signature parsing is going wrong (invalid signature ?)
50 pass
51
52 class Constructor:
53
54 def __init__(self, bridge_template, options):
55 self.bridge_template = bridge_template
56 self.options = options
57
58 def getValues(self, name):
59 """Return values of a function in a dict
60 @param name: Name of the function to get
61 @return: dict, each key has the config value or None if the value is not set"""
62 function={}
63 for option in ['type','category','sig_in','sig_out','doc']:
64 try:
65 value = self.bridge_template.get(name, option)
66 except NoOptionError:
67 value = None
68 function[option] = value
69 return function
70
71 def getArguments(self, signature):
72 """Return arguments to user given a signature
73 @param signature: signature in the short form (using s,a,i,b etc)
74 @return: list of arguments that correspond to a signature (e.g.: "sss" return "arg1, arg2, arg3")"""
75 i=0
76 idx=0
77 attr_string=""
78 while i<len(signature):
79 if signature[i] not in ['b','y','n','i','x','q','u','t','d','s','a']:
80 raise ParseError("Unmanaged attribute type [%c]" % signature[i])
81
82 attr_string += ("" if idx==0 else ", ") + ("arg_%i" % idx)
83 idx+=1
84
85 if signature[i] == 'a':
86 i+=1
87 if signature[i]!='{' and signature[i]!='(': #FIXME: must manage tuples out of arrays
88 i+=1
89 continue #we have a simple type for the array
90 opening_car = signature[i]
91 closing_car = '}' if opening_car == '{' else ')'
92 opening_count = 1
93 while (True): #we have a dict or a list of tuples
94 i+=1
95 if i>=len(signature):
96 raise ParseError("missing }")
97 if signature[i] == opening_car:
98 opening_count+=1
99 if signature[i] == closing_car:
100 opening_count-=1
101 if opening_count == 0:
102 break
103 i+=1
104 return attr_string
105
106 def generateCoreSide(self):
107 """create the constructor in SàT core side (backend)"""
108 raise NotImplementedError
109
110 class DbusConstructor(Constructor):
111
112 def __init__(self, bridge_template, options):
113 Constructor.__init__(self, bridge_template, options)
114 self.core_template="dbus_core_template.py"
115 self.core_dest="DBus.py"
116
117 def generateCoreSide(self):
118 signals_part = []
119 methods_part = []
120 direct_calls = []
121 sections = self.bridge_template.sections()
122 sections.sort()
123 for section in sections:
124 function = self.getValues(section)
125 print ("Adding %s %s" % (section, function["type"]))
126 completion = {
127 'sig_in':function['sig_in'] or '',
128 'sig_out':function['sig_out'] or '',
129 'category':'REQ' if function['category'] == 'request' else 'COMM',
130 'name':section,
131 'args':self.getArguments(function['sig_in'])
132 }
133
134 if function["type"] == "signal":
135 completion['body'] = "pass" if not self.options.debug else 'debug ("%s")' % section
136 signals_part.append("""\
137 @dbus.service.signal(const_INT_PREFIX+const_%(category)s_SUFFIX,
138 signature='%(sig_in)s')
139 def %(name)s(self, %(args)s):
140 %(body)s
141 """ % completion)
142 direct_calls.append("""\
143 def %(name)s(self, %(args)s):
144 self.dbus_bridge.%(name)s(%(args)s)
145 """ % completion)
146
147 elif function["type"] == "method":
148 completion['debug'] = "" if not self.options.debug else 'debug ("%s")\n%s' % (section,8*' ')
149 methods_part.append("""\
150 @dbus.service.method(const_INT_PREFIX+const_%(category)s_SUFFIX,
151 in_signature='%(sig_in)s', out_signature='%(sig_out)s')
152 def %(name)s(self, %(args)s):
153 %(debug)sreturn self.cb["%(name)s"](%(args)s)
154 """ % completion)
155
156 #at this point, signals_part, methods_part and direct_calls should be filled,
157 #we just have to place them in the right part of the template
158 core_bridge = []
159 try:
160 with open(self.core_template) as core_template:
161 for line in core_template:
162 if line.startswith('##SIGNALS_PART##'):
163 core_bridge.extend(signals_part)
164 elif line.startswith('##METHODS_PART##'):
165 core_bridge.extend(methods_part)
166 elif line.startswith('##DIRECT_CALLS##'):
167 core_bridge.extend(direct_calls)
168 else:
169 core_bridge.append(line.replace('\n',''))
170 except IOError:
171 print ("Can't open template file [%s]" % self.core_template)
172 sys.exit(1)
173
174 #now we write to final file
175 if os.path.exists(self.core_dest) and not self.options.force:
176 print ("The destination file [%s] already exists ! Use --force to overwrite it" % self.core_dest)
177 try:
178 with open(self.core_dest,'w') as dest_file:
179 dest_file.write('\n'.join(core_bridge))
180 except IOError:
181 print ("Can't open destination file [%s]" % self.dest_file)
182
183 class ConstructorError(Exception):
184 pass
185
186 class ConstructorFactory:
187 def create(self, bridge_template, options):
188 if options.protocole=='dbus':
189 return DbusConstructor(bridge_template, options)
190
191 raise ConstructorError('Unknown constructor type')
192
193 class BridgeConstructor:
194 def __init__(self):
195 self.options = None
196
197 def check_options(self):
198 """Check command line options"""
199 _usage="""
200 %prog [options]
201
202 %prog --help for options list
203 """
204 parser = OptionParser(usage=_usage,version=ABOUT % VERSION)
205
206 parser.add_option("-p", "--protocole", action="store", type="string", default=DEFAULT_PROTOCOLE,
207 help="Generate bridge using PROTOCOLE (default: %s, possible values: [%s])" % (DEFAULT_PROTOCOLE, ", ".join(MANAGED_PROTOCOLES)))
208 parser.add_option("-s", "--side", action="store", type="string", default="core",
209 help="Which side of the bridge do you want to make ? (default: %default, possible values: [core, frontend])")
210 parser.add_option("-t", "--template", action="store", type="string", default='bridge_template.ini',
211 help="Use TEMPLATE to generate bridge (default: %default)")
212 parser.add_option("-f", "--force", action="store_true", default=False,
213 help=("Force overwritting of existing files"))
214 parser.add_option("-d", "--debug", action="store_true", default=False,
215 help=("Add debug information printing"))
216
217 (self.options, args) = parser.parse_args()
218 return args
219
220 def go(self):
221 self.check_options()
222 self.template = Parser()
223 try:
224 self.template.readfp(open(self.options.template))
225 except IOError:
226 print ("The template file doesn't exist or is not accessible")
227 exit(1)
228 constructor = ConstructorFactory().create(self.template, self.options)
229 if self.options.side == "core":
230 constructor.generateCoreSide()
231
232 if __name__ == "__main__":
233 bc = BridgeConstructor()
234 bc.go()