1from prompt_toolkit.validation import ValidationError, Validator 2 3from .utils import unindent_code 4 5__all__ = ["PythonValidator"] 6 7 8class PythonValidator(Validator): 9 """ 10 Validation of Python input. 11 12 :param get_compiler_flags: Callable that returns the currently 13 active compiler flags. 14 """ 15 16 def __init__(self, get_compiler_flags=None): 17 self.get_compiler_flags = get_compiler_flags 18 19 def validate(self, document): 20 """ 21 Check input for Python syntax errors. 22 """ 23 text = unindent_code(document.text) 24 25 # When the input starts with Ctrl-Z, always accept. This means EOF in a 26 # Python REPL. 27 if text.startswith("\x1a"): 28 return 29 30 # When the input starts with an exclamation mark. Accept as shell 31 # command. 32 if text.lstrip().startswith("!"): 33 return 34 35 try: 36 if self.get_compiler_flags: 37 flags = self.get_compiler_flags() 38 else: 39 flags = 0 40 41 compile(text, "<input>", "exec", flags=flags, dont_inherit=True) 42 except SyntaxError as e: 43 # Note, the 'or 1' for offset is required because Python 2.7 44 # gives `None` as offset in case of '4=4' as input. (Looks like 45 # fixed in Python 3.) 46 # TODO: This is not correct if indentation was removed. 47 index = document.translate_row_col_to_index( 48 e.lineno - 1, (e.offset or 1) - 1 49 ) 50 raise ValidationError(index, f"Syntax Error: {e}") 51 except TypeError as e: 52 # e.g. "compile() expected string without null bytes" 53 raise ValidationError(0, str(e)) 54 except ValueError as e: 55 # In Python 2, compiling "\x9" (an invalid escape sequence) raises 56 # ValueError instead of SyntaxError. 57 raise ValidationError(0, "Syntax Error: %s" % e) 58