The runners are the processes that perform long-running tasks, such as moving messages around the Mailman queues. Some runners don’t manage queues, such as the LMTP and REST API handling runners. Each runner that manages a queue directory though, is responsible for a slice of the hash space. It processes all the files in its slice, sleeps a little while, then wakes up and runs through its queue files again.

Basic architecture

The basic architecture of runner is implemented in the base class that all runners inherit from. This base class implements a .run() method that runs continuously in a loop until the .stop() method is called.

>>> mlist = create_list('')

Here is a very simple derived runner class. Runners use a configuration section in the configuration files to determine run characteristics, such as the queue directory to use. Here we push a configuration section for the test runner.

>>> config.push('test-runner', """
... [runner.test]
... max_restarts: 1
... """)

>>> from mailman.core.runner import Runner
>>> class TestableRunner(Runner):
...     def _dispose(self, mlist, msg, msgdata):
...         self.msg = msg
...         self.msgdata = msgdata
...         return False
...     def _do_periodic(self):
...         self.stop()
...     def _snooze(self, filecnt):
...         return

>>> runner = TestableRunner('test')

This runner doesn’t do much except run once, storing the message and metadata on instance variables.

>>> msg = message_from_string("""\
... From:
... To:
... A test message.
... """)
>>> switchboard = config.switchboards['test']
>>> filebase = switchboard.enqueue(msg, listid=mlist.list_id,
...                                foo='yes', bar='no')
>>> print(runner.msg.as_string())

A test message.

>>> dump_msgdata(runner.msgdata)
_parsemsg: False
bar      : no
foo      : yes
lang     : en
listid   :
version  : 3

XXX More of the Runner API should be tested.