summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--IkiWiki/Plugin/external.pm25
-rw-r--r--plugins/proxy.py58
-rwxr-xr-xplugins/pythondemo6
3 files changed, 69 insertions, 20 deletions
diff --git a/IkiWiki/Plugin/external.pm b/IkiWiki/Plugin/external.pm
index 4c2e5d2fe..204442c1e 100644
--- a/IkiWiki/Plugin/external.pm
+++ b/IkiWiki/Plugin/external.pm
@@ -68,7 +68,22 @@ sub rpc_call ($$;@) { #{{{
return @{$value->value};
}
elsif ($value->isa('RPC::XML::struct')) {
- return %{$value->value};
+ my %hash=%{$value->value};
+
+ # XML-RPC v1 does not allow for
+ # nil/null/None/undef values to be
+ # transmitted, so until
+ # XML::RPC::Parser honours v2
+ # (<nil/>), external plugins send
+ # a hash with one key "null" pointing
+ # to an empty string.
+ if (exists $hash{null} &&
+ $hash{null} eq "" &&
+ int(keys(%hash)) == 1) {
+ return undef;
+ }
+
+ return %hash;
}
else {
return $value->value;
@@ -92,6 +107,14 @@ sub rpc_call ($$;@) { #{{{
error("XML RPC call error, unknown function: $name");
}
+ # XML-RPC v1 does not allow for nil/null/None/undef
+ # values to be transmitted, so until XML::RPC::Parser
+ # honours v2 (<nil/>), send a hash with one key "null"
+ # pointing to an empty string.
+ if (! defined $ret) {
+ $ret={"null" => ""};
+ }
+
my $string=eval { RPC::XML::response->new($ret)->as_string };
if ($@ && ref $ret) {
# One common reason for serialisation to
diff --git a/plugins/proxy.py b/plugins/proxy.py
index 6f9b1f852..9042f389d 100644
--- a/plugins/proxy.py
+++ b/plugins/proxy.py
@@ -13,8 +13,6 @@ __author__ = 'martin f. krafft <madduck@madduck.net>'
__copyright__ = 'Copyright © ' + __author__
__licence__ = 'GPLv2'
-LOOP_DELAY = 0.1
-
import sys
import time
import xmlrpclib
@@ -31,6 +29,9 @@ class _IkiWikiExtPluginXMLRPCDispatcher(SimpleXMLRPCDispatcher):
# python2.4 and before only took one argument
SimpleXMLRPCDispatcher.__init__(self)
+ def dispatch(self, method, params):
+ return self._dispatch(method, params)
+
class _XMLStreamParser(object):
def __init__(self):
@@ -77,8 +78,8 @@ class _XMLStreamParser(object):
class _IkiWikiExtPluginXMLRPCHandler(object):
- def __init__(self, debug_fn, allow_none=False, encoding=None):
- self._dispatcher = _IkiWikiExtPluginXMLRPCDispatcher(allow_none, encoding)
+ def __init__(self, debug_fn):
+ self._dispatcher = _IkiWikiExtPluginXMLRPCDispatcher()
self.register_function = self._dispatcher.register_function
self._debug_fn = debug_fn
@@ -125,20 +126,28 @@ class _IkiWikiExtPluginXMLRPCHandler(object):
def handle_rpc(self, in_fd, out_fd):
self._debug_fn('waiting for procedure calls from ikiwiki...')
- ret = _IkiWikiExtPluginXMLRPCHandler._read(in_fd)
- if ret is None:
+ xml = _IkiWikiExtPluginXMLRPCHandler._read(in_fd)
+ if xml is None:
# ikiwiki is going down
self._debug_fn('ikiwiki is going down, and so are we...')
return
- self._debug_fn('received procedure call from ikiwiki: [%s]' % ret)
- ret = self._dispatcher._marshaled_dispatch(ret)
- self._debug_fn('sending procedure response to ikiwiki: [%s]' % ret)
- _IkiWikiExtPluginXMLRPCHandler._write(out_fd, ret)
+ self._debug_fn('received procedure call from ikiwiki: [%s]' % xml)
+ params, method = xmlrpclib.loads(xml)
+ ret = self._dispatcher.dispatch(method, params)
+ xml = xmlrpclib.dumps((ret,), methodresponse=True)
+ self._debug_fn('sending procedure response to ikiwiki: [%s]' % xml)
+ _IkiWikiExtPluginXMLRPCHandler._write(out_fd, xml)
return ret
class IkiWikiProcedureProxy(object):
+ # how to communicate None to ikiwiki
+ _IKIWIKI_NIL_SENTINEL = {'null':''}
+
+ # sleep during each iteration
+ _LOOP_DELAY = 0.1
+
def __init__(self, id, in_fd=sys.stdin, out_fd=sys.stdout, debug_fn=None):
self._id = id
self._in_fd = in_fd
@@ -151,9 +160,25 @@ class IkiWikiProcedureProxy(object):
self._xmlrpc_handler = _IkiWikiExtPluginXMLRPCHandler(self._debug_fn)
self._xmlrpc_handler.register_function(self._importme, name='import')
- def hook(self, type, function):
- self._hooks.append((type, function.__name__))
- self._xmlrpc_handler.register_function(function)
+ def hook(self, type, function, name=None):
+ if name is None:
+ name = function.__name__
+ self._hooks.append((type, name))
+
+ def hook_proxy(*args):
+# curpage = args[0]
+# kwargs = dict([args[i:i+2] for i in xrange(1, len(args), 2)])
+ ret = function(*args)
+ self._debug_fn("%s hook `%s' returned: [%s]" % (type, name, ret))
+ if ret == IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL:
+ raise IkiWikiProcedureProxy.InvalidReturnValue, \
+ 'hook functions are not allowed to return %s' \
+ % IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL
+ if ret is None:
+ ret = IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL
+ return ret
+
+ self._xmlrpc_handler.register_function(hook_proxy, name=name)
def _importme(self):
self._debug_fn('importing...')
@@ -161,7 +186,7 @@ class IkiWikiProcedureProxy(object):
self._debug_fn('hooking %s into %s chain...' % (function, type))
self._xmlrpc_handler.send_rpc('hook', self._in_fd, self._out_fd,
id=self._id, type=type, call=function)
- return 0
+ return IkiWikiProcedureProxy._IKIWIKI_NIL_SENTINEL
def run(self):
try:
@@ -169,10 +194,13 @@ class IkiWikiProcedureProxy(object):
ret = self._xmlrpc_handler.handle_rpc(self._in_fd, self._out_fd)
if ret is None:
return
- time.sleep(LOOP_DELAY)
+ time.sleep(IkiWikiProcedureProxy._LOOP_DELAY)
except Exception, e:
print >>sys.stderr, 'uncaught exception: %s' % e
import traceback
print >>sys.stderr, traceback.format_exc(sys.exc_info()[2])
import posix
sys.exit(posix.EX_SOFTWARE)
+
+ class InvalidReturnValue(Exception):
+ pass
diff --git a/plugins/pythondemo b/plugins/pythondemo
index 6d632d524..5038c603b 100755
--- a/plugins/pythondemo
+++ b/plugins/pythondemo
@@ -151,7 +151,7 @@ def htmlize_demo(*args):
kwargs = _arglist_to_dict(args)
debug("hook `htmlize' called with arguments %s" % kwargs)
return kwargs['content']
-proxy.hook('htmlize', htmlize_demo)
+#proxy.hook('htmlize', htmlize_demo)
def pagetemplate_demo(*args):
# Templates are filled out for many different things in ikiwiki, like
@@ -178,12 +178,10 @@ def templatefile_demo(*args):
# change the default ("page.tmpl"). Template files are looked for in
# /usr/share/ikiwiki/templates by default.
#
- # TODO: we cannot really pass undef/None via xml-rpc, so what to do?
kwargs = _arglist_to_dict(args)
debug("hook `templatefile' called with arguments %s" % kwargs)
- raise NotImplementedError
return None
-#proxy.hook('templatefile', templatefile_demo)
+proxy.hook('templatefile', templatefile_demo)
def sanitize_demo(*args):
# Use this to implement html sanitization or anything else that needs to