summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-06-30 12:39:11 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2014-06-30 12:39:11 (GMT)
commitb75380f3336db6dae82e8f75a2a5b020ae8fd377 (patch)
tree8779c580d7fcc1e10467da07586d87e2116c6b58 /Lib
parenta6ec5ee3c8fcc7ca28db737a0ad60d4d63b116c0 (diff)
downloadcpython-b75380f3336db6dae82e8f75a2a5b020ae8fd377.zip
cpython-b75380f3336db6dae82e8f75a2a5b020ae8fd377.tar.gz
cpython-b75380f3336db6dae82e8f75a2a5b020ae8fd377.tar.bz2
asyncio: sync with Tulip
- Sort imports - Simplify/optimize iscoroutine(). Inline inspect.isgenerator(obj): replace it with isinstance(obj, types.GeneratorType) - CoroWrapper: check at runtime if Python has the yield-from bug #21209. If Python has the bug, check if CoroWrapper.send() was called by yield-from to decide if parameters must be unpacked or not. - Fix "Task was destroyed but it is pending!" warning in test_task_source_traceback()
Diffstat (limited to 'Lib')
-rw-r--r--Lib/asyncio/base_events.py4
-rw-r--r--Lib/asyncio/coroutines.py57
-rw-r--r--Lib/test/test_asyncio/test_tasks.py1
3 files changed, 52 insertions, 10 deletions
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index c42e7f9..b3d6e03 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -19,11 +19,11 @@ import concurrent.futures
import heapq
import inspect
import logging
+import os
import socket
import subprocess
-import traceback
import time
-import os
+import traceback
import sys
from . import coroutines
diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py
index 5b6d93f..cdb1ea8 100644
--- a/Lib/asyncio/coroutines.py
+++ b/Lib/asyncio/coroutines.py
@@ -3,14 +3,20 @@ __all__ = ['coroutine',
import functools
import inspect
+import opcode
import os
import sys
import traceback
+import types
from . import events
from . import futures
from .log import logger
+
+# Opcode of "yield from" instruction
+_YIELD_FROM = opcode.opmap['YIELD_FROM']
+
# If you set _DEBUG to true, @coroutine will wrap the resulting
# generator objects in a CoroWrapper instance (defined below). That
# instance will log a message when the generator is never iterated
@@ -25,6 +31,31 @@ _DEBUG = (not sys.flags.ignore_environment
_PY35 = (sys.version_info >= (3, 5))
+
+# Check for CPython issue #21209
+def has_yield_from_bug():
+ class MyGen:
+ def __init__(self):
+ self.send_args = None
+ def __iter__(self):
+ return self
+ def __next__(self):
+ return 42
+ def send(self, *what):
+ self.send_args = what
+ return None
+ def yield_from_gen(gen):
+ yield from gen
+ value = (1, 2, 3)
+ gen = MyGen()
+ coro = yield_from_gen(gen)
+ next(coro)
+ coro.send(value)
+ return gen.send_args != (value,)
+_YIELD_FROM_BUG = has_yield_from_bug()
+del has_yield_from_bug
+
+
class CoroWrapper:
# Wrapper for coroutine in _DEBUG mode.
@@ -40,13 +71,21 @@ class CoroWrapper:
def __next__(self):
return next(self.gen)
- def send(self, *value):
- # We use `*value` because of a bug in CPythons prior
- # to 3.4.1. See issue #21209 and test_yield_from_corowrapper
- # for details. This workaround should be removed in 3.5.0.
- if len(value) == 1:
- value = value[0]
- return self.gen.send(value)
+ if _YIELD_FROM_BUG:
+ # For for CPython issue #21209: using "yield from" and a custom
+ # generator, generator.send(tuple) unpacks the tuple instead of passing
+ # the tuple unchanged. Check if the caller is a generator using "yield
+ # from" to decide if the parameter should be unpacked or not.
+ def send(self, *value):
+ frame = sys._getframe()
+ caller = frame.f_back
+ assert caller.f_lasti >= 0
+ if caller.f_code.co_code[caller.f_lasti] != _YIELD_FROM:
+ value = value[0]
+ return self.gen.send(value)
+ else:
+ def send(self, value):
+ return self.gen.send(value)
def throw(self, exc):
return self.gen.throw(exc)
@@ -119,9 +158,11 @@ def iscoroutinefunction(func):
return getattr(func, '_is_coroutine', False)
+_COROUTINE_TYPES = (CoroWrapper, types.GeneratorType)
+
def iscoroutine(obj):
"""Return True if obj is a coroutine object."""
- return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj)
+ return isinstance(obj, _COROUTINE_TYPES)
def _format_coroutine(coro):
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index d509768..a5706ae 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -1621,6 +1621,7 @@ class TaskTests(test_utils.TestCase):
(__file__,
lineno,
'test_task_source_traceback'))
+ self.loop.run_until_complete(task)
class GatherTestsBase: