Barry Warsaw pushed to branch master at mailman / Mailman

Commits:
7c97e8fb by Barry Warsaw at 2017-01-28T13:19:58-05:00
Handle PEP 475 fallout.

Closes #255

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.

The lmtp and rest runners implement their own run loops, so they also have to
handle this exception, by ignoring it.

- - - - -
91e99645 by Barry Warsaw at 2017-01-28T13:23:39-05:00
Merge branch 'master' into issue255.

- - - - -
064435ab by Barry Warsaw at 2017-01-28T19:04:50+00:00
Merge branch 'issue255' into 'master'

Handle PEP 475 fallout

Closes #255

See merge request !238
- - - - -


5 changed files:

- src/mailman/core/runner.py
- src/mailman/docs/NEWS.rst
- src/mailman/interfaces/runner.py
- src/mailman/runners/lmtp.py
- src/mailman/runners/rest.py


Changes:

=====================================
src/mailman/core/runner.py
=====================================
--- 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.


=====================================
src/mailman/docs/NEWS.rst
=====================================
--- 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
 -------------


=====================================
src/mailman/interfaces/runner.py
=====================================
--- 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."""
 


=====================================
src/mailman/runners/lmtp.py
=====================================
--- 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()


=====================================
src/mailman/runners/rest.py
=====================================
--- 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()



View it on GitLab: 
https://gitlab.com/mailman/mailman/compare/ba08609f28f7193a020a57e90bb887d59f1543bc...064435abdfd26a77ea1e3c665156ff468ec52ec0
_______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: 
https://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to