async_helpers.py
96 lines
| 2.4 KiB
| text/x-python
|
PythonLexer
Matthias Bussonnier
|
r24463 | """ | ||
Async helper function that are invalid syntax on Python 3.5 and below. | ||||
Known limitation and possible improvement. | ||||
Top level code that contain a return statement (instead of, or in addition to | ||||
await) will be detected as requiring being wrapped in async calls. This should | ||||
be prevented as early return will not work. | ||||
""" | ||||
import ast | ||||
import sys | ||||
import inspect | ||||
from textwrap import dedent, indent | ||||
from types import CodeType | ||||
def _asyncio_runner(coro): | ||||
""" | ||||
Handler for asyncio autoawait | ||||
""" | ||||
import asyncio | ||||
Matthias Bussonnier
|
r24474 | |||
Matthias Bussonnier
|
r24463 | return asyncio.get_event_loop().run_until_complete(coro) | ||
def _curio_runner(coroutine): | ||||
""" | ||||
handler for curio autoawait | ||||
""" | ||||
import curio | ||||
Matthias Bussonnier
|
r24474 | |||
Matthias Bussonnier
|
r24463 | return curio.run(coroutine) | ||
if sys.version_info > (3, 5): | ||||
# nose refuses to avoid this file and async def is invalidsyntax | ||||
Matthias Bussonnier
|
r24474 | s = dedent( | ||
''' | ||||
Matthias Bussonnier
|
r24463 | def _trio_runner(function): | ||
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, function) | ||||
Matthias Bussonnier
|
r24474 | ''' | ||
) | ||||
Matthias Bussonnier
|
r24463 | exec(s, globals(), locals()) | ||
def _asyncify(code: str) -> str: | ||||
"""wrap code in async def definition. | ||||
And setup a bit of context to run it later. | ||||
""" | ||||
Matthias Bussonnier
|
r24474 | res = dedent( | ||
""" | ||||
Matthias Bussonnier
|
r24463 | async def __wrapper__(): | ||
try: | ||||
{usercode} | ||||
finally: | ||||
locals() | ||||
Matthias Bussonnier
|
r24474 | """ | ||
).format(usercode=indent(code, " " * 8)[8:]) | ||||
Matthias Bussonnier
|
r24463 | return res | ||
def _should_be_async(cell: str) -> bool: | ||||
"""Detect if a block of code need to be wrapped in an `async def` | ||||
Attempt to parse the block of code, it it compile we're fine. | ||||
Otherwise we wrap if and try to compile. | ||||
If it works, assume it should be async. Otherwise Return False. | ||||
Not handled yet: If the block of code has a return statement as the top | ||||
level, it will be seen as async. This is a know limitation. | ||||
""" | ||||
try: | ||||
Matthias Bussonnier
|
r24468 | # we can't limit ourself to ast.parse, as it __accepts__ to parse on | ||
# 3.7+, but just does not _compile_ | ||||
Matthias Bussonnier
|
r24474 | compile(cell, "<>", "exec") | ||
Matthias Bussonnier
|
r24463 | return False | ||
except SyntaxError: | ||||
try: | ||||
ast.parse(_asyncify(cell)) | ||||
Matthias Bussonnier
|
r24475 | # TODO verify ast has not "top level" return or yield. | ||
Matthias Bussonnier
|
r24463 | except SyntaxError: | ||
return False | ||||
return True | ||||
return False | ||||