def test_async_def(self): # Test that types.coroutine passes 'async def' coroutines # without modification async def foo(): pass foo_code = foo.__code__ foo_flags = foo.__code__.co_flags decorated_foo = types.coroutine(foo) self.assertIs(foo, decorated_foo) self.assertEqual(foo.__code__.co_flags, foo_flags) self.assertIs(decorated_foo.__code__, foo_code) foo_coro = foo() def bar(): return foo_coro for _ in range(2): bar = types.coroutine(bar) coro = bar() self.assertIs(foo_coro, coro) self.assertEqual(coro.cr_code.co_flags, foo_flags) coro.close()
def test_convert_generator_to_coroutine(): def gen(): yield 100 assert not inspect.isawaitable(gen()) coroutine(gen) assert inspect.isawaitable(gen())
def test_wrong_args(self): class Foo: def __call__(self): pass def bar(): pass samples = [Foo, Foo(), bar, None, int, 1] for sample in samples: with self.assertRaisesRegex(TypeError, 'expects a generator'): types.coroutine(sample)
def test_wrong_args(self): class Foo: def __call__(self): pass def bar(): pass samples = [None, 1, object()] for sample in samples: with self.assertRaisesRegex(TypeError, 'types.coroutine.*expects a callable'): types.coroutine(sample)
def test_genfunc(self): def gen(): yield self.assertIs(types.coroutine(gen), gen) self.assertIs(types.coroutine(types.coroutine(gen)), gen) self.assertTrue(gen.__code__.co_flags & inspect.CO_ITERABLE_COROUTINE) self.assertFalse(gen.__code__.co_flags & inspect.CO_COROUTINE) g = gen() self.assertTrue(g.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE) self.assertFalse(g.gi_code.co_flags & inspect.CO_COROUTINE) self.assertIs(types.coroutine(gen), gen)
async def aiorun(): if not async_target: core = types.coroutine(threading.Thread(target=target, args=args)) await core.start() else: core = threading.Thread(target=target, args=args) core.start()
def test_gen(self): def gen_func(): yield 1 return (yield 2) gen = gen_func() @types.coroutine def foo(): return gen wrapper = foo() self.assertIsInstance(wrapper, types._GeneratorWrapper) self.assertIs(wrapper.__await__(), gen) for name in ('__name__', 'gi_code', 'gi_running', 'gi_frame'): self.assertIs(getattr(foo(), name), getattr(gen, name)) self.assertIs(foo().cr_code, gen.gi_code) self.assertEqual(next(wrapper), 1) self.assertEqual(wrapper.send(None), 2) with self.assertRaisesRegexp(StopIteration, 'spam'): wrapper.send('spam') gen = gen_func() wrapper = foo() wrapper.send(None) with self.assertRaisesRegexp(Exception, 'ham'): wrapper.throw(Exception, Exception('ham')) # decorate foo second time foo = types.coroutine(foo) self.assertIs(foo().__await__(), gen)
def __init__( self, coro: Callable[..., Awaitable[None]], args: Tuple[Any, ...], kwargs: Dict[str, Any], ) -> None: self.coro = types.coroutine(coro) self.args = args self.kwargs = kwargs
def coroutine(func): """Decorator to mark coroutines. If the coroutine is not yielded from before it is destroyed, an error message is logged. """ warnings.warn( '"@coroutine" decorator is deprecated since Python 3.8, use "async def" instead', DeprecationWarning, stacklevel=2) if inspect.iscoroutinefunction(func): # In Python 3.5 that's all we need to do for coroutines # defined with "async def". return func if inspect.isgeneratorfunction(func): coro = func else: @functools.wraps(func) def coro(*args, **kw): res = func(*args, **kw) if (base_futures.isfuture(res) or inspect.isgenerator(res) or isinstance(res, CoroWrapper)): res = yield from res else: # If 'res' is an awaitable, run it. try: await_meth = res.__await__ except AttributeError: pass else: if isinstance(res, collections.abc.Awaitable): res = yield from await_meth() return res coro = types.coroutine(coro) if not _DEBUG: wrapper = coro else: @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func=func) if w._source_traceback: del w._source_traceback[-1] # Python < 3.5 does not implement __qualname__ # on generator objects, so we set it manually. # We use getattr as some callables (such as # functools.partial may lack __qualname__). w.__name__ = getattr(func, '__name__', None) w.__qualname__ = getattr(func, '__qualname__', None) return w wrapper._is_coroutine = _is_coroutine # For iscoroutinefunction(). return wrapper
def add_route(app, fn): method = getattr(fn, '__method__', None) # 获取method path = getattr(fn, '__route__', None) # 获取路径 if path is None or method is None: raise ValueError('@get or @post not defined in %s.' % str(fn)) if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn): # 如果fn视图函数不是协程 # fn = asyncio.coroutine(fn) #python3.8弃用? fn = types.coroutine(fn) logging.info('add route %s %s => %s(%s)' % (method, path, fn.__name__, ', '.join(inspect.signature(fn).parameters.keys()))) app.router.add_route(method, path, RequestHandler(app, fn))
async def setup_redis(loop, stop_app): rd = redis.StrictRedis(decode_responses=True) ps = rd.pubsub(ignore_subscribe_messages=True) ps.subscribe('video:new') listen = types.coroutine(ps.listen) while not stop_app.is_set(): msg = ps.get_message() if msg: new_video_handler(msg) await asyncio.sleep(0.01)
def asyncfunction(fn): # Set the coroutine flag fn = types.coroutine(fn) # Then wrap it in an 'async def', to enable the "coroutine was not # awaited" warning @wraps(fn) async def wrapper(*args, **kwargs): return await fn(*args, **kwargs) return wrapper
def coroutine(func): """Decorator to mark coroutines. If the coroutine is not yielded from before it is destroyed, an error message is logged. """ is_coroutine = _iscoroutinefunction(func) if is_coroutine and _is_native_coro_code(func.__code__): # In Python 3.5 that's all we need to do for coroutines # defiend with "async def". # Wrapping in CoroWrapper will happen via # 'sys.set_coroutine_wrapper' function. return func if inspect.isgeneratorfunction(func): coro = func else: @functools.wraps(func) def coro(*args, **kw): res = func(*args, **kw) if isinstance(res, futures.Future) or inspect.isgenerator(res): res = yield from res elif AwaitableABC is not None: # If 'func' returns an Awaitable (new in 3.5) we # want to run it. try: await_meth = res.__await__ except AttributeError: pass else: if isinstance(res, AwaitableABC): res = yield from await_meth() return res if not _DEBUG: if native_coroutine_support: wrapper = types.coroutine(coro) else: wrapper = coro else: @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func=func) if w._source_traceback: del w._source_traceback[-1] # Python < 3.5 does not implement __qualname__ # on generator objects, so we set it manually. # We use getattr as some callables (such as # functools.partial may lack __qualname__). w.__name__ = getattr(func, '__name__', None) w.__qualname__ = getattr(func, '__qualname__', None) return w wrapper._is_coroutine = True # For iscoroutinefunction(). return wrapper
def m_make_coroutine_wrapper(func, replace_callback=True): if hasattr(types, 'm_coroutine'): func = types.coroutine(func) # ipdb.set_trace() def wrapper(*args, **kwargs): ipdb.set_trace() future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future(future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: # ipdb.set_trace() result = gen._value_from_stopiteration(e) except Exception: # ipdb.set_trace() future.set_exc_info(sys.exc_info()) return future else: # ipdb.set_trace() if isinstance(result, GeneratorType): try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception(stack_context.StackContextInconsistentError('stack_context inconsistency (probably caused')) except (StopIteration, Return) as e: future.set_result(gen._value_from_stopiteration(e)) except Exception: future.set_exc_info(sys.exc_info()) else: try: result.send(yielded.result()) except (StopIteration, Return) as e: ipdb.set_trace() future.set_result(gen._value_from_stopiteration(e)) # gen.Runner(result, future, yielded) try: return future finally: future = None future.set_result(result) return future return wrapper
def coroutine(func): """Decorator to mark coroutines. If the coroutine is not yielded from before it is destroyed, an error message is logged. """ if inspect.iscoroutinefunction(func): # In Python 3.5 that's all we need to do for coroutines # defined with "async def". return func if inspect.isgeneratorfunction(func): coro = func else: @functools.wraps(func) def coro(*args, **kw): res = func(*args, **kw) if (base_futures.isfuture(res) or inspect.isgenerator(res) or isinstance(res, CoroWrapper)): res = yield from res else: # If 'res' is an awaitable, run it. try: await_meth = res.__await__ except AttributeError: pass else: if isinstance(res, collections.abc.Awaitable): res = yield from await_meth() return res coro = types.coroutine(coro) if not _DEBUG: wrapper = coro else: @functools.wraps(func) def wrapper(*args, **kwds): w = CoroWrapper(coro(*args, **kwds), func=func) if w._source_traceback: del w._source_traceback[-1] # Python < 3.5 does not implement __qualname__ # on generator objects, so we set it manually. # We use getattr as some callables (such as # functools.partial may lack __qualname__). w.__name__ = getattr(func, '__name__', None) w.__qualname__ = getattr(func, '__qualname__', None) return w wrapper._is_coroutine = _is_coroutine # For iscoroutinefunction(). return wrapper
def test_genfunc(self): def gen(): yield self.assertFalse(isinstance(gen(), collections.abc.Coroutine)) self.assertFalse(isinstance(gen(), collections.abc.Awaitable)) gen_code = gen.__code__ decorated_gen = types.coroutine(gen) self.assertIs(decorated_gen, gen) self.assertIsNot(decorated_gen.__code__, gen_code) decorated_gen2 = types.coroutine(decorated_gen) self.assertIs(decorated_gen2.__code__, decorated_gen.__code__) self.assertTrue(gen.__code__.co_flags & inspect.CO_ITERABLE_COROUTINE) self.assertFalse(gen.__code__.co_flags & inspect.CO_COROUTINE) g = gen() self.assertTrue(g.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE) self.assertFalse(g.gi_code.co_flags & inspect.CO_COROUTINE) self.assertTrue(isinstance(g, collections.abc.Coroutine)) self.assertTrue(isinstance(g, collections.abc.Awaitable)) g.close() # silence warning
def primed_coroutine(function: Callable[..., Generator]) -> Callable: """ Primes a coroutine at creation. :param function: A coroutine function. :return: The coroutine function wrapped to prime the coroutine at creation. """ function = types.coroutine(function) def inner(*args: Any, **kwargs: Any) -> Generator: output = function(*args, **kwargs) next(output) return output return inner
def test_returning_itercoro(self): @types.coroutine def gen(): yield gencoro = gen() @types.coroutine def foo(): return gencoro self.assertIs(foo(), gencoro) # decorate foo second time foo = types.coroutine(foo) self.assertIs(foo(), gencoro)
def add_route(app, fn): method = getattr(fn, '__method__', None) path = getattr(fn, '__route__', None) if path is None or method is None: raise ValueError('@get or @post not defined in %s.' % str(fn)) # asyncio.coroutine已经不支持了,这里删除好像对代码没有影响 # 尝试了自定义一个toCoroutine,在其中async def,但是会造成runtime error: index was never awaited # 最终尝试types.coroutine,这个在官方文档中也能看见是把一个生成器函数转换为一个协程函数 # if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn): # fn = asyncio.coroutine(fn) if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn): fn = types.coroutine(fn) logging.info('add route %s %s => %s(%s)' % (method, path, fn.__name__, ', '.join(inspect.signature(fn).parameters.keys()))) app.router.add_route(method, path, RequestHandler(app, fn))
def test_non_gen_values(self): @types.coroutine def foo(): return 'spam' self.assertEqual(foo(), 'spam') class Awaitable: def __await__(self): return () aw = Awaitable() @types.coroutine def foo(): return aw self.assertIs(aw, foo()) # decorate foo second time foo = types.coroutine(foo) self.assertIs(aw, foo())
def test_genfunc(self): def gen(): yield self.assertFalse(isinstance(gen(), collections.abc.Coroutine)) self.assertFalse(isinstance(gen(), collections.abc.Awaitable)) self.assertIs(types.coroutine(gen), gen) self.assertTrue(gen.__code__.co_flags & inspect.CO_ITERABLE_COROUTINE) self.assertFalse(gen.__code__.co_flags & inspect.CO_COROUTINE) g = gen() self.assertTrue(g.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE) self.assertFalse(g.gi_code.co_flags & inspect.CO_COROUTINE) self.assertTrue(isinstance(g, collections.abc.Coroutine)) self.assertTrue(isinstance(g, collections.abc.Awaitable)) g.close() # silence warning
def test_generator_coroutine(self): module = self.import_extension('test_gen', [('is_coroutine', 'METH_O', ''' if (!PyGen_CheckExact(args)) Py_RETURN_NONE; PyObject* co = ((PyGenObject*)args)->gi_code; if (((PyCodeObject*)co)->co_flags & CO_ITERABLE_COROUTINE) Py_RETURN_TRUE; else Py_RETURN_FALSE; ''')]) def it(): yield 42 print(module.is_coroutine(it())) assert module.is_coroutine(it()) is False self.debug_collect() # don't crash while deallocating from types import coroutine assert module.is_coroutine(coroutine(it)()) is True
def coroutine(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]: """Decorator to apply to methods to convert them to ESPHome coroutines.""" if getattr(func, "_esphome_coroutine", False): # If func is already a coroutine, do not re-wrap it (performance) return func if inspect.isasyncgenfunction(func): # Trade-off: In ESPHome, there's not really a use-case for async generators. # and during the transition to new-style syntax it will happen that a `yield` # is not replaced properly, so don't accept async generators. raise ValueError( f"Async generator functions are not allowed. " f"Please check whether you've replaced all yields with awaits/returns. " f"See {func} in {func.__module__}" ) if inspect.iscoroutinefunction(func): # A new-style async-def coroutine function, no conversion needed. return func if inspect.isgeneratorfunction(func): @functools.wraps(func) def coro(*args, **kwargs): gen = func(*args, **kwargs) ret = yield from _flatten_generator(gen) return ret else: # A "normal" function with no `yield` statements, convert to generator # that includes a yield just so it's also a generator function @functools.wraps(func) def coro(*args, **kwargs): res = func(*args, **kwargs) yield return res # Add coroutine internal python flag so that it can be awaited from new-style coroutines. coro = types.coroutine(coro) # pylint: disable=protected-access coro._esphome_coroutine = True return coro
def futurize(func: PromiseCoroutineFunction[T], *, spawn=True) -> Callable[..., AbstractFuture[T]]: """ Makes this coroutine a function that returns a Future instead of a coroutine-object. :param func: The function to convert. :param spawn: If true, this function will spawn a new thread to execute the functions. :return: The function that returns a future. """ func = coroutine(func) async def wrapped(coro): if spawn: await sleep(0) return await coro @functools.wraps(func) def _wrapper(*args, **kwargs) -> Callable[..., AbstractFuture[T]]: c = func(*args, **kwargs) return run_coroutine(wrapped(c)) return _wrapper
def _make_coroutine_wrapper(func, replace_callback): """The inner workings of ``@gen.coroutine`` and ``@gen.engine``. The two decorators differ in their treatment of the ``callback`` argument, so we cannot simply implement ``@engine`` in terms of ``@coroutine``. """ # On Python 3.5, set the coroutine flag on our generator, to allow it # to be used with 'await'. if hasattr(types, 'coroutine'): func = types.coroutine(func) @functools.wraps(func) def wrapper(*args, **kwargs): # 这个 future 对象是用来存储 coroutine 执行完毕后的结果的。结果通常有 # 两种来源,一个是在 coroutine 中 yield Return 对象,另一种则是 # coroutine 作为 generator 执行完毕后 raise # StopIteration,并有结果数据存储在 StopIteration 这个异常对象中。 future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = _value_from_stopiteration(e) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is \ not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)') ) except (StopIteration, Return) as e: future.set_result(_value_from_stopiteration(e)) except Exception: future.set_exc_info(sys.exc_info()) else: # Runner 负责执行一个 coroutine,如果 coroutine 中的异步 # 操作已经完成,则 future 会被填充该操作的结果,否则 future # 会继续处于等待结果的状态,并紧接着在下一行代码返回给 # coroutine 的调用者。 Runner(result, future, yielded) try: return future finally: # Subtle memory optimization: if next() raised an # exception, the future's exc_info contains a traceback # which includes this stack frame. This creates a cycle, # which will be collected at the next full GC but has # been shown to greatly increase memory usage of # benchmarks (relative to the refcount-based scheme # used in the absence of cycles). We can avoid the # cycle by clearing the local variable after we return it. future = None future.set_result(result) return future return wrapper
def _make_coroutine_wrapper(func, replace_callback): """The inner workings of ``@gen.coroutine`` and ``@gen.engine``. The two decorators differ in their treatment of the ``callback`` argument, so we cannot simply implement ``@engine`` in terms of ``@coroutine``. """ # On Python 3.5, set the coroutine flag on our generator, to allow it # to be used with 'await'. wrapped = func if hasattr(types, 'coroutine'): func = types.coroutine(func) @functools.wraps(wrapped) def wrapper(*args, **kwargs): future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = _value_from_stopiteration(e) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)')) except (StopIteration, Return) as e: future.set_result(_value_from_stopiteration(e)) except Exception: future.set_exc_info(sys.exc_info()) else: _futures_to_runners[future] = Runner(result, future, yielded) try: return future finally: # Subtle memory optimization: if next() raised an exception, # the future's exc_info contains a traceback which # includes this stack frame. This creates a cycle, # which will be collected at the next full GC but has # been shown to greatly increase memory usage of # benchmarks (relative to the refcount-based scheme # used in the absence of cycles). We can avoid the # cycle by clearing the local variable after we return it. future = None future.set_result(result) return future wrapper.__wrapped__ = wrapped wrapper.__tornado_coroutine__ = True return wrapper
def _make_coroutine_wrapper(func, replace_callback): """The inner workings of ``@gen.coroutine`` and ``@gen.engine``. The two decorators differ in their treatment of the ``callback`` argument, so we cannot simply implement ``@engine`` in terms of ``@coroutine``. """ # On Python 3.5, set the coroutine flag on our generator, to allow it # to be used with 'await'. wrapped = func if hasattr(types, 'coroutine'): func = types.coroutine(func) @functools.wraps(wrapped) def wrapper(*args, **kwargs): future = _create_future() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = _value_from_stopiteration(e) except Exception: future_set_exc_info(future, sys.exc_info()) try: return future finally: # Avoid circular references future = None else: if isinstance(result, GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = _create_future() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)') ) except (StopIteration, Return) as e: future_set_result_unless_cancelled( future, _value_from_stopiteration(e)) except Exception: future_set_exc_info(future, sys.exc_info()) else: _futures_to_runners[future] = Runner( result, future, yielded) yielded = None try: return future finally: # Subtle memory optimization: if next() raised an exception, # the future's exc_info contains a traceback which # includes this stack frame. This creates a cycle, # which will be collected at the next full GC but has # been shown to greatly increase memory usage of # benchmarks (relative to the refcount-based scheme # used in the absence of cycles). We can avoid the # cycle by clearing the local variable after we return it. future = None future_set_result_unless_cancelled(future, result) return future wrapper.__wrapped__ = wrapped wrapper.__tornado_coroutine__ = True return wrapper
def generator_hook(f): return functools.wraps(f)(coroutine_hook(types.coroutine(f)))
def test_wrong_args(self): samples = [None, 1, object()] for sample in samples: with self.assertRaisesRegexp(TypeError, 'types.coroutine.*expects a callable'): types.coroutine(sample)
def update_event(self, inp=-1): self.set_output_val(0, types.coroutine(self.input(0)))
def test_wrong_args(self): samples = [None, 1, object()] for sample in samples: with self.assertRaisesRegexp( TypeError, 'types.coroutine.*expects a callable'): types.coroutine(sample)