diff options
| author | Barry Warsaw | 2012-07-06 21:08:41 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2012-07-06 21:08:41 -0400 |
| commit | 8d8ab1655b51e277570005b445d3b014afcfbc57 (patch) | |
| tree | 6ba0147d975636e129a787c9dfa64dae8cffae89 /src/mailman/core | |
| parent | cd3f84b301c2150fea5402129a2e7bc862fbb52b (diff) | |
| parent | 01415190ab44e69a8f09a6411564a7cb288404e8 (diff) | |
| download | mailman-8d8ab1655b51e277570005b445d3b014afcfbc57.tar.gz mailman-8d8ab1655b51e277570005b445d3b014afcfbc57.tar.zst mailman-8d8ab1655b51e277570005b445d3b014afcfbc57.zip | |
Diffstat (limited to 'src/mailman/core')
| -rw-r--r-- | src/mailman/core/constants.py | 7 | ||||
| -rw-r--r-- | src/mailman/core/i18n.py | 8 | ||||
| -rw-r--r-- | src/mailman/core/initialize.py | 6 | ||||
| -rw-r--r-- | src/mailman/core/pipelines.py | 5 | ||||
| -rw-r--r-- | src/mailman/core/runner.py | 17 | ||||
| -rw-r--r-- | src/mailman/core/switchboard.py | 37 | ||||
| -rw-r--r-- | src/mailman/core/system.py | 7 | ||||
| -rw-r--r-- | src/mailman/core/tests/test_pipelines.py | 10 | ||||
| -rw-r--r-- | src/mailman/core/tests/test_runner.py | 89 |
9 files changed, 147 insertions, 39 deletions
diff --git a/src/mailman/core/constants.py b/src/mailman/core/constants.py index 02d46a088..4562f4c74 100644 --- a/src/mailman/core/constants.py +++ b/src/mailman/core/constants.py @@ -17,7 +17,7 @@ """Various constants and enumerations.""" -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ @@ -26,7 +26,7 @@ __all__ = [ from zope.component import getUtility -from zope.interface import implements +from zope.interface import implementer from mailman.config import config from mailman.interfaces.languages import ILanguageManager @@ -35,11 +35,10 @@ from mailman.interfaces.preferences import IPreferences +@implementer(IPreferences) class SystemDefaultPreferences: """The default system preferences.""" - implements(IPreferences) - acknowledge_posts = False hide_address = True receive_list_copy = True diff --git a/src/mailman/core/i18n.py b/src/mailman/core/i18n.py index 2d7c382c7..73453ae65 100644 --- a/src/mailman/core/i18n.py +++ b/src/mailman/core/i18n.py @@ -31,7 +31,7 @@ import time from flufl.i18n import PackageStrategy, registry import mailman.messages - +from mailman.interfaces.configuration import ConfigurationUpdatedEvent _ = None @@ -113,3 +113,9 @@ def ctime(date): wday = daysofweek[wday] mon = months[mon] return _('$wday $mon $day $hh:$mm:$ss $tzname $year') + + + +def handle_ConfigurationUpdatedEvent(event): + if isinstance(event, ConfigurationUpdatedEvent): + _.default = event.config.mailman.default_language diff --git a/src/mailman/core/initialize.py b/src/mailman/core/initialize.py index 5659661a7..b359928cc 100644 --- a/src/mailman/core/initialize.py +++ b/src/mailman/core/initialize.py @@ -110,6 +110,10 @@ def initialize_1(config_path=None): # o-rwx although I think in most cases it doesn't hurt if other can read # or write the files. os.umask(007) + # Initialize configuration event subscribers. This must be done before + # setting up the configuration system. + from mailman.app.events import initialize as initialize_events + initialize_events() # config_path will be set if the command line argument -C is given. That # case overrides all others. When not given on the command line, the # configuration file is searched for in the file system. @@ -155,7 +159,6 @@ def initialize_2(debug=False, propagate_logs=None): # Initialize the rules and chains. Do the imports here so as to avoid # circular imports. from mailman.app.commands import initialize as initialize_commands - from mailman.app.events import initialize as initialize_events from mailman.core.chains import initialize as initialize_chains from mailman.core.pipelines import initialize as initialize_pipelines from mailman.core.rules import initialize as initialize_rules @@ -164,7 +167,6 @@ def initialize_2(debug=False, propagate_logs=None): initialize_chains() initialize_pipelines() initialize_commands() - initialize_events() def initialize_3(): diff --git a/src/mailman/core/pipelines.py b/src/mailman/core/pipelines.py index 25bb68030..972417c2c 100644 --- a/src/mailman/core/pipelines.py +++ b/src/mailman/core/pipelines.py @@ -32,7 +32,7 @@ __all__ = [ import logging -from zope.interface import implements +from zope.interface import implementer from zope.interface.verify import verifyObject from mailman.app.bounces import bounce_message @@ -75,11 +75,10 @@ def process(mlist, msg, msgdata, pipeline_name='built-in'): +@implementer(IPipeline) class BasePipeline: """Base pipeline implementation.""" - implements(IPipeline) - _default_handlers = () def __init__(self): diff --git a/src/mailman/core/runner.py b/src/mailman/core/runner.py index e86741c41..a79f19fbc 100644 --- a/src/mailman/core/runner.py +++ b/src/mailman/core/runner.py @@ -17,7 +17,7 @@ """The process runner base class.""" -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ @@ -32,14 +32,15 @@ import traceback from cStringIO import StringIO from lazr.config import as_boolean, as_timedelta from zope.component import getUtility -from zope.interface import implements +from zope.event import notify +from zope.interface import implementer from mailman.config import config from mailman.core.i18n import _ from mailman.core.switchboard import Switchboard from mailman.interfaces.languages import ILanguageManager from mailman.interfaces.listmanager import IListManager -from mailman.interfaces.runner import IRunner +from mailman.interfaces.runner import IRunner, RunnerCrashEvent from mailman.utilities.string import expand @@ -48,9 +49,8 @@ elog = logging.getLogger('mailman.error') +@implementer(IRunner) class Runner: - implements(IRunner) - intercept_signals = True def __init__(self, name, slice=None): @@ -217,7 +217,12 @@ class Runner: language = mlist.preferred_language with _.using(language.code): msgdata['lang'] = language.code - keepqueued = self._dispose(mlist, msg, msgdata) + try: + keepqueued = self._dispose(mlist, msg, msgdata) + except Exception as error: + # Trigger the Zope event and re-raise + notify(RunnerCrashEvent(self, mlist, msg, msgdata, error)) + raise if keepqueued: self.switchboard.enqueue(msg, msgdata) diff --git a/src/mailman/core/switchboard.py b/src/mailman/core/switchboard.py index 7cab4f4ad..1f16cb5fb 100644 --- a/src/mailman/core/switchboard.py +++ b/src/mailman/core/switchboard.py @@ -24,11 +24,12 @@ written. First, the message is written to the pickle, then the metadata dictionary is written. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ 'Switchboard', + 'handle_ConfigurationUpdatedEvent', ] @@ -40,10 +41,11 @@ import cPickle import hashlib import logging -from zope.interface import implements +from zope.interface import implementer from mailman.config import config from mailman.email.message import Message +from mailman.interfaces.configuration import ConfigurationUpdatedEvent from mailman.interfaces.switchboard import ISwitchboard from mailman.utilities.filesystem import makedirs from mailman.utilities.string import expand @@ -63,20 +65,9 @@ elog = logging.getLogger('mailman.error') +@implementer(ISwitchboard) class Switchboard: - implements(ISwitchboard) - - @staticmethod - def initialize(): - """Initialize the global switchboards for input/output.""" - for conf in config.runner_configs: - name = conf.name.split('.')[-1] - assert name not in config.switchboards, ( - 'Duplicate runner name: {0}'.format(name)) - substitutions = config.paths - substitutions['name'] = name - path = expand(conf.path, substitutions) - config.switchboards[name] = Switchboard(name, path) + """See `ISwitchboard`.""" def __init__(self, name, queue_directory, slice=None, numslices=1, recover=False): @@ -263,3 +254,19 @@ class Switchboard: self.finish(filebase, preserve=True) else: os.rename(src, dst) + + + +def handle_ConfigurationUpdatedEvent(event): + """Initialize the global switchboards for input/output.""" + if not isinstance(event, ConfigurationUpdatedEvent): + return + config = event.config + for conf in config.runner_configs: + name = conf.name.split('.')[-1] + assert name not in config.switchboards, ( + 'Duplicate runner name: {0}'.format(name)) + substitutions = config.paths + substitutions['name'] = name + path = expand(conf.path, substitutions) + config.switchboards[name] = Switchboard(name, path) diff --git a/src/mailman/core/system.py b/src/mailman/core/system.py index ce66761a7..b29567827 100644 --- a/src/mailman/core/system.py +++ b/src/mailman/core/system.py @@ -17,7 +17,7 @@ """System information.""" -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals __metaclass__ = type __all__ = [ @@ -27,15 +27,16 @@ __all__ = [ import sys -from zope.interface import implements +from zope.interface import implementer from mailman import version from mailman.interfaces.system import ISystem +@implementer(ISystem) class System: - implements(ISystem) + """See `ISystem`.""" @property def mailman_version(self): diff --git a/src/mailman/core/tests/test_pipelines.py b/src/mailman/core/tests/test_pipelines.py index 8f851de95..8e76cf033 100644 --- a/src/mailman/core/tests/test_pipelines.py +++ b/src/mailman/core/tests/test_pipelines.py @@ -29,7 +29,7 @@ __all__ = [ import unittest from zope.component import getUtility -from zope.interface import implements +from zope.interface import implementer from mailman.app.lifecycle import create_list from mailman.config import config @@ -48,24 +48,24 @@ from mailman.testing.layers import ConfigLayer +@implementer(IHandler) class DiscardingHandler: - implements(IHandler) name = 'discarding' def process(self, mlist, msg, msgdata): raise DiscardMessage('by test handler') +@implementer(IHandler) class RejectHandler: - implements(IHandler) name = 'rejecting' def process(self, mlist, msg, msgdata): raise RejectMessage('by test handler') +@implementer(IPipeline) class DiscardingPipeline: - implements(IPipeline) name = 'test-discarding' description = 'Discarding test pipeline' @@ -73,8 +73,8 @@ class DiscardingPipeline: yield DiscardingHandler() +@implementer(IPipeline) class RejectingPipeline: - implements(IPipeline) name = 'test-rejecting' description = 'Rejectinging test pipeline' diff --git a/src/mailman/core/tests/test_runner.py b/src/mailman/core/tests/test_runner.py new file mode 100644 index 000000000..ad2548adc --- /dev/null +++ b/src/mailman/core/tests/test_runner.py @@ -0,0 +1,89 @@ +# Copyright (C) 2012 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. + +"""Test some Runner base class behavior.""" + +from __future__ import absolute_import, print_function, unicode_literals + +__metaclass__ = type +__all__ = [ + 'TestRunner', + ] + + +import unittest + +from mailman.app.lifecycle import create_list +from mailman.config import config +from mailman.core.runner import Runner +from mailman.interfaces.runner import RunnerCrashEvent +from mailman.testing.helpers import ( + configuration, event_subscribers, get_queue_messages, + make_testable_runner, specialized_message_from_string as mfs) +from mailman.testing.layers import ConfigLayer + + + +class CrashingRunner(Runner): + def _dispose(self, mlist, msg, msgdata): + raise RuntimeError('borked') + + + +class TestRunner(unittest.TestCase): + """Test the Runner base class behavior.""" + + layer = ConfigLayer + + def setUp(self): + self._mlist = create_list('test@example.com') + self._events = [] + + def _got_event(self, event): + self._events.append(event) + + @configuration('runner.crashing', + **{'class': 'mailman.core.tests.CrashingRunner'}) + def test_crash_event(self): + runner = make_testable_runner(CrashingRunner, 'in') + # When an exception occurs in Runner._process_one_file(), a zope.event + # gets triggered containing the exception object. + msg = mfs("""\ +From: anne@example.com +To: test@example.com +Message-ID: <ant> + +""") + config.switchboards['in'].enqueue(msg, listname='test@example.com') + with event_subscribers(self._got_event): + runner.run() + # We should now have exactly one event, which will contain the + # exception, plus additional metadata containing the mailing list, + # message, and metadata. + self.assertEqual(len(self._events), 1) + event = self._events[0] + self.assertTrue(isinstance(event, RunnerCrashEvent)) + self.assertEqual(event.mailing_list, self._mlist) + self.assertEqual(event.message['message-id'], '<ant>') + self.assertEqual(event.metadata['listname'], 'test@example.com') + self.assertTrue(isinstance(event.error, RuntimeError)) + self.assertEqual(event.error.message, 'borked') + self.assertTrue(isinstance(event.runner, CrashingRunner)) + # The message should also have ended up in the shunt queue. + shunted = get_queue_messages('shunt') + self.assertEqual(len(shunted), 1) + self.assertEqual(shunted[0].msg['message-id'], '<ant>') |
