# HG changeset patch # User Goffi # Date 1295828520 -3600 # Node ID b5f1f3dc9ac6bfa0aaae43f5d45c3485c1ffa395 # Parent 27bc5d7732a3fac5d5a802c4ab611a3758b86b58 bridge: automatic bridge generator, first draft diff -r 27bc5d7732a3 -r b5f1f3dc9ac6 src/bridge/bridge_contructor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/bridge/bridge_contructor.py Mon Jan 24 01:22:00 2011 +0100 @@ -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 . +""" + + +#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): + 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() diff -r 27bc5d7732a3 -r b5f1f3dc9ac6 src/bridge/bridge_template.ini --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/bridge/bridge_template.ini Mon Jan 24 01:22:00 2011 +0100 @@ -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 +