bridge: automatic bridge generator, first draft

This commit is contained in:
Goffi 2011-01-24 01:22:00 +01:00
commit f1dab02efa
2 changed files with 550 additions and 0 deletions

234
src/bridge/bridge_contructor.py Executable file
View file

@ -0,0 +1,234 @@
#!/usr/bin/python
#-*- coding: utf-8 -*-
"""
SAT: a jabber client
Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
#consts
NAME = u"bridge_constructor"
VERSION="0.1.0"
ABOUT = NAME+u""" v%s (c) Jérôme Poisson (aka Goffi) 2011
---
"""+NAME+u""" Copyright (C) 2011 Jérôme Poisson (aka Goffi)
This program comes with ABSOLUTELY NO WARRANTY;
This is free software, and you are welcome to redistribute it
under certain conditions.
---
This script construct a SàT bridge using the given protocol
"""
MANAGED_PROTOCOLES=['dbus']
DEFAULT_PROTOCOLE='dbus'
import sys
import os
from os import path
from optparse import OptionParser
from ConfigParser import RawConfigParser as Parser
from ConfigParser import NoOptionError
class ParseError(Exception):
#Used when the signature parsing is going wrong (invalid signature ?)
pass
class Constructor:
def __init__(self, bridge_template, options):
self.bridge_template = bridge_template
self.options = options
def getValues(self, name):
"""Return values of a function in a dict
@param name: Name of the function to get
@return: dict, each key has the config value or None if the value is not set"""
function={}
for option in ['type','category','sig_in','sig_out','doc']:
try:
value = self.bridge_template.get(name, option)
except NoOptionError:
value = None
function[option] = value
return function
def getArguments(self, signature):
"""Return arguments to user given a signature
@param signature: signature in the short form (using s,a,i,b etc)
@return: list of arguments that correspond to a signature (e.g.: "sss" return "arg1, arg2, arg3")"""
i=0
idx=0
attr_string=""
while i<len(signature):
if signature[i] not in ['b','y','n','i','x','q','u','t','d','s','a']:
raise ParseError("Unmanaged attribute type [%c]" % signature[i])
attr_string += ("" if idx==0 else ", ") + ("arg_%i" % idx)
idx+=1
if signature[i] == 'a':
i+=1
if signature[i]!='{' and signature[i]!='(': #FIXME: must manage tuples out of arrays
i+=1
continue #we have a simple type for the array
opening_car = signature[i]
closing_car = '}' if opening_car == '{' else ')'
opening_count = 1
while (True): #we have a dict or a list of tuples
i+=1
if i>=len(signature):
raise ParseError("missing }")
if signature[i] == opening_car:
opening_count+=1
if signature[i] == closing_car:
opening_count-=1
if opening_count == 0:
break
i+=1
return attr_string
def generateCoreSide(self):
"""create the constructor in SàT core side (backend)"""
raise NotImplementedError
class DbusConstructor(Constructor):
def __init__(self, bridge_template, options):
Constructor.__init__(self, bridge_template, options)
self.core_template="dbus_core_template.py"
self.core_dest="DBus.py"
def generateCoreSide(self):
signals_part = []
methods_part = []
direct_calls = []
sections = self.bridge_template.sections()
sections.sort()
for section in sections:
function = self.getValues(section)
print ("Adding %s %s" % (section, function["type"]))
completion = {
'sig_in':function['sig_in'] or '',
'sig_out':function['sig_out'] or '',
'category':'REQ' if function['category'] == 'request' else 'COMM',
'name':section,
'args':self.getArguments(function['sig_in'])
}
if function["type"] == "signal":
completion['body'] = "pass" if not self.options.debug else 'debug ("%s")' % section
signals_part.append("""\
@dbus.service.signal(const_INT_PREFIX+const_%(category)s_SUFFIX,
signature='%(sig_in)s')
def %(name)s(self, %(args)s):
%(body)s
""" % completion)
direct_calls.append("""\
def %(name)s(self, %(args)s):
self.dbus_bridge.%(name)s(%(args)s)
""" % completion)
elif function["type"] == "method":
completion['debug'] = "" if not self.options.debug else 'debug ("%s")\n%s' % (section,8*' ')
methods_part.append("""\
@dbus.service.method(const_INT_PREFIX+const_%(category)s_SUFFIX,
in_signature='%(sig_in)s', out_signature='%(sig_out)s')
def %(name)s(self, %(args)s):
%(debug)sreturn self.cb["%(name)s"](%(args)s)
""" % completion)
#at this point, signals_part, methods_part and direct_calls should be filled,
#we just have to place them in the right part of the template
core_bridge = []
try:
with open(self.core_template) as core_template:
for line in core_template:
if line.startswith('##SIGNALS_PART##'):
core_bridge.extend(signals_part)
elif line.startswith('##METHODS_PART##'):
core_bridge.extend(methods_part)
elif line.startswith('##DIRECT_CALLS##'):
core_bridge.extend(direct_calls)
else:
core_bridge.append(line.replace('\n',''))
except IOError:
print ("Can't open template file [%s]" % self.core_template)
sys.exit(1)
#now we write to final file
if os.path.exists(self.core_dest) and not self.options.force:
print ("The destination file [%s] already exists ! Use --force to overwrite it" % self.core_dest)
try:
with open(self.core_dest,'w') as dest_file:
dest_file.write('\n'.join(core_bridge))
except IOError:
print ("Can't open destination file [%s]" % self.dest_file)
class ConstructorError(Exception):
pass
class ConstructorFactory:
def create(self, bridge_template, options):
if options.protocole=='dbus':
return DbusConstructor(bridge_template, options)
raise ConstructorError('Unknown constructor type')
class BridgeConstructor:
def __init__(self):
self.options = None
def check_options(self):
"""Check command line options"""
_usage="""
%prog [options]
%prog --help for options list
"""
parser = OptionParser(usage=_usage,version=ABOUT % VERSION)
parser.add_option("-p", "--protocole", action="store", type="string", default=DEFAULT_PROTOCOLE,
help="Generate bridge using PROTOCOLE (default: %s, possible values: [%s])" % (DEFAULT_PROTOCOLE, ", ".join(MANAGED_PROTOCOLES)))
parser.add_option("-s", "--side", action="store", type="string", default="core",
help="Which side of the bridge do you want to make ? (default: %default, possible values: [core, frontend])")
parser.add_option("-t", "--template", action="store", type="string", default='bridge_template.ini',
help="Use TEMPLATE to generate bridge (default: %default)")
parser.add_option("-f", "--force", action="store_true", default=False,
help=("Force overwritting of existing files"))
parser.add_option("-d", "--debug", action="store_true", default=False,
help=("Add debug information printing"))
(self.options, args) = parser.parse_args()
return args
def go(self):
self.check_options()
self.template = Parser()
try:
self.template.readfp(open(self.options.template))
except IOError:
print ("The template file doesn't exist or is not accessible")
exit(1)
constructor = ConstructorFactory().create(self.template, self.options)
if self.options.side == "core":
constructor.generateCoreSide()
if __name__ == "__main__":
bc = BridgeConstructor()
bc.go()

View file

@ -0,0 +1,316 @@
;signals
[connected]
type=signal
category=communication
sig_in=s
doc=Connection is done
[disconnected]
type=signal
category=communication
sig_in=s
doc=Connection is finished or lost
[connection_error]
type=signal
category=communication
sig_in=ss
doc=Something went wront with the connection
[newContact]
type=signal
category=communication
sig_in=sa{ss}ass
doc=New contact received in roster
[newMessage]
type=signal
category=communication
sig_in=sssss
doc=A message has been received
[newAlert]
type=signal
category=communication
sig_in=ssss
doc=A message has been received
[presenceUpdate]
type=signal
category=communication
sig_in=ssia{ss}s
doc=Somebody changed his presence informations.
[subscribe]
type=signal
category=communication
sig_in=sss
doc=Somebody wants to be added in roster list
[paramUpdate]
type=signal
category=communication
sig_in=ssss
doc=A parameter has been changed
[contactDeleted]
type=signal
category=communication
sig_in=ss
doc=A contact has been supressed from roster list
[askConfirmation]
type=signal
category=request
sig_in=ssa{ss}
doc=A confirmation is needed for an action
[actionResult]
type=signal
category=request
sig_in=ssa{ss}
doc=Requested result of an action
[actionResultExt]
type=signal
category=request
sig_in=ssa{sa{ss}}
doc=Requested result of an action (Extended)
[updatedValue]
type=signal
category=request
sig_in=sa{ss}
doc=A value has been updated
;methods
[getVersion]
type=method
category=request
sig_in=
sig_out=s
doc=Get "Salut à Toi" version
[getProfileName]
type=method
category=request
sig_in=s
sig_out=s
doc=Get real profile name from profile key
[getProfilesList]
type=method
category=request
sig_in=
sig_out=as
doc=Get all profiles
[createProfile]
type=method
category=request
sig_in=s
sig_out=i
doc=Create a new profile
[deleteProfile]
type=method
category=request
sig_in=s
sig_out=i
doc=Delete a profile
[registerNewAccount]
type=method
category=communication
sig_in=sssi
sig_out=s
doc=Register a new account on a given server
[connect]
type=method
category=communication
sig_in=s
sig_out=
param_0_default="@DEFAULT@"
doc=Connect a profile
[disconnect]
type=method
category=communication
sig_in=s
sig_out=
param_0_default="@DEFAULT@"
doc=Disconnect a profile
[isConnected]
type=method
category=communication
sig_in=
sig_out=b
param_0_default="@DEFAULT@"
doc=Tell if a profile is connected
[getContacts]
type=method
category=communication
sig_in=s
sig_out=a(sa{ss}as)
param_0_default="@DEFAULT@"
doc=Return informations about all contacts
[getPresenceStatus]
type=method
category=communication
sig_in=s
sig_out=a{sa{s(sia{ss})}}
param_0_default="@DEFAULT@"
doc=Return presence informations of all contacts
[getWaitingSub]
type=method
category=communication
sig_in=s
sig_out=a{ss}
param_0_default="@DEFAULT@"
doc=Get subscription requests in queue
[sendMessage]
type=method
category=communication
sig_in=sssss
sig_out=
param_4_default="@DEFAULT@"
doc=Send a message
[setPresence]
type=method
category=communication
sig_in=ssia{ss}s
sig_out=
param_4_default="@DEFAULT@"
doc=Set presence information for the profile
[subscription]
type=method
category=communication
sig_in=sss
sig_out=
param_2_default="@DEFAULT@"
doc=Send subscription request/answer to a contact
[setParam]
type=method
category=communication
sig_in=ssss
sig_out=
param_3_default="@DEFAULT@"
doc=Change a parameter
[getParamA]
type=method
category=communication
sig_in=sss
sig_out=s
param_2_default="@DEFAULT@"
doc=Helper method to get a parameter's attribute
[getParamsUI]
type=method
category=communication
sig_in=s
sig_out=s
param_0_default="@DEFAULT@"
doc=Return a SàT XMLUI for parameters
[getParams]
type=method
category=communication
sig_in=s
sig_out=s
param_0_default="@DEFAULT@"
doc=Return XML of parameters
[getParamsForCategory]
type=method
category=communication
sig_in=ss
sig_out=s
param_1_default="@DEFAULT@"
doc=Return a xml of all params in a category
[getParamsCategories]
type=method
category=communication
sig_in=
sig_out=as
doc=Get all categories currently existing in parameters
[getHistory]
type=method
category=communication
sig_in=ssi
sig_out=a{i(ss)}
doc=Get history of a communication between two entities
[addContact]
type=method
category=communication
sig_in=ss
sig_out=
param_1_default="@DEFAULT@"
doc=Add a contact to profile's roster list
[delContact]
type=method
category=communication
sig_in=ss
sig_out=
param_1_default="@DEFAULT@"
doc=Remove a contact from profile's roster list
[launchAction]
type=method
category=request
sig_in=sa{ss}s
sig_out=s
param_2_default="@DEFAULT@"
doc=Launch a specific action
[confirmationAnswer]
type=method
category=request
sig_in=sba{ss}
sig_out=
doc=Give answer to a confirmation request
[getProgress]
type=method
category=request
sig_in=s
sig_out=a{ss}
doc=Get progress information for an action
[getMenus]
type=method
category=request
sig_in=
sig_out=a(sss)
doc=Get all additional menus
[getMenuHelp]
type=method
category=request
sig_in=sss
sig_out=s
param_2="NORMAL"
doc=Get help informationd for a menu
[callMenu]
type=method
category=request
sig_in=ssss
sig_out=s
doc=Execute action associated with a menu