Пример #1
0
    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 = []
Пример #2
0
 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 = []
Пример #3
0
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