summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBarry Warsaw2015-01-03 00:06:17 -0500
committerBarry Warsaw2015-01-03 00:06:17 -0500
commitde181c1a40965a3a7deedd56a034a946f45b6984 (patch)
treef5afb9467d8dde66b6c9693f225bfc01f18fd8aa /src
parentc5e5a12e9a79cbf2cc6bf65ceec7391ce3844ba3 (diff)
downloadmailman-de181c1a40965a3a7deedd56a034a946f45b6984.tar.gz
mailman-de181c1a40965a3a7deedd56a034a946f45b6984.tar.zst
mailman-de181c1a40965a3a7deedd56a034a946f45b6984.zip
Diffstat (limited to 'src')
-rw-r--r--src/mailman/app/inject.py8
-rw-r--r--src/mailman/commands/docs/info.rst29
-rw-r--r--src/mailman/config/config.py1
-rw-r--r--src/mailman/config/mailman.cfg4
-rw-r--r--src/mailman/config/schema.cfg2
-rw-r--r--src/mailman/config/tests/test_configuration.py6
-rw-r--r--src/mailman/docs/NEWS.rst8
-rw-r--r--src/mailman/rest/docs/queues.rst174
-rw-r--r--src/mailman/rest/queues.py129
-rw-r--r--src/mailman/rest/root.py13
-rw-r--r--src/mailman/rest/tests/test_queues.py107
-rw-r--r--src/mailman/rest/tests/test_systemconf.py1
-rw-r--r--src/mailman/runners/docs/command.rst17
-rw-r--r--src/mailman/runners/docs/incoming.rst10
14 files changed, 477 insertions, 32 deletions
diff --git a/src/mailman/app/inject.py b/src/mailman/app/inject.py
index 77ad8dedb..7e8c359ea 100644
--- a/src/mailman/app/inject.py
+++ b/src/mailman/app/inject.py
@@ -49,6 +49,8 @@ def inject_message(mlist, msg, recipients=None, switchboard=None, **kws):
:type switchboard: string
:param kws: Additional values for the message metadata.
:type kws: dictionary
+ :return: filebase of enqueued message
+ :rtype: string
"""
if switchboard is None:
switchboard = 'in'
@@ -68,7 +70,7 @@ def inject_message(mlist, msg, recipients=None, switchboard=None, **kws):
msgdata.update(kws)
if recipients is not None:
msgdata['recipients'] = recipients
- config.switchboards[switchboard].enqueue(msg, **msgdata)
+ return config.switchboards[switchboard].enqueue(msg, **msgdata)
@@ -91,6 +93,8 @@ def inject_text(mlist, text, recipients=None, switchboard=None, **kws):
:type switchboard: string
:param kws: Additional values for the message metadata.
:type kws: dictionary
+ :return: filebase of enqueued message
+ :rtype: string
"""
message = message_from_string(text, Message)
- inject_message(mlist, message, recipients, switchboard, **kws)
+ return inject_message(mlist, message, recipients, switchboard, **kws)
diff --git a/src/mailman/commands/docs/info.rst b/src/mailman/commands/docs/info.rst
index 8bc7579e6..6ce223403 100644
--- a/src/mailman/commands/docs/info.rst
+++ b/src/mailman/commands/docs/info.rst
@@ -62,20 +62,21 @@ definition.
Python ...
...
File system paths:
- ARCHIVE_DIR = /var/lib/mailman/archives
- BIN_DIR = /sbin
- DATA_DIR = /var/lib/mailman/data
- ETC_DIR = /etc
- EXT_DIR = /etc/mailman.d
- LIST_DATA_DIR = /var/lib/mailman/lists
- LOCK_DIR = /var/lock/mailman
- LOCK_FILE = /var/lock/mailman/master.lck
- LOG_DIR = /var/log/mailman
- MESSAGES_DIR = /var/lib/mailman/messages
- PID_FILE = /var/run/mailman/master.pid
- QUEUE_DIR = /var/spool/mailman
- TEMPLATE_DIR = .../mailman/templates
- VAR_DIR = /var/lib/mailman
+ ARCHIVE_DIR = /var/lib/mailman/archives
+ BIN_DIR = /sbin
+ CFG_FILE = .../test.cfg
+ DATA_DIR = /var/lib/mailman/data
+ ETC_DIR = /etc
+ EXT_DIR = /etc/mailman.d
+ LIST_DATA_DIR = /var/lib/mailman/lists
+ LOCK_DIR = /var/lock/mailman
+ LOCK_FILE = /var/lock/mailman/master.lck
+ LOG_DIR = /var/log/mailman
+ MESSAGES_DIR = /var/lib/mailman/messages
+ PID_FILE = /var/run/mailman/master.pid
+ QUEUE_DIR = /var/spool/mailman
+ TEMPLATE_DIR = .../mailman/templates
+ VAR_DIR = /var/lib/mailman
.. _`Filesystem Hierarchy Standard`: http://www.pathname.com/fhs/
diff --git a/src/mailman/config/config.py b/src/mailman/config/config.py
index 1aab4a82a..779fa27e5 100644
--- a/src/mailman/config/config.py
+++ b/src/mailman/config/config.py
@@ -166,6 +166,7 @@ class Configuration:
# path is relative.
var_dir = os.environ.get('MAILMAN_VAR_DIR', category.var_dir)
substitutions = dict(
+ cwd = os.getcwd(),
argv = bin_dir,
# Directories.
bin_dir = category.bin_dir,
diff --git a/src/mailman/config/mailman.cfg b/src/mailman/config/mailman.cfg
index ab2853e42..aea420280 100644
--- a/src/mailman/config/mailman.cfg
+++ b/src/mailman/config/mailman.cfg
@@ -27,6 +27,10 @@
# where the mailman.cfg file lives.
var_dir: $cfg_file/../..
+[paths.here]
+# Layout where the var directory is put in the current working directory.
+var_dir: $cwd/var
+
[paths.fhs]
# Filesystem Hiearchy Standard 2.3
# http://www.pathname.com/fhs/pub/fhs-2.3.html
diff --git a/src/mailman/config/schema.cfg b/src/mailman/config/schema.cfg
index c7a63e794..4a896eec5 100644
--- a/src/mailman/config/schema.cfg
+++ b/src/mailman/config/schema.cfg
@@ -59,7 +59,7 @@ pre_hook:
post_hook:
# Which paths.* file system layout to use.
-layout: dev
+layout: here
# Can MIME filtered messages be preserved by list owners?
filtered_messages_are_preservable: no
diff --git a/src/mailman/config/tests/test_configuration.py b/src/mailman/config/tests/test_configuration.py
index b4b2145c0..253b63239 100644
--- a/src/mailman/config/tests/test_configuration.py
+++ b/src/mailman/config/tests/test_configuration.py
@@ -116,14 +116,14 @@ layout: nonesuch
self.assertEqual(cm.exception.args, (1,))
def test_path_expansion_infloop(self):
- # A path expansion never completes because it references a
- # non-existent substitution variable.
+ # A path expansion never completes because it references a non-existent
+ # substitution variable.
fd, filename = tempfile.mkstemp()
self.addCleanup(os.remove, filename)
os.close(fd)
with open(filename, 'w') as fp:
print("""\
-[paths.dev]
+[paths.here]
log_dir: $nopath/log_dir
""", file=fp)
config = Configuration()
diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst
index 342ce75eb..9115c9bdb 100644
--- a/src/mailman/docs/NEWS.rst
+++ b/src/mailman/docs/NEWS.rst
@@ -19,6 +19,14 @@ Configuration
default ``[paths.dev]`` section, ``$var_dir`` is now specified relative to
``$cfg_file`` so that it won't accidentally be relative to the current
working directory, if ``-C`` is given.
+ * ``$cwd`` is now an additional substitution variable for the ``mailman.cfg``
+ file's ``[paths.*]`` sections. A new ``[paths.here]`` section is added,
+ which puts the ``var_dir`` in ``$cwd``. It is made the default layout.
+
+REST
+----
+ * You can now view the contents of, inject messages into, and delete messages
+ from the various queue directories via the ``<api>/queues`` resource.
3.0 beta 5 -- "Carve Away The Stone"
diff --git a/src/mailman/rest/docs/queues.rst b/src/mailman/rest/docs/queues.rst
new file mode 100644
index 000000000..861b6806f
--- /dev/null
+++ b/src/mailman/rest/docs/queues.rst
@@ -0,0 +1,174 @@
+======
+Queues
+======
+
+You can get information about what messages are currently in the Mailman
+queues by querying the top-level ``queues`` resource. Of course, this
+information may be out-of-date by the time you receive a response, since queue
+management is asynchronous, but the information will be as current as
+possible.
+
+You can get the list of all queue names.
+
+ >>> dump_json('http://localhost:9001/3.0/queues')
+ entry 0:
+ count: 0
+ directory: .../queue/archive
+ files: []
+ http_etag: ...
+ name: archive
+ self_link: http://localhost:9001/3.0/queues/archive
+ entry 1:
+ count: 0
+ directory: .../queue/bad
+ files: []
+ http_etag: ...
+ name: bad
+ self_link: http://localhost:9001/3.0/queues/bad
+ entry 2:
+ count: 0
+ directory: .../queue/bounces
+ files: []
+ http_etag: ...
+ name: bounces
+ self_link: http://localhost:9001/3.0/queues/bounces
+ entry 3:
+ count: 0
+ directory: .../queue/command
+ files: []
+ http_etag: ...
+ name: command
+ self_link: http://localhost:9001/3.0/queues/command
+ entry 4:
+ count: 0
+ directory: .../queue/digest
+ files: []
+ http_etag: ...
+ name: digest
+ self_link: http://localhost:9001/3.0/queues/digest
+ entry 5:
+ count: 0
+ directory: .../queue/in
+ files: []
+ http_etag: ...
+ name: in
+ self_link: http://localhost:9001/3.0/queues/in
+ entry 6:
+ count: 0
+ directory: .../queue/nntp
+ files: []
+ http_etag: ...
+ name: nntp
+ self_link: http://localhost:9001/3.0/queues/nntp
+ entry 7:
+ count: 0
+ directory: .../queue/out
+ files: []
+ http_etag: ...
+ name: out
+ self_link: http://localhost:9001/3.0/queues/out
+ entry 8:
+ count: 0
+ directory: .../queue/pipeline
+ files: []
+ http_etag: ...
+ name: pipeline
+ self_link: http://localhost:9001/3.0/queues/pipeline
+ entry 9:
+ count: 0
+ directory: .../queue/retry
+ files: []
+ http_etag: ...
+ name: retry
+ self_link: http://localhost:9001/3.0/queues/retry
+ entry 10:
+ count: 0
+ directory: .../queue/shunt
+ files: []
+ http_etag: ...
+ name: shunt
+ self_link: http://localhost:9001/3.0/queues/shunt
+ entry 11:
+ count: 0
+ directory: .../queue/virgin
+ files: []
+ http_etag: ...
+ name: virgin
+ self_link: http://localhost:9001/3.0/queues/virgin
+ http_etag: ...
+ self_link: http://localhost:9001/3.0/queues
+ start: 0
+ total_size: 12
+
+Query an individual queue to get a count of, and the list of file base names
+in the queue. There are currently no files in the ``bad`` queue.
+
+ >>> dump_json('http://localhost:9001/3.0/queues/bad')
+ count: 0
+ directory: .../queue/bad
+ files: []
+ http_etag: ...
+ name: bad
+ self_link: http://localhost:9001/3.0/queues/bad
+
+We can inject a message into the ``bad`` queue. It must be destined for an
+existing mailing list.
+
+ >>> dump_json('http://localhost:9001/3.0/lists', {
+ ... 'fqdn_listname': 'ant@example.com',
+ ... })
+ content-length: 0
+ date: ...
+ location: http://localhost:9001/3.0/lists/ant.example.com
+ server: WSGIServer/0.2 CPython/3.4.2
+ status: 201
+
+While list creation takes an FQDN list name, injecting a message to the queue
+requires a List ID.
+
+ >>> dump_json('http://localhost:9001/3.0/queues/bad', {
+ ... 'list_id': 'ant.example.com',
+ ... 'text': """\
+ ... From: anne@example.com
+ ... To: ant@example.com
+ ... Subject: Testing
+ ...
+ ... """})
+ content-length: 0
+ date: ...
+ location: http://localhost:9001/3.0/queues/bad/...
+ server: ...
+ status: 201
+
+And now the ``bad`` queue has at least one message in it.
+
+ >>> dump_json('http://localhost:9001/3.0/queues/bad')
+ count: 1
+ directory: .../queue/bad
+ files: ['...']
+ http_etag: ...
+ name: bad
+ self_link: http://localhost:9001/3.0/queues/bad
+
+We can delete the injected message.
+
+ >>> json = call_http('http://localhost:9001/3.0/queues/bad')
+ >>> len(json['files'])
+ 1
+ >>> dump_json('http://localhost:9001/3.0/queues/bad/{}'.format(
+ ... json['files'][0]),
+ ... method='DELETE')
+ content-length: 0
+ date: ...
+ server: ...
+ status: 204
+
+And now the queue has no files.
+
+ >>> dump_json('http://localhost:9001/3.0/queues/bad')
+ count: 0
+ directory: .../queue/bad
+ files: []
+ http_etag: ...
+ name: bad
+ self_link: http://localhost:9001/3.0/queues/bad
diff --git a/src/mailman/rest/queues.py b/src/mailman/rest/queues.py
new file mode 100644
index 000000000..f1007052e
--- /dev/null
+++ b/src/mailman/rest/queues.py
@@ -0,0 +1,129 @@
+# Copyright (C) 2015 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman 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 General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+
+"""<api>/queues."""
+
+__all__ = [
+ 'AQueue',
+ 'AQueueFile',
+ 'AllQueues',
+ ]
+
+
+import six
+
+from mailman.config import config
+from mailman.app.inject import inject_text
+from mailman.interfaces.listmanager import IListManager
+from mailman.rest.helpers import (
+ CollectionMixin, bad_request, created, etag, no_content, not_found, okay,
+ paginate, path_to)
+from mailman.rest.validator import Validator
+from zope.component import getUtility
+
+
+
+class _QueuesBase(CollectionMixin):
+ """Shared base class for queues."""
+
+ def _resource_as_dict(self, name):
+ """See `CollectionMixin`."""
+ switchboard = config.switchboards[name]
+ files = switchboard.files
+ return dict(
+ name=switchboard.name,
+ directory=switchboard.queue_directory,
+ count=len(files),
+ files=files,
+ self_link=path_to('queues/{}'.format(name)),
+ )
+
+ @paginate
+ def _get_collection(self, request):
+ """See `CollectionMixin`."""
+ return sorted(config.switchboards)
+
+
+
+class AQueue(_QueuesBase):
+ """A single queue."""
+
+ def __init__(self, name):
+ self._name = name
+
+ def on_get(self, request, response):
+ """Return a single queue resource."""
+ if self._name not in config.switchboards:
+ not_found(response)
+ else:
+ okay(response, self._resource_as_json(self._name))
+
+ def on_post(self, request, response):
+ """Inject a message into the queue."""
+ try:
+ validator = Validator(list_id=six.text_type,
+ text=six.text_type)
+ values = validator(request)
+ except ValueError as error:
+ bad_request(response, str(error))
+ return
+ list_id = values['list_id']
+ mlist = getUtility(IListManager).get_by_list_id(list_id)
+ if mlist is None:
+ bad_request(response, 'No such list: {}'.format(list_id))
+ return
+ try:
+ filebase = inject_text(
+ mlist, values['text'], switchboard=self._name)
+ except Exception as error:
+ bad_request(response, str(error))
+ return
+ else:
+ location = path_to('queues/{}/{}'.format(self._name, filebase))
+ created(response, location)
+
+
+
+class AQueueFile:
+ def __init__(self, name, filebase):
+ self._name = name
+ self._filebase = filebase
+
+ def on_delete(self, request, response):
+ """Delete the queue file."""
+ switchboard = config.switchboards.get(self._name)
+ if switchboard is None:
+ not_found(response, 'No such queue: {}'.format(self._name))
+ return
+ try:
+ switchboard.dequeue(self._filebase)
+ except FileNotFoundError:
+ not_found(response,
+ 'No such queue file: {}'.format(self._filebase))
+ else:
+ no_content(response)
+
+
+
+class AllQueues(_QueuesBase):
+ """All queues."""
+
+ def on_get(self, request, response):
+ """<api>/queues"""
+ resource = self._make_collection(request)
+ resource['self_link'] = path_to('queues')
+ okay(response, etag(resource))
diff --git a/src/mailman/rest/root.py b/src/mailman/rest/root.py
index 34f058bf2..381bec751 100644
--- a/src/mailman/rest/root.py
+++ b/src/mailman/rest/root.py
@@ -36,6 +36,7 @@ from mailman.rest.helpers import (
from mailman.rest.lists import AList, AllLists, Styles
from mailman.rest.members import AMember, AllMembers, FindMembers
from mailman.rest.preferences import ReadOnlyPreferences
+from mailman.rest.queues import AQueue, AQueueFile, AllQueues
from mailman.rest.templates import TemplateFinder
from mailman.rest.users import AUser, AllUsers
from zope.component import getUtility
@@ -213,3 +214,15 @@ class TopLevel:
content_type = None
return TemplateFinder(
fqdn_listname, template, language, content_type)
+
+ @child()
+ def queues(self, request, segments):
+ """/<api>/queues[/<name>[/file]]"""
+ if len(segments) == 0:
+ return AllQueues()
+ elif len(segments) == 1:
+ return AQueue(segments[0]), []
+ elif len(segments) == 2:
+ return AQueueFile(segments[0], segments[1]), []
+ else:
+ return BadRequest(), []
diff --git a/src/mailman/rest/tests/test_queues.py b/src/mailman/rest/tests/test_queues.py
new file mode 100644
index 000000000..43659a2e4
--- /dev/null
+++ b/src/mailman/rest/tests/test_queues.py
@@ -0,0 +1,107 @@
+# Copyright (C) 2015 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman 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 General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+
+"""Test the `queues` resource."""
+
+__all__ = [
+ 'TestQueues',
+ ]
+
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.config import config
+from mailman.database.transaction import transaction
+from mailman.testing.helpers import call_api, get_queue_messages
+from mailman.testing.layers import RESTLayer
+from six.moves.urllib_error import HTTPError
+
+
+TEXT = """\
+From: anne@example.com
+To: test@example.com
+Subject: A test
+Message-ID: <ant>
+
+"""
+
+
+
+class TestQueues(unittest.TestCase):
+ layer = RESTLayer
+
+ def setUp(self):
+ with transaction():
+ self._mlist = create_list('test@example.com')
+
+ def test_missing_queue(self):
+ # Trying to print a missing queue gives a 404.
+ with self.assertRaises(HTTPError) as cm:
+ call_api('http://localhost:9001/3.0/queues/notaq')
+ self.assertEqual(cm.exception.code, 404)
+
+ def test_no_such_list(self):
+ # POSTing to a queue with a bad list-id gives a 400.
+ with self.assertRaises(HTTPError) as cm:
+ call_api('http://localhost:9001/3.0/queues/bad', {
+ 'list_id': 'nosuchlist.example.com',
+ 'text': TEXT,
+ })
+ self.assertEqual(cm.exception.code, 400)
+
+ def test_inject(self):
+ # Injecting a message leaves the message in the queue.
+ starting_messages = get_queue_messages('bad')
+ self.assertEqual(len(starting_messages), 0)
+ content, response = call_api('http://localhost:9001/3.0/queues/bad', {
+ 'list_id': 'test.example.com',
+ 'text': TEXT})
+ self.assertEqual(response.status, 201)
+ location = response['location']
+ filebase = location.split('/')[-1]
+ # The message is in the 'bad' queue.
+ content, response = call_api('http://localhost:9001/3.0/queues/bad')
+ files = content['files']
+ self.assertEqual(len(files), 1)
+ self.assertEqual(files[0], filebase)
+ # Verify the files directly.
+ files = list(config.switchboards['bad'].files)
+ self.assertEqual(len(files), 1)
+ self.assertEqual(files[0], filebase)
+ # Verify the content.
+ items = get_queue_messages('bad')
+ self.assertEqual(len(items), 1)
+ msg = items[0].msg
+ # Remove some headers that get added by Mailman.
+ del msg['date']
+ self.assertEqual(msg['x-message-id-hash'],
+ 'MS6QLWERIJLGCRF44J7USBFDELMNT2BW')
+ del msg['x-message-id-hash']
+ self.assertMultiLineEqual(msg.as_string(), TEXT)
+
+ def test_delete_file(self):
+ # Inject a file, then delete it.
+ content, response = call_api('http://localhost:9001/3.0/queues/bad', {
+ 'list_id': 'test.example.com',
+ 'text': TEXT})
+ location = response['location']
+ self.assertEqual(len(config.switchboards['bad'].files), 1)
+ # Delete the file through REST.
+ content, response = call_api(location, method='DELETE')
+ self.assertEqual(response.status, 204)
+ self.assertEqual(len(config.switchboards['bad'].files), 0)
diff --git a/src/mailman/rest/tests/test_systemconf.py b/src/mailman/rest/tests/test_systemconf.py
index 2eb4fa251..2158a024a 100644
--- a/src/mailman/rest/tests/test_systemconf.py
+++ b/src/mailman/rest/tests/test_systemconf.py
@@ -128,6 +128,7 @@ class TestSystemConfiguration(unittest.TestCase):
'passwords',
'paths.dev',
'paths.fhs',
+ 'paths.here',
'paths.local',
'paths.testing',
'runner.archive',
diff --git a/src/mailman/runners/docs/command.rst b/src/mailman/runners/docs/command.rst
index fe3311d87..82ee33fbc 100644
--- a/src/mailman/runners/docs/command.rst
+++ b/src/mailman/runners/docs/command.rst
@@ -27,7 +27,7 @@ the sender. The command can be in the ``Subject`` header.
... """)
>>> from mailman.app.inject import inject_message
- >>> inject_message(mlist, msg, switchboard='command')
+ >>> filebase = inject_message(mlist, msg, switchboard='command')
>>> from mailman.runners.command import CommandRunner
>>> from mailman.testing.helpers import make_testable_runner
>>> command = make_testable_runner(CommandRunner)
@@ -85,7 +85,7 @@ message is plain text.
... echo foo bar
... """)
- >>> inject_message(mlist, msg, switchboard='command')
+ >>> filebase = inject_message(mlist, msg, switchboard='command')
>>> command.run()
>>> messages = get_queue_messages('virgin')
>>> len(messages)
@@ -133,7 +133,8 @@ address, and the other is the results of his email command.
...
... """)
- >>> inject_message(mlist, msg, switchboard='command', subaddress='join')
+ >>> filebase = inject_message(
+ ... mlist, msg, switchboard='command', subaddress='join')
>>> command.run()
>>> messages = get_queue_messages('virgin', sort_on='subject')
>>> len(messages)
@@ -165,7 +166,8 @@ Similarly, to leave a mailing list, the user need only email the ``-leave`` or
...
... """)
- >>> inject_message(mlist, msg, switchboard='command', subaddress='leave')
+ >>> filebase = inject_message(
+ ... mlist, msg, switchboard='command', subaddress='leave')
>>> command.run()
>>> messages = get_queue_messages('virgin')
>>> len(messages)
@@ -200,7 +202,8 @@ The ``-confirm`` address is also available as an implicit command.
...
... """)
- >>> inject_message(mlist, msg, switchboard='command', subaddress='confirm')
+ >>> filebase = inject_message(
+ ... mlist, msg, switchboard='command', subaddress='confirm')
>>> command.run()
>>> messages = get_queue_messages('virgin')
>>> len(messages)
@@ -244,7 +247,7 @@ looked at by the command queue.
... echo baz qux
... """)
- >>> inject_message(mlist, msg, switchboard='command')
+ >>> filebase = inject_message(mlist, msg, switchboard='command')
>>> command.run()
>>> messages = get_queue_messages('virgin')
>>> len(messages)
@@ -276,7 +279,7 @@ The ``stop`` command is an alias for ``end``.
... echo baz qux
... """)
- >>> inject_message(mlist, msg, switchboard='command')
+ >>> filebase = inject_message(mlist, msg, switchboard='command')
>>> command.run()
>>> messages = get_queue_messages('virgin')
>>> len(messages)
diff --git a/src/mailman/runners/docs/incoming.rst b/src/mailman/runners/docs/incoming.rst
index 0ae3336ca..d4fb65c85 100644
--- a/src/mailman/runners/docs/incoming.rst
+++ b/src/mailman/runners/docs/incoming.rst
@@ -54,7 +54,7 @@ Inject the message into the incoming queue, similar to the way the upstream
mail server normally would.
>>> from mailman.app.inject import inject_message
- >>> inject_message(mlist, msg)
+ >>> filebase = inject_message(mlist, msg)
The incoming runner runs until it is empty.
@@ -103,7 +103,7 @@ that it will be accepted and forward to the pipeline queue.
Inject the message into the incoming queue and run until the queue is empty.
- >>> inject_message(mlist, msg)
+ >>> filebase = inject_message(mlist, msg)
>>> incoming.run()
There are no messages left in the incoming queue.
@@ -156,7 +156,7 @@ pipeline queue.
>>> from mailman.testing.helpers import event_subscribers
>>> with event_subscribers(on_chain):
- ... inject_message(mlist, msg)
+ ... filebase = inject_message(mlist, msg)
... incoming.run()
<mailman.interfaces.chain.HoldEvent ...>
<mailman.chains.hold.HoldChain ...>
@@ -191,7 +191,7 @@ new chain and set it as the mailing list's start chain.
>>> msg.replace_header('message-id', '<second>')
>>> with event_subscribers(on_chain):
- ... inject_message(mlist, msg)
+ ... filebase = inject_message(mlist, msg)
... incoming.run()
<mailman.interfaces.chain.DiscardEvent ...>
<mailman.chains.discard.DiscardChain ...>
@@ -220,7 +220,7 @@ just create a new chain that does.
>>> msg.replace_header('message-id', '<third>')
>>> with event_subscribers(on_chain):
- ... inject_message(mlist, msg)
+ ... filebase = inject_message(mlist, msg)
... incoming.run()
<mailman.interfaces.chain.RejectEvent ...>
<mailman.chains.reject.RejectChain ...>