Revision: 3658
Author: pekka.klarck
Date: Sat May 29 07:03:02 2010
Log: Now that parallel is no more (win!) only timeouts use threads and our custom threading module could be simplified.
http://code.google.com/p/robotframework/source/detail?r=3658

Modified:
 /trunk/src/robot/running/timeouts.py
 /trunk/src/robot/utils/robotthread.py
 /trunk/src/robot/utils/stoppablethread.py
 /trunk/utest/utils/test_robotthread.py

=======================================
--- /trunk/src/robot/running/timeouts.py        Thu May 20 23:49:18 2010
+++ /trunk/src/robot/running/timeouts.py        Sat May 29 07:03:02 2010
@@ -15,7 +15,7 @@
 import time

 from robot import utils
-from robot.utils.robotthread import Thread, Runner, Event
+from robot.utils.robotthread import ThreadedRunner
 from robot.errors import TimeoutError, DataError, FrameworkError

 from signalhandler import STOP_SIGNAL_MONITOR
@@ -72,7 +72,7 @@
         return cmp(self.time_left(), other.time_left())

     def run(self, runnable, args=None, kwargs=None, logger=None):
-        if self.error is not None:
+        if self.error:
             raise DataError(self.error)
         if not self.active():
             raise FrameworkError('Timeout is not active')
@@ -84,21 +84,10 @@
         STOP_SIGNAL_MONITOR.start_running_keyword()
         if timeout <= 0:
             raise TimeoutError(self.get_message())
-        notifier = Event()
-        runner = Runner(runnable, args, kwargs, notifier)
-        # Thread's name is important - it's used in utils.outputcapture
- thread = Thread(runner, stoppable=True, daemon=True, name='TIMED_RUN')
-        thread.start()
-        time.sleep(0.001)
-        notifier.wait(timeout)
-        if runner.is_done():
+        runner = ThreadedRunner(runnable, args, kwargs)
+        if runner.run_in_thread(timeout):
             return runner.get_result()
-        try:
-            thread.stop()
-        except utils.RERAISED_EXCEPTIONS:
-            raise
-        except:
-            pass
+        runner.stop_thread()
         raise TimeoutError(self.get_message())

     def get_message(self):
=======================================
--- /trunk/src/robot/utils/robotthread.py       Tue Mar 23 04:15:41 2010
+++ /trunk/src/robot/utils/robotthread.py       Sat May 29 07:03:02 2010
@@ -13,62 +13,36 @@
 #  limitations under the License.


-import os
 import sys
-import threading
-
-from robot.errors import FrameworkError
-
-if os.name == 'java':
-    from java.lang import Runnable, Throwable
-    from java.lang import Thread as JavaThread
-    java_exceptions = (Throwable,)
-else:
-    from stoppablethread import StoppablePythonThread
-    class Runnable:
-        pass
-    java_exceptions = ()
-
-
-class _FakeSemaphore:
-    def acquire(self):
+from threading import Event
+
+from robot.utils import RERAISED_EXCEPTIONS
+
+
+if sys.platform.startswith('java'):
+    from java.lang import Thread, Runnable, Throwable
+    JAVA_EXCEPTIONS = (Throwable,)
+
+else:
+    from stoppablethread import Thread
+    class Runnable(object):
         pass
-    def release(self):
-        pass
-
-def Semaphore():
-    # Cygwin Python threads are buggy so use a fake semaphore when possible
-    if sys.platform.count('cygwin') > 0 \
-            and threading.currentThread().getName() == 'MainThread':
-        return _FakeSemaphore()
-    return threading.Semaphore()
-
-
-Event = threading.Event
-
-
-def current_thread():
-    if os.name == 'java':
-        return JavaThread.currentThread()
-    return threading.currentThread()
-
-
-class Runner(Runnable):
+    JAVA_EXCEPTIONS = ()
+
+
+class ThreadedRunner(Runnable):

     def __init__(self, runnable, args=None, kwargs=None, notifier=None):
-        self._runnable = runnable
-        self._args = args is not None and args or ()
-        self._kwargs = kwargs is not None and kwargs or {}
- self._notifier = notifier is not None and notifier or threading.Event()
+        self._runnable = lambda: runnable(*(args or ()), **(kwargs or {}))
+        self._notifier = Event()
         self._result = None
         self._error = None
+        self._thread = None

     def run(self):
-        if self.is_done():
-            raise FrameworkError('Runner can be run only once')
         try:
-            self._result = self._runnable(*self._args, **self._kwargs)
-        except java_exceptions, error:
+            self._result = self._runnable()
+        except JAVA_EXCEPTIONS, error:
             self._error = error
         except:
             self._error = sys.exc_info()[1]
@@ -76,25 +50,22 @@

     __call__ = run

-    def is_done(self):
+    def run_in_thread(self, timeout):
+        self._thread = Thread(self)
+        self._thread.setDaemon(True)
+        self._thread.start()
+        self._notifier.wait(timeout)
         return self._notifier.isSet()

     def get_result(self):
-        if not self.is_done():
-            self._notifier.wait()
-        if self._error is not None:
+        if self._error:
             raise self._error
         return self._result

-
-def Thread(runner, stoppable=False, daemon=False, name=None):
-    if os.name == 'java':
-        thread = JavaThread(runner)   # This is always stoppable
-    elif not stoppable:
-        thread = threading.Thread(target=runner)
-    else:
-        thread = StoppablePythonThread(target=runner)
-    thread.setDaemon(daemon)
-    if name is not None:
-        thread.setName(name)
-    return thread
+    def stop_thread(self):
+        try:
+            self._thread.stop()
+        except RERAISED_EXCEPTIONS:
+            raise
+        except:
+            pass
=======================================
--- /trunk/src/robot/utils/stoppablethread.py   Fri May 28 04:17:47 2010
+++ /trunk/src/robot/utils/stoppablethread.py   Sat May 29 07:03:02 2010
@@ -16,8 +16,7 @@
 import threading


-class StoppablePythonThread(threading.Thread):
-
+class Thread(threading.Thread):
     """A subclass of threading.Thread, with a stop() method.

Original version posted by Connelly Barnes to python-list and available at
@@ -29,14 +28,13 @@
     in Python because in Jython we can use java.lang.Thread.
     """

-    def __init__(self, *args, **kwargs):
-        threading.Thread.__init__(self, *args, **kwargs)
+    def __init__(self, runner):
+        threading.Thread.__init__(self, target=runner)
         self._stopped = False

     def start(self):
-        """Start the thread."""
         self.__run_backup = self.run
-        self.run = self.__run      # Force the Thread to install our trace.
+        self.run = self.__run
         threading.Thread.start(self)

     def stop(self):
=======================================
--- /trunk/utest/utils/test_robotthread.py      Thu Mar  4 20:58:41 2010
+++ /trunk/utest/utils/test_robotthread.py      Sat May 29 07:03:02 2010
@@ -1,83 +1,39 @@
-import unittest, time, os, sys
-from threading import Event
-if os.name == 'java':
-    import java.lang
+import unittest
+import sys

 from robot.utils.asserts import *
-from robot.errors import *
-
-from robot.utils.robotthread import Runner, Thread
+
+from robot.utils.robotthread import ThreadedRunner
 from thread_resources import *


 class TestRunner(unittest.TestCase):

     def test_passing(self):
-        runner = Runner(passing)
-        assert_false(runner.is_done())
+        runner = ThreadedRunner(passing)
         runner.run()
-        assert_true(runner.is_done())
         assert_none(runner.get_result())
-
-    def test_notifier(self):
-        notifier = Event()
-        runner = Runner(passing, notifier=notifier)
-        assert_false(runner.is_done())
-        assert_false(notifier.isSet())
-        runner.run()
-        assert_true(runner.is_done())
-        assert_true(notifier.isSet())

     def test_returning(self):
         for arg in [ 10, 'hello', ['l','i','s','t'], unittest]:
-            runner = Runner(returning, args=(arg,))
-            assert_false(runner.is_done())
+            runner = ThreadedRunner(returning, args=(arg,))
             runner.run()
-            assert_true(runner.is_done())
             assert_equals(runner.get_result(), arg)

     def test_failing(self):
-        runner = Runner(failing, args=('hello world',))
-        assert_false(runner.is_done())
+        runner = ThreadedRunner(failing, args=('hello world',))
         runner.run()
-        assert_true(runner.is_done())
-        try:
-            runner.get_result()
-            fail('get_result did not raise an exception as expected')
-        except Exception, err:
-            assert_equals(str(err), 'hello world')
-
-    if os.name == 'java':
+        assert_raises_with_msg(Exception, 'hello world', runner.get_result)
+
+    if sys.platform.startswith('java'):
+        from java.lang import Error
+
         def test_java_failing(self):
-            runner = Runner(java_failing, args=('hi tellus',))
-            assert_false(runner.is_done())
+            runner = ThreadedRunner(java_failing, args=('hi tellus',))
             runner.run()
-            assert_true(runner.is_done())
-            try:
-                runner.get_result()
-                fail('get_result did not raise an exception as expected')
-            except java.lang.Error, err:
-                assert_equals(err.getMessage(), 'hi tellus')
+            assert_raises_with_msg(Error, 'java.lang.Error: hi tellus',
+                                   runner.get_result)


-class TestThread(unittest.TestCase):
-
-    def test_stoppable(self):
-        thread = Thread(Runner(None), stoppable=True)
-        assert_true(hasattr(thread, 'stop'))
-
-    def test_daemon(self):
-        assert_true(Thread(Runner(None), daemon=True).isDaemon())
-        assert_false(Thread(Runner(None), daemon=False).isDaemon())
-
-    def test_name(self):
-        thread = Thread(Runner(None), name='My Name')
-        assert_equals(thread.getName(), 'My Name')
-
-    def test_noname(self):
-        name = Thread(Runner(None)).getName()
-        assert_true(isinstance(name, basestring))
-        assert_true(len(name) > 0)
-
 if __name__ == '__main__':
     unittest.main()

Reply via email to