diff options
| author | Barry Warsaw | 2017-01-28 13:17:25 -0500 |
|---|---|---|
| committer | Barry Warsaw | 2017-01-28 13:19:58 -0500 |
| commit | 7c97e8fbdb90a1a0de1526d7a6f108e95415d6a0 (patch) | |
| tree | d49c10cccf7bfafa08fa7145a5860fa43e5615fb /src/mailman | |
| parent | dfb807e04037786a62e7635d0b37e0695a77d5d6 (diff) | |
| download | mailman-7c97e8fbdb90a1a0de1526d7a6f108e95415d6a0.tar.gz mailman-7c97e8fbdb90a1a0de1526d7a6f108e95415d6a0.tar.zst mailman-7c97e8fbdb90a1a0de1526d7a6f108e95415d6a0.zip | |
Diffstat (limited to 'src/mailman')
| -rw-r--r-- | src/mailman/core/runner.py | 15 | ||||
| -rw-r--r-- | src/mailman/docs/NEWS.rst | 2 | ||||
| -rw-r--r-- | src/mailman/interfaces/runner.py | 13 | ||||
| -rw-r--r-- | src/mailman/runners/lmtp.py | 11 | ||||
| -rw-r--r-- | src/mailman/runners/rest.py | 8 |
5 files changed, 41 insertions, 8 deletions
diff --git a/src/mailman/core/runner.py b/src/mailman/core/runner.py index 757d8837e..9f8094153 100644 --- a/src/mailman/core/runner.py +++ b/src/mailman/core/runner.py @@ -31,7 +31,8 @@ from mailman.core.logging import reopen from mailman.core.switchboard import Switchboard from mailman.interfaces.languages import ILanguageManager from mailman.interfaces.listmanager import IListManager -from mailman.interfaces.runner import IRunner, RunnerCrashEvent +from mailman.interfaces.runner import ( + IRunner, RunnerCrashEvent, RunnerInterrupt) from mailman.utilities.string import expand from public import public from zope.component import getUtility @@ -98,6 +99,16 @@ class Runner: elif signum == signal.SIGHUP: reopen() rlog.info('%s runner caught SIGHUP. Reopening logs.', self.name) + # As of Python 3.5, PEP 475 gets in our way. Runners with long + # time.sleep()'s in their _snooze() method (e.g. the retry runner) will + # have their system call implemented time.sleep() automatically retried + # at the C layer. The only reliable way to prevent this is to raise an + # exception in the signal handler. The standard run() method + # automatically suppresses this exception, meaning, it's caught and + # ignored, but effectively breaks the run() loop, which is just what we + # want. Runners which implement their own run() method must be + # prepared to catch RunnerInterrupts, usually also ignoring them. + raise RunnerInterrupt def set_signals(self): """See `IRunner`.""" @@ -113,7 +124,7 @@ class Runner: def run(self): """See `IRunner`.""" # Start the main loop for this runner. - with suppress(KeyboardInterrupt): + with suppress(KeyboardInterrupt, RunnerInterrupt): while True: # Once through the loop that processes all the files in the # queue directory. diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst index 5736c1354..dfe71755f 100644 --- a/src/mailman/docs/NEWS.rst +++ b/src/mailman/docs/NEWS.rst @@ -106,6 +106,8 @@ Bugs * Messages were shunted when non-ASCII characters appeared in a mailing list's description. Given by Mark Sapiro. (Closes: #215) * Fix confirmation of unsubscription requests. (Closes: #294) + * Fix ``mailman stop`` not stopping some runners due to PEP 475 interaction. + (Closes: #255) Configuration ------------- diff --git a/src/mailman/interfaces/runner.py b/src/mailman/interfaces/runner.py index 12a7e49ad..f85c759c1 100644 --- a/src/mailman/interfaces/runner.py +++ b/src/mailman/interfaces/runner.py @@ -34,6 +34,19 @@ class RunnerCrashEvent: @public +class RunnerInterrupt(Exception): + """A runner received a system call interrupting signal. + + PEP 475 automatically, and at the C layer, retries system calls such as + time.sleep(). This can mean runners with long sleeps in their _snooze() + method won't actually exit. This exception is always raised in Mailman's + runner signal handlers to prevent this behavior. Runners that implement + their own .run() method must be prepared to handle this, usually by + ignoring it. + """ + + +@public class IRunner(Interface): """The runner.""" diff --git a/src/mailman/runners/lmtp.py b/src/mailman/runners/lmtp.py index b5c7bbb1f..f24107bd7 100644 --- a/src/mailman/runners/lmtp.py +++ b/src/mailman/runners/lmtp.py @@ -40,12 +40,14 @@ import logging from aiosmtpd.controller import Controller from aiosmtpd.lmtp import LMTP +from contextlib import suppress from email.utils import parseaddr from mailman.config import config from mailman.core.runner import Runner from mailman.database.transaction import transactional from mailman.email.message import Message from mailman.interfaces.listmanager import IListManager +from mailman.interfaces.runner import RunnerInterrupt from mailman.utilities.datetime import now from mailman.utilities.email import add_message_hash from public import public @@ -240,7 +242,8 @@ class LMTPRunner(Runner): def run(self): """See `IRunner`.""" - self.lmtp.start() - while not self._stop: - self._snooze(0) - self.lmtp.stop() + with suppress(RunnerInterrupt): + self.lmtp.start() + while not self._stop: + self._snooze(0) + self.lmtp.stop() diff --git a/src/mailman/runners/rest.py b/src/mailman/runners/rest.py index a93d164c1..7758972ed 100644 --- a/src/mailman/runners/rest.py +++ b/src/mailman/runners/rest.py @@ -21,7 +21,9 @@ import signal import logging import threading +from contextlib import suppress from mailman.core.runner import Runner +from mailman.interfaces.runner import RunnerInterrupt from mailman.rest.wsgiapp import make_server from public import public @@ -59,10 +61,12 @@ class RESTRunner(Runner): def run(self): """See `IRunner`.""" - self._server.serve_forever() + with suppress(RunnerInterrupt): + self._server.serve_forever() def signal_handler(self, signum, frame): - super().signal_handler(signum, frame) + with suppress(RunnerInterrupt): + super().signal_handler(signum, frame) if signum in (signal.SIGTERM, signal.SIGINT, signal.SIGUSR1): # Set the flag that will terminate the TCPserver loop. self._event.set() |
