------------------------------------------------------------
revno: 6666
committer: Barry Warsaw <[email protected]>
branch nick: configs
timestamp: Tue 2009-01-06 19:55:59 -0500
message:
  The conversion from Defaults.py to lazr.config is complete.
  
  lazr.config 1.1 now has everything we need, so we don't need the special
  develop hack.
  
  make_instance is no longer necessary.
  
  Refactor the style stuff into their own directory.
  
  Finally!  Move the delivery module into the mailman.mta package.
removed:
  mailman/bin/make_instance.py
added:
  mailman/styles/manager.py
renamed:
  mailman/pipeline/smtp_direct.py => mailman/mta/smtp_direct.py
modified:
  buildout.cfg
  mailman/Archiver/HyperArch.py
  mailman/Mailbox.py
  mailman/app/notifications.py
  mailman/app/replybot.py
  mailman/archiving/mhonarc.py
  mailman/config/config.py
  mailman/config/schema.cfg
  mailman/database/pending.py
  mailman/docs/lifecycle.txt
  mailman/docs/mlist-addresses.txt
  mailman/docs/pipelines.txt
  mailman/docs/styles.txt
  mailman/interfaces/styles.py
  mailman/mta/postfix.py
  mailman/pipeline/cleanse_dkim.py
  mailman/pipeline/docs/cook-headers.txt
  mailman/pipeline/docs/scrubber.txt
  mailman/pipeline/docs/to-outgoing.txt
  mailman/queue/archive.py
  mailman/queue/bounce.py
  mailman/queue/command.py
  mailman/queue/lmtp.py
  mailman/queue/news.py
  mailman/queue/outgoing.py
  mailman/rules/docs/header-matching.txt
  mailman/testing/layers.py

=== modified file 'buildout.cfg'
--- a/buildout.cfg      2009-01-05 05:54:19 +0000
+++ b/buildout.cfg      2009-01-07 00:55:59 +0000
@@ -4,8 +4,7 @@
     tags
     test
 unzip = true
-# bzr branch lp:~barry/lazr.config/megamerge
-develop = . /Users/barry/projects/lazr/megamerge
+develop = .
 
 [interpreter]
 recipe = zc.recipe.egg

=== modified file 'mailman/Archiver/HyperArch.py'
--- a/mailman/Archiver/HyperArch.py     2009-01-03 10:47:41 +0000
+++ b/mailman/Archiver/HyperArch.py     2009-01-07 00:55:59 +0000
@@ -40,10 +40,10 @@
 from email.Charset import Charset
 from email.Errors import HeaderParseError
 from email.Header import decode_header, make_header
+from lazr.config import as_boolean
 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
@@ -176,7 +176,7 @@
         listname = mlist.fqdn_listname
     if lang is None:
         if mlist is None:
-            lang = Defaults.DEFAULT_SERVER_LANGUAGE
+            lang = config.mailman.default_language
         else:
             lang = mlist.preferred_language
     cachekey = (templatefile, lang, listname)
@@ -252,7 +252,7 @@
         self._lang = lang
         self._mlist = mlist
 
-        if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
+        if as_boolean(config.archiver.pipermail.obscure_email_addresses):
             # 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
@@ -327,7 +327,7 @@
             if hasattr(self, '_mlist'):
                 self._lang = self._mlist.preferred_language
             else:
-                self._lang = Defaults.DEFAULT_SERVER_LANGUAGE
+                self._lang = config.mailman.default_language
         if not d.has_key('cenc'):
             self.cenc = None
         if not d.has_key('decoded'):
@@ -359,7 +359,7 @@
             if email:
                 self.decoded['email'] = email
         if subject:
-            if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
+            if as_boolean(config.archiver.pipermail.obscure_email_addresses):
                 with i18n.using_language(self._lang):
                     atmark = _(' at ')
                     subject = re.sub(r'([-+,.\w]+)@([-+.\w]+)',
@@ -407,7 +407,7 @@
             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 Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
+            if as_boolean(config.archiver.pipermail.obscure_email_addresses):
                 # Point the mailto url back to the list
                 author = re.sub('@', _(' at '), self.author)
                 emailurl = self._mlist.posting_address
@@ -511,7 +511,7 @@
         # Coerce the body to Unicode and replace any invalid characters.
         if not isinstance(body, unicode):
             body = unicode(body, cset, 'replace')
-        if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
+        if as_boolean(config.archiver.pipermail.obscure_email_addresses):
             with i18n.using_language(self._lang):
                 atmark = _(' at ')
                 body = re.sub(r'([-+,.\w]+)@([-+.\w]+)',
@@ -707,7 +707,7 @@
         # 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 Defaults.PUBLIC_MBOX:
+        if as_boolean(config.archiver.pipermail.public_mbox):
             template = 'archtoc.html'
         else:
             template = 'archtocnombox.html'
@@ -964,7 +964,7 @@
     def write_index_entry(self, article):
         subject = self.get_header("subject", article)
         author = self.get_header("author", article)
-        if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
+        if as_boolean(config.archiver.pipermail.obscure_email_addresses):
             try:
                 author = re.sub('@', _(' at '), author)
             except UnicodeError:
@@ -1137,7 +1137,8 @@
                 if j != -1 and (j < k or k == -1):
                     text = jr.group(1)
                     length = len(text)
-                    if Defaults.ARCHIVER_OBSCURES_EMAILADDRS:
+                    if as_boolean(
+                        config.archiver.pipermail.obscure_email_addresses):
                         text = re.sub('@', atmark, text)
                         URL = self.maillist.script_url('listinfo')
                     else:

=== modified file 'mailman/Mailbox.py'
--- a/mailman/Mailbox.py        2009-01-05 05:54:19 +0000
+++ b/mailman/Mailbox.py        2009-01-07 00:55:59 +0000
@@ -22,10 +22,11 @@
 import email
 import mailbox
 
-from email.Errors import MessageParseError
-from email.Generator import Generator
+from email.errors import MessageParseError
+from email.generator import Generator
 
 from mailman.Message import Message
+from mailman.config import config
 
 
 

=== modified file 'mailman/app/notifications.py'
--- a/mailman/app/notifications.py      2009-01-05 05:54:19 +0000
+++ b/mailman/app/notifications.py      2009-01-07 00:55:59 +0000
@@ -31,8 +31,10 @@
 from mailman import Message
 from mailman import Utils
 from mailman import i18n
+from mailman.config import config
 from mailman.interfaces.member import DeliveryMode
 
+
 _ = i18n._
 
 

=== modified file 'mailman/app/replybot.py'
--- a/mailman/app/replybot.py   2009-01-05 05:54:19 +0000
+++ b/mailman/app/replybot.py   2009-01-07 00:55:59 +0000
@@ -31,6 +31,7 @@
 
 from mailman import Utils
 from mailman import i18n
+from mailman.config import config
 
 
 log = logging.getLogger('mailman.vette')

=== modified file 'mailman/archiving/mhonarc.py'
--- a/mailman/archiving/mhonarc.py      2009-01-03 10:13:41 +0000
+++ b/mailman/archiving/mhonarc.py      2009-01-07 00:55:59 +0000
@@ -32,7 +32,6 @@
 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 +52,7 @@
         """See `IArchiver`."""
         # XXX What about private MHonArc archives?
         web_host = config.domains[mlist.host_name].url_host
-        return Template(Defaults.PUBLIC_ARCHIVE_URL).safe_substitute(
+        return Template(config.archiver.mhonarc.base_url).safe_substitute(
             listname=mlist.fqdn_listname,
             hostname=web_host,
             fqdn_listname=mlist.fqdn_listname,

=== removed file 'mailman/bin/make_instance.py'
--- a/mailman/bin/make_instance.py      2009-01-01 22:16:51 +0000
+++ b/mailman/bin/make_instance.py      1970-01-01 00:00:00 +0000
@@ -1,171 +0,0 @@
-# Copyright (C) 2007-2009 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/>.
-
-import os
-import grp
-import pwd
-import sys
-import errno
-import shutil
-import optparse
-import setuptools
-
-from pkg_resources import resource_string
-from string import Template
-
-from mailman import Defaults
-from mailman.version import MAILMAN_VERSION
-from mailman.i18n import _
-
-
-SPACE = ' '
-
-
-
-def parseargs():
-    parser = optparse.OptionParser(version=MAILMAN_VERSION,
-                                   usage=_("""\
-%prog [options]
-
-Create a Mailman instance by generating all the necessary basic configuration
-support and intervening directories.
-"""))
-    parser.add_option('-d', '--var-dir',
-                      type='string', default=Defaults.DEFAULT_VAR_DIRECTORY,
-                      help=_("""\
-The top-level runtime data directory.  All supporting runtime data will be
-placed in subdirectories of this directory.  It will be created if necessary,
-although this might require superuser privileges."""))
-    parser.add_option('-f', '--force',
-                      default=False, action='store_true', help=_("""\
-Force overwriting of mailman.cfg file with new values.  Ordinarily, Mailman
-will never overwrite this file because it would cause you to lose your
-configuration data."""))
-    parser.add_option('-p', '--permchecks',
-                      default=False, action='store_true', help=_("""\
-Perform permission checks on the runtime directory."""))
-    parser.add_option('-u', '--user',
-                      type='string', default=None, help=_("""\
-The user id or name to use for the runtime environment.  If not specified, the
-current user is used."""))
-    parser.add_option('-g', '--group',
-                      type='string', default=None, help=_("""\
-The group id or name to use for the runtime environment.  If not specified, the
-current group is used."""))
-    parser.add_option('-l', '--languages',
-                      default=Defaults.DEFAULT_SERVER_LANGUAGE, type='string',
-                      help=_("""\
-Space separated list of language codes to enable.  Use -L to print all
-available language codes and the name of the associated native language.
-Default is to enable just English.  Use the special code 'all' to enable all
-available languages."""))
-    opts, args = parser.parse_args()
-    if args:
-        unexpected = SPACE.join(args)
-        parser.error(_('Unexpected arguments: $unexpected'))
-    return parser, opts, args
-
-
-
-def instantiate(var_dir, user, group, languages, force):
-    # XXX This needs to be converted to use package resources.
-    etc_dir = os.path.join(var_dir, 'etc')
-    # Figure out which user name and group name to use.
-    if user is None:
-        uid = os.getuid()
-    else:
-        try:
-            uid = int(user)
-        except ValueError:
-            try:
-                uid = pwd.getpwnam(user).pw_uid
-            except KeyError:
-                parser.print_error(_('Unknown user: $user'))
-    try:
-        user_name = pwd.getpwuid(uid).pw_name
-    except KeyError:
-        parser.print_error(_('Unknown user: $user'))
-    if group is None:
-        gid = os.getgid()
-    else:
-        try:
-            gid = int(group)
-        except ValueError:
-            try:
-                gid = grp.getgrnam(group).gr_gid
-            except KeyError:
-                parser.print_error(_('Unknown group: $group'))
-    try:
-        group_name = grp.getgrgid(gid).gr_name
-    except KeyError:
-        parser.print_error(_('Unknown group: $group'))
-    # Make the runtime dir if it doesn't yet exist.
-    try:
-        omask = os.umask(0)
-        try:
-            os.makedirs(etc_dir, 02775)
-        finally:
-            os.umask(omask)
-    except OSError, e:
-        # Ignore the exceptions if the directory already exists
-        if e.errno <> errno.EEXIST:
-            raise
-    os.chown(etc_dir, uid, gid)
-    # Create an etc/mailman.cfg file which contains just a few configuration
-    # variables about the run-time environment that can't be calculated.
-    # Don't overwrite mailman.cfg unless the -f flag was given.
-    out_file_path = os.path.join(etc_dir, 'mailman.cfg')
-    if os.path.exists(out_file_path) and not force:
-        # The logging subsystem isn't up yet, so just print this to stderr.
-        print >> sys.stderr, 'File exists:', out_file_path
-        print >> sys.stderr, 'Use --force to override.'
-    else:
-        raw = Template(resource_string('mailman.extras', 'mailman.cfg.in'))
-        processed = raw.safe_substitute(var_dir=var_dir,
-                                        user_id=uid,
-                                        user_name=user_name,
-                                        group_name=group_name,
-                                        group_id=gid,
-                                        languages=SPACE.join(languages),
-                                        )
-        with open(out_file_path, 'w') as fp:
-            fp.write(processed)
-    # XXX Do --permchecks
-    
-
-
-def main():
-    parser, opts, args = parseargs()
-    available_languages = set(Defaults._DEFAULT_LANGUAGE_DATA)
-    enable_languages = set(opts.languages.split())
-    if 'all' in enable_languages:
-        languages = available_languages
-    else:
-        unknown_language = enable_languages - available_languages
-        if unknown_language:
-            print >> sys.stderr, 'Ignoring unknown language codes:', \
-                  SPACE.join(unknown_language)
-        languages = available_languages & enable_languages
-    # We need an absolute path for var_dir.
-    var_dir = os.path.abspath(opts.var_dir)
-    instantiate(var_dir, opts.user, opts.group, languages, opts.force)
-
-
-
-if __name__ == '__main__':
-    main()
-

=== modified file 'mailman/config/config.py'
--- a/mailman/config/config.py  2009-01-05 05:54:19 +0000
+++ b/mailman/config/config.py  2009-01-07 00:55:59 +0000
@@ -50,6 +50,7 @@
         self.domains = {}       # email host -> IDomain
         self.switchboards = {}
         self.languages = LanguageManager()
+        self.style_manager = StyleManager()
         self.QFILE_SCHEMA_VERSION = version.QFILE_SCHEMA_VERSION
         self._config = None
         self.filename = None
@@ -149,7 +150,7 @@
         # Always enable the server default language, which must be defined.
         self.languages.enable_language(self._config.mailman.default_language)
         self.ensure_directories_exist()
-        self.style_manager = StyleManager()
+        self.style_manager.populate()
 
     @property
     def logger_configs(self):

=== modified file 'mailman/config/schema.cfg'
--- a/mailman/config/schema.cfg 2009-01-05 05:54:19 +0000
+++ b/mailman/config/schema.cfg 2009-01-07 00:55:59 +0000
@@ -266,7 +266,7 @@
 incoming: mailman.mta.postfix.LMTP
 
 # The class defining the interface to the outgoing mail transport agent.
-outgoing: mailman.mta.direct.SMTPDirect
+outgoing: mailman.mta.smtp_direct.process
 
 # How to connect to the outgoing MTA.
 smtp_host: localhost
@@ -296,6 +296,11 @@
 # to 0.
 max_delivery_threads: 0
 
+# How long should messages which have delivery failures continue to be
+# retried?  After this period of time, a message that has failed recipients
+# will be dequeued and those recipients will never receive the message.
+delivery_retry_period: 5d
+
 # These variables control the format and frequency of VERP-like delivery for
 # better bounce detection.  VERP is Variable Envelope Return Path, defined
 # here:
@@ -370,6 +375,12 @@
 # 1 to VERP every delivery, or to some number > 1 for only occasional VERPs.
 verp_delivery_interval: 0
 
+# VERP format and regexp for probe messages.
+verp_probe_format: %(bounces)s+%(token)s
+verp_probe_regexp: ^(?P<bounces>[^+]+?)\+(?P<token>[...@]+)@.*$
+# Set this 'yes' to activate VERP probe for disabling by bounce.
+verp_probes: no
+
 # This is the maximum number of automatic responses sent to an address because
 # of -request messages or posting hold messages.  This limit prevents response
 # loops between Mailman and misconfigured remote email robots.  Mailman
@@ -388,6 +399,17 @@
 # may wish to remove these headers by setting this to 'yes'.
 remove_dkim_headers: no
 
+# This variable describe the program to use for regenerating the transport map
+# db file, from the associated plain text files.  The file being updated will
+# be appended to this string (with a separating space), so it must be
+# appropriate for os.system().
+postfix_map_cmd: /usr/sbin/postmap
+
+
+[bounces]
+# How often should the bounce qrunner process queued detected bounces?
+register_bounces_every: 15m
+
 
 [archiver.master]
 # To add new archivers, define a new section based on this one, overriding the
@@ -416,6 +438,9 @@
 # This is the stock MHonArc archiver.
 class: mailman.archiving.mhonarc.MHonArc
 
+base_url: http://$hostname/archives/$fqdn_listname
+
+
 [archiver.mail_archive]
 # This is the stock mail-archive.com archiver.
 class: mailman.archiving.mailarchive.MailArchive
@@ -424,6 +449,29 @@
 # This is the stock Pipermail archiver.
 class: mailman.archiving.pipermail.Pipermail
 
+# This sets the default `clobber date' policy for the archiver.  When a
+# message is to be archived either by Pipermail or an external archiver,
+# Mailman can modify the Date: header to be the date the message was received
+# instead of the Date: in the original message.  This is useful if you
+# typically receive messages with outrageous dates.  Set this to 0 to retain
+# the date of the original message, or to 1 to always clobber the date.  Set
+# it to 2 to perform `smart overrides' on the date; when the date is outside
+# allowable_sane_date_skew (either too early or too late), then the received
+# date is substituted instead.
+clobber_date_policy: 2
+allowable_sane_date_skew: 15d
+
+# Pipermail archives contain the raw email addresses of the posting authors.
+# Some view this as a goldmine for spam harvesters.  Set this to 'yes' to
+# moderately obscure email addresses, but note that this breaks mailto: URLs
+# in the archives too.
+obscure_email_addresses: yes
+
+# When the archive is public, should Pipermail also make the raw Unix mbox
+# file publically available?
+public_mbox: no
+
+
 [archiver.prototype]
 # This is a prototypical sample archiver.
 class: mailman.archiving.prototype.Prototype
@@ -505,3 +553,37 @@
     Subject To Cc
     Message-ID Keywords
     Content-Type
+
+
+[nntp]
+# Set these variables if you need to authenticate to your NNTP server for
+# Usenet posting or reading.  If no authentication is necessary, specify None
+# for both variables.
+username:
+password:
+
+# Set this if you have an NNTP server you prefer gatewayed lists to use.
+host:
+
+# This controls how headers must be cleansed in order to be accepted by your
+# NNTP server.  Some servers like INN reject messages containing prohibited
+# headers, or duplicate headers.  The NNTP server may reject the message for
+# other reasons, but there's little that can be programmatically done about
+# that.
+#
+# These headers (case ignored) are removed from the original message.  This is
+# a whitespace separate list of headers.
+remove_headers:
+    nntp-posting-host nntp-posting-date x-trace
+    x-complaints-to xref date-received posted
+    posting-version relay-version received
+
+# These headers are left alone, unless there are duplicates in the original
+# message.  Any second and subsequent headers are rewritten to the second
+# named header (case preserved).  This is a list of header pairs, one pair per
+# line.
+rewrite_duplicate_headers:
+    To X-Original-To
+    CC X-Original-CC
+    Content-Transfer-Encoding X-Original-Content-Transfer-Encoding
+    MIME-Version X-MIME-Version

=== modified file 'mailman/database/pending.py'
--- a/mailman/database/pending.py       2009-01-05 05:54:19 +0000
+++ b/mailman/database/pending.py       2009-01-07 00:55:59 +0000
@@ -87,7 +87,7 @@
         verifyObject(IPendable, pendable)
         # Calculate the token and the lifetime.
         if lifetime is None:
-            lifetime = as_timedelta(config.pending_request_life)
+            lifetime = as_timedelta(config.mailman.pending_request_life)
         # Calculate a unique token.  Algorithm vetted by the Timbot.  time()
         # has high resolution on Linux, clock() on Windows.  random gives us
         # about 45 bits in Python 2.2, 53 bits on Python 2.3.  The time and

=== modified file 'mailman/docs/lifecycle.txt'
--- a/mailman/docs/lifecycle.txt        2009-01-04 05:22:08 +0000
+++ b/mailman/docs/lifecycle.txt        2009-01-07 00:55:59 +0000
@@ -58,8 +58,7 @@
     ...         if 'test' in mailing_list.fqdn_listname:
     ...             styles.append(self)
 
-    >>> from mailman.core.styles import style_manager
-    >>> style_manager.register(TestStyle())
+    >>> config.style_manager.register(TestStyle())
 
 Using the higher level interface for creating a list, applies all matching
 list styles.

=== modified file 'mailman/docs/mlist-addresses.txt'
--- a/mailman/docs/mlist-addresses.txt  2008-12-30 04:28:56 +0000
+++ b/mailman/docs/mlist-addresses.txt  2009-01-07 00:55:59 +0000
@@ -67,9 +67,10 @@
     >>> mlist.confirm_address('wookie')
     u'[email protected]'
 
-    >>> from mailman import Defaults
-    >>> old_format = Defaults.VERP_CONFIRM_FORMAT
-    >>> Defaults.VERP_CONFIRM_FORMAT = '$address---$cookie'
+    >>> config.push('test config', """
+    ... [mta]
+    ... verp_confirm_format: $address---$cookie
+    ... """)
     >>> mlist.confirm_address('cookie')
     u'[email protected]'
-    >>> config.VERP_CONFIRM_FORMAT = old_format
+    >>> config.pop('test config')

=== modified file 'mailman/docs/pipelines.txt'
--- a/mailman/docs/pipelines.txt        2009-01-03 10:13:41 +0000
+++ b/mailman/docs/pipelines.txt        2009-01-07 00:55:59 +0000
@@ -10,8 +10,8 @@
 
     >>> from mailman.app.lifecycle import create_list
     >>> mlist = create_list(u'[email protected]')
-    >>> mlist.pipeline
-    u'built-in'
+    >>> print mlist.pipeline
+    built-in
     >>> from mailman.core.pipelines import process
 
 
@@ -49,11 +49,11 @@
         <http://lists.example.com/listinfo/[email protected]>,
        <mailto:[email protected]>
     Archived-At:
-        http://lists.example.com/pipermail/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
+        http://lists.example.com/archives/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
     List-Unsubscribe:
         <http://lists.example.com/listinfo/[email protected]>,
        <mailto:[email protected]>
-    List-Archive: <http://lists.example.com/pipermail/[email protected]>
+    List-Archive: <http://lists.example.com/archives/[email protected]>
     List-Help: <mailto:[email protected]?subject=help>
     <BLANKLINE>
     First post!
@@ -89,11 +89,11 @@
         <http://lists.example.com/listinfo/[email protected]>,
         <mailto:[email protected]>
     Archived-At:
-        http://lists.example.com/pipermail/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
+        http://lists.example.com/archives/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
     List-Unsubscribe:
         <http://lists.example.com/listinfo/[email protected]>,
         <mailto:[email protected]>
-    List-Archive: <http://lists.example.com/pipermail/[email protected]>
+    List-Archive: <http://lists.example.com/archives/[email protected]>
     List-Help: <mailto:[email protected]?subject=help>
     <BLANKLINE>
     First post!
@@ -132,11 +132,11 @@
         <http://lists.example.com/listinfo/[email protected]>,
         <mailto:[email protected]>
     Archived-At:
-        http://lists.example.com/pipermail/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
+        http://lists.example.com/archives/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
     List-Unsubscribe:
         <http://lists.example.com/listinfo/[email protected]>,
         <mailto:[email protected]>
-    List-Archive: <http://lists.example.com/pipermail/[email protected]>
+    List-Archive: <http://lists.example.com/archives/[email protected]>
     List-Help: <mailto:[email protected]?subject=help>
     <BLANKLINE>
     First post!
@@ -170,11 +170,11 @@
         <http://lists.example.com/listinfo/[email protected]>,
         <mailto:[email protected]>
     Archived-At:
-        http://lists.example.com/pipermail/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
+        http://lists.example.com/archives/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
     List-Unsubscribe:
         <http://lists.example.com/listinfo/[email protected]>,
         <mailto:[email protected]>
-    List-Archive: <http://lists.example.com/pipermail/[email protected]>
+    List-Archive: <http://lists.example.com/archives/[email protected]>
     List-Help: <mailto:[email protected]?subject=help>
     <BLANKLINE>
     First post!

=== modified file 'mailman/docs/styles.txt'
--- a/mailman/docs/styles.txt   2009-01-04 05:22:08 +0000
+++ b/mailman/docs/styles.txt   2009-01-07 00:55:59 +0000
@@ -14,17 +14,21 @@
 Let's start with a vanilla mailing list and a default style manager.
 
     >>> mlist = config.db.list_manager.create(u'[email protected]')
-    >>> from mailman.core.styles import StyleManager
+    >>> from mailman.styles.manager import StyleManager
     >>> style_manager = StyleManager()
+    >>> style_manager.populate()
+    >>> sorted(style.name for style in style_manager.styles)
+    ['default']
 
 
 The default style
 -----------------
 
 There is a default style which implements the legacy application of list
-defaults from Defaults.py.  This style only matching a mailing list when no
-other styles match, and it has the lowest priority.  The low priority means
-that it is matched last and if it matches, it is applied last.
+defaults from previous versions of Mailman.  This style only matching a
+mailing list when no other styles match, and it has the lowest priority.  The
+low priority means that it is matched last and if it matches, it is applied
+last.
 
     >>> default_style = style_manager.get('default')
     >>> default_style.name
@@ -43,17 +47,6 @@
     >>> [style.name for style in style_manager.lookup(mlist)]
     ['default']
 
-If the site administrator modified their mailman.cfg file, the default style
-would pick this up and apply it to the mailing list.
-
-    >>> print mlist.msg_footer
-    None
-    >>> from mailman import Defaults
-    >>> Defaults.DEFAULT_MSG_FOOTER = u'default footer'
-    >>> default_style.apply(mlist)
-    >>> mlist.msg_footer
-    u'default footer'
-
 
 Registering styles
 ------------------

=== modified file 'mailman/interfaces/styles.py'
--- a/mailman/interfaces/styles.py      2009-01-01 22:16:51 +0000
+++ b/mailman/interfaces/styles.py      2009-01-07 00:55:59 +0000
@@ -109,3 +109,10 @@
         :param style: an IStyle.
         :raises KeyError: If the style's name is not currently registered.
         """
+
+    def populate():
+        """Populate the styles from the configuration files.
+
+        This clears the current set of styles and resets them from those
+        defined in the configuration files.
+        """

=== modified file 'mailman/mta/postfix.py'
--- a/mailman/mta/postfix.py    2009-01-03 10:13:41 +0000
+++ b/mailman/mta/postfix.py    2009-01-07 00:55:59 +0000
@@ -34,7 +34,6 @@
 from locknix.lockfile import Lock
 from zope.interface import implements
 
-from mailman import Defaults
 from mailman import Utils
 from mailman.config import config
 from mailman.interfaces.mta import IMailTransportAgent
@@ -113,7 +112,7 @@
         os.rename(path + '.new', path)
         # Now that the new aliases file has been written, we must tell Postfix
         # to generate a new .db file.
-        command = Defaults.POSTFIX_MAP_CMD + ' ' + path
+        command = config.mta.postfix_map_cmd + ' ' + path
         status = (os.system(command) >> 8) & 0xff
         if status:
             msg = 'command failure: %s, %s, %s'

=== renamed file 'mailman/pipeline/smtp_direct.py' => 
'mailman/mta/smtp_direct.py'
=== modified file 'mailman/pipeline/cleanse_dkim.py'
--- a/mailman/pipeline/cleanse_dkim.py  2009-01-05 05:54:19 +0000
+++ b/mailman/pipeline/cleanse_dkim.py  2009-01-07 00:55:59 +0000
@@ -34,6 +34,7 @@
 from lazr.config import as_boolean
 from zope.interface import implements
 
+from mailman.config import config
 from mailman.i18n import _
 from mailman.interfaces.handler import IHandler
 

=== modified file 'mailman/pipeline/docs/cook-headers.txt'
--- a/mailman/pipeline/docs/cook-headers.txt    2009-01-04 05:22:08 +0000
+++ b/mailman/pipeline/docs/cook-headers.txt    2009-01-07 00:55:59 +0000
@@ -188,7 +188,7 @@
     >>> process(mlist, msg, {})
     >>> list_headers(msg)
     ---start---
-    List-Archive: <http://lists.example.com/pipermail/[email protected]>
+    List-Archive: <http://lists.example.com/archives/[email protected]>
     List-Help: <mailto:[email protected]?subject=help>
     List-Id: <_xtest.example.com>
     List-Post: <mailto:[email protected]>
@@ -209,7 +209,7 @@
     >>> process(mlist, msg, {})
     >>> list_headers(msg)
     ---start---
-    List-Archive: <http://lists.example.com/pipermail/[email protected]>
+    List-Archive: <http://lists.example.com/archives/[email protected]>
     List-Help: <mailto:[email protected]?subject=help>
     List-Id: My test mailing list <_xtest.example.com>
     List-Post: <mailto:[email protected]>
@@ -248,7 +248,7 @@
     >>> process(mlist, msg, {})
     >>> list_headers(msg)
     ---start---
-    List-Archive: <http://lists.example.com/pipermail/[email protected]>
+    List-Archive: <http://lists.example.com/archives/[email protected]>
     List-Help: <mailto:[email protected]?subject=help>
     List-Id: My test mailing list <_xtest.example.com>
     List-Subscribe: <http://lists.example.com/listinfo/[email protected]>,

=== modified file 'mailman/pipeline/docs/scrubber.txt'
--- a/mailman/pipeline/docs/scrubber.txt        2009-01-01 22:07:06 +0000
+++ b/mailman/pipeline/docs/scrubber.txt        2009-01-07 00:55:59 +0000
@@ -50,8 +50,10 @@
 enabled, the filename will be used when this header attribute is present (yes,
 this is an unfortunate double negative).
 
-    >>> from mailman import Defaults
-    >>> Defaults.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME = False
+    >>> config.push('test config', """
+    ... [scrubber]
+    ... use_attachment_filename: yes
+    ... """)
     >>> msg = message_from_string("""\
     ... Content-Type: image/gif; name="xtest.gif"
     ... Content-Transfer-Encoding: base64
@@ -77,10 +79,13 @@
     R0lGODdhAQABAIAAAAAAAAAAACwAAAAAAQABAAACAQUAOw==
 
 The site administrator can also configure Mailman to ignore the
-Content-Disposition: filename.  This is the default for reasons described in
-the Defaults.py.in file.
+Content-Disposition: filename.  This is the default.
 
-    >>> Defaults.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME = True
+    >>> config.pop('test config')
+    >>> config.push('test config', """
+    ... [scrubber]
+    ... use_attachment_filename: no
+    ... """)
     >>> msg = message_from_string("""\
     ... Content-Type: image/gif; name="xtest.gif"
     ... Content-Transfer-Encoding: base64
@@ -212,3 +217,9 @@
     <BLANKLINE>
     >>> read_url_from_message(msg)
     'This is a text attachment.'
+
+
+Clean up
+--------
+
+    >>> config.pop('test config')

=== modified file 'mailman/pipeline/docs/to-outgoing.txt'
--- a/mailman/pipeline/docs/to-outgoing.txt     2009-01-04 05:22:08 +0000
+++ b/mailman/pipeline/docs/to-outgoing.txt     2009-01-07 00:55:59 +0000
@@ -64,10 +64,11 @@
 option to VERP personalized deliveries is set, then the message will be
 VERP'd.
 
-    # Save the original value for clean up.
-    >>> from mailman import Defaults
-    >>> verp_personalized_delivieries = Defaults.VERP_PERSONALIZED_DELIVERIES
-    >>> Defaults.VERP_PERSONALIZED_DELIVERIES = True
+    >>> config.push('test config', """
+    ... [mta]
+    ... verp_personalized_deliveries: yes
+    ... """)
+
     >>> from mailman.interfaces.mailinglist import Personalization
     >>> mlist.personalize = Personalization.individual
     >>> msgdata = dict(foo=1, bar=2)
@@ -80,7 +81,12 @@
 However, if the global configuration variable prohibits VERP'ing, even
 personalized lists will not VERP.
 
-    >>> Defaults.VERP_PERSONALIZED_DELIVERIES = False
+    >>> config.pop('test config')
+    >>> config.push('test config', """
+    ... [mta]
+    ... verp_personalized_deliveries: no
+    ... """)
+
     >>> msgdata = dict(foo=1, bar=2)
     >>> handler.process(mlist, msg, msgdata)
     >>> print msgdata.get('verp')
@@ -93,9 +99,12 @@
 Mailman how often to VERP even non-personalized mailing lists.  It can be set
 to zero, which means non-personalized messages will never be VERP'd.
 
-    # Save the original value for clean up.
-    >>> verp_delivery_interval = Defaults.VERP_DELIVERY_INTERVAL
-    >>> Defaults.VERP_DELIVERY_INTERVAL = 0
+    >>> config.pop('test config')
+    >>> config.push('test config', """
+    ... [mta]
+    ... verp_delivery_interval: 0
+    ... """)
+
     >>> mlist.personalize = Personalization.none
     >>> msgdata = dict(foo=1, bar=2)
     >>> handler.process(mlist, msg, msgdata)
@@ -106,7 +115,12 @@
 
 If the interval is set to 1, then every message will be VERP'd.
 
-    >>> Defaults.VERP_DELIVERY_INTERVAL = 1
+    >>> config.pop('test config')
+    >>> config.push('test config', """
+    ... [mta]
+    ... verp_delivery_interval: 1
+    ... """)
+
     >>> for i in range(10):
     ...     msgdata = dict(foo=1, bar=2)
     ...     handler.process(mlist, msg, msgdata)
@@ -127,7 +141,12 @@
 If the interval is set to some other number, then one out of that many posts
 will be VERP'd.
 
-    >>> Defaults.VERP_DELIVERY_INTERVAL = 3
+    >>> config.pop('test config')
+    >>> config.push('test config', """
+    ... [mta]
+    ... verp_delivery_interval: 3
+    ... """)
+
     >>> for i in range(10):
     ...     mlist.post_id = i
     ...     msgdata = dict(foo=1, bar=2)
@@ -150,5 +169,4 @@
 Clean up
 ========
 
-    >>> Defaults.VERP_PERSONALIZED_DELIVERIES = verp_personalized_delivieries
-    >>> Defaults.VERP_DELIVERY_INTERVAL = verp_delivery_interval
+    >>> config.pop('test config')

=== modified file 'mailman/queue/archive.py'
--- a/mailman/queue/archive.py  2009-01-03 10:13:41 +0000
+++ b/mailman/queue/archive.py  2009-01-07 00:55:59 +0000
@@ -30,10 +30,9 @@
 
 from datetime import datetime
 from email.Utils import parsedate_tz, mktime_tz, formatdate
-from lazr.config import as_boolean
+from lazr.config import as_boolean, as_timedelta
 from locknix.lockfile import Lock
 
-from mailman import Defaults
 from mailman.config import config
 from mailman.queue import Runner
 
@@ -53,9 +52,9 @@
         received_time = formatdate(msgdata['received_time'])
         if not original_date:
             clobber = True
-        elif Defaults.ARCHIVER_CLOBBER_DATE_POLICY == 1:
+        elif int(config.archiver.pipermail.clobber_date_policy) == 1:
             clobber = True
-        elif Defaults.ARCHIVER_CLOBBER_DATE_POLICY == 2:
+        elif int(config.archiver.pipermail.clobber_date_policy) == 2:
             # What's the timestamp on the original message?
             timetup = parsedate_tz(original_date)
             now = datetime.now()
@@ -64,8 +63,9 @@
                     clobber = True
                 else:
                     utc_timestamp = datetime.fromtimestamp(mktime_tz(timetup))
-                    clobber = (abs(now - utc_timestamp) > 
-                               Defaults.ARCHIVER_ALLOWABLE_SANE_DATE_SKEW)
+                    date_skew = as_timedelta(
+                        config.archiver.pipermail.allowable_sane_date_skew)
+                    clobber = (abs(now - utc_timestamp) > date_skew)
             except (ValueError, OverflowError):
                 # The likely cause of this is that the year in the Date: field
                 # is horribly incorrect, e.g. (from SF bug # 571634):

=== modified file 'mailman/queue/bounce.py'
--- a/mailman/queue/bounce.py   2009-01-05 00:41:05 +0000
+++ b/mailman/queue/bounce.py   2009-01-07 00:55:59 +0000
@@ -24,8 +24,8 @@
 import datetime
 
 from email.Utils import parseaddr
+from lazr.config import as_timedelta
 
-from mailman import Defaults
 from mailman import Utils
 from mailman.Bouncers import BouncerAPI
 from mailman.config import config
@@ -77,8 +77,9 @@
             config.DATA_DIR, 'bounce-events-%05d.pck' % os.getpid())
         self._bounce_events_fp = None
         self._bouncecnt = 0
-        self._nextaction = (datetime.datetime.now() +
-                            Defaults.REGISTER_BOUNCES_EVERY)
+        self._nextaction = (
+            datetime.datetime.now() +
+            as_timedelta(config.bounces.register_bounces_every))
 
     def _queue_bounces(self, listname, addrs, msg):
         today = datetime.date.today()
@@ -130,7 +131,8 @@
         if self._nextaction > now or self._bouncecnt == 0:
             return
         # Let's go ahead and register the bounces we've got stored up
-        self._nextaction = now + Defaults.REGISTER_BOUNCES_EVERY
+        self._nextaction = now + as_timedelta(
+            config.bounces.register_bounces_every)
         self._register_bounces()
 
     def _probe_bounce(self, mlist, token):
@@ -239,7 +241,7 @@
         to = parseaddr(field)[1]
         if not to:
             continue                          # empty header
-        mo = re.search(Defaults.VERP_REGEXP, to)
+        mo = re.search(config.mta.verp_regexp, to)
         if not mo:
             continue                          # no match of regexp
         try:
@@ -248,8 +250,8 @@
             # All is good
             addr = '%...@%s' % mo.group('mailbox', 'host')
         except IndexError:
-            elog.error("VERP_REGEXP doesn't yield the right match groups: %s",
-                       Defaults.VERP_REGEXP)
+            elog.error("verp_regexp doesn't yield the right match groups: %s",
+                       config.mta.verp_regexp)
             return []
         return [addr]
 
@@ -270,7 +272,7 @@
         to = parseaddr(field)[1]
         if not to:
             continue                          # empty header
-        mo = re.search(Defaults.VERP_PROBE_REGEXP, to)
+        mo = re.search(config.mta.verp_probe_regexp, to)
         if not mo:
             continue                          # no match of regexp
         try:
@@ -283,8 +285,8 @@
                 return token
         except IndexError:
             elog.error(
-                "VERP_PROBE_REGEXP doesn't yield the right match groups: %s",
-                Defaults.VERP_PROBE_REGEXP)
+                "verp_probe_regexp doesn't yield the right match groups: %s",
+                config.mta.verp_probe_regexp)
     return None
 
 

=== modified file 'mailman/queue/command.py'
--- a/mailman/queue/command.py  2009-01-05 00:41:05 +0000
+++ b/mailman/queue/command.py  2009-01-07 00:55:59 +0000
@@ -37,7 +37,6 @@
 from email.Iterators import typed_subpart_iterator
 from zope.interface import implements
 
-from mailman import Defaults
 from mailman import Message
 from mailman.config import config
 from mailman.i18n import _
@@ -66,7 +65,7 @@
         elif msgdata.get('toleave'):
             self.command_lines.append('leave')
         elif msgdata.get('toconfirm'):
-            mo = re.match(Defaults.VERP_CONFIRM_REGEXP, msg.get('to', ''))
+            mo = re.match(config.mta.verp_confirm_regexp, msg.get('to', ''))
             if mo:
                 self.command_lines.append('confirm ' + mo.group('cookie'))
         # Extract the subject header and do RFC 2047 decoding.
@@ -95,8 +94,9 @@
         assert isinstance(body, basestring), 'Non-string decoded payload'
         lines = body.splitlines()
         # Use no more lines than specified
-        self.command_lines.extend(lines[:Defaults.EMAIL_COMMANDS_MAX_LINES])
-        self.ignored_lines.extend(lines[Defaults.EMAIL_COMMANDS_MAX_LINES:])
+        max_lines = int(config.mailman.email_commands_max_lines)
+        self.command_lines.extend(lines[:max_lines])
+        self.ignored_lines.extend(lines[max_lines:])
 
     def __iter__(self):
         """Return each command line, split into commands and arguments.

=== modified file 'mailman/queue/lmtp.py'
--- a/mailman/queue/lmtp.py     2009-01-05 00:41:05 +0000
+++ b/mailman/queue/lmtp.py     2009-01-07 00:55:59 +0000
@@ -29,9 +29,6 @@
 
 [1] RFC 2033 Local Mail Transport Protocol
     http://www.faqs.org/rfcs/rfc2033.html
-
-See the variable USE_LMTP in Defaults.py.in for enabling this delivery
-mechanism.
 """
 
 import email
@@ -41,7 +38,6 @@
 
 from email.utils import parseaddr
 
-from mailman import Defaults
 from mailman.Message import Message
 from mailman.config import config
 from mailman.database.transaction import txn
@@ -63,7 +59,7 @@
 ERR_451 = '451 Requested action aborted: error in processing'
 ERR_501 = '501 Message has defects'
 ERR_502 = '502 Error: command HELO not implemented'
-ERR_550 = Defaults.LMTP_ERR_550
+ERR_550 = '550 Requested action not taken: mailbox unavailable'
 
 # XXX Blech
 smtpd.__version__ = 'Python LMTP queue runner 1.0'
@@ -86,7 +82,7 @@
         subaddress may be None if this is the list's posting address.
     """
     localpart, domain = address.split('@', 1)
-    localpart = localpart.split(Defaults.VERP_DELIMITER, 1)[0]
+    localpart = localpart.split(config.mta.verp_delimiter, 1)[0]
     parts = localpart.split(DASH)
     if parts[-1] in SUBADDRESS_NAMES:
         listname = DASH.join(parts[:-1])
@@ -186,7 +182,6 @@
                     msgdata.update(dict(
                         toowner=True,
                         envsender=config.mailman.site_owner,
-                        pipeline=Defaults.OWNER_PIPELINE,
                         ))
                     queue = 'in'
                 elif subaddress is None:

=== modified file 'mailman/queue/news.py'
--- a/mailman/queue/news.py     2009-01-05 00:41:05 +0000
+++ b/mailman/queue/news.py     2009-01-07 00:55:59 +0000
@@ -25,15 +25,15 @@
 
 from cStringIO import StringIO
 
-COMMASPACE = ', '
-
-from mailman import Defaults
 from mailman import Utils
+from mailman.config import config
 from mailman.interfaces import NewsModeration
 from mailman.queue import Runner
 
+COMMASPACE = ', '
 log = logging.getLogger('mailman.error')
 
+
 # Matches our Mailman crafted Message-IDs.  See Utils.unique_message_id()
 # XXX The move to email.utils.make_msgid() breaks this.
 mcre = re.compile(r"""
@@ -64,8 +64,8 @@
                     nntp_host, nntp_port = Utils.nntpsplit(mlist.nntp_host)
                     conn = nntplib.NNTP(nntp_host, nntp_port,
                                         readermode=True,
-                                        user=Defaults.NNTP_USERNAME,
-                                        password=Defaults.NNTP_PASSWORD)
+                                        user=config.nntp.username,
+                                        password=config.nntp.password)
                     conn.post(fp)
                 except nntplib.error_temp, e:
                     log.error('(NNTPDirect) NNTP error for list "%s": %s',
@@ -147,9 +147,12 @@
     # woon't completely sanitize the message, but it will eliminate the bulk
     # of the rejections based on message headers.  The NNTP server may still
     # reject the message because of other problems.
-    for header in Defaults.NNTP_REMOVE_HEADERS:
+    for header in config.nntp.remove_headers.split():
         del msg[header]
-    for header, rewrite in Defaults.NNTP_REWRITE_DUPLICATE_HEADERS:
+    for rewrite_pairs in config.nntp.rewrite_duplicate_headers.splitlines():
+        if len(rewrite_pairs.strip()) == 0:
+            continue
+        header, rewrite = rewrite_pairs.split()
         values = msg.get_all(header, [])
         if len(values) < 2:
             # We only care about duplicates

=== modified file 'mailman/queue/outgoing.py'
--- a/mailman/queue/outgoing.py 2009-01-05 00:41:05 +0000
+++ b/mailman/queue/outgoing.py 2009-01-07 00:55:59 +0000
@@ -18,12 +18,13 @@
 """Outgoing queue runner."""
 
 import os
+import sys
 import socket
 import logging
 
 from datetime import datetime
+from lazr.config import as_timedelta
 
-from mailman import Defaults
 from mailman.config import config
 from mailman.core import errors
 from mailman.queue import Runner
@@ -43,9 +44,10 @@
     def __init__(self, slice=None, numslices=1):
         Runner.__init__(self, slice, numslices)
         BounceMixin.__init__(self)
-        # We look this function up only at startup time
-        handler = config.handlers[Defaults.DELIVERY_MODULE]
-        self._func = handler.process
+        # We look this function up only at startup time.
+        module_name, callable_name = config.mta.outgoing.rsplit('.', 1)
+        __import__(module_name)
+        self._func = getattr(sys.modules[module_name], callable_name)
         # This prevents smtp server connection problems from filling up the
         # error log.  It gets reset if the message was successfully sent, and
         # set if there was a socket.error.
@@ -112,7 +114,8 @@
                             return False
                     else:
                         # Keep trying to delivery this message for a while
-                        deliver_until = now + Defaults.DELIVERY_RETRY_PERIOD
+                        deliver_until = now + as_timedelta(
+                            config.mta.delivery_retry_period)
                     msgdata['last_recip_count'] = len(recips)
                     msgdata['deliver_until'] = deliver_until
                     msgdata['recips'] = recips

=== modified file 'mailman/rules/docs/header-matching.txt'
--- a/mailman/rules/docs/header-matching.txt    2008-12-23 04:26:58 +0000
+++ b/mailman/rules/docs/header-matching.txt    2009-01-07 00:55:59 +0000
@@ -2,15 +2,15 @@
 ===============
 
 Mailman can do pattern based header matching during its normal rule
-processing.  There is a set of site-wide default header matchines specified in
-the configuaration file under the HEADER_MATCHES variable.
+processing.  There is a set of site-wide default header matches specified in
+the configuration file under the [spam.headers] section.
 
     >>> from mailman.app.lifecycle import create_list
     >>> mlist = create_list(u'[email protected]')
 
-Because the default HEADER_MATCHES variable is empty when the configuration
-file is read, we'll just extend the current header matching chain with a
-pattern that matches 4 or more stars, discarding the message if it hits.
+Because the default [spam.headers] section is empty, we'll just extend the
+current header matching chain with a pattern that matches 4 or more stars,
+discarding the message if it hits.
 
     >>> chain = config.chains['header-match']
     >>> chain.extend('x-spam-score', '[*]{4,}', 'discard')
@@ -98,7 +98,7 @@
 
 Each mailing list can also be configured with a set of header matching regular
 expression rules.  These are used to impose list-specific header filtering
-with the same semantics as the global `HEADER_MATCHES` variable.
+with the same semantics as the global [spam.headers] section.
 
 The list administrator wants to match not on four stars, but on three plus
 signs, but only for the current mailing list.

=== added file 'mailman/styles/manager.py'
--- a/mailman/styles/manager.py 1970-01-01 00:00:00 +0000
+++ b/mailman/styles/manager.py 2009-01-07 00:55:59 +0000
@@ -0,0 +1,92 @@
+# Copyright (C) 2007-2009 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/>.
+
+"""Style manager."""
+
+__metaclass__ = type
+__all__ = [
+    'StyleManager',
+    ]
+
+
+import sys
+
+from operator import attrgetter
+from zope.interface import implements
+from zope.interface.verify import verifyObject
+
+from mailman.interfaces.styles import (
+    DuplicateStyleError, IStyle, IStyleManager)
+
+
+
+class StyleManager:
+    """The built-in style manager."""
+
+    implements(IStyleManager)
+
+    def __init__(self):
+        """Install all styles from the configuration files."""
+        self._styles = {}
+
+    def populate(self):
+        self._styles.clear()
+        # Avoid circular imports.
+        from mailman.config import config
+        # Install all the styles described by the configuration files.
+        for section in config.style_configs:
+            class_path = section['class']
+            module_name, class_name = class_path.rsplit('.', 1)
+            __import__(module_name)
+            style = getattr(sys.modules[module_name], class_name)()
+            assert section.name.startswith('style'), (
+                'Bad style section name: %s' % section.name)
+            style.name = section.name[6:]
+            style.priority = int(section.priority)
+            self.register(style)
+
+    def get(self, name):
+        """See `IStyleManager`."""
+        return self._styles.get(name)
+
+    def lookup(self, mailing_list):
+        """See `IStyleManager`."""
+        matched_styles = []
+        for style in self.styles:
+            style.match(mailing_list, matched_styles)
+        for style in matched_styles:
+            yield style
+
+    @property
+    def styles(self):
+        """See `IStyleManager`."""
+        for style in sorted(self._styles.values(),
+                            key=attrgetter('priority'),
+                            reverse=True):
+            yield style
+
+    def register(self, style):
+        """See `IStyleManager`."""
+        verifyObject(IStyle, style)
+        if style.name in self._styles:
+            raise DuplicateStyleError(style.name)
+        self._styles[style.name] = style
+
+    def unregister(self, style):
+        """See `IStyleManager`."""
+        # Let KeyErrors percolate up.
+        del self._styles[style.name]

=== modified file 'mailman/testing/layers.py'
--- a/mailman/testing/layers.py 2009-01-05 05:54:19 +0000
+++ b/mailman/testing/layers.py 2009-01-07 00:55:59 +0000
@@ -49,6 +49,7 @@
     """Layer for pushing and popping test configurations."""
 
     var_dir = None
+    styles = None
 
     @classmethod
     def setUp(cls):
@@ -136,9 +137,7 @@
 
     @classmethod
     def testSetUp(cls):
-        # Record the current (default) set of styles so that we can reset them
-        # easily in the tear down.
-        cls.styles = set(config.style_manager.styles)
+        pass
 
     @classmethod
     def testTearDown(cls):
@@ -153,10 +152,7 @@
             config.db.message_store.delete_message(message['message-id'])
         config.db.commit()
         # Reset the global style manager.
-        new_styles = set(config.style_manager.styles) - cls.styles
-        for style in new_styles:
-            config.style_manager.unregister(style)
-        cls.styles = None
+        config.style_manager.populate()
 
     # Flag to indicate that loggers should propagate to the console.
     stderr = False



--
Primary development focus
https://code.launchpad.net/~mailman-coders/mailman/3.0

You are receiving this branch notification because you are subscribed to it.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to