Refactor remote procedure calls in the proxy

Add an rpc() method to the proxy to allow users to call remote
procedures, and route the proxy's own import registration via this
function.

Also, implement convenience functions for the RPC calls exported in the
IkiWiki::XML::RPC namespace.

Signed-off-by: martin f. krafft <madduck@madduck.net>
master
martin f. krafft 2008-03-21 19:12:15 +01:00 committed by Joey Hess
parent 33e3528cd9
commit 86ec72c826
1 changed files with 81 additions and 14 deletions

View File

@ -107,8 +107,8 @@ class _IkiWikiExtPluginXMLRPCHandler(object):
if ret is not None: if ret is not None:
return ret return ret
def send_rpc(self, cmd, in_fd, out_fd, **kwargs): def send_rpc(self, cmd, in_fd, out_fd, *args, **kwargs):
xml = xmlrpclib.dumps(sum(kwargs.iteritems(), ()), cmd) xml = xmlrpclib.dumps(sum(kwargs.iteritems(), args), cmd)
self._debug_fn("calling ikiwiki procedure `%s': [%s]" % (cmd, xml)) self._debug_fn("calling ikiwiki procedure `%s': [%s]" % (cmd, xml))
_IkiWikiExtPluginXMLRPCHandler._write(out_fd, xml) _IkiWikiExtPluginXMLRPCHandler._write(out_fd, xml)
@ -120,7 +120,7 @@ class _IkiWikiExtPluginXMLRPCHandler(object):
# ikiwiki is going down # ikiwiki is going down
return None return None
data = xmlrpclib.loads(xml)[0] data = xmlrpclib.loads(xml)[0][0]
self._debug_fn('parsed data from response to procedure %s: [%s]' % (cmd, data)) self._debug_fn('parsed data from response to procedure %s: [%s]' % (cmd, data))
return data return data
@ -153,6 +153,8 @@ class IkiWikiProcedureProxy(object):
self._in_fd = in_fd self._in_fd = in_fd
self._out_fd = out_fd self._out_fd = out_fd
self._hooks = list() self._hooks = list()
self._functions = list()
self._imported = False
if debug_fn is not None: if debug_fn is not None:
self._debug_fn = debug_fn self._debug_fn = debug_fn
else: else:
@ -160,10 +162,28 @@ class IkiWikiProcedureProxy(object):
self._xmlrpc_handler = _IkiWikiExtPluginXMLRPCHandler(self._debug_fn) self._xmlrpc_handler = _IkiWikiExtPluginXMLRPCHandler(self._debug_fn)
self._xmlrpc_handler.register_function(self._importme, name='import') self._xmlrpc_handler.register_function(self._importme, name='import')
def rpc(self, cmd, *args, **kwargs):
def subst_none(seq):
for i in seq:
if i is None:
yield IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL
else:
yield i
args = list(subst_none(args))
kwargs = dict(zip(kwargs.keys(), list(subst_none(kwargs.itervalues()))))
ret = self._xmlrpc_handler.send_rpc(cmd, self._in_fd, self._out_fd,
*args, **kwargs)
if ret == IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL:
ret = None
return ret
def hook(self, type, function, name=None, last=False): def hook(self, type, function, name=None, last=False):
if self._imported:
raise IkiWikiProcedureProxy.AlreadyImported
if name is None: if name is None:
name = function.__name__ name = function.__name__
self._hooks.append((type, name, last))
def hook_proxy(*args): def hook_proxy(*args):
# curpage = args[0] # curpage = args[0]
@ -178,16 +198,49 @@ class IkiWikiProcedureProxy(object):
ret = IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL ret = IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL
return ret return ret
self._hooks.append((type, name, last))
self._xmlrpc_handler.register_function(hook_proxy, name=name) self._xmlrpc_handler.register_function(hook_proxy, name=name)
def _importme(self): def inject(self, rname, function, name=None, memoize=True):
self._debug_fn('importing...') if self._imported:
for type, function, last in self._hooks: raise IkiWikiProcedureProxy.AlreadyImported
self._debug_fn('hooking %s into %s chain...' % (function, type))
self._xmlrpc_handler.send_rpc('hook', self._in_fd, self._out_fd, if name is None:
id=self._id, type=type, call=function, name = function.__name__
last=last)
return IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL self._functions.append((rname, name, memoize))
self._xmlrpc_handler.register_function(function, name=name)
def getargv(self):
return self.rpc('getargv')
def setargv(self, argv):
return self.rpc('setargv', argv)
def getvar(self, hash, key):
return self.rpc('getvar', hash, key)
def setvar(self, hash, key, value):
return self.rpc('setvar', hash, key, value)
def getstate(self, page, id, key):
return self.rpc('getstate', page, id, key)
def setstate(self, page, id, key, value):
return self.rpc('setstate', page, id, key, value)
def pagespec_match(self, spec):
return self.rpc('pagespec_match', spec)
def error(self, msg):
try:
self.rpc('error', msg)
except IOError, e:
if e.errno != 32:
raise
import posix
sys.exit(posix.EX_SOFTWARE)
def run(self): def run(self):
try: try:
@ -197,11 +250,25 @@ class IkiWikiProcedureProxy(object):
return return
time.sleep(IkiWikiProcedureProxy._LOOP_DELAY) time.sleep(IkiWikiProcedureProxy._LOOP_DELAY)
except Exception, e: except Exception, e:
print >>sys.stderr, 'uncaught exception: %s' % e
import traceback import traceback
print >>sys.stderr, traceback.format_exc(sys.exc_info()[2]) self.error('uncaught exception: %s\n%s' \
% (e, traceback.format_exc(sys.exc_info()[2])))
import posix import posix
sys.exit(posix.EX_SOFTWARE) sys.exit(posix.EX_SOFTWARE)
def _importme(self):
self._debug_fn('importing...')
for type, function, last in self._hooks:
self._debug_fn('hooking %s into %s chain...' % (function, type))
self.rpc('hook', id=self._id, type=type, call=function, last=last)
for rname, function, memoize in self._functions:
self._debug_fn('injecting %s as %s...' % (function, rname))
self.rpc('inject', name=rname, call=function, memoize=memoize)
self._imported = True
return IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL
class InvalidReturnValue(Exception): class InvalidReturnValue(Exception):
pass pass
class AlreadyImported(Exception):
pass