[Python-checkins] r80476 - in python/trunk: Doc/library/unittest.rst Lib/unittest/__init__.py Lib/unittest/signals.py Lib/unittest/test/test_break.py

michael.foord python-checkins at python.org
Sun Apr 25 21:02:46 CEST 2010


Author: michael.foord
Date: Sun Apr 25 21:02:46 2010
New Revision: 80476

Log:
Adding unittest.removeHandler function / decorator for removing the signal.SIGINT signal handler. With tests and docs.

Modified:
   python/trunk/Doc/library/unittest.rst
   python/trunk/Lib/unittest/__init__.py
   python/trunk/Lib/unittest/signals.py
   python/trunk/Lib/unittest/test/test_break.py

Modified: python/trunk/Doc/library/unittest.rst
==============================================================================
--- python/trunk/Doc/library/unittest.rst	(original)
+++ python/trunk/Doc/library/unittest.rst	Sun Apr 25 21:02:46 2010
@@ -1855,8 +1855,17 @@
 and report all the results so far. A second control-c will raise a
 ``KeyboardInterrupt`` in the usual way.
 
-There are a few utility functions for framework authors to enable this
-functionality within test frameworks.
+The control-c handling signal handler attempts to remain compatible with code or
+tests that install their own :const:`signal.SIGINT` handler. If the ``unittest``
+handler is called but *isn't* the installed :const:`signal.SIGINT` handler,
+i.e. it has been replaced by the system under test and delegated to, then it
+calls the default handler. This will normally be the expected behavior by code
+that replaces an installed handler and delegates to it. For individual tests
+that need ``unittest`` control-c handling disabled the :func:`removeHandler`
+decorator can be used.
+
+There are a few utility functions for framework authors to enable control-c
+handling functionality within test frameworks.
 
 .. function:: installHandler()
 
@@ -1870,9 +1879,23 @@
    result stores a weak reference to it, so it doesn't prevent the result from
    being garbage collected.
 
+   Registering a :class:`TestResult` object has no side-effects if control-c
+   handling is not enabled, so test frameworks can unconditionally register
+   all results they create independently of whether or not handling is enabled.
+
 .. function:: removeResult(result)
 
    Remove a registered result. Once a result has been removed then
    :meth:`~TestResult.stop` will no longer be called on that result object in
    response to a control-c.
 
+.. function:: removeHandler(function=None)
+
+   When called without arguments this function removes the control-c handler
+   if it has been installed. This function can also be used as a test decorator
+   to temporarily remove the handler whilst the test is being executed::
+
+      @unittest.removeHandler
+      def test_signal_handling(self):
+          ...
+

Modified: python/trunk/Lib/unittest/__init__.py
==============================================================================
--- python/trunk/Lib/unittest/__init__.py	(original)
+++ python/trunk/Lib/unittest/__init__.py	Sun Apr 25 21:02:46 2010
@@ -48,7 +48,7 @@
            'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main',
            'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless',
            'expectedFailure', 'TextTestResult', 'installHandler',
-           'registerResult', 'removeResult']
+           'registerResult', 'removeResult', 'removeHandler']
 
 # Expose obsolete functions for backwards compatibility
 __all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
@@ -63,7 +63,7 @@
                      findTestCases)
 from .main import TestProgram, main
 from .runner import TextTestRunner, TextTestResult
-from .signals import installHandler, registerResult, removeResult
+from .signals import installHandler, registerResult, removeResult, removeHandler
 
 # deprecated
 _TextTestResult = TextTestResult

Modified: python/trunk/Lib/unittest/signals.py
==============================================================================
--- python/trunk/Lib/unittest/signals.py	(original)
+++ python/trunk/Lib/unittest/signals.py	Sun Apr 25 21:02:46 2010
@@ -1,6 +1,8 @@
 import signal
 import weakref
 
+from functools import wraps
+
 __unittest = True
 
 
@@ -36,3 +38,20 @@
         default_handler = signal.getsignal(signal.SIGINT)
         _interrupt_handler = _InterruptHandler(default_handler)
         signal.signal(signal.SIGINT, _interrupt_handler)
+
+
+def removeHandler(method=None):
+    if method is not None:
+        @wraps(method)
+        def inner(*args, **kwargs):
+            initial = signal.getsignal(signal.SIGINT)
+            removeHandler()
+            try:
+                return method(*args, **kwargs)
+            finally:
+                signal.signal(signal.SIGINT, initial)
+        return inner
+
+    global _interrupt_handler
+    if _interrupt_handler is not None:
+        signal.signal(signal.SIGINT, _interrupt_handler.default_handler)

Modified: python/trunk/Lib/unittest/test/test_break.py
==============================================================================
--- python/trunk/Lib/unittest/test/test_break.py	(original)
+++ python/trunk/Lib/unittest/test/test_break.py	Sun Apr 25 21:02:46 2010
@@ -229,3 +229,24 @@
         self.assertEqual(p.result, result)
 
         self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler)
+
+    def testRemoveHandler(self):
+        default_handler = signal.getsignal(signal.SIGINT)
+        unittest.installHandler()
+        unittest.removeHandler()
+        self.assertEqual(signal.getsignal(signal.SIGINT), default_handler)
+
+        # check that calling removeHandler multiple times has no ill-effect
+        unittest.removeHandler()
+        self.assertEqual(signal.getsignal(signal.SIGINT), default_handler)
+
+    def testRemoveHandlerAsDecorator(self):
+        default_handler = signal.getsignal(signal.SIGINT)
+        unittest.installHandler()
+
+        @unittest.removeHandler
+        def test():
+            self.assertEqual(signal.getsignal(signal.SIGINT), default_handler)
+
+        test()
+        self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler)


More information about the Python-checkins mailing list