Commit b75380f3 authored by Victor Stinner's avatar Victor Stinner

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()
parent a6ec5ee3
...@@ -19,11 +19,11 @@ import concurrent.futures ...@@ -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', ...@@ -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 ...@@ -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: ...@@ -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): ...@@ -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): ...@@ -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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment