summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-11-21 22:29:37 +0100
committerVictor Stinner <victor.stinner@gmail.com>2014-11-21 22:29:37 +0100
commit88bf0d65af930b9a5bd650aa397d2d3adcc91952 (patch)
tree3eae5504b96cbcbc8e4949049867d5f416569ef9
parentd8d4824afac4e79ed4326f237f0768c1debfcbd0 (diff)
downloadaioeventlet-88bf0d65af930b9a5bd650aa397d2d3adcc91952.tar.gz
wrap_greenthread() now raises an exception if the greenthread is running
or already finished.
-rw-r--r--aiogreen.py37
-rw-r--r--doc/changelog.rst4
-rw-r--r--doc/status.rst2
-rw-r--r--doc/using.rst17
-rw-r--r--tests/test_eventlet.py65
5 files changed, 74 insertions, 51 deletions
diff --git a/aiogreen.py b/aiogreen.py
index 988b9cc..84759f4 100644
--- a/aiogreen.py
+++ b/aiogreen.py
@@ -240,31 +240,35 @@ class EventLoopPolicy(asyncio.DefaultEventLoopPolicy):
def wrap_greenthread(gt, loop=None):
"""Wrap an eventlet GreenThread or a greenlet into a Future object.
- The greenlet must not be running."""
+ The greenthread or greenlet must be wrapped before its execution starts.
+ If the greenthread or greenlet is running or already finished, an exception
+ is raised.
+ """
if loop is None:
loop = asyncio.get_event_loop()
fut = asyncio.Future(loop=loop)
- if isinstance(gt, eventlet.greenthread.GreenThread):
- if loop.get_debug() and gt:
- logger.warning("wrap_greenthread() called on "
- "a running greenthread")
+ if not isinstance(gt, greenlet.greenlet):
+ raise TypeError("greenthread or greenlet request, not %s"
+ % type(gt))
- def copy_result(gt):
+ if gt:
+ raise RuntimeError("wrap_greenthread: the greenthread is running")
+ if gt.dead:
+ raise RuntimeError("wrap_greenthread: the greenthread already finished")
+
+ if isinstance(gt, eventlet.greenthread.GreenThread):
+ orig_main = gt.run
+ def wrap_func(*args, **kw):
try:
- result = gt.wait()
+ orig_main(*args, **kw)
except Exception as exc:
loop.call_soon(fut.set_exception, exc)
else:
+ result = gt.wait()
loop.call_soon(fut.set_result, result)
-
- gt.link(copy_result)
- elif isinstance(gt, greenlet.greenlet):
- if gt:
- raise RuntimeError("cannot wrap a running greenlet")
- if gt.dead:
- raise RuntimeError("cannot wrap a greenlet which already finished")
-
+ gt.run = wrap_func
+ else:
orig_func = gt.run
def wrap_func(*args, **kw):
try:
@@ -274,9 +278,6 @@ def wrap_greenthread(gt, loop=None):
else:
loop.call_soon(fut.set_result, result)
gt.run = wrap_func
- else:
- raise TypeError("greenthread or greenlet request, not %s"
- % type(gt))
return fut
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 5853cce..41343a2 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -4,7 +4,9 @@ Changelog
Version 0.3 (development version)
---------------------------------
-* :func:`wrap_greenthread` now also works on greenlet objects.
+* :func:`wrap_greenthread` now raises an exception if the greenthread is
+ running or already finished. In debug mode, the exception is not more logged
+ to sys.stderr for greenthreads.
2014-10-21: version 0.2
-----------------------
diff --git a/doc/status.rst b/doc/status.rst
index 7b0802d..439af37 100644
--- a/doc/status.rst
+++ b/doc/status.rst
@@ -1,8 +1,6 @@
To do
=====
-* wrap_greenthread() must not log the exception to sys.stderr if the
- greenthread didn't start
* register signals in eventlet hub, only needed for pyevent hub?
* port greenio examples to aiogreen
* write unit tests for, and maybe also examples for:
diff --git a/doc/using.rst b/doc/using.rst
index 8e62110..e647614 100644
--- a/doc/using.rst
+++ b/doc/using.rst
@@ -111,12 +111,13 @@ aiogreen specific functions:
raise Return(x + y)
def green_sum():
- task = asyncio.async(coro_slow_sum(1, 2))
-
loop.call_soon(progress)
+ task = asyncio.async(coro_slow_sum(1, 2))
+
value = aiogreen.link_future(task)
print("1 + 2 = %s" % value)
+
loop.stop()
asyncio.set_event_loop_policy(aiogreen.EventLoopPolicy())
@@ -138,11 +139,9 @@ aiogreen specific functions:
The Future object waits for the completion of a greenthread.
- The greenlet must not be running.
-
- In debug mode, if the greenthread raises an exception, the exception is
- logged to ``sys.stderr`` by eventlet, even if the exception is copied to the
- Future object.
+ The greenthread or greenlet must be wrapped before its execution starts.
+ If the greenthread or greenlet is running or already finished, an exception
+ is raised.
Example of trollius coroutine waiting for a greenthread. The ``progress()``
callback is called regulary to see that the event loop in not blocked::
@@ -162,11 +161,11 @@ aiogreen specific functions:
@asyncio.coroutine
def coro_sum():
- gt = eventlet.spawn(slow_sum, 1, 2)
-
loop.call_soon(progress)
+ gt = eventlet.spawn(slow_sum, 1, 2)
fut = aiogreen.wrap_greenthread(gt, loop=loop)
+
result = yield From(fut)
print("1 + 2 = %s" % result)
diff --git a/tests/test_eventlet.py b/tests/test_eventlet.py
index 07dde9d..80b3080 100644
--- a/tests/test_eventlet.py
+++ b/tests/test_eventlet.py
@@ -1,5 +1,6 @@
import aiogreen
import eventlet
+import sys
import tests
from tests import unittest
@@ -169,6 +170,21 @@ class EventletTests(tests.TestCase):
self.loop.run_forever()
self.assertEqual(result, [1, 10, 2, 20, 'error', 4])
+ def test_set_debug(self):
+ hub = eventlet.hubs.get_hub()
+ self.assertIs(self.loop._hub, hub)
+
+ self.loop.set_debug(False)
+ self.assertEqual(hub.debug_exceptions, False)
+ self.assertEqual(hub.debug_blocking, False)
+
+ self.loop.set_debug(True)
+ self.assertEqual(hub.debug_exceptions, True)
+ if sys.platform != 'win32':
+ self.assertEqual(hub.debug_blocking, True)
+ else:
+ self.assertEqual(hub.debug_blocking, False)
+
class WrapGreenthreadTests(tests.TestCase):
def test_wrap_greenthread(self):
@@ -181,31 +197,29 @@ class WrapGreenthreadTests(tests.TestCase):
result = self.loop.run_until_complete(fut)
self.assertEqual(result, 'ok')
- def test_wrap_greenthread_running(self):
- event = eventlet.event.Event()
+ def test_wrap_greenthread_exc(self):
+ self.loop.set_debug(True)
def func():
- return aiogreen.wrap_greenthread(gt)
+ raise ValueError(7)
- self.loop.set_debug(False)
- gt = eventlet.spawn(func)
- fut1 = aiogreen.wrap_greenthread(gt)
- fut2 = self.loop.run_until_complete(fut1)
- fut3 = self.loop.run_until_complete(fut2)
- self.assertIs(fut3, fut2)
+ # FIXME: the unit test must fail!?
+ with tests.mock.patch('traceback.print_exception') as print_exception:
+ gt = eventlet.spawn(func)
+ fut = aiogreen.wrap_greenthread(gt)
+ self.assertRaises(ValueError, self.loop.run_until_complete, fut)
+
+ # the exception must not be logger by traceback: the caller must
+ # consume the exception from the future object
+ self.assertFalse(print_exception.called)
- @tests.mock.patch('aiogreen.logger')
- def test_wrap_greenthread_running_log(self, m_log):
+ def test_wrap_greenthread_running(self):
def func():
return aiogreen.wrap_greenthread(gt)
- self.loop.set_debug(True)
+ self.loop.set_debug(False)
gt = eventlet.spawn(func)
- fut1 = aiogreen.wrap_greenthread(gt)
- fut2 = self.loop.run_until_complete(fut1)
- m_log.warning.assert_called_with("wrap_greenthread() called on "
- "a running greenthread")
-
+ self.assertRaises(RuntimeError, gt.wait)
def test_wrap_greenthread_dead(self):
def func():
@@ -215,9 +229,7 @@ class WrapGreenthreadTests(tests.TestCase):
result = gt.wait()
self.assertEqual(result, 'ok')
- fut = aiogreen.wrap_greenthread(gt)
- result = self.loop.run_until_complete(fut)
- self.assertEqual(result, 'ok')
+ self.assertRaises(RuntimeError, aiogreen.wrap_greenthread, gt)
def test_coro_wrap_greenthread(self):
result = self.loop.run_until_complete(coro_wrap_greenthread())
@@ -241,11 +253,22 @@ class WrapGreenletTests(tests.TestCase):
def func():
eventlet.sleep(0.010)
return "ok"
+
gt = eventlet.spawn_n(func)
fut = aiogreen.wrap_greenthread(gt)
result = self.loop.run_until_complete(fut)
self.assertEqual(result, "ok")
+ def test_wrap_greenlet_exc(self):
+ self.loop.set_debug(True)
+
+ def func():
+ raise ValueError(7)
+
+ gt = eventlet.spawn_n(func)
+ fut = aiogreen.wrap_greenthread(gt)
+ self.assertRaises(ValueError, self.loop.run_until_complete, fut)
+
def test_wrap_greenlet_running(self):
event = eventlet.event.Event()
@@ -265,9 +288,9 @@ class WrapGreenletTests(tests.TestCase):
event = eventlet.event.Event()
def func():
event.send('done')
+
gt = eventlet.spawn_n(func)
event.wait()
-
self.assertRaises(RuntimeError, aiogreen.wrap_greenthread, gt)