diff options
66 files changed, 263 insertions, 128 deletions
diff --git a/mailman/Message.py b/mailman/Message.py index e78678dc4..c8fb17f3b 100644 --- a/mailman/Message.py +++ b/mailman/Message.py @@ -118,14 +118,15 @@ class Message(email.message.Message): header value found is returned. However the search order is determined by the following: - - If config.USE_ENVELOPE_SENDER is true, then the search order is - Sender:, From:, unixfrom + - If config.mailman.use_envelope_sender is true, then the search order + is Sender:, From:, unixfrom - Otherwise, the search order is From:, Sender:, unixfrom The optional argument use_envelope, if given overrides the - config.USE_ENVELOPE_SENDER setting. It should be set to either 0 or 1 - (don't use None since that indicates no-override). + config.mailman.use_envelope_sender setting. It should be set to + either True or False (don't use None since that indicates + no-override). unixfrom should never be empty. The return address is always lowercased, unless preserve_case is true. @@ -133,7 +134,7 @@ class Message(email.message.Message): This method differs from get_senders() in that it returns one and only one address, and uses a different search order. """ - senderfirst = config.USE_ENVELOPE_SENDER + senderfirst = config.mailman.use_envelope_sender if use_envelope is not None: senderfirst = use_envelope if senderfirst: diff --git a/mailman/Utils.py b/mailman/Utils.py index d7c193799..f03e14d07 100644 --- a/mailman/Utils.py +++ b/mailman/Utils.py @@ -475,7 +475,7 @@ def findtext(templatefile, dict=None, raw=False, lang=None, mlist=None): languages.add(lang) if mlist is not None: languages.add(mlist.preferred_language) - languages.add(config.DEFAULT_SERVER_LANGUAGE) + languages.add(config.mailman.default_language) assert None not in languages, 'None in languages' # Calculate the locations to scan searchdirs = [] diff --git a/mailman/config/config.py b/mailman/config/config.py index 031324eff..fd4345203 100644 --- a/mailman/config/config.py +++ b/mailman/config/config.py @@ -52,8 +52,23 @@ class Configuration(object): self.domains = {} # email host -> IDomain self.qrunners = {} self.qrunner_shortcuts = {} + self.languages = LanguageManager() self.QFILE_SCHEMA_VERSION = version.QFILE_SCHEMA_VERSION self._config = None + # Create various registries. + self.archivers = {} + self.chains = {} + self.rules = {} + self.handlers = {} + self.pipelines = {} + self.commands = {} + + def _clear(self): + """Clear the cached configuration variables.""" + self.domains.clear() + self.qrunners.clear() + self.qrunner_shortcuts.clear() + self.languages = LanguageManager() def __getattr__(self, name): """Delegate to the configuration object.""" @@ -68,9 +83,21 @@ class Configuration(object): self._config = schema.loadFile(StringIO(''), '<default>') else: self._config = schema.load(filename) - self.post_process() + self._post_process() - def post_process(self): + def push(self, config_name, config_string): + """Push a new configuration onto the stack.""" + self._clear() + self._config.push(config_name, config_string) + self._post_process() + + def pop(self, config_name): + """Pop a configuration from the stack.""" + self._clear() + self._config.pop(config_name) + self._post_process() + + def _post_process(self): """Perform post-processing after loading the configuration files.""" # Set up the domains. try: @@ -144,7 +171,6 @@ class Configuration(object): self.CONFIG_FILE = join(etcdir, 'mailman.cfg') self.LOCK_FILE = join(lockdir, 'master-qrunner') # Set up all the languages. - self.languages = LanguageManager() try: languages = self._config.getByCategory('language') except NoCategoryError: @@ -155,13 +181,6 @@ class Configuration(object): section.charset, section.enable) # Always enable the server default language, which must be defined. self.languages.enable_language(self._config.mailman.default_language) - # Create various registries. - self.archivers = {} - self.chains = {} - self.rules = {} - self.handlers = {} - self.pipelines = {} - self.commands = {} @property def logger_configs(self): diff --git a/mailman/config/helpers.py b/mailman/config/helpers.py index 306ef21b4..5c5cecc67 100644 --- a/mailman/config/helpers.py +++ b/mailman/config/helpers.py @@ -26,6 +26,8 @@ __all__ = [ import logging +# XXX BAW 20-Dec-2008 Donate these back to the lazr.config project. + def as_boolean(value): diff --git a/mailman/config/schema.cfg b/mailman/config/schema.cfg index 181116e74..5a50b6845 100644 --- a/mailman/config/schema.cfg +++ b/mailman/config/schema.cfg @@ -42,12 +42,30 @@ var_dir: /tmp/mailman # The default language for this server. default_language: en +# When allowing only members to post to a mailing list, how is the sender of +# the message determined? If this variable is set to Yes, then first the +# message's envelope sender is used, with a fallback to the sender if there is +# no envelope sender. Set this variable to No to always use the sender. +# +# The envelope sender is set by the SMTP delivery and is thus less easily +# spoofed than the sender, which is typically just taken from the From: header +# and thus easily spoofed by the end-user. However, sometimes the envelope +# sender isn't set correctly and this will manifest itself by postings being +# held for approval even if they appear to come from a list member. If you +# are having this problem, set this variable to No, but understand that some +# spoofed messages may get through. +use_envelope_sender: no + +# Mail command processor will ignore mail command lines after designated max. +email_commands_max_lines: 10 + [qrunner.template] # Define which process queue runners, and how many of them, to start. class: mailman.queue.runner.Runner count: 1 start: yes +max_restarts: 10 [qrunner.archive] class: mailman.queue.archive.ArchiveRunner @@ -188,7 +206,7 @@ failure: [$msgid] delivery to $recip failed with code $smtpcode, $smtpmsg [logging.vette] -[domain.template] +[domain.master] # Site-wide domain defaults. To configure an individual # domain, add a [domain.example_com] section with the overrides. @@ -204,7 +222,7 @@ contact_address: postmaster@example.com description: An example domain. -[language.template] +[language.master] # Template for language definitions. The section name must be [language.xx] # where xx is the 2-character ISO code for the language. @@ -233,3 +251,21 @@ pattern: xyz # The chain to jump to if the pattern matches. Maybe be any existing chain # such as 'discard', 'reject', 'hold', or 'accept'. chain: hold + + +[mta] +# The class defining the interface to the incoming mail transport agent. +incoming: mailman.mta.postfix.LMTP + +# The class defining the interface to the outgoing mail transport agent. +outgoing: mailman.mta.direct.SMTPDirect + +# How to connect to the outgoing MTA. +smtp_host: localhost +smtp_port: 25 + + +[archiver.master] +base_url: http://archive.example.com/ +recipient: archive@archive.example.com +command: /bin/echo diff --git a/mailman/docs/addresses.txt b/mailman/docs/addresses.txt index 749068433..6ddb8b8a9 100644 --- a/mailman/docs/addresses.txt +++ b/mailman/docs/addresses.txt @@ -6,7 +6,6 @@ those addresses, such as their registration date, and whether and when they've been validated. Addresses may be linked to the users that Mailman knows about. Addresses are subscribed to mailing lists though members. - >>> from mailman.configuration import config >>> usermgr = config.db.user_manager diff --git a/mailman/docs/archivers.txt b/mailman/docs/archivers.txt index c8ddb73e4..529938d94 100644 --- a/mailman/docs/archivers.txt +++ b/mailman/docs/archivers.txt @@ -24,7 +24,6 @@ Pipermail does not support a permalink, so that interface returns None. Mailman defines a draft spec for how list servers and archivers can interoperate. - >>> from mailman.configuration import config >>> for archiver_name, archiver in sorted(config.archivers.items()): ... print archiver.name ... print ' ', archiver.list_url(mlist) diff --git a/mailman/docs/bounces.txt b/mailman/docs/bounces.txt index ff110c509..6d0fad688 100644 --- a/mailman/docs/bounces.txt +++ b/mailman/docs/bounces.txt @@ -13,7 +13,6 @@ Mailman can also bounce messages back to the original sender. This is essentially equivalent to rejecting the message with notification. Mailing lists can bounce a message with an optional error message. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' diff --git a/mailman/docs/chains.txt b/mailman/docs/chains.txt index 1118df687..d89ec2be4 100644 --- a/mailman/docs/chains.txt +++ b/mailman/docs/chains.txt @@ -15,7 +15,6 @@ The Discard chain The Discard chain simply throws the message away. >>> from zope.interface.verify import verifyObject - >>> from mailman.configuration import config >>> from mailman.interfaces import IChain >>> chain = config.chains['discard'] >>> verifyObject(IChain, chain) diff --git a/mailman/docs/domains.txt b/mailman/docs/domains.txt index d29e6ef9f..d2980cd05 100644 --- a/mailman/docs/domains.txt +++ b/mailman/docs/domains.txt @@ -7,7 +7,6 @@ calling the add_domain() method. At a minimum, the email host name must be specified. - >>> from mailman.configuration import config >>> config.add_domain('example.net') The domain object can be looked up by email host name. diff --git a/mailman/docs/lifecycle.txt b/mailman/docs/lifecycle.txt index 69f656208..8531c6245 100644 --- a/mailman/docs/lifecycle.txt +++ b/mailman/docs/lifecycle.txt @@ -102,7 +102,6 @@ However, all addresses are linked to users. If you create a mailing list with owner addresses that are already known to the system, they won't be created again. - >>> from mailman.configuration import config >>> usermgr = config.db.user_manager >>> user_a = usermgr.get_user(u'aperson@example.com') >>> user_b = usermgr.get_user(u'bperson@example.com') diff --git a/mailman/docs/listmanager.txt b/mailman/docs/listmanager.txt index 48968da61..1925e9466 100644 --- a/mailman/docs/listmanager.txt +++ b/mailman/docs/listmanager.txt @@ -6,7 +6,6 @@ objects. The Mailman system instantiates an IListManager for you based on the configuration variable MANAGERS_INIT_FUNCTION. The instance is accessible on the global config object. - >>> from mailman.configuration import config >>> from mailman.interfaces import IListManager >>> listmgr = config.db.list_manager >>> IListManager.providedBy(listmgr) diff --git a/mailman/docs/membership.txt b/mailman/docs/membership.txt index 8ae2159e5..cb6515532 100644 --- a/mailman/docs/membership.txt +++ b/mailman/docs/membership.txt @@ -14,7 +14,6 @@ store mailing list data in a different database than user data. When we create a mailing list, it starts out with no members... - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist <mailing list "_xtest@example.com" at ...> diff --git a/mailman/docs/message.txt b/mailman/docs/message.txt index 25c38300c..b580091cc 100644 --- a/mailman/docs/message.txt +++ b/mailman/docs/message.txt @@ -12,7 +12,6 @@ When Mailman needs to send a message to a user, it creates a UserNotification instance, and then calls the .send() method on this object. This method requires a mailing list instance. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' diff --git a/mailman/docs/messagestore.txt b/mailman/docs/messagestore.txt index 8b9706ad9..6e04568c5 100644 --- a/mailman/docs/messagestore.txt +++ b/mailman/docs/messagestore.txt @@ -7,7 +7,6 @@ message's List-Archive header to create a globally unique URI to the message object in the internet facing interface of the message store. The X-Message-ID-Hash is the Base32 SHA1 hash of the Message-ID. - >>> from mailman.configuration import config >>> store = config.db.message_store If you try to add a message to the store which is missing the Message-ID diff --git a/mailman/docs/mlist-addresses.txt b/mailman/docs/mlist-addresses.txt index 6dc35a249..bfffe10eb 100644 --- a/mailman/docs/mlist-addresses.txt +++ b/mailman/docs/mlist-addresses.txt @@ -4,7 +4,6 @@ Mailing list addresses Every mailing list has a number of addresses which are publicly available. These are defined in the IMailingListAddresses interface. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') The posting address is where people send messages to be posted to the mailing diff --git a/mailman/docs/pending.txt b/mailman/docs/pending.txt index 457705352..f6895b818 100644 --- a/mailman/docs/pending.txt +++ b/mailman/docs/pending.txt @@ -6,7 +6,6 @@ are stored. These can include email address registration events, held messages (but only for user confirmation), auto-approvals, and probe bounces. This is not where messages held for administrator approval are kept. - >>> from mailman.configuration import config >>> from zope.interface import implements >>> from zope.interface.verify import verifyObject diff --git a/mailman/docs/pipelines.txt b/mailman/docs/pipelines.txt index ab6b99aa3..5a84bb4b9 100644 --- a/mailman/docs/pipelines.txt +++ b/mailman/docs/pipelines.txt @@ -21,7 +21,6 @@ Processing a message Messages hit the pipeline after they've been accepted for posting. - >>> from mailman.configuration import config >>> config.archivers['pipermail'].is_enabled = True >>> msg = message_from_string("""\ ... From: aperson@example.com diff --git a/mailman/docs/registration.txt b/mailman/docs/registration.txt index 73fb149e6..752f49672 100644 --- a/mailman/docs/registration.txt +++ b/mailman/docs/registration.txt @@ -8,7 +8,6 @@ may supply. All registered email addresses must be verified before Mailman will send them any list traffic. >>> from mailman.app.registrar import Registrar - >>> from mailman.configuration import config >>> from mailman.interfaces import IRegistrar The IUserManager manages users, but it does so at a fairly low level. diff --git a/mailman/docs/requests.txt b/mailman/docs/requests.txt index a1401b7ff..1e1347ed7 100644 --- a/mailman/docs/requests.txt +++ b/mailman/docs/requests.txt @@ -5,8 +5,6 @@ Various actions will be held for moderator approval, such as subscriptions to closed lists, or postings by non-members. The requests database is the low level interface to these actions requiring approval. - >>> from mailman.configuration import config - Here is a helper function for printing out held requests. >>> def show_holds(requests): diff --git a/mailman/docs/styles.txt b/mailman/docs/styles.txt index 37b3b20e2..dd3aa8d07 100644 --- a/mailman/docs/styles.txt +++ b/mailman/docs/styles.txt @@ -13,7 +13,6 @@ modify the mailing list any way it wants. Let's start with a vanilla mailing list and a default style manager. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> from mailman.core.styles import style_manager diff --git a/mailman/docs/usermanager.txt b/mailman/docs/usermanager.txt index 01822eed2..5f03d84a6 100644 --- a/mailman/docs/usermanager.txt +++ b/mailman/docs/usermanager.txt @@ -6,7 +6,6 @@ system instantiates an IUserManager for you based on the configuration variable MANAGERS_INIT_FUNCTION. The instance is accessible on the global config object. - >>> from mailman.configuration import config >>> from mailman.interfaces import IUserManager >>> from zope.interface.verify import verifyObject >>> usermgr = config.db.user_manager diff --git a/mailman/docs/users.txt b/mailman/docs/users.txt index 0b8c9a648..e1f0e3302 100644 --- a/mailman/docs/users.txt +++ b/mailman/docs/users.txt @@ -7,7 +7,6 @@ they control. A user also knows which mailing lists they are subscribed to. See usermanager.txt for examples of how to create, delete, and find users. - >>> from mailman.configuration import config >>> usermgr = config.db.user_manager diff --git a/mailman/pipeline/docs/ack-headers.txt b/mailman/pipeline/docs/ack-headers.txt index 28a8eed9e..ca41df03e 100644 --- a/mailman/pipeline/docs/ack-headers.txt +++ b/mailman/pipeline/docs/ack-headers.txt @@ -7,7 +7,6 @@ transformations. Some headers get added, others get changed. Some of these changes depend on mailing list settings and others depend on how the message is getting sent through the system. We'll take things one-by-one. - >>> from mailman.configuration import config >>> from mailman.pipeline.cook_headers import process >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.subject_prefix = u'' diff --git a/mailman/pipeline/docs/acknowledge.txt b/mailman/pipeline/docs/acknowledge.txt index 76c8fdf21..ccbb70e79 100644 --- a/mailman/pipeline/docs/acknowledge.txt +++ b/mailman/pipeline/docs/acknowledge.txt @@ -5,7 +5,6 @@ When a user posts a message to a mailing list, and that user has chosen to receive acknowledgments of their postings, Mailman will sent them such an acknowledgment. - >>> from mailman.configuration import config >>> handler = config.handlers['acknowledge'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.real_name = u'XTest' diff --git a/mailman/pipeline/docs/after-delivery.txt b/mailman/pipeline/docs/after-delivery.txt index 5bc9b5936..b910e89a6 100644 --- a/mailman/pipeline/docs/after-delivery.txt +++ b/mailman/pipeline/docs/after-delivery.txt @@ -6,7 +6,6 @@ by the rest of the handlers in the incoming queue pipeline, a couple of bookkeeping pieces of information are updated. >>> import datetime - >>> from mailman.configuration import config >>> handler = config.handlers['after-delivery'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> post_time = datetime.datetime.now() - datetime.timedelta(minutes=10) diff --git a/mailman/pipeline/docs/archives.txt b/mailman/pipeline/docs/archives.txt index 67ad45c89..9595a36e0 100644 --- a/mailman/pipeline/docs/archives.txt +++ b/mailman/pipeline/docs/archives.txt @@ -8,7 +8,6 @@ archivers to work in a separate process from the main Mailman delivery processes. >>> from mailman.app.lifecycle import create_list - >>> from mailman.configuration import config >>> from mailman.queue import Switchboard >>> handler = config.handlers['to-archive'] >>> mlist = create_list(u'_xtest@example.com') diff --git a/mailman/pipeline/docs/avoid-duplicates.txt b/mailman/pipeline/docs/avoid-duplicates.txt index 9fd332d1b..e1d31716a 100644 --- a/mailman/pipeline/docs/avoid-duplicates.txt +++ b/mailman/pipeline/docs/avoid-duplicates.txt @@ -6,7 +6,6 @@ reduce the reception of duplicate messages. It does this by removing certain recipients from the list of recipients that earlier handler modules (e.g. CalcRecips) calculates. - >>> from mailman.configuration import config >>> handler = config.handlers['avoid-duplicates'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') diff --git a/mailman/pipeline/docs/calc-recips.txt b/mailman/pipeline/docs/calc-recips.txt index 057351873..550edff51 100644 --- a/mailman/pipeline/docs/calc-recips.txt +++ b/mailman/pipeline/docs/calc-recips.txt @@ -5,7 +5,6 @@ Every message that makes it through to the list membership gets sent to a set of recipient addresses. These addresses are calculated by one of the handler modules and depends on a host of factors. - >>> from mailman.configuration import config >>> handler = config.handlers['calculate-recipients'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') diff --git a/mailman/pipeline/docs/cleanse.txt b/mailman/pipeline/docs/cleanse.txt index 1597095b3..0940cdb4b 100644 --- a/mailman/pipeline/docs/cleanse.txt +++ b/mailman/pipeline/docs/cleanse.txt @@ -5,7 +5,6 @@ All messages posted to a list get their headers cleansed. Some headers are related to additional permissions that can be granted to the message and other headers can be used to fish for membership. - >>> from mailman.configuration import config >>> handler = config.handlers['cleanse'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') diff --git a/mailman/pipeline/docs/cook-headers.txt b/mailman/pipeline/docs/cook-headers.txt index 4fbdf58bb..985214079 100644 --- a/mailman/pipeline/docs/cook-headers.txt +++ b/mailman/pipeline/docs/cook-headers.txt @@ -8,7 +8,6 @@ changes depend on mailing list settings and others depend on how the message is getting sent through the system. We'll take things one-by-one. >>> from mailman.pipeline.cook_headers import process - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.subject_prefix = u'' >>> mlist.include_list_post_header = False diff --git a/mailman/pipeline/docs/decorate.txt b/mailman/pipeline/docs/decorate.txt index 60afb0170..b805e23cf 100644 --- a/mailman/pipeline/docs/decorate.txt +++ b/mailman/pipeline/docs/decorate.txt @@ -6,7 +6,6 @@ original message. A handler module takes care of this based on the settings of the mailing list and the type of message being processed. >>> from mailman.pipeline.decorate import process - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> msg_text = """\ ... From: aperson@example.org diff --git a/mailman/pipeline/docs/digests.txt b/mailman/pipeline/docs/digests.txt index df01379b9..f478c1ec0 100644 --- a/mailman/pipeline/docs/digests.txt +++ b/mailman/pipeline/docs/digests.txt @@ -8,7 +8,6 @@ digests, although only two are currently supported: MIME digests and RFC 1153 >>> from mailman.pipeline.to_digest import process >>> from mailman.queue import Switchboard - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' >>> mlist.web_page_url = u'http://www.example.com/' diff --git a/mailman/pipeline/docs/file-recips.txt b/mailman/pipeline/docs/file-recips.txt index e93bba9aa..d006151ba 100644 --- a/mailman/pipeline/docs/file-recips.txt +++ b/mailman/pipeline/docs/file-recips.txt @@ -5,7 +5,6 @@ Mailman can calculate the recipients for a message from a Sendmail-style include file. This file must be called members.txt and it must live in the list's data directory. - >>> from mailman.configuration import config >>> handler = config.handlers['file-recipients'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') diff --git a/mailman/pipeline/docs/filtering.txt b/mailman/pipeline/docs/filtering.txt index c5dca1531..70ca3098d 100644 --- a/mailman/pipeline/docs/filtering.txt +++ b/mailman/pipeline/docs/filtering.txt @@ -7,7 +7,6 @@ message. It does this with the MimeDel handler module, although other handlers can potentially do other kinds of finer level content filtering. >>> from mailman.pipeline.mime_delete import process - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' diff --git a/mailman/pipeline/docs/nntp.txt b/mailman/pipeline/docs/nntp.txt index 5652d7924..3ef3b2413 100644 --- a/mailman/pipeline/docs/nntp.txt +++ b/mailman/pipeline/docs/nntp.txt @@ -6,7 +6,6 @@ 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 - >>> from mailman.configuration import config >>> handler = config.handlers['to-usenet'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' diff --git a/mailman/pipeline/docs/reply-to.txt b/mailman/pipeline/docs/reply-to.txt index ad9100ce1..570ffb7ea 100644 --- a/mailman/pipeline/docs/reply-to.txt +++ b/mailman/pipeline/docs/reply-to.txt @@ -8,7 +8,6 @@ changes depend on mailing list settings and others depend on how the message is getting sent through the system. We'll take things one-by-one. >>> from mailman.pipeline.cook_headers import process - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.subject_prefix = u'' diff --git a/mailman/pipeline/docs/replybot.txt b/mailman/pipeline/docs/replybot.txt index 2e3765cab..7325417ed 100644 --- a/mailman/pipeline/docs/replybot.txt +++ b/mailman/pipeline/docs/replybot.txt @@ -7,7 +7,6 @@ responses are subject to various conditions, such as headers in the original message or the amount of time since the last auto-response. >>> from mailman.pipeline.replybot import process - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.real_name = u'XTest' >>> mlist.web_page_url = u'http://www.example.com/' diff --git a/mailman/pipeline/docs/scrubber.txt b/mailman/pipeline/docs/scrubber.txt index 744925f34..9c9367cda 100644 --- a/mailman/pipeline/docs/scrubber.txt +++ b/mailman/pipeline/docs/scrubber.txt @@ -7,7 +7,6 @@ scrub attachments from messages so that binary goop doesn't end up in an archive message. >>> from mailman.pipeline.scrubber import process, save_attachment - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' diff --git a/mailman/pipeline/docs/subject-munging.txt b/mailman/pipeline/docs/subject-munging.txt index 02677d6e2..b2972683b 100644 --- a/mailman/pipeline/docs/subject-munging.txt +++ b/mailman/pipeline/docs/subject-munging.txt @@ -8,7 +8,6 @@ changes depend on mailing list settings and others depend on how the message is getting sent through the system. We'll take things one-by-one. >>> from mailman.pipeline.cook_headers import process - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.subject_prefix = u'' diff --git a/mailman/pipeline/docs/tagger.txt b/mailman/pipeline/docs/tagger.txt index 778f7cc73..64996755b 100644 --- a/mailman/pipeline/docs/tagger.txt +++ b/mailman/pipeline/docs/tagger.txt @@ -10,7 +10,6 @@ expressions. The message then gets tagged with the topic names of each hit. >>> from mailman.pipeline.tagger import process >>> from mailman.queue import Switchboard - >>> from mailman.configuration import config >>> 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 3840b71ee..70f93cfae 100644 --- a/mailman/pipeline/docs/to-outgoing.txt +++ b/mailman/pipeline/docs/to-outgoing.txt @@ -10,7 +10,6 @@ basically describes how to encode the recipient's address in the originator headers for unambigous bounce processing. >>> from mailman.queue import Switchboard - >>> from mailman.configuration import config >>> handler = config.handlers['to-outgoing'] >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> switchboard = Switchboard(config.OUTQUEUE_DIR) diff --git a/mailman/queue/docs/archiver.txt b/mailman/queue/docs/archiver.txt index 8c34e3537..98ead0aa7 100644 --- a/mailman/queue/docs/archiver.txt +++ b/mailman/queue/docs/archiver.txt @@ -5,7 +5,6 @@ Mailman can archive to any number of archivers that adhere to the IArchiver interface. By default, there's a Pipermail archiver. >>> from mailman.app.lifecycle import create_list - >>> from mailman.configuration import config >>> mlist = create_list(u'test@example.com') >>> mlist.web_page_url = u'http://www.example.com/' >>> config.db.commit() diff --git a/mailman/queue/docs/command.txt b/mailman/queue/docs/command.txt index 470a632b7..ce4279fff 100644 --- a/mailman/queue/docs/command.txt +++ b/mailman/queue/docs/command.txt @@ -24,7 +24,6 @@ sender. The command can be in the Subject header. ... ... """) - >>> from mailman.configuration import config >>> from mailman.inject import inject_message >>> inject_message(mlist, msg, qdir=config.CMDQUEUE_DIR) >>> from mailman.queue.command import CommandRunner diff --git a/mailman/queue/docs/incoming.txt b/mailman/queue/docs/incoming.txt index 997eac07e..6bb5ead15 100644 --- a/mailman/queue/docs/incoming.txt +++ b/mailman/queue/docs/incoming.txt @@ -48,7 +48,6 @@ The incoming queue runner runs until it is empty. And now the message is in the pipeline queue. - >>> from mailman.configuration import config >>> from mailman.queue import Switchboard >>> pipeline_queue = Switchboard(config.PIPELINEQUEUE_DIR) >>> len(pipeline_queue.files) diff --git a/mailman/queue/docs/news.txt b/mailman/queue/docs/news.txt index 0b89de2bc..3375b3d54 100644 --- a/mailman/queue/docs/news.txt +++ b/mailman/queue/docs/news.txt @@ -6,7 +6,6 @@ NNTP newsgroup. One of the most important things this runner does is prepare the message for Usenet (yes, I know that NNTP is not Usenet, but this runner was originally written to gate to Usenet, which has its own rules). - >>> from mailman.configuration import config >>> from mailman.queue.news import prepare_message >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.linked_newsgroup = u'comp.lang.python' diff --git a/mailman/queue/docs/outgoing.txt b/mailman/queue/docs/outgoing.txt index edc806d47..9af554af7 100644 --- a/mailman/queue/docs/outgoing.txt +++ b/mailman/queue/docs/outgoing.txt @@ -43,7 +43,6 @@ Normally, messages would show up in the outgoing queue after the message has been processed by the rule set and pipeline. But we can simulate that here by injecting a message directly into the outgoing queue. - >>> from mailman.configuration import config >>> msgdata = {} >>> handler = config.handlers['calculate-recipients'] >>> handler.process(mlist, msg, msgdata) diff --git a/mailman/queue/docs/runner.txt b/mailman/queue/docs/runner.txt index a9e17370b..70bab6539 100644 --- a/mailman/queue/docs/runner.txt +++ b/mailman/queue/docs/runner.txt @@ -16,7 +16,6 @@ continuously in a loop until the .stop() method is called. >>> import os >>> from mailman.queue import Runner, Switchboard - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.preferred_language = u'en' diff --git a/mailman/queue/docs/switchboard.txt b/mailman/queue/docs/switchboard.txt index 633bdabe6..7baee7b54 100644 --- a/mailman/queue/docs/switchboard.txt +++ b/mailman/queue/docs/switchboard.txt @@ -14,7 +14,6 @@ instance of a switchboard is responsible for one queue directory. Create a switchboard by giving its queue directory. >>> import os - >>> from mailman.configuration import config >>> queue_directory = os.path.join(config.QUEUE_DIR, 'test') >>> from mailman.queue import Switchboard >>> switchboard = Switchboard(queue_directory) diff --git a/mailman/rules/administrivia.py b/mailman/rules/administrivia.py index b4fa9b172..593f311e3 100644 --- a/mailman/rules/administrivia.py +++ b/mailman/rules/administrivia.py @@ -82,7 +82,7 @@ class Administrivia: if line == '': continue lineno += 1 - if lineno > config.EMAIL_COMMANDS_MAX_LINES: + if lineno > config.mailman.email_commands_max_lines: break lines_to_check.append(line) # Only look at the first text/plain part. diff --git a/mailman/rules/docs/administrivia.txt b/mailman/rules/docs/administrivia.txt index 65742fba2..df6685074 100644 --- a/mailman/rules/docs/administrivia.txt +++ b/mailman/rules/docs/administrivia.txt @@ -6,7 +6,6 @@ commands in the Subject header or first few lines of the payload. This is used to catch messages posted to the list which should have been sent to the -request robot address. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.administrivia = True >>> rule = config.rules['administrivia'] diff --git a/mailman/rules/docs/approve.txt b/mailman/rules/docs/approve.txt index 3b5065849..cb3093e0d 100644 --- a/mailman/rules/docs/approve.txt +++ b/mailman/rules/docs/approve.txt @@ -13,7 +13,6 @@ approval queue. This has several use cases: In order to support this, a mailing list can be given a 'moderator password' which is shared among all the administrators. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.moderator_password = u'abcxyz' diff --git a/mailman/rules/docs/emergency.txt b/mailman/rules/docs/emergency.txt index 6437ba626..0f1005410 100644 --- a/mailman/rules/docs/emergency.txt +++ b/mailman/rules/docs/emergency.txt @@ -27,7 +27,6 @@ There are two messages in the virgin queue. The one addressed to the original sender will contain a token we can use to grab the held message out of the pending requests. - >>> from mailman.configuration import config >>> from mailman.queue import Switchboard >>> virginq = Switchboard(config.VIRGINQUEUE_DIR) diff --git a/mailman/rules/docs/header-matching.txt b/mailman/rules/docs/header-matching.txt index 0dd917a71..5e8d01ead 100644 --- a/mailman/rules/docs/header-matching.txt +++ b/mailman/rules/docs/header-matching.txt @@ -12,7 +12,6 @@ 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. - >>> from mailman.configuration import config >>> chain = config.chains['header-match'] >>> chain.extend('x-spam-score', '[*]{4,}', 'discard') diff --git a/mailman/rules/docs/implicit-dest.txt b/mailman/rules/docs/implicit-dest.txt index 8857a397e..30db86611 100644 --- a/mailman/rules/docs/implicit-dest.txt +++ b/mailman/rules/docs/implicit-dest.txt @@ -4,7 +4,6 @@ Implicit destination The 'implicit-dest' rule matches when the mailing list's posting address is not explicitly mentioned in the set of message recipients. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> rule = config.rules['implicit-dest'] >>> rule.name diff --git a/mailman/rules/docs/loop.txt b/mailman/rules/docs/loop.txt index ba3cbbc1d..6391864b2 100644 --- a/mailman/rules/docs/loop.txt +++ b/mailman/rules/docs/loop.txt @@ -4,7 +4,6 @@ Posting loops To avoid a posting loop, Mailman has a rule to check for the existence of an X-BeenThere header with the value of the list's posting address. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> rule = config.rules['loop'] >>> rule.name diff --git a/mailman/rules/docs/max-size.txt b/mailman/rules/docs/max-size.txt index 7ece6a54a..ae7921f9c 100644 --- a/mailman/rules/docs/max-size.txt +++ b/mailman/rules/docs/max-size.txt @@ -6,7 +6,6 @@ specified maximum. Generally this is used to prevent huge attachments from getting posted to the list. This value is calculated in terms of KB (1024 bytes). - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> rule = config.rules['max-size'] >>> rule.name diff --git a/mailman/rules/docs/moderation.txt b/mailman/rules/docs/moderation.txt index 3af5f2520..9f3663517 100644 --- a/mailman/rules/docs/moderation.txt +++ b/mailman/rules/docs/moderation.txt @@ -6,7 +6,6 @@ postings, then only members with a cleared moderation flag will be able to email the list without having those messages be held for approval. The 'moderation' rule determines whether the message should be moderated or not. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> rule = config.rules['moderation'] >>> rule.name diff --git a/mailman/rules/docs/news-moderation.txt b/mailman/rules/docs/news-moderation.txt index 1ecbe12cf..36310797c 100644 --- a/mailman/rules/docs/news-moderation.txt +++ b/mailman/rules/docs/news-moderation.txt @@ -8,7 +8,6 @@ posted to the newsgroup, and from there, gated to the mailing list. It's a circuitous route, but it works nonetheless by holding all messages posted directly to the mailing list. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> rule = config.rules['news-moderation'] >>> rule.name diff --git a/mailman/rules/docs/no-subject.txt b/mailman/rules/docs/no-subject.txt index 0a67ccbef..4fa001308 100644 --- a/mailman/rules/docs/no-subject.txt +++ b/mailman/rules/docs/no-subject.txt @@ -4,7 +4,6 @@ No Subject header This rule matches if the message has no Subject header, or if the header is the empty string when stripped. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> rule = config.rules['no-subject'] >>> rule.name diff --git a/mailman/rules/docs/recipients.txt b/mailman/rules/docs/recipients.txt index 3408e7e51..b364dd031 100644 --- a/mailman/rules/docs/recipients.txt +++ b/mailman/rules/docs/recipients.txt @@ -4,7 +4,6 @@ Maximum number of recipients The 'max-recipients' rule matches when there are more than the maximum allowed number of explicit recipients addressed by the message. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> rule = config.rules['max-recipients'] >>> rule.name diff --git a/mailman/rules/docs/rules.txt b/mailman/rules/docs/rules.txt index fff833462..bae9f594a 100644 --- a/mailman/rules/docs/rules.txt +++ b/mailman/rules/docs/rules.txt @@ -13,7 +13,6 @@ Rules are maintained in the configuration object as a dictionary mapping rule names to rule objects. >>> from zope.interface.verify import verifyObject - >>> from mailman.configuration import config >>> from mailman.interfaces import IRule >>> for rule_name in sorted(config.rules): ... rule = config.rules[rule_name] diff --git a/mailman/rules/docs/suspicious.txt b/mailman/rules/docs/suspicious.txt index df2bc64af..0cc515ae4 100644 --- a/mailman/rules/docs/suspicious.txt +++ b/mailman/rules/docs/suspicious.txt @@ -5,7 +5,6 @@ Suspicious headers are a way for Mailman to hold messages that match a particular regular expression. This mostly historical feature is fairly confusing to users, and the list attribute that controls this is misnamed. - >>> from mailman.configuration import config >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> rule = config.rules['suspicious-header'] >>> rule.name diff --git a/mailman/rules/docs/truth.txt b/mailman/rules/docs/truth.txt index 3c11e4954..f331e852b 100644 --- a/mailman/rules/docs/truth.txt +++ b/mailman/rules/docs/truth.txt @@ -4,7 +4,6 @@ Truth The 'truth' rule always matches. This makes it useful as a terminus rule for unconditionally jumping to another chain. - >>> from mailman.configuration import config >>> rule = config.rules['truth'] >>> rule.check(False, False, False) True diff --git a/mailman/testing/layers.py b/mailman/testing/layers.py new file mode 100644 index 000000000..97d6ade23 --- /dev/null +++ b/mailman/testing/layers.py @@ -0,0 +1,139 @@ +# Copyright (C) 2008 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/>. + +"""Mailman test layers.""" + +__metaclass__ = type +__all__ = [ + 'ConfigLayer', + 'SMTPLayer', + ] + + +import os +import shutil +import tempfile +import textwrap + +from mailman.config import config +from mailman.core.initialize import initialize +from mailman.testing.helpers import SMTPServer + + +NL = '\n' + + + +class ConfigLayer: + """Layer for pushing and popping test configurations.""" + + var_dir = None + + @classmethod + def setUp(cls): + initialize() + assert cls.var_dir is None, 'Layer already set up' + test_config = [] + # Calculate a temporary VAR_DIR directory so that run-time artifacts + # of the tests won't tread on the installation's data. This also + # makes it easier to clean up after the tests are done, and insures + # isolation of test suite runs. + cls.var_dir = tempfile.mkdtemp() + # lazr.config says it doesn't care about indentation, but we've + # actually got mixed indentation above because of the for-loop. It's + # just safer to dedent the whole string now. + test_config.append(textwrap.dedent(""" + [mailman] + var_dir: %s + """ % cls.var_dir)) + # Push a high port for our test SMTP server. + test_config.append(textwrap.dedent(""" + [mta] + smtp_port: 9025 + """)) + # Set the qrunners to exit after one error. + for qrunner in config.qrunner_shortcuts: + test_config.append(textwrap.dedent(""" + [qrunner.%s] + max_restarts: 1 + """ % qrunner)) + # Add stuff for the archiver and a sample domain. + test_config.append(textwrap.dedent(""" + [archiver.mail_archive] + base_url: http://go.mail-archive.dev/ + recipient: archive@mail-archive.dev + + [domain.example_dot_com] + email_host: example.com + base_url: http://www.example.com + contact_address: postmaster@example.com + """)) + config_string = NL.join(test_config) + import pdb; pdb.set_trace() + config.push('test config', config_string) + config._config.getByCategory('domain') + + @classmethod + def tearDown(cls): + assert cls.var_dir is not None, 'Layer not set up' + config.pop('test config') + shutil.rmtree(cls.var_dir) + cls.var_dir = None + + @classmethod + def testSetUp(self): + pass + + @classmethod + def testTearDown(self): + # Reset the database between tests. + config.db._reset() + # Remove all residual queue files. + for dirpath, dirnames, filenames in os.walk(config.QUEUE_DIR): + for filename in filenames: + os.remove(os.path.join(dirpath, filename)) + # Clear out messages in the message store. + for message in config.db.message_store.messages: + config.db.message_store.delete_message(message['message-id']) + config.db.commit() + + + +class SMTPLayer(ConfigLayer): + """Layer for starting, stopping, and accessing a test SMTP server.""" + + smtpd = None + + @classmethod + def setUp(cls): + assert cls.smtpd is None, 'Layer already set up' + cls.smtpd = SMTPServer() + cls.smtpd.start() + + @classmethod + def tearDown(cls): + assert cls.smtpd is not None, 'Layer not set up' + cls.smtpd.clear() + cls.smtpd.stop() + + @classmethod + def testSetUp(cls): + pass + + @classmethod + def testTearDown(cls): + pass diff --git a/mailman/tests/test_documentation.py b/mailman/tests/test_documentation.py index ffb955c58..51dc784fe 100644 --- a/mailman/tests/test_documentation.py +++ b/mailman/tests/test_documentation.py @@ -15,7 +15,17 @@ # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see <http://www.gnu.org/licenses/>. -"""Harness for testing Mailman's documentation.""" +"""Harness for testing Mailman's documentation. + +Note that doctest extraction does not currently work for zip file +distributions. doctest discovery currently requires file system traversal. +""" + +__metaclass__ = type +__all__ = [ + 'test_suite', + ] + import os import random @@ -29,7 +39,7 @@ import mailman from mailman.Message import Message from mailman.config import config from mailman.core.styles import style_manager -from mailman.testing.helpers import SMTPServer +from mailman.testing.layers import SMTPLayer DOT = '.' @@ -37,6 +47,23 @@ COMMASPACE = ', ' +class chdir: + """A context manager for temporary directory changing.""" + def __init__(self, directory): + self._curdir = None + self._directory = directory + + def __enter__(self): + self._curdir = os.getcwd() + os.chdir(self._directory) + + def __exit__(self, exc_type, exc_val, exc_tb): + os.chdir(self._curdir) + # Don't suppress exceptions. + return False + + + def specialized_message_from_string(text): """Parse text into a message object. @@ -63,47 +90,15 @@ def stop(): def setup(testobj): """Test setup.""" - smtpd = SMTPServer() - smtpd.start() # In general, I don't like adding convenience functions, since I think # doctests should do the imports themselves. It makes for better # documentation that way. However, a few are really useful, or help to # hide some icky test implementation details. - testobj.globs['message_from_string'] = specialized_message_from_string testobj.globs['commit'] = config.db.commit - testobj.globs['smtpd'] = smtpd + testobj.globs['config'] = config + testobj.globs['message_from_string'] = specialized_message_from_string + testobj.globs['smtpd'] = SMTPLayer.smtpd testobj.globs['stop'] = stop - # Stash the current state of the global domains away for restoration in - # the teardown. - testobj._domains = config.domains.copy() - - - -def cleaning_teardown(testobj): - """Clear all persistent data at the end of a doctest.""" - # Clear the database of all rows. - config.db._reset() - # Reset the global domains. - config.domains = testobj._domains - # Remove all but the default style. - for style in style_manager.styles: - if style.name <> 'default': - style_manager.unregister(style) - # Remove all queue files. - for dirpath, dirnames, filenames in os.walk(config.QUEUE_DIR): - for filename in filenames: - os.remove(os.path.join(dirpath, filename)) - # Clear out messages in the message store. - for message in config.db.message_store.messages: - config.db.message_store.delete_message(message['message-id']) - config.db.commit() - # Reset all archivers by disabling them. - for archiver in config.archivers.values(): - archiver.is_enabled = False - # Shutdown the smtp server. - smtpd = testobj.globs['smtpd'] - smtpd.clear() - smtpd.stop() @@ -120,27 +115,29 @@ def test_suite(): flags = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF) - if config.tests.verbosity <= 2: - flags |= doctest.REPORT_ONLY_FIRST_FAILURE +## if config.tests.verbosity <= 2: +## flags |= doctest.REPORT_ONLY_FIRST_FAILURE # Add all the doctests in all subpackages. doctest_files = {} - for docsdir in packages: - for filename in os.listdir(os.path.join('mailman', docsdir)): - if os.path.splitext(filename)[1] == '.txt': - doctest_files[filename] = os.path.join(docsdir, filename) + with chdir(topdir): + for docsdir in packages: + for filename in os.listdir(docsdir): + if os.path.splitext(filename)[1] == '.txt': + doctest_files[filename] = os.path.join(docsdir, filename) # Sort or randomize the tests. - if config.tests.randomize: - files = doctest_files.keys() - random.shuffle(files) - else: - files = sorted(doctest_files) +## if config.tests.randomize: +## files = doctest_files.keys() +## random.shuffle(files) +## else: +## files = sorted(doctest_files) + files = sorted(doctest_files) for filename in files: path = doctest_files[filename] test = doctest.DocFileSuite( path, package='mailman', optionflags=flags, - setUp=setup, - tearDown=cleaning_teardown) + setUp=setup) + test.layer = SMTPLayer suite.addTest(test) return suite |
