summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-12-15 17:05:35 +0100
committerVictor Stinner <victor.stinner@gmail.com>2014-12-15 17:05:35 +0100
commitca26b61ac28cfb49252a6103c1196dc364ca3625 (patch)
treef35feb45ab1e939cce77c772f8c8e17b499661f1
parente0fff7b987182b5a5d063441ac039f8075765b00 (diff)
downloadtrollius-pep479.tar.gz
Support the PEP 479pep479
-rw-r--r--doc/changelog.rst3
-rw-r--r--tests/test_tasks.py2
-rw-r--r--trollius/coroutines.py111
-rw-r--r--trollius/tasks.py7
4 files changed, 82 insertions, 41 deletions
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 51d4d2e..9679a71 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -14,6 +14,9 @@ Changes:
functions. Patch by Donald Stufft.
* Add run_aiotest.py: run the aiotest test suite.
* tox now also run the aiotest test suite
+* On Python 3.3 and newer, Return now also logs an error when used without
+ raise.
+* Support Python 3.5 with the PEP 479.
Bugfixes:
diff --git a/tests/test_tasks.py b/tests/test_tasks.py
index e1a8a27..6d04c4f 100644
--- a/tests/test_tasks.py
+++ b/tests/test_tasks.py
@@ -1585,7 +1585,7 @@ class TaskTests(test_utils.TestCase):
cw.send(None)
try:
cw.send(arg)
- except StopIteration as ex:
+ except Return as ex:
ex.raised = True
return ex.value
else:
diff --git a/trollius/coroutines.py b/trollius/coroutines.py
index 650bc13..81a44f6 100644
--- a/trollius/coroutines.py
+++ b/trollius/coroutines.py
@@ -6,6 +6,7 @@ import inspect
import opcode
import os
import sys
+import textwrap
import traceback
import types
@@ -59,51 +60,34 @@ else:
_YIELD_FROM_BUG = False
-if compat.PY33:
- # Don't use the Return class on Python 3.3 and later to support asyncio
- # coroutines (to avoid the warning emited in Return destructor).
- #
- # The problem is that Return inherits from StopIteration. "yield from
- # trollius_coroutine". Task._step() does not receive the Return exception,
- # because "yield from" handles it internally. So it's not possible to set
- # the raised attribute to True to avoid the warning in Return destructor.
- def Return(*args):
+class Return(Exception):
+ def __init__(self, *args):
+ Exception.__init__(self)
if not args:
- value = None
+ self.value = None
elif len(args) == 1:
- value = args[0]
+ self.value = args[0]
else:
- value = args
- return StopIteration(value)
-else:
- class Return(StopIteration):
- def __init__(self, *args):
- StopIteration.__init__(self)
- if not args:
- self.value = None
- elif len(args) == 1:
- self.value = args[0]
- else:
- self.value = args
- self.raised = False
- if _DEBUG:
- frame = sys._getframe(1)
- self._source_traceback = traceback.extract_stack(frame)
- # explicitly clear the reference to avoid reference cycles
- frame = None
- else:
- self._source_traceback = None
+ self.value = args
+ self.raised = False
+ if _DEBUG:
+ frame = sys._getframe(1)
+ self._source_traceback = traceback.extract_stack(frame)
+ # explicitly clear the reference to avoid reference cycles
+ frame = None
+ else:
+ self._source_traceback = None
- def __del__(self):
- if self.raised:
- return
+ def __del__(self):
+ if self.raised:
+ return
- fmt = 'Return(%r) used without raise'
- if self._source_traceback:
- fmt += '\nReturn created at (most recent call last):\n'
- tb = ''.join(traceback.format_list(self._source_traceback))
- fmt += tb.rstrip()
- logger.error(fmt, self.value)
+ fmt = 'Return(%r) used without raise'
+ if self._source_traceback:
+ fmt += '\nReturn created at (most recent call last):\n'
+ tb = ''.join(traceback.format_list(self._source_traceback))
+ fmt += tb.rstrip()
+ logger.error(fmt, self.value)
def _coroutine_at_yield_from(coro):
@@ -245,6 +229,48 @@ if not compat.PY34:
else:
_wraps = functools.wraps
+_PEP479 = (sys.version_info >= (3,))
+if _PEP479:
+ exec(textwrap.dedent('''
+ def pep479_wrapper(func, coro_func):
+ @_wraps(func)
+ def pep479_wrapped(*args, **kw):
+ coro = coro_func(*args, **kw)
+ value = None
+ error = None
+ while True:
+ try:
+ if error is not None:
+ value = coro.throw(error)
+ elif value is not None:
+ value = coro.send(value)
+ else:
+ value = next(coro)
+ except RuntimeError as exc:
+ if isinstance(exc.__context__, StopIteration):
+ # a trollius coroutine uses "raise Return"
+ raise
+ return exc.__context__.value
+ else:
+ raise
+ except StopIteration as exc:
+ return exc.value
+ except Return as exc:
+ exc.raised = True
+ return exc.value
+ except BaseException as exc:
+ raise
+
+ try:
+ value = yield value
+ error = None
+ except BaseException as exc:
+ value = None
+ error = exc
+ return pep479_wrapped
+ '''))
+
+
def coroutine(func):
"""Decorator to mark coroutines.
@@ -262,6 +288,11 @@ def coroutine(func):
res = yield From(res)
raise Return(res)
+ if _PEP479:
+ # FIXME: use @_wraps
+ coro = pep479_wrapper(func, coro)
+ coro = _wraps(func)(coro)
+
if not _DEBUG:
wrapper = coro
else:
diff --git a/trollius/tasks.py b/trollius/tasks.py
index 4d91de8..ec34f58 100644
--- a/trollius/tasks.py
+++ b/trollius/tasks.py
@@ -253,6 +253,13 @@ class Task(futures.Future):
result = coro.send(value)
else:
result = next(coro)
+ except coroutines.Return as exc:
+ if isinstance(exc, Return):
+ exc.raised = True
+ result = exc.value
+ else:
+ result = None
+ self.set_result(result)
except StopIteration as exc:
if compat.PY33:
# asyncio Task object? get the result of the coroutine