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)
| 93 | |
| 94 | |
| 95 | class _AsyncSyntaxErrorVisitor(ast.NodeVisitor): |
| 96 | """ |
| 97 | Find syntax errors that would be an error in an async repl, but because |
| 98 | the implementation involves wrapping the repl in an async function, it |
| 99 | is erroneously allowed (e.g. yield or return at the top level) |
| 100 | """ |
| 101 | def __init__(self): |
| 102 | if sys.version_info >= (3,8): |
| 103 | raise ValueError('DEPRECATED in Python 3.8+') |
| 104 | self.depth = 0 |
| 105 | super().__init__() |
| 106 | |
| 107 | def generic_visit(self, node): |
| 108 | func_types = (ast.FunctionDef, ast.AsyncFunctionDef) |
| 109 | invalid_types_by_depth = { |
| 110 | 0: (ast.Return, ast.Yield, ast.YieldFrom), |
| 111 | 1: (ast.Nonlocal,) |
| 112 | } |
| 113 | |
| 114 | should_traverse = self.depth < max(invalid_types_by_depth.keys()) |
| 115 | if isinstance(node, func_types) and should_traverse: |
| 116 | self.depth += 1 |
| 117 | super().generic_visit(node) |
| 118 | self.depth -= 1 |
| 119 | elif isinstance(node, invalid_types_by_depth[self.depth]): |
| 120 | raise SyntaxError() |
| 121 | else: |
| 122 | super().generic_visit(node) |
| 123 | |
| 124 | |
| 125 | def _async_parse_cell(cell: str) -> ast.AST: |