bridge (constructor): embedded bridge generator:
"embedded" is used to have backend and frontend together in the same process (frontend call backend as a module).
This commit is contained in:
parent
372f54ec2a
commit
6ad826bb0d
5 changed files with 220 additions and 2 deletions
|
|
@ -49,6 +49,9 @@ class Constructor(object):
|
|||
FRONTEND_TEMPLATE = None
|
||||
FRONTEND_DEST = None
|
||||
|
||||
# set to False if your bridge need only core
|
||||
FRONTEND_ACTIVATE = True
|
||||
|
||||
def __init__(self, bridge_template, options):
|
||||
self.bridge_template = bridge_template
|
||||
self.args = options
|
||||
|
|
@ -170,7 +173,7 @@ class Constructor(object):
|
|||
"""Return arguments to user given a signature
|
||||
|
||||
@param signature: signature in the short form (using s,a,i,b etc)
|
||||
@param name: dictionary of arguments name like given by getArguments
|
||||
@param name: dictionary of arguments name like given by getArgumentsDoc
|
||||
@param default: dictionary of default values, like given by getDefault
|
||||
@param unicode_protect: activate unicode protection on strings (return strings as unicode(str))
|
||||
@return: list of arguments that correspond to a signature (e.g.: "sss" return "arg1, arg2, arg3")
|
||||
|
|
@ -222,6 +225,9 @@ class Constructor(object):
|
|||
if side == "core":
|
||||
method = self.generateCoreSide
|
||||
elif side == "frontend":
|
||||
if not self.FRONTEND_ACTIVATE:
|
||||
print(u"This constructor only handle core, please use core side")
|
||||
sys.exit(1)
|
||||
method = self.generateFrontendSide
|
||||
except AttributeError:
|
||||
self._generate(side)
|
||||
|
|
@ -260,7 +266,9 @@ class Constructor(object):
|
|||
'sig_out': function['sig_out'] or '',
|
||||
'category': 'plugin' if function['category'] == 'plugin' else 'core',
|
||||
'name': section,
|
||||
'args': self.getArguments(function['sig_in'], name=arg_doc, default=default)}
|
||||
# arguments with default values
|
||||
'args': self.getArguments(function['sig_in'], name=arg_doc, default=default),
|
||||
}
|
||||
|
||||
extend_method = getattr(self, "{}_completion_{}".format(side, function["type"]))
|
||||
extend_method(completion, function, default, arg_doc, async_)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python2
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
# SàT: a XMPP client
|
||||
# Copyright (C) 2009-2016 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 Affero 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 Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sat.bridge.bridge_constructor import base_constructor
|
||||
# from textwraps import dedent
|
||||
|
||||
|
||||
class EmbeddedConstructor(base_constructor.Constructor):
|
||||
NAME = "embedded"
|
||||
CORE_TEMPLATE = "embedded_template.py"
|
||||
CORE_DEST = "embedded.py"
|
||||
CORE_FORMATS = {
|
||||
'methods': """\
|
||||
def {name}(self, {args}{args_comma}callback=None, errback=None):
|
||||
{ret_routine}
|
||||
""",
|
||||
'signals': """\
|
||||
def {name}(self, {args}):
|
||||
try:
|
||||
cb = self._signals_cbs["{category}"]["{name}"]
|
||||
except KeyError:
|
||||
log.warning(u"ignoring signal {{}}: no callback registered".format({name}))
|
||||
else:
|
||||
cb({args_result})
|
||||
"""
|
||||
}
|
||||
FRONTEND_TEMPLATE = "embedded_frontend_template.py"
|
||||
FRONTEND_DEST = CORE_DEST
|
||||
FRONTEND_FORMATS = {}
|
||||
|
||||
def core_completion_method(self, completion, function, default, arg_doc, async_):
|
||||
completion.update({
|
||||
'debug': "" if not self.args.debug else 'log.debug ("%s")\n%s' % (completion['name'], 8 * ' '),
|
||||
'args_result': self.getArguments(function['sig_in'], name=arg_doc),
|
||||
'args_comma': ', ' if function['sig_in'] else '',
|
||||
})
|
||||
|
||||
if async_:
|
||||
completion["cb_or_lambda"] = "callback" if function['sig_out'] else "lambda dummy: callback()"
|
||||
completion["ret_routine"] = """\
|
||||
d = self._methods_cbs["{name}"]({args_result})
|
||||
if callback is not None:
|
||||
d.addCallback({cb_or_lambda})
|
||||
if errback is None:
|
||||
d.addErrback(lambda failure_: log.error(failure_))
|
||||
else:
|
||||
d.addErrback(errback)
|
||||
return d
|
||||
""".format(**completion)
|
||||
else:
|
||||
completion['ret_or_nothing'] = 'ret' if function['sig_out'] else ''
|
||||
completion["ret_routine"] = """\
|
||||
try:
|
||||
ret = self._methods_cbs["{name}"]({args_result})
|
||||
except Exception as e:
|
||||
if errback is not None:
|
||||
errback(e)
|
||||
else:
|
||||
raise e
|
||||
else:
|
||||
if callback is None:
|
||||
return ret
|
||||
else:
|
||||
callback({ret_or_nothing})""".format(**completion)
|
||||
|
||||
def core_completion_signal(self, completion, function, default, arg_doc, async_):
|
||||
completion.update({
|
||||
'args_result': self.getArguments(function['sig_in'], name=arg_doc),
|
||||
})
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python2
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
# SàT: a XMPP client
|
||||
# Copyright (C) 2009-2016 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 Affero 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 Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sat.bridge.embedded import Bridge
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/env python2
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
# SàT: a XMPP client
|
||||
# Copyright (C) 2009-2016 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 Affero 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 Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sat.core.log import getLogger
|
||||
log = getLogger(__name__)
|
||||
from sat.core import exceptions
|
||||
|
||||
|
||||
class _Bridge(object):
|
||||
def __init__(self):
|
||||
log.info(u"Init embedded bridge...")
|
||||
self._methods_cbs = {}
|
||||
self._signals_cbs = {
|
||||
"core": {},
|
||||
"plugin": {}
|
||||
}
|
||||
|
||||
def register_method(self, name, callback):
|
||||
log.debug(u"registering embedded bridge method [{}]".format(name))
|
||||
if name in self._methods_cbs:
|
||||
raise exceptions.ConflictError(u"method {} is already regitered".format(name))
|
||||
self._methods_cbs[name] = callback
|
||||
|
||||
def register_signal(self, functionName, handler, iface="core"):
|
||||
iface_dict = self._signals_cbs[iface]
|
||||
if functionName in iface_dict:
|
||||
raise exceptions.ConflictError(u"signal {name} is already regitered for interface {iface}".format(name=functionName, iface=iface))
|
||||
iface_dict[functionName] = handler
|
||||
|
||||
def call_method(self, name, out_sign, async_, args, kwargs):
|
||||
callback = kwargs.pop("callback", None)
|
||||
errback = kwargs.pop("errback", None)
|
||||
if async_:
|
||||
d = self._methods_cbs[name](*args, **kwargs)
|
||||
if callback is not None:
|
||||
d.addCallback(callback if out_sign else lambda dummy: callback())
|
||||
if errback is None:
|
||||
d.addErrback(lambda failure_: log.error(failure_))
|
||||
else:
|
||||
d.addErrback(errback)
|
||||
return d
|
||||
else:
|
||||
try:
|
||||
ret = self._methods_cbs[name](*args, **kwargs)
|
||||
except Exception as e:
|
||||
if errback is not None:
|
||||
errback(e)
|
||||
else:
|
||||
raise e
|
||||
else:
|
||||
if callback is None:
|
||||
return ret
|
||||
else:
|
||||
if out_sign:
|
||||
callback(ret)
|
||||
else:
|
||||
callback()
|
||||
|
||||
def send_signal(self, name, args, kwargs):
|
||||
try:
|
||||
cb = self._signals_cbs["plugin"][name]
|
||||
except KeyError:
|
||||
log.warning(u"ignoring signal {}: no callback registered".format(name))
|
||||
else:
|
||||
cb(*args, **kwargs)
|
||||
|
||||
def addMethod(self, name, int_suffix, in_sign, out_sign, method, async=False, doc={}):
|
||||
#FIXME: doc parameter is kept only temporary, the time to remove it from calls
|
||||
log.debug("Adding method [{}] to embedded bridge".format(name))
|
||||
self.register_method(name, method)
|
||||
setattr(self.__class__, name, lambda self_, *args, **kwargs: self.call_method(name, out_sign, async, args, kwargs))
|
||||
|
||||
def addSignal(self, name, int_suffix, signature, doc={}):
|
||||
setattr(self.__class__, name, lambda self_, *args, **kwargs: self.send_signal(name, args, kwargs))
|
||||
|
||||
## signals ##
|
||||
|
||||
##SIGNALS_PART##
|
||||
## methods ##
|
||||
|
||||
##METHODS_PART##
|
||||
|
||||
# we want the same instance for both core and frontend
|
||||
bridge = None
|
||||
def Bridge():
|
||||
global bridge
|
||||
if bridge is None:
|
||||
bridge = _Bridge()
|
||||
return bridge
|
||||
Loading…
Reference in a new issue