mirror of https://github.com/python/cpython
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()
This commit is contained in:
commit
91dd20ba60
|
@ -19,11 +19,11 @@ import concurrent.futures
|
||||||
import heapq
|
import heapq
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
import traceback
|
|
||||||
import time
|
import time
|
||||||
import os
|
import traceback
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from . import coroutines
|
from . import coroutines
|
||||||
|
|
|
@ -3,14 +3,20 @@ __all__ = ['coroutine',
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
import opcode
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import types
|
||||||
|
|
||||||
from . import events
|
from . import events
|
||||||
from . import futures
|
from . import futures
|
||||||
from .log import logger
|
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
|
# If you set _DEBUG to true, @coroutine will wrap the resulting
|
||||||
# generator objects in a CoroWrapper instance (defined below). That
|
# generator objects in a CoroWrapper instance (defined below). That
|
||||||
# instance will log a message when the generator is never iterated
|
# 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))
|
_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:
|
class CoroWrapper:
|
||||||
# Wrapper for coroutine in _DEBUG mode.
|
# Wrapper for coroutine in _DEBUG mode.
|
||||||
|
|
||||||
|
@ -40,13 +71,21 @@ class CoroWrapper:
|
||||||
def __next__(self):
|
def __next__(self):
|
||||||
return next(self.gen)
|
return next(self.gen)
|
||||||
|
|
||||||
def send(self, *value):
|
if _YIELD_FROM_BUG:
|
||||||
# We use `*value` because of a bug in CPythons prior
|
# For for CPython issue #21209: using "yield from" and a custom
|
||||||
# to 3.4.1. See issue #21209 and test_yield_from_corowrapper
|
# generator, generator.send(tuple) unpacks the tuple instead of passing
|
||||||
# for details. This workaround should be removed in 3.5.0.
|
# the tuple unchanged. Check if the caller is a generator using "yield
|
||||||
if len(value) == 1:
|
# from" to decide if the parameter should be unpacked or not.
|
||||||
value = value[0]
|
def send(self, *value):
|
||||||
return self.gen.send(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):
|
def throw(self, exc):
|
||||||
return self.gen.throw(exc)
|
return self.gen.throw(exc)
|
||||||
|
@ -119,9 +158,11 @@ def iscoroutinefunction(func):
|
||||||
return getattr(func, '_is_coroutine', False)
|
return getattr(func, '_is_coroutine', False)
|
||||||
|
|
||||||
|
|
||||||
|
_COROUTINE_TYPES = (CoroWrapper, types.GeneratorType)
|
||||||
|
|
||||||
def iscoroutine(obj):
|
def iscoroutine(obj):
|
||||||
"""Return True if obj is a coroutine object."""
|
"""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):
|
def _format_coroutine(coro):
|
||||||
|
|
|
@ -1621,6 +1621,7 @@ class TaskTests(test_utils.TestCase):
|
||||||
(__file__,
|
(__file__,
|
||||||
lineno,
|
lineno,
|
||||||
'test_task_source_traceback'))
|
'test_task_source_traceback'))
|
||||||
|
self.loop.run_until_complete(task)
|
||||||
|
|
||||||
|
|
||||||
class GatherTestsBase:
|
class GatherTestsBase:
|
||||||
|
|
Loading…
Reference in New Issue