Permalink
Browse files

Python external plugin: Catches Ikiwiki pseudo exceptions

Detail of the closed bug (discussed here [1]). Let say we have a python
plugin `foo`, defining a `foo` directive, communicating with Ikiwiki
unsing RPC calls.

- Ikiwiki has to render a `[[!foo]]` directive, and calls the `preprocess` function of the `foo` plugin.
- The `foo` plugin needs some data to handle this request, and calls an Ikiwiki function.
- An error occurs (for instance, requested data is about a non-existent
  page), so Ikiwiki skips the current function call. Instead, it will
  render an error as the directive.

Here, the `foo` plugin has not been notified that the `preprocess` call
is cancelled, and is still waiting for its answer.

- Ikiwiki has to process another `[[!foo]]` directive, and calls the
  `preprocess` function of `foo`.
- The `foo` plugin misinterprets this request as the answer to its
  previous request, and the whole RPC communication is messed up.

This patch corrects this, in the last item of the description, by making
the `foo` plugin raise an exception if the expected answer is a new
request. This exception is catched, and the request is normally handled.

[1] http://ikiwiki.info/bugs/Error_with_external_plugins/
  • Loading branch information...
Louis
Louis committed Jul 4, 2015
1 parent 1ed0f1e commit 32da347566a7559ca1ef145880efe961dbcc012f
Showing with 35 additions and 7 deletions.
  1. +35 −7 plugins/proxy.py
View
@@ -69,6 +69,13 @@ class AlreadyImported (Exception):
pass
class AbortedCall (Exception):
"""Raised when RPC call has been aborted by Ikiwiki."""
def __init__(self, line):
self.line = line
class _IkiWikiExtPluginXMLRPCDispatcher(_xmlrpc_server.SimpleXMLRPCDispatcher):
def __init__(self, allow_none=False, encoding=None):
@@ -181,15 +188,26 @@ def send_rpc(self, cmd, in_fd, out_fd, *args, **kwargs):
self._debug_fn('ikiwiki is going down, and so are we...')
raise GoingDown()
data = _xmlrpc_client.loads(xml)[0][0]
data = _xmlrpc_client.loads(xml)
methodname = data[1]
if methodname is not None:
# We expected an answer to our RCP call, but an error occured on
# the ikiwiki side, and Ikiwiki is cancelling this request, and
# doing a new one.
raise AbortedCall(xml)
self._debug_fn(
'parsed data from response to procedure {0}: [{1}]'.format(
cmd, repr(data)))
return data
cmd, repr(data[0][0])))
return data[0][0]
def handle_rpc(self, in_fd, out_fd):
def handle_rpc(self, in_fd, out_fd, first_line):
self._debug_fn('waiting for procedure calls from ikiwiki...')
xml = _IkiWikiExtPluginXMLRPCHandler._read(in_fd)
if first_line is None:
xml = _IkiWikiExtPluginXMLRPCHandler._read(in_fd)
else:
xml = XMLStreamParser().parse(first_line)
if xml is None:
# ikiwiki is going down
self._debug_fn('ikiwiki is going down, and so are we...')
@@ -313,9 +331,19 @@ def error(self, msg):
def run(self):
try:
first_line = None
while True:
ret = self._xmlrpc_handler.handle_rpc(
self._in_fd, self._out_fd)
try:
ret = self._xmlrpc_handler.handle_rpc(
self._in_fd, self._out_fd,
first_line=first_line)
first_line = None
except AbortedCall as error:
# Current call has been aborted on the Ikiwiki side: we
# received a method call instead of the answer we were
# expecting. So we begin handling a new request, and the
# first line of this request is the `error.line`.
first_line = error.line
time.sleep(IkiWikiProcedureProxy._LOOP_DELAY)
except GoingDown:
return

0 comments on commit 32da347

Please sign in to comment.