[Checkins] SVN: zc.thread/trunk/ - If a thread raises an exception (subclass of Exception), the

jim cvs-admin at zope.org
Tue Jan 1 21:40:01 UTC 2013


Log message for revision 128985:
  
  - If a thread raises an exception (subclass of Exception), the
    exception is logged and a traceback is printed to standard error.
  
  - A restart argument can be used to rerun a thread target function if
    there's an uncaught exception.  Value passed to the restart argument
    is passed to time.sleep before restarting the function.
  
  

Changed:
  U   zc.thread/trunk/README.txt
  U   zc.thread/trunk/src/zc/thread/__init__.py
  U   zc.thread/trunk/src/zc/thread/tests.py

-=-
Modified: zc.thread/trunk/README.txt
===================================================================
--- zc.thread/trunk/README.txt	2013-01-01 21:08:21 UTC (rev 128984)
+++ zc.thread/trunk/README.txt	2013-01-01 21:40:00 UTC (rev 128985)
@@ -55,11 +55,17 @@
 Changes
 *******
 
-0.1.1 (2013-01-01)
+0.1.1 (2013-01-??)
 ==================
 
 - Thread names now include a function's module name.
 
+- Unhandled exceptions in thread and process targets are now logged
+  and printed with tracebacks.
+
+- A restart argument can be used to automatically restart thread
+  targets after a rest.
+
 0.1.0 (2011-11-27)
 ==================
 

Modified: zc.thread/trunk/src/zc/thread/__init__.py
===================================================================
--- zc.thread/trunk/src/zc/thread/__init__.py	2013-01-01 21:08:21 UTC (rev 128984)
+++ zc.thread/trunk/src/zc/thread/__init__.py	2013-01-01 21:40:00 UTC (rev 128985)
@@ -17,7 +17,7 @@
 exiting = False
 
 @atexit.register
-def _():
+def set_exiting():
     global exiting
     exiting = True
 
@@ -42,7 +42,7 @@
                     return
                 import logging
                 logging.getLogger(name).exception(
-                    'Exception in %s', class_.name)
+                    'Exception in %s', class_.__name__)
                 import traceback
                 traceback.print_exc()
                 if not restart:

Modified: zc.thread/trunk/src/zc/thread/tests.py
===================================================================
--- zc.thread/trunk/src/zc/thread/tests.py	2013-01-01 21:08:21 UTC (rev 128984)
+++ zc.thread/trunk/src/zc/thread/tests.py	2013-01-01 21:40:00 UTC (rev 128985)
@@ -33,20 +33,30 @@
             foo.setDaemon.assert_called_with(True)
             foo.start.assert_called_with()
 
-    def test_undecorated_and_exception_return(self):
+    @mock.patch('logging.getLogger')
+    @mock.patch('traceback.print_exc')
+    def test_undecorated_and_exception_return(self, print_exc, getLogger):
         with mock.patch('threading.Thread') as Thread:
             def foo2():
                 raise ValueError(42)
 
+            Thread.__name__ = 'Thread'
             t = zc.thread.Thread(foo2)
             Thread.call_args[1].pop('target')()
-            Thread.assert_called_with(name='zc.thread.tests.foo2', args=(), kwargs=dict())
+            Thread.assert_called_with(
+                name='zc.thread.tests.foo2', args=(), kwargs=dict())
             t.setDaemon.assert_called_with(True)
             t.start.assert_called_with()
             self.assert_(t.value is None)
             self.assert_(isinstance(t.exception, ValueError))
             self.assert_(t.exception.args == (42,))
 
+            getLogger.assert_called_with('zc.thread.tests.foo2')
+            getLogger.return_value.exception.assert_called_with(
+                "Exception in %s", "Thread")
+
+            print_exc.assert_called_with()
+
             t = zc.thread.Thread(foo2, args=(1, 2))
             Thread.call_args[1].pop('target')(1, 2)
             Thread.assert_called_with(name='zc.thread.tests.foo2',
@@ -56,6 +66,44 @@
             self.assert_(t.value is None)
             self.assert_(isinstance(t.exception, TypeError))
 
+
+    @mock.patch('logging.getLogger')
+    @mock.patch('traceback.print_exc')
+    @mock.patch('threading.Thread')
+    def test_no_exceotion_handling_if_exiting(
+        self, Thread, print_exc, getLogger):
+        self.assertEqual(zc.thread.exiting, False)
+        zc.thread.set_exiting()
+        self.assertEqual(zc.thread.exiting, True)
+
+        @zc.thread.Thread
+        def foo2():
+            raise ValueError(42)
+        Thread.call_args[1].pop('target')()
+        getLogger.assert_not_called()
+        print_exc.assert_not_called()
+        zc.thread.exiting = False
+
+    @mock.patch('time.sleep')
+    @mock.patch('logging.getLogger')
+    @mock.patch('traceback.print_exc')
+    @mock.patch('threading.Thread')
+    def test_restart(self, Thread, print_exc, getLogger, sleep):
+        called = []
+        Thread.__name__ = 'Thread'
+
+        @zc.thread.Thread(restart=9)
+        def foo2():
+            called.append(0)
+            if len(called) < 3:
+                raise ValueError(42)
+            else:
+                raise BaseException
+
+        self.assertRaises(BaseException, Thread.call_args[1].pop('target'))
+        self.assertEqual(len(called), 3)
+        sleep.assert_called_with(9)
+
     def test_passing_arguments(self):
         with mock.patch('threading.Thread') as Thread:
             @zc.thread.Thread(args=(1, 2), kwargs=dict(a=1), daemon=False,



More information about the checkins mailing list