From b75380f3336db6dae82e8f75a2a5b020ae8fd377 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Jun 2014 14:39:11 +0200 Subject: 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() --- Lib/asyncio/base_events.py | 4 +-- Lib/asyncio/coroutines.py | 57 +++++++++++++++++++++++++++++++------ Lib/test/test_asyncio/test_tasks.py | 1 + 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: -- cgit v0.12