def __init__(self, line_input_checker=True, physical_line_transforms=None, logical_line_transforms=None, python_line_transforms=None): super(IPythonInputSplitter, self).__init__() self._buffer_raw = [] self._validate = True if physical_line_transforms is not None: self.physical_line_transforms = physical_line_transforms else: self.physical_line_transforms = [ leading_indent(), classic_prompt(), ipy_prompt(), strip_encoding_cookie(), cellmagic(end_on_blank_line=line_input_checker), ] self.assemble_logical_lines = assemble_logical_lines() if logical_line_transforms is not None: self.logical_line_transforms = logical_line_transforms else: self.logical_line_transforms = [ help_end(), escaped_commands(), assign_from_magic(), assign_from_system(), ] self.assemble_python_lines = assemble_python_lines() if python_line_transforms is not None: self.python_line_transforms = python_line_transforms else: # We don't use any of these at present self.python_line_transforms = []
def __init__(self, line_input_checker=True, physical_line_transforms=None, logical_line_transforms=None, python_line_transforms=None): super(IPythonInputSplitter, self).__init__() self._buffer_raw = [] self._validate = True if physical_line_transforms is not None: self.physical_line_transforms = physical_line_transforms else: self.physical_line_transforms = [ leading_indent(), classic_prompt(), ipy_prompt(), cellmagic(end_on_blank_line=line_input_checker), ] self.assemble_logical_lines = assemble_logical_lines() if logical_line_transforms is not None: self.logical_line_transforms = logical_line_transforms else: self.logical_line_transforms = [ help_end(), escaped_commands(), assign_from_magic(), assign_from_system(), ] self.assemble_python_lines = assemble_python_lines() if python_line_transforms is not None: self.python_line_transforms = python_line_transforms else: # We don't use any of these at present self.python_line_transforms = []
def generate_parser_func(line_parser): """ `IPython>=7.0.0`-specific implementation of `generate_parser_func`. Given a function that parses a single line of code, returns the full `davos` parser to be registered as an `IPython` input transformer. Parameters ---------- line_parser : callable Function that parses a single line of user code (typically, `davos.core.core.parse_line`). Returns ------- callable The `davos` parser for use as an `IPython` input transformer. Given user input consisting of one or more lines (e.g., a notebook cell), returns the input with all lines parsed. See Also -------- davos.core.core.parse_line : Single-line parser function. Notes ----- 1. In order to handle multiline `smuggle` statements, the `line_parser` function (`davos.core.core.parse_line`) works on "*logical*"/"*assembled*" lines, rather than "*physical*" lines. A logical line may consist of a single physical line, or multiple physical lines joined by explicit (i.e., backslash-based) or implicit (i.e., parenthesis/bracket/brace/etc.-based). For example, each of the following comprise multiple physical lines, but a single logical line: ```python from foo import bar \ baz \ qux \ quux spam = [ham, eggs, ni] the_night = { 'dark': True, 'full_of_terrors': True } ``` 2. The API for input transformations was completely overhauled in `IPython` v7.0. Among other changes, there is no longer a hook exposed for input transformers that work on fully assembled multiline statements (previously called `python_line_transforms`). Additionally, all input transformer functions are now called once per input area (i.e., notebook cell or interactive shell prompt) and are passed the full input, rather being called on each individual line. The `IPython>=7.0.0` implementation of the `davos` parser handles this by tokenizing the and assembling logical lines from the full input before passing each to the `line_parser` function. While this adds some additional overhead compared to the `IPython<7.0.0` `davos` parser, the difference is functionally de minimis, and in fact outweighed by the new parser's ability to skip parsing cells that don't contain `smuggle` statements altogether. 3. Before it is returned, the full `davos` parser function is assigned an attribute "`has_side_effects`", which is set to `True`. In `IPython>=7.17`, this will prevent the parser from being run when `IPython` checks whether or not the user input is complete (i.e., after pressing enter in the `IPython` shell, is the existing input a full statement, or part of a multiline statement/code block?). In `IPython<7.17`, this will have no effect. """ pyline_assembler = assemble_python_lines() def full_parser(lines): if 'smuggle ' not in ''.join(lines): # if cell contains no potential smuggle statements, don't # bother parsing line-by-line return lines parsed_lines = [] curr_buff = [] for raw_line in lines: # don't include trailing '\n' python_line = pyline_assembler.push(raw_line[:-1]) if python_line is None: # currently accumulating multiline logical line curr_buff.append(raw_line) continue # pass single-line parser full logical lines -- may be # single physical line or fully accumulated multiline # statement parsed_line = line_parser(python_line) if curr_buff: # logical line consists of multiple physical lines if parsed_line == python_line: # multiline statement is not a smuggle statement; # don't combine physical lines in output parsed_lines.extend(curr_buff) # last line isn't in curr_buff; add it separately parsed_lines.append(raw_line) else: # logical line is a multiline smuggle statement parsed_lines.append(f'{parsed_line}\n') # reset partially accumulated lines curr_buff.clear() else: # logical line consists of a single physical line parsed_lines.append(f'{parsed_line}\n') # .reset() clears pyline_assembler's .buf & .tokenizer for next # cell. Returns ''.join(pyline_assembler.buf) if .buf list is # not empty, otherwise None if pyline_assembler.reset(): # Presence of an incomplete logical line after parsing the # last physical line means there's a SyntaxError somewhere. # Include remaining physical lines to let IPython/Python # deal with raising the SyntaxError from the proper location parsed_lines.extend(curr_buff) return parsed_lines # prevents transformer from being run multiple times when IPython # parses partial line to determine whether input is complete full_parser.has_side_effects = True return full_parser