Spaces:
Runtime error
Runtime error
| """ | |
| Async helper function that are invalid syntax on Python 3.5 and below. | |
| This code is best effort, and may have edge cases not behaving as expected. In | |
| particular it contain a number of heuristics to detect whether code is | |
| effectively async and need to run in an event loop or not. | |
| Some constructs (like top-level `return`, or `yield`) are taken care of | |
| explicitly to actually raise a SyntaxError and stay as close as possible to | |
| Python semantics. | |
| """ | |
| import ast | |
| import asyncio | |
| import inspect | |
| from functools import wraps | |
| _asyncio_event_loop = None | |
| def get_asyncio_loop(): | |
| """asyncio has deprecated get_event_loop | |
| Replicate it here, with our desired semantics: | |
| - always returns a valid, not-closed loop | |
| - not thread-local like asyncio's, | |
| because we only want one loop for IPython | |
| - if called from inside a coroutine (e.g. in ipykernel), | |
| return the running loop | |
| .. versionadded:: 8.0 | |
| """ | |
| try: | |
| return asyncio.get_running_loop() | |
| except RuntimeError: | |
| # not inside a coroutine, | |
| # track our own global | |
| pass | |
| # not thread-local like asyncio's, | |
| # because we only track one event loop to run for IPython itself, | |
| # always in the main thread. | |
| global _asyncio_event_loop | |
| if _asyncio_event_loop is None or _asyncio_event_loop.is_closed(): | |
| _asyncio_event_loop = asyncio.new_event_loop() | |
| return _asyncio_event_loop | |
| class _AsyncIORunner: | |
| def __call__(self, coro): | |
| """ | |
| Handler for asyncio autoawait | |
| """ | |
| return get_asyncio_loop().run_until_complete(coro) | |
| def __str__(self): | |
| return "asyncio" | |
| _asyncio_runner = _AsyncIORunner() | |
| class _AsyncIOProxy: | |
| """Proxy-object for an asyncio | |
| Any coroutine methods will be wrapped in event_loop.run_ | |
| """ | |
| def __init__(self, obj, event_loop): | |
| self._obj = obj | |
| self._event_loop = event_loop | |
| def __repr__(self): | |
| return f"<_AsyncIOProxy({self._obj!r})>" | |
| def __getattr__(self, key): | |
| attr = getattr(self._obj, key) | |
| if inspect.iscoroutinefunction(attr): | |
| # if it's a coroutine method, | |
| # return a threadsafe wrapper onto the _current_ asyncio loop | |
| def _wrapped(*args, **kwargs): | |
| concurrent_future = asyncio.run_coroutine_threadsafe( | |
| attr(*args, **kwargs), self._event_loop | |
| ) | |
| return asyncio.wrap_future(concurrent_future) | |
| return _wrapped | |
| else: | |
| return attr | |
| def __dir__(self): | |
| return dir(self._obj) | |
| def _curio_runner(coroutine): | |
| """ | |
| handler for curio autoawait | |
| """ | |
| import curio | |
| return curio.run(coroutine) | |
| def _trio_runner(async_fn): | |
| import trio | |
| async def loc(coro): | |
| """ | |
| We need the dummy no-op async def to protect from | |
| trio's internal. See https://github.com/python-trio/trio/issues/89 | |
| """ | |
| return await coro | |
| return trio.run(loc, async_fn) | |
| def _pseudo_sync_runner(coro): | |
| """ | |
| A runner that does not really allow async execution, and just advance the coroutine. | |
| See discussion in https://github.com/python-trio/trio/issues/608, | |
| Credit to Nathaniel Smith | |
| """ | |
| try: | |
| coro.send(None) | |
| except StopIteration as exc: | |
| return exc.value | |
| else: | |
| # TODO: do not raise but return an execution result with the right info. | |
| raise RuntimeError( | |
| "{coro_name!r} needs a real async loop".format(coro_name=coro.__name__) | |
| ) | |
| def _should_be_async(cell: str) -> bool: | |
| """Detect if a block of code needs to be wrapped in an `async def` | |
| If the code block has a top-level return statement or is otherwise | |
| invalid, `False` will be returned. | |
| """ | |
| try: | |
| code = compile( | |
| cell, "<>", "exec", flags=getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0) | |
| ) | |
| return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE | |
| except (SyntaxError, ValueError, MemoryError): | |
| return False | |