summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2008-12-25 23:57:07 -0500
committerBarry Warsaw2008-12-25 23:57:07 -0500
commitb8e68e7577aa12e0e355aabe2845981f0d73e3b5 (patch)
tree4d988c8de9e29b080ac258d0bc1e4eee8e5ee32b
parentd4de7996e6d4fb5db04dfed3b3fd12747622b164 (diff)
downloadmailman-b8e68e7577aa12e0e355aabe2845981f0d73e3b5.tar.gz
mailman-b8e68e7577aa12e0e355aabe2845981f0d73e3b5.tar.zst
mailman-b8e68e7577aa12e0e355aabe2845981f0d73e3b5.zip
-rw-r--r--buildout.cfg3
-rw-r--r--mailman/Archiver/HyperArch.py22
-rw-r--r--mailman/Defaults.py2
-rw-r--r--mailman/Mailbox.py9
-rw-r--r--mailman/Message.py6
-rw-r--r--mailman/archiving/mailarchive.py15
-rw-r--r--mailman/archiving/mhonarc.py5
-rw-r--r--mailman/archiving/pipermail.py9
-rw-r--r--mailman/core/logging.py33
-rw-r--r--mailman/core/styles.py15
-rw-r--r--mailman/docs/bounces.txt3
-rw-r--r--mailman/docs/pipelines.txt6
-rw-r--r--mailman/pipeline/cleanse_dkim.py4
-rw-r--r--mailman/pipeline/decorate.py3
-rw-r--r--mailman/pipeline/docs/acknowledge.txt3
-rw-r--r--mailman/pipeline/docs/archives.txt3
-rw-r--r--mailman/pipeline/docs/digests.txt16
-rw-r--r--mailman/pipeline/docs/nntp.txt3
-rw-r--r--mailman/pipeline/docs/replybot.txt3
-rw-r--r--mailman/pipeline/docs/tagger.txt1
-rw-r--r--mailman/pipeline/docs/to-outgoing.txt3
-rw-r--r--mailman/pipeline/scrubber.py12
-rw-r--r--mailman/pipeline/to_archive.py7
-rw-r--r--mailman/pipeline/to_digest.py10
-rw-r--r--mailman/pipeline/to_outgoing.py10
-rw-r--r--mailman/queue/archive.py12
-rw-r--r--mailman/testing/helpers.py7
-rw-r--r--mailman/testing/layers.py44
-rw-r--r--mailman/testing/testing.cfg11
-rw-r--r--setup.py1
30 files changed, 183 insertions, 98 deletions
diff --git a/buildout.cfg b/buildout.cfg
index d4ba46351..e4f46a0fc 100644
--- a/buildout.cfg
+++ b/buildout.cfg
@@ -27,3 +27,6 @@ recipe = zc.recipe.testrunner
eggs =
mailman
defaults = '--tests-pattern ^tests --exit-with-status'.split()
+# Hack in extra arguments to zope.testrunner.
+initialization = from mailman.testing.layers import ConfigLayer;
+ ConfigLayer.hack_options_parser()
diff --git a/mailman/Archiver/HyperArch.py b/mailman/Archiver/HyperArch.py
index 0395f6482..a4dc13423 100644
--- a/mailman/Archiver/HyperArch.py
+++ b/mailman/Archiver/HyperArch.py
@@ -43,6 +43,7 @@ from email.Header import decode_header, make_header
from locknix.lockfile import Lock
from string import Template
+from mailman import Defaults
from mailman import Utils
from mailman import i18n
from mailman.Archiver import HyperDatabase
@@ -50,6 +51,7 @@ from mailman.Archiver import pipermail
from mailman.Mailbox import ArchiverMailbox
from mailman.config import config
+
log = logging.getLogger('mailman.error')
# Set up i18n. Assume the current language has already been set in the caller.
@@ -174,7 +176,7 @@ def quick_maketext(templatefile, dict=None, lang=None, mlist=None):
listname = mlist.fqdn_listname
if lang is None:
if mlist is None:
- lang = config.DEFAULT_SERVER_LANGUAGE
+ lang = Defaults.DEFAULT_SERVER_LANGUAGE
else:
lang = mlist.preferred_language
cachekey = (templatefile, lang, listname)
@@ -250,7 +252,7 @@ class Article(pipermail.Article):
self._lang = lang
self._mlist = mlist
- if config.ARCHIVER_OBSCURES_EMAILADDRS:
+ if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
# Avoid i18n side-effects. Note that the language for this
# article (for this list) could be different from the site-wide
# preferred language, so we need to ensure no side-effects will
@@ -325,7 +327,7 @@ class Article(pipermail.Article):
if hasattr(self, '_mlist'):
self._lang = self._mlist.preferred_language
else:
- self._lang = config.DEFAULT_SERVER_LANGUAGE
+ self._lang = Defaults.DEFAULT_SERVER_LANGUAGE
if not d.has_key('cenc'):
self.cenc = None
if not d.has_key('decoded'):
@@ -357,7 +359,7 @@ class Article(pipermail.Article):
if email:
self.decoded['email'] = email
if subject:
- if config.ARCHIVER_OBSCURES_EMAILADDRS:
+ if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
with i18n.using_language(self._lang):
atmark = _(' at ')
subject = re.sub(r'([-+,.\w]+)@([-+.\w]+)',
@@ -405,7 +407,7 @@ class Article(pipermail.Article):
d["subject_html"] = self.quote(self.subject)
d["subject_url"] = url_quote(self.subject)
d["in_reply_to_url"] = url_quote(self.in_reply_to)
- if config.ARCHIVER_OBSCURES_EMAILADDRS:
+ if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
# Point the mailto url back to the list
author = re.sub('@', _(' at '), self.author)
emailurl = self._mlist.posting_address
@@ -509,7 +511,7 @@ class Article(pipermail.Article):
# Coerce the body to Unicode and replace any invalid characters.
if not isinstance(body, unicode):
body = unicode(body, cset, 'replace')
- if config.ARCHIVER_OBSCURES_EMAILADDRS:
+ if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
with i18n.using_language(self._lang):
atmark = _(' at ')
body = re.sub(r'([-+,.\w]+)@([-+.\w]+)',
@@ -705,7 +707,7 @@ class HyperArchive(pipermail.T):
# The TOC is always in the charset of the list's preferred language
d['meta'] += html_charset % Utils.GetCharSet(mlist.preferred_language)
# The site can disable public access to the mbox file.
- if config.PUBLIC_MBOX:
+ if Defaults.PUBLIC_MBOX:
template = 'archtoc.html'
else:
template = 'archtocnombox.html'
@@ -962,7 +964,7 @@ class HyperArchive(pipermail.T):
def write_index_entry(self, article):
subject = self.get_header("subject", article)
author = self.get_header("author", article)
- if config.ARCHIVER_OBSCURES_EMAILADDRS:
+ if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
try:
author = re.sub('@', _(' at '), author)
except UnicodeError:
@@ -1038,7 +1040,7 @@ class HyperArchive(pipermail.T):
def update_archive(self, archive):
self.__super_update_archive(archive)
# only do this if the gzip module was imported globally, and
- # gzip'ing was enabled via config.GZIP_ARCHIVE_TXT_FILES. See
+ # gzip'ing was enabled via Defaults.GZIP_ARCHIVE_TXT_FILES. See
# above.
if gzip:
archz = None
@@ -1135,7 +1137,7 @@ class HyperArchive(pipermail.T):
if j != -1 and (j < k or k == -1):
text = jr.group(1)
length = len(text)
- if config.ARCHIVER_OBSCURES_EMAILADDRS:
+ if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
text = re.sub('@', atmark, text)
URL = self.maillist.GetScriptURL(
'listinfo', absolute=1)
diff --git a/mailman/Defaults.py b/mailman/Defaults.py
index a0aad0b8e..4f4f7981e 100644
--- a/mailman/Defaults.py
+++ b/mailman/Defaults.py
@@ -1051,7 +1051,7 @@ DEFAULT_DIGEST_FOOTER = DEFAULT_MSG_FOOTER
DEFAULT_DIGEST_IS_DEFAULT = No
DEFAULT_MIME_IS_DEFAULT_DIGEST = No
-DEFAULT_DIGEST_SIZE_THRESHHOLD = 30 # KB
+DEFAULT_DIGEST_SIZE_THRESHOLD = 30 # KB
DEFAULT_DIGEST_SEND_PERIODIC = Yes
# Headers which should be kept in both RFC 1153 (plain) and MIME digests. RFC
diff --git a/mailman/Mailbox.py b/mailman/Mailbox.py
index cbc38585a..bb4982fc6 100644
--- a/mailman/Mailbox.py
+++ b/mailman/Mailbox.py
@@ -26,8 +26,8 @@ from email.Errors import MessageParseError
from email.Generator import Generator
from email.Parser import Parser
+from mailman import Defaults
from mailman.Message import Message
-from mailman.config import config
@@ -91,9 +91,10 @@ class ArchiverMailbox(Mailbox):
# scrub() method, giving the scrubber module a chance to do its thing
# before the message is archived.
def __init__(self, fp, mlist):
- if config.ARCHIVE_SCRUBBER:
- __import__(config.ARCHIVE_SCRUBBER)
- self._scrubber = sys.modules[config.ARCHIVE_SCRUBBER].process
+ scrubber_module = Defaults.ARCHIVE_SCRUBBER
+ if scrubber_module:
+ __import__(scrubber_module)
+ self._scrubber = sys.modules[scrubber_module].process
else:
self._scrubber = None
self._mlist = mlist
diff --git a/mailman/Message.py b/mailman/Message.py
index c8fb17f3b..07d7f15bb 100644
--- a/mailman/Message.py
+++ b/mailman/Message.py
@@ -264,8 +264,7 @@ class UserNotification(Message):
def _enqueue(self, mlist, **_kws):
# Not imported at module scope to avoid import loop
- from mailman.queue import Switchboard
- virginq = Switchboard(config.VIRGINQUEUE_DIR)
+ virginq = config.switchboards['virgin']
# The message metadata better have a 'recip' attribute.
enqueue_kws = dict(
recips=self.recips,
@@ -298,8 +297,7 @@ class OwnerNotification(UserNotification):
def _enqueue(self, mlist, **_kws):
# Not imported at module scope to avoid import loop
- from mailman.queue import Switchboard
- virginq = Switchboard(config.VIRGINQUEUE_DIR)
+ virginq = config.switchboards['virgin']
# The message metadata better have a `recip' attribute
virginq.enqueue(self,
listname=mlist.fqdn_listname,
diff --git a/mailman/archiving/mailarchive.py b/mailman/archiving/mailarchive.py
index 3edeb8c39..ded402ab4 100644
--- a/mailman/archiving/mailarchive.py
+++ b/mailman/archiving/mailarchive.py
@@ -32,7 +32,6 @@ from zope.interface import implements
from mailman.config import config
from mailman.interfaces.archiver import IArchiver
-from mailman.queue import Switchboard
@@ -73,15 +72,13 @@ class MailArchive:
message_id_hash = urlsafe_b64encode(sha.digest())
del msg['x-message-id-hash']
msg['X-Message-ID-Hash'] = message_id_hash
- return urljoin(config.MAIL_ARCHIVE_BASEURL, message_id_hash)
+ return urljoin(config.archiver.mail_archive.base_url, message_id_hash)
@staticmethod
def archive_message(mlist, msg):
"""See `IArchiver`."""
- if mlist.archive_private:
- return
- outq = Switchboard(config.OUTQUEUE_DIR)
- outq.enqueue(
- msg,
- listname=mlist.fqdn_listname,
- recips=[config.MAIL_ARCHIVE_RECIPIENT])
+ if not mlist.archive_private:
+ config.switchboards['out'].enqueue(
+ msg,
+ listname=mlist.fqdn_listname,
+ recips=[config.archiver.mail_archive.recipient])
diff --git a/mailman/archiving/mhonarc.py b/mailman/archiving/mhonarc.py
index 3d17ffd13..c20c34ba0 100644
--- a/mailman/archiving/mhonarc.py
+++ b/mailman/archiving/mhonarc.py
@@ -32,6 +32,7 @@ from string import Template
from urlparse import urljoin
from zope.interface import implements
+from mailman import Defaults
from mailman.config import config
from mailman.interfaces.archiver import IArchiver
@@ -53,7 +54,7 @@ class MHonArc:
"""See `IArchiver`."""
# XXX What about private MHonArc archives?
web_host = config.domains[mlist.host_name].url_host
- return Template(config.PUBLIC_ARCHIVE_URL).safe_substitute(
+ return Template(Defaults.PUBLIC_ARCHIVE_URL).safe_substitute(
listname=mlist.fqdn_listname,
hostname=web_host,
fqdn_listname=mlist.fqdn_listname,
@@ -82,7 +83,7 @@ class MHonArc:
"""See `IArchiver`."""
substitutions = config.__dict__.copy()
substitutions['listname'] = mlist.fqdn_listname
- command = Template(config.MHONARC_COMMAND).safe_substitute(
+ command = Template(Defaults.MHONARC_COMMAND).safe_substitute(
substitutions)
proc = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
diff --git a/mailman/archiving/pipermail.py b/mailman/archiving/pipermail.py
index f42f064ed..ea3ccef5e 100644
--- a/mailman/archiving/pipermail.py
+++ b/mailman/archiving/pipermail.py
@@ -30,6 +30,8 @@ from string import Template
from zope.interface import implements
from zope.interface.interface import adapter_hooks
+from mailman import Defaults
+from mailman.Utils import makedirs
from mailman.config import config
from mailman.interfaces.archiver import IArchiver, IPipermailMailingList
from mailman.interfaces.mailinglist import IMailingList
@@ -55,7 +57,10 @@ class PipermailMailingListAdapter:
basedir = config.PRIVATE_ARCHIVE_FILE_DIR
else:
basedir = config.PUBLIC_ARCHIVE_FILE_DIR
- return os.path.join(basedir, self._mlist.fqdn_listname)
+ # Make sure the archive directory exists.
+ archive_dir = os.path.join(basedir, self._mlist.fqdn_listname)
+ makedirs(archive_dir)
+ return archive_dir
def adapt_mailing_list_for_pipermail(iface, obj):
@@ -91,7 +96,7 @@ class Pipermail:
url = mlist.script_url('private') + '/index.html'
else:
web_host = config.domains[mlist.host_name].url_host
- url = Template(config.PUBLIC_ARCHIVE_URL).safe_substitute(
+ url = Template(config.archiver.pipermail.base_url).safe_substitute(
listname=mlist.fqdn_listname,
hostname=web_host,
fqdn_listname=mlist.fqdn_listname,
diff --git a/mailman/core/logging.py b/mailman/core/logging.py
index ed400215b..8c1463be9 100644
--- a/mailman/core/logging.py
+++ b/mailman/core/logging.py
@@ -19,6 +19,13 @@
from __future__ import absolute_import
+__metaclass__ = type
+__all__ = [
+ 'initialize',
+ 'reopen',
+ ]
+
+
import os
import sys
import codecs
@@ -75,22 +82,35 @@ class ReopenableFileHandler(logging.Handler):
-def initialize(propagate=False):
+def initialize(propagate=None):
+ """Initialize all logs.
+
+ :param propagate: Flag specifying whether logs should propagate their
+ messages to the root logger. If omitted, propagation is determined
+ from the configuration files.
+ :type propagate: bool or None
+ """
# First, find the root logger and configure the logging subsystem.
- # Initialize the root logger, then create a formatter for all the sublogs.
+ # Initialize the root logger, then create a formatter for all the
+ # sublogs. The root logger should log to stderr.
logging.basicConfig(format=config.logging.root.format,
datefmt=config.logging.root.datefmt,
- level=as_log_level(config.logging.root.level))
- # Create the subloggers
+ level=as_log_level(config.logging.root.level),
+ stream=sys.stderr)
+ # Create the subloggers.
for logger_config in config.logger_configs:
- logger_name = 'mailman.' + logger_config.name.split('.')[-1]
+ sub_name = logger_config.name.split('.')[-1]
+ if sub_name == 'root':
+ continue
+ logger_name = 'mailman.' + sub_name
log = logging.getLogger(logger_name)
# Get settings from log configuration file (or defaults).
log_format = logger_config.format
log_datefmt = logger_config.datefmt
# Propagation to the root logger is how we handle logging to stderr
# when the qrunners are not run as a subprocess of mailmanctl.
- log.propagate = as_boolean(logger_config.propagate)
+ log.propagate = (as_boolean(logger_config.propagate)
+ if propagate is None else propagate)
# Set the logger's level.
log.setLevel(as_log_level(logger_config.level))
# Create a formatter for this logger, then a handler, and link the
@@ -106,5 +126,6 @@ def initialize(propagate=False):
def reopen():
+ """Re-open all log files."""
for handler in _handlers:
handler.reopen()
diff --git a/mailman/core/styles.py b/mailman/core/styles.py
index 76428a955..b8fff8278 100644
--- a/mailman/core/styles.py
+++ b/mailman/core/styles.py
@@ -85,7 +85,8 @@ class DefaultStyle:
mlist.welcome_msg = u''
mlist.goodbye_msg = u''
mlist.subscribe_policy = Defaults.DEFAULT_SUBSCRIBE_POLICY
- mlist.subscribe_auto_approval = Defaults.DEFAULT_SUBSCRIBE_AUTO_APPROVAL
+ mlist.subscribe_auto_approval = (
+ Defaults.DEFAULT_SUBSCRIBE_AUTO_APPROVAL)
mlist.unsubscribe_policy = Defaults.DEFAULT_UNSUBSCRIBE_POLICY
mlist.private_roster = Defaults.DEFAULT_PRIVATE_ROSTER
mlist.obscure_addresses = Defaults.DEFAULT_OBSCURE_ADDRESSES
@@ -98,7 +99,8 @@ class DefaultStyle:
mlist.pass_mime_types = Defaults.DEFAULT_PASS_MIME_TYPES
mlist.filter_filename_extensions = (
Defaults.DEFAULT_FILTER_FILENAME_EXTENSIONS)
- mlist.pass_filename_extensions = Defaults.DEFAULT_PASS_FILENAME_EXTENSIONS
+ mlist.pass_filename_extensions = (
+ Defaults.DEFAULT_PASS_FILENAME_EXTENSIONS)
mlist.filter_content = Defaults.DEFAULT_FILTER_CONTENT
mlist.collapse_alternatives = Defaults.DEFAULT_COLLAPSE_ALTERNATIVES
mlist.convert_html_to_plaintext = (
@@ -108,13 +110,13 @@ class DefaultStyle:
mlist.digestable = Defaults.DEFAULT_DIGESTABLE
mlist.digest_is_default = Defaults.DEFAULT_DIGEST_IS_DEFAULT
mlist.mime_is_default_digest = Defaults.DEFAULT_MIME_IS_DEFAULT_DIGEST
- mlist.digest_size_threshhold = Defaults.DEFAULT_DIGEST_SIZE_THRESHHOLD
+ mlist.digest_size_threshold = Defaults.DEFAULT_DIGEST_SIZE_THRESHOLD
mlist.digest_send_periodic = Defaults.DEFAULT_DIGEST_SEND_PERIODIC
mlist.digest_header = Defaults.DEFAULT_DIGEST_HEADER
mlist.digest_footer = Defaults.DEFAULT_DIGEST_FOOTER
- mlist.digest_volume_frequency = Defaults.DEFAULT_DIGEST_VOLUME_FREQUENCY
+ mlist.digest_volume_frequency = (
+ Defaults.DEFAULT_DIGEST_VOLUME_FREQUENCY)
mlist.one_last_digest = {}
- mlist.digest_members = {}
mlist.next_digest_number = 1
mlist.nondigestable = Defaults.DEFAULT_NONDIGESTABLE
mlist.personalize = Personalization.none
@@ -174,7 +176,8 @@ class DefaultStyle:
# Bounces
mlist.bounce_processing = Defaults.DEFAULT_BOUNCE_PROCESSING
mlist.bounce_score_threshold = Defaults.DEFAULT_BOUNCE_SCORE_THRESHOLD
- mlist.bounce_info_stale_after = Defaults.DEFAULT_BOUNCE_INFO_STALE_AFTER
+ mlist.bounce_info_stale_after = (
+ Defaults.DEFAULT_BOUNCE_INFO_STALE_AFTER)
mlist.bounce_you_are_disabled_warnings = (
Defaults.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS)
mlist.bounce_you_are_disabled_warnings_interval = (
diff --git a/mailman/docs/bounces.txt b/mailman/docs/bounces.txt
index 6d0fad688..9e8bcd23b 100644
--- a/mailman/docs/bounces.txt
+++ b/mailman/docs/bounces.txt
@@ -30,8 +30,7 @@ Bounce a message by passing in the original message, and an optional error
message. The bounced message ends up in the virgin queue, awaiting sending
to the original messageauthor.
- >>> from mailman.queue import Switchboard
- >>> switchboard = Switchboard(config.VIRGINQUEUE_DIR)
+ >>> switchboard = config.switchboards['virgin']
>>> from mailman.app.bounces import bounce_message
>>> bounce_message(mlist, msg)
>>> len(switchboard.files)
diff --git a/mailman/docs/pipelines.txt b/mailman/docs/pipelines.txt
index 5a84bb4b9..94cc792cd 100644
--- a/mailman/docs/pipelines.txt
+++ b/mailman/docs/pipelines.txt
@@ -70,7 +70,7 @@ However there are currently no recipients for this message.
And the message is now sitting in various other processing queues.
>>> from mailman.testing.helpers import get_queue_messages
- >>> messages = get_queue_messages(config.ARCHQUEUE_DIR)
+ >>> messages = get_queue_messages('archive')
>>> len(messages)
1
>>> print messages[0].msg.as_string()
@@ -104,13 +104,13 @@ And the message is now sitting in various other processing queues.
This mailing list is not linked to an NNTP newsgroup, so there's nothing in
the outgoing nntp queue.
- >>> messages = get_queue_messages(config.NEWSQUEUE_DIR)
+ >>> messages = get_queue_messages('news')
>>> len(messages)
0
This is the message that will actually get delivered to end recipients.
- >>> messages = get_queue_messages(config.OUTQUEUE_DIR)
+ >>> messages = get_queue_messages('out')
>>> len(messages)
1
>>> print messages[0].msg.as_string()
diff --git a/mailman/pipeline/cleanse_dkim.py b/mailman/pipeline/cleanse_dkim.py
index bc76e2726..3f3be98f8 100644
--- a/mailman/pipeline/cleanse_dkim.py
+++ b/mailman/pipeline/cleanse_dkim.py
@@ -31,7 +31,7 @@ __all__ = ['CleanseDKIM']
from zope.interface import implements
-from mailman.config import config
+from mailman import Defaults
from mailman.i18n import _
from mailman.interfaces import IHandler
@@ -47,7 +47,7 @@ class CleanseDKIM:
def process(self, mlist, msg, msgdata):
"""See `IHandler`."""
- if config.REMOVE_DKIM_HEADERS:
+ if Defaults.REMOVE_DKIM_HEADERS:
del msg['domainkey-signature']
del msg['dkim-signature']
del msg['authentication-results']
diff --git a/mailman/pipeline/decorate.py b/mailman/pipeline/decorate.py
index bc684a1b1..f150d6d0c 100644
--- a/mailman/pipeline/decorate.py
+++ b/mailman/pipeline/decorate.py
@@ -28,6 +28,7 @@ from email.MIMEText import MIMEText
from string import Template
from zope.interface import implements
+from mailman import Defaults
from mailman import Utils
from mailman.Message import Message
from mailman.config import config
@@ -204,7 +205,7 @@ def decorate(mlist, template, extradict=None):
web_page_url = mlist.web_page_url,
description = mlist.description,
info = mlist.info,
- cgiext = config.CGIEXT,
+ cgiext = Defaults.CGIEXT,
)
if extradict is not None:
d.update(extradict)
diff --git a/mailman/pipeline/docs/acknowledge.txt b/mailman/pipeline/docs/acknowledge.txt
index ccbb70e79..d1206b6f3 100644
--- a/mailman/pipeline/docs/acknowledge.txt
+++ b/mailman/pipeline/docs/acknowledge.txt
@@ -15,8 +15,7 @@ acknowledgment.
>>> # Ensure that the virgin queue is empty, since we'll be checking this
>>> # for new auto-response messages.
- >>> from mailman.queue import Switchboard
- >>> virginq = Switchboard(config.VIRGINQUEUE_DIR)
+ >>> virginq = config.switchboards['virgin']
>>> virginq.files
[]
diff --git a/mailman/pipeline/docs/archives.txt b/mailman/pipeline/docs/archives.txt
index 9595a36e0..d81f6e27b 100644
--- a/mailman/pipeline/docs/archives.txt
+++ b/mailman/pipeline/docs/archives.txt
@@ -8,10 +8,9 @@ archivers to work in a separate process from the main Mailman delivery
processes.
>>> from mailman.app.lifecycle import create_list
- >>> from mailman.queue import Switchboard
>>> handler = config.handlers['to-archive']
>>> mlist = create_list(u'_xtest@example.com')
- >>> switchboard = Switchboard(config.ARCHQUEUE_DIR)
+ >>> switchboard = config.switchboards['archive']
A helper function.
diff --git a/mailman/pipeline/docs/digests.txt b/mailman/pipeline/docs/digests.txt
index f478c1ec0..1d7112fd5 100644
--- a/mailman/pipeline/docs/digests.txt
+++ b/mailman/pipeline/docs/digests.txt
@@ -7,14 +7,13 @@ digests, although only two are currently supported: MIME digests and RFC 1153
(a.k.a. plain text) digests.
>>> from mailman.pipeline.to_digest import process
- >>> from mailman.queue import Switchboard
>>> mlist = config.db.list_manager.create(u'_xtest@example.com')
>>> mlist.preferred_language = u'en'
>>> mlist.web_page_url = u'http://www.example.com/'
>>> mlist.real_name = u'XTest'
>>> mlist.subject_prefix = u'[_XTest] '
>>> mlist.one_last_digest = set()
- >>> switchboard = Switchboard(config.VIRGINQUEUE_DIR)
+ >>> switchboard = config.switchboards['virgin']
This is a helper function used to iterate through all the accumulated digest
messages, in the order in which they were posted. This makes it easier to
@@ -406,11 +405,16 @@ When messages come in with a content-type character set different than that of
the list's preferred language, recipients wil get an internationalized
digest. French is not enabled by default site-wide, so enable that now.
-XXX We also have to set the default server language to French, otherwise the
-English template will be found and the masthead won't be translated.
-
>>> config.languages.enable_language('fr')
- >>> config.DEFAULT_SERVER_LANGUAGE = u'fr'
+
+ # Simulate the site administrator setting the default server language to
+ # French in the configuration file. Without this, the English template
+ # will be found and the masthead won't be translated.
+ >>> config.push('french', """
+ ... [mailman]
+ ... default_language: fr
+ ... """)
+
>>> mlist.preferred_language = u'fr'
>>> msg = message_from_string("""\
... From: aperson@example.org
diff --git a/mailman/pipeline/docs/nntp.txt b/mailman/pipeline/docs/nntp.txt
index 3ef3b2413..0120de394 100644
--- a/mailman/pipeline/docs/nntp.txt
+++ b/mailman/pipeline/docs/nntp.txt
@@ -5,11 +5,10 @@ Mailman has an NNTP gateway, whereby messages posted to the mailing list can
be forwarded onto an NNTP newsgroup. Typically this means Usenet, but since
NNTP is to Usenet as IP is to the web, it's more general than that.
- >>> from mailman.queue import Switchboard
>>> handler = config.handlers['to-usenet']
>>> mlist = config.db.list_manager.create(u'_xtest@example.com')
>>> mlist.preferred_language = u'en'
- >>> switchboard = Switchboard(config.NEWSQUEUE_DIR)
+ >>> switchboard = config.switchboards['news']
Gatewaying from the mailing list to the newsgroup happens through a separate
'nntp' queue and happen immediately when the message is posted through to the
diff --git a/mailman/pipeline/docs/replybot.txt b/mailman/pipeline/docs/replybot.txt
index 7325417ed..f9f824e4e 100644
--- a/mailman/pipeline/docs/replybot.txt
+++ b/mailman/pipeline/docs/replybot.txt
@@ -13,8 +13,7 @@ message or the amount of time since the last auto-response.
>>> # Ensure that the virgin queue is empty, since we'll be checking this
>>> # for new auto-response messages.
- >>> from mailman.queue import Switchboard
- >>> virginq = Switchboard(config.VIRGINQUEUE_DIR)
+ >>> virginq = config.switchboards['virgin']
>>> virginq.files
[]
diff --git a/mailman/pipeline/docs/tagger.txt b/mailman/pipeline/docs/tagger.txt
index 64996755b..9f0bcd4b2 100644
--- a/mailman/pipeline/docs/tagger.txt
+++ b/mailman/pipeline/docs/tagger.txt
@@ -9,7 +9,6 @@ its Subject: and Keywords: headers compared against these regular
expressions. The message then gets tagged with the topic names of each hit.
>>> from mailman.pipeline.tagger import process
- >>> from mailman.queue import Switchboard
>>> mlist = config.db.list_manager.create(u'_xtest@example.com')
Topics must be enabled for Mailman to do any topic matching, even if topics
diff --git a/mailman/pipeline/docs/to-outgoing.txt b/mailman/pipeline/docs/to-outgoing.txt
index 70f93cfae..046ed9be8 100644
--- a/mailman/pipeline/docs/to-outgoing.txt
+++ b/mailman/pipeline/docs/to-outgoing.txt
@@ -9,10 +9,9 @@ term somewhat incorrectly, but within the spirit of the standard, which
basically describes how to encode the recipient's address in the originator
headers for unambigous bounce processing.
- >>> from mailman.queue import Switchboard
>>> handler = config.handlers['to-outgoing']
>>> mlist = config.db.list_manager.create(u'_xtest@example.com')
- >>> switchboard = Switchboard(config.OUTQUEUE_DIR)
+ >>> switchboard = config.switchboards['out']
>>> def queue_size():
... size = len(switchboard.files)
diff --git a/mailman/pipeline/scrubber.py b/mailman/pipeline/scrubber.py
index abc904613..e8268f0cf 100644
--- a/mailman/pipeline/scrubber.py
+++ b/mailman/pipeline/scrubber.py
@@ -38,8 +38,8 @@ from locknix.lockfile import Lock
from mimetypes import guess_all_extensions
from zope.interface import implements
+from mailman import Defaults
from mailman import Utils
-from mailman.config import config
from mailman.core.errors import DiscardMessage
from mailman.core.plugins import get_plugin
from mailman.i18n import _
@@ -158,7 +158,7 @@ def replace_payload_by_text(msg, text, charset):
def process(mlist, msg, msgdata=None):
- sanitize = config.ARCHIVE_HTML_SANITIZER
+ sanitize = Defaults.ARCHIVE_HTML_SANITIZER
outer = True
if msgdata is None:
msgdata = {}
@@ -394,7 +394,7 @@ def makedirs(dir):
def save_attachment(mlist, msg, dir, filter_html=True):
- fsdir = os.path.join(config.PRIVATE_ARCHIVE_FILE_DIR,
+ fsdir = os.path.join(Defaults.PRIVATE_ARCHIVE_FILE_DIR,
mlist.fqdn_listname, dir)
makedirs(fsdir)
# Figure out the attachment type and get the decoded data
@@ -409,7 +409,7 @@ def save_attachment(mlist, msg, dir, filter_html=True):
filename, fnext = os.path.splitext(filename)
# For safety, we should confirm this is valid ext for content-type
# but we can use fnext if we introduce fnext filtering
- if config.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION:
+ if Defaults.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION:
# HTML message doesn't have filename :-(
ext = fnext or guess_extension(ctype, fnext)
else:
@@ -430,7 +430,7 @@ def save_attachment(mlist, msg, dir, filter_html=True):
with Lock(os.path.join(fsdir, 'attachments.lock')):
# Now base the filename on what's in the attachment, uniquifying it if
# necessary.
- if not filename or config.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME:
+ if not filename or Defaults.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME:
filebase = 'attachment'
else:
# Sanitize the filename given in the message headers
@@ -475,7 +475,7 @@ def save_attachment(mlist, msg, dir, filter_html=True):
try:
fp.write(decodedpayload)
fp.close()
- cmd = config.ARCHIVE_HTML_SANITIZER % {'filename' : tmppath}
+ cmd = Defaults.ARCHIVE_HTML_SANITIZER % {'filename' : tmppath}
progfp = os.popen(cmd, 'r')
decodedpayload = progfp.read()
status = progfp.close()
diff --git a/mailman/pipeline/to_archive.py b/mailman/pipeline/to_archive.py
index 4d8c27cf1..6ecb860c2 100644
--- a/mailman/pipeline/to_archive.py
+++ b/mailman/pipeline/to_archive.py
@@ -26,7 +26,6 @@ from zope.interface import implements
from mailman.config import config
from mailman.i18n import _
from mailman.interfaces import IHandler
-from mailman.queue import Switchboard
@@ -48,7 +47,5 @@ class ToArchive:
# presence. I'm keeping "X-Archive: no" for backwards compatibility.
if 'x-no-archive' in msg or msg.get('x-archive', '').lower() == 'no':
return
- # Send the message to the archiver queue
- archq = Switchboard(config.ARCHQUEUE_DIR)
- # Send the message to the queue
- archq.enqueue(msg, msgdata)
+ # Send the message to the archiver queue.
+ config.switchboards['archive'].enqueue(msg, msgdata)
diff --git a/mailman/pipeline/to_digest.py b/mailman/pipeline/to_digest.py
index 78984c92e..42f92df4e 100644
--- a/mailman/pipeline/to_digest.py
+++ b/mailman/pipeline/to_digest.py
@@ -48,6 +48,7 @@ from email.parser import Parser
from email.utils import formatdate, getaddresses, make_msgid
from zope.interface import implements
+from mailman import Defaults
from mailman import Message
from mailman import Utils
from mailman import i18n
@@ -58,7 +59,6 @@ from mailman.core import errors
from mailman.interfaces import DeliveryMode, DeliveryStatus, IHandler
from mailman.pipeline.decorate import decorate
from mailman.pipeline.scrubber import process as scrubber
-from mailman.queue import Switchboard
_ = i18n._
@@ -268,8 +268,8 @@ def send_i18n_digests(mlist, mboxfp):
# for the specific MIME or plain digests.
keeper = {}
all_keepers = {}
- for header in (config.MIME_DIGEST_KEEP_HEADERS +
- config.PLAIN_DIGEST_KEEP_HEADERS):
+ for header in (Defaults.MIME_DIGEST_KEEP_HEADERS +
+ Defaults.PLAIN_DIGEST_KEEP_HEADERS):
all_keepers[header] = True
all_keepers = all_keepers.keys()
for keep in all_keepers:
@@ -325,7 +325,7 @@ def send_i18n_digests(mlist, mboxfp):
print >> plainmsg, _('[Message discarded by content filter]')
continue
# Honor the default setting
- for h in config.PLAIN_DIGEST_KEEP_HEADERS:
+ for h in Defaults.PLAIN_DIGEST_KEEP_HEADERS:
if msg[h]:
uh = Utils.wrap('%s: %s' % (h, Utils.oneline(msg[h],
in_unicode=True)))
@@ -378,7 +378,7 @@ def send_i18n_digests(mlist, mboxfp):
# Do our final bit of housekeeping, and then send each message to the
# outgoing queue for delivery.
mlist.next_digest_number += 1
- virginq = Switchboard(config.VIRGINQUEUE_DIR)
+ virginq = config.switchboards['virgin']
# Calculate the recipients lists
plainrecips = set()
mimerecips = set()
diff --git a/mailman/pipeline/to_outgoing.py b/mailman/pipeline/to_outgoing.py
index d8d1ec935..bbaf43301 100644
--- a/mailman/pipeline/to_outgoing.py
+++ b/mailman/pipeline/to_outgoing.py
@@ -28,10 +28,10 @@ __all__ = ['ToOutgoing']
from zope.interface import implements
+from mailman import Defaults
from mailman.config import config
from mailman.i18n import _
from mailman.interfaces import IHandler, Personalization
-from mailman.queue import Switchboard
@@ -45,7 +45,7 @@ class ToOutgoing:
def process(self, mlist, msg, msgdata):
"""See `IHandler`."""
- interval = config.VERP_DELIVERY_INTERVAL
+ interval = Defaults.VERP_DELIVERY_INTERVAL
# Should we VERP this message? If personalization is enabled for this
# list and VERP_PERSONALIZED_DELIVERIES is true, then yes we VERP it.
# Also, if personalization is /not/ enabled, but
@@ -57,7 +57,7 @@ class ToOutgoing:
if 'verp' in msgdata:
pass
elif mlist.personalize <> Personalization.none:
- if config.VERP_PERSONALIZED_DELIVERIES:
+ if Defaults.VERP_PERSONALIZED_DELIVERIES:
msgdata['verp'] = True
elif interval == 0:
# Never VERP
@@ -69,5 +69,5 @@ class ToOutgoing:
# VERP every `interval' number of times
msgdata['verp'] = not (int(mlist.post_id) % interval)
# And now drop the message in qfiles/out
- outq = Switchboard(config.OUTQUEUE_DIR)
- outq.enqueue(msg, msgdata, listname=mlist.fqdn_listname)
+ config.switchboards['out'].enqueue(
+ msg, msgdata, listname=mlist.fqdn_listname)
diff --git a/mailman/queue/archive.py b/mailman/queue/archive.py
index e9fd5f7ad..7725f6f97 100644
--- a/mailman/queue/archive.py
+++ b/mailman/queue/archive.py
@@ -25,6 +25,7 @@ __all__ = [
import os
import time
+import logging
from datetime import datetime
from email.Utils import parsedate_tz, mktime_tz, formatdate
@@ -34,6 +35,8 @@ from mailman import Defaults
from mailman.core.plugins import get_plugins
from mailman.queue import Runner
+log = logging.getLogger('mailman.error')
+
class ArchiveRunner(Runner):
@@ -78,5 +81,10 @@ class ArchiveRunner(Runner):
# While a list archiving lock is acquired, archive the message.
with Lock(os.path.join(mlist.data_path, 'archive.lck')):
for archive_factory in get_plugins('mailman.archiver'):
- archive_factory().archive_message(mlist, msg)
-
+ # A problem in one archiver should not prevent any other
+ # archiver from running.
+ try:
+ archive = archive_factory()
+ archive.archive_message(mlist, msg)
+ except Exception:
+ log.exception('Broken archiver: %s' % archive.name)
diff --git a/mailman/testing/helpers.py b/mailman/testing/helpers.py
index 2f5b4af01..62b0d8d95 100644
--- a/mailman/testing/helpers.py
+++ b/mailman/testing/helpers.py
@@ -80,15 +80,14 @@ class _Bag:
setattr(self, key, value)
-def get_queue_messages(queue):
+def get_queue_messages(queue_name):
"""Return and clear all the messages in the given queue.
- :param queue: An ISwitchboard or a string naming a queue.
+ :param queue_name: A string naming a queue.
:return: A list of 2-tuples where each item contains the message and
message metadata.
"""
- if isinstance(queue, basestring):
- queue = Switchboard(queue)
+ queue = config.switchboards[queue_name]
messages = []
for filebase in queue.files:
msg, msgdata = queue.dequeue(filebase)
diff --git a/mailman/testing/layers.py b/mailman/testing/layers.py
index bda18289c..883a5c784 100644
--- a/mailman/testing/layers.py
+++ b/mailman/testing/layers.py
@@ -25,7 +25,9 @@ __all__ = [
import os
+import sys
import shutil
+import logging
import tempfile
from pkg_resources import resource_string
@@ -33,6 +35,7 @@ from textwrap import dedent
from mailman.config import config
from mailman.core.initialize import initialize
+from mailman.i18n import _
from mailman.testing.helpers import SMTPServer
@@ -59,9 +62,27 @@ class ConfigLayer:
[mailman]
var_dir: %s
""" % cls.var_dir)
- # Read the testing config, but don't push it yet.
+ # Read the testing config and push it.
test_config += resource_string('mailman.testing', 'testing.cfg')
config.push('test config', test_config)
+ # Enable log message propagation.
+ for logger_config in config.logger_configs:
+ sub_name = logger_config.name.split('.')[-1]
+ if sub_name == 'root':
+ continue
+ logger_name = 'mailman.' + sub_name
+ log = logging.getLogger(logger_name)
+ log.propagate = True
+ log.setLevel(logging.DEBUG)
+ # zope.testing sets up logging before we get to our own initialization
+ # function. This messes with the root logger, so explicitly set it to
+ # go to stderr.
+ if cls.stderr:
+ console = logging.StreamHandler(sys.stderr)
+ formatter = logging.Formatter(config.logging.root.format,
+ config.logging.root.datefmt)
+ console.setFormatter(formatter)
+ logging.getLogger().addHandler(console)
@classmethod
def tearDown(cls):
@@ -87,6 +108,27 @@ class ConfigLayer:
config.db.message_store.delete_message(message['message-id'])
config.db.commit()
+ # Flag to indicate that loggers should propagate to the console.
+ stderr = False
+
+ @classmethod
+ def handle_stderr(cls, *ignore):
+ cls.stderr = True
+
+ @classmethod
+ def hack_options_parser(cls):
+ """Hack our way into the zc.testing framework.
+
+ Add our custom command line option parsing into zc.testing's. We do
+ the imports here so that if zc.testing isn't invoked, this stuff never
+ gets in the way. This is pretty fragile, depend on changes in the
+ zc.testing package. There should be a better way!
+ """
+ from zope.testing.testrunner.options import parser
+ parser.add_option('-e', '--stderr',
+ action='callback', callback=cls.handle_stderr,
+ help=_('Propagate log errors to stderr.'))
+
class SMTPLayer(ConfigLayer):
diff --git a/mailman/testing/testing.cfg b/mailman/testing/testing.cfg
index baac7d803..8bb720ab7 100644
--- a/mailman/testing/testing.cfg
+++ b/mailman/testing/testing.cfg
@@ -60,7 +60,18 @@ max_restarts: 1
base_url: http://go.mail-archive.dev/
recipient: archive@mail-archive.dev
+[archiver.pipermail]
+base_url: http://www.example.com/pipermail/$listname
+
[domain.example_dot_com]
email_host: example.com
base_url: http://www.example.com
contact_address: postmaster@example.com
+
+[language.ja]
+description: Japanese
+charset: euc-jp
+
+[language.fr]
+description: French
+charset: iso-8859-1
diff --git a/setup.py b/setup.py
index 4a5acb595..1e3613611 100644
--- a/setup.py
+++ b/setup.py
@@ -90,7 +90,6 @@ case second `m'. Any other spelling is incorrect.""",
include_package_data = True,
entry_points = {
'console_scripts': list(scripts),
- # Entry point for plugging in different database backends.
'mailman.archiver' : [
'mail-archive = mailman.archiving.mailarchive:MailArchive',
'mhonarc = mailman.archiving.mhonarc:MHonArc',