##// END OF EJS Templates
Merge pull request #12167 from foobarbyte/master...
Merge pull request #12167 from foobarbyte/master Set .py tempfile suffix when using vi/emacs editor shortcuts.

File last commit:

r25429:24cb49b3
r25570:b7beef6e merge
Show More
async_helpers.py
173 lines | 4.9 KiB | text/x-python | PythonLexer
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 """
Async helper function that are invalid syntax on Python 3.5 and below.
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 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.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 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.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 """
import ast
import sys
Matthias Bussonnier
workign async with
r25022 import inspect
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 from textwrap import dedent, indent
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 class _AsyncIORunner:
def __call__(self, coro):
"""
Handler for asyncio autoawait
"""
import asyncio
return asyncio.get_event_loop().run_until_complete(coro)
Matthias Bussonnier
reformat with black
r24474
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 def __str__(self):
return 'asyncio'
_asyncio_runner = _AsyncIORunner()
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
def _curio_runner(coroutine):
"""
handler for curio autoawait
"""
import curio
Matthias Bussonnier
reformat with black
r24474
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 return curio.run(coroutine)
Matthias Bussonnier
Add pseudo sync mode
r24481 def _trio_runner(async_fn):
Matthias Bussonnier
att triio runner at top level now that 3.4 drop + docs
r24480 import trio
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490
Matthias Bussonnier
att triio runner at top level now that 3.4 drop + docs
r24480 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
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490
Matthias Bussonnier
Add pseudo sync mode
r24481 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.
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 raise RuntimeError(
"{coro_name!r} needs a real async loop".format(coro_name=coro.__name__)
)
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
def _asyncify(code: str) -> str:
"""wrap code in async def definition.
And setup a bit of context to run it later.
"""
Matthias Bussonnier
reformat with black
r24474 res = dedent(
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 """
Paul Ganssle
Fix indentation problem with _asyncify...
r24487 async def __wrapper__():
try:
{usercode}
finally:
locals()
"""
).format(usercode=indent(code, " " * 8))
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 return res
Paul Ganssle
Improve async detection mechanism with blacklist...
r24485 class _AsyncSyntaxErrorVisitor(ast.NodeVisitor):
"""
Find syntax errors that would be an error in an async repl, but because
the implementation involves wrapping the repl in an async function, it
is erroneously allowed (e.g. yield or return at the top level)
"""
felixzhuologist
generalize to arbitrary errors by depth
r24665 def __init__(self):
Matthias Bussonnier
workign async with
r25022 if sys.version_info >= (3,8):
raise ValueError('DEPRECATED in Python 3.8+')
felixzhuologist
generalize to arbitrary errors by depth
r24665 self.depth = 0
felixzhuologist
check for nonlocal inside toplevel functions
r24664 super().__init__()
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490
Paul Ganssle
Improve async detection mechanism with blacklist...
r24485 def generic_visit(self, node):
func_types = (ast.FunctionDef, ast.AsyncFunctionDef)
felixzhuologist
generalize to arbitrary errors by depth
r24665 invalid_types_by_depth = {
0: (ast.Return, ast.Yield, ast.YieldFrom),
1: (ast.Nonlocal,)
}
should_traverse = self.depth < max(invalid_types_by_depth.keys())
if isinstance(node, func_types) and should_traverse:
self.depth += 1
felixzhuologist
check for nonlocal inside toplevel functions
r24664 super().generic_visit(node)
Matthias Bussonnier
Fix improper non-SyntaxError in return outside of function....
r24964 self.depth -= 1
felixzhuologist
generalize to arbitrary errors by depth
r24665 elif isinstance(node, invalid_types_by_depth[self.depth]):
Paul Ganssle
Improve async detection mechanism with blacklist...
r24485 raise SyntaxError()
else:
super().generic_visit(node)
felixzhuologist
generalize to arbitrary errors by depth
r24665
Paul Ganssle
Improve async detection mechanism with blacklist...
r24485 def _async_parse_cell(cell: str) -> ast.AST:
"""
This is a compatibility shim for pre-3.7 when async outside of a function
is a syntax error at the parse stage.
It will return an abstract syntax tree parsed as if async and await outside
of a function were not a syntax error.
"""
if sys.version_info < (3, 7):
# Prior to 3.7 you need to asyncify before parse
wrapped_parse_tree = ast.parse(_asyncify(cell))
return wrapped_parse_tree.body[0].body[0]
else:
return ast.parse(cell)
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 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.
yangyang
catch `MemoryError` in compiling
r25429 Not handled yet: If the block of code has a return statement as the top
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 level, it will be seen as async. This is a know limitation.
"""
Matthias Bussonnier
clenup a bit
r25052 if sys.version_info > (3, 8):
Matthias Bussonnier
some more fixes for 3.8
r25055 try:
code = compile(cell, "<>", "exec", flags=getattr(ast,'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0))
return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
yangyang
catch `MemoryError` in compiling
r25429 except (SyntaxError, MemoryError):
Matthias Bussonnier
some more fixes for 3.8
r25055 return False
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 try:
Matthias Bussonnier
fix runnign on 3.7
r24468 # we can't limit ourself to ast.parse, as it __accepts__ to parse on
# 3.7+, but just does not _compile_
Matthias Bussonnier
more work
r25024 code = compile(cell, "<>", "exec")
yangyang
catch `MemoryError` in compiling
r25429 except (SyntaxError, MemoryError):
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 try:
Paul Ganssle
Improve async detection mechanism with blacklist...
r24485 parse_tree = _async_parse_cell(cell)
# Raise a SyntaxError if there are top-level return or yields
v = _AsyncSyntaxErrorVisitor()
v.visit(parse_tree)
yangyang
catch `MemoryError` in compiling
r25429 except (SyntaxError, MemoryError):
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 return False
return True
return False