Exemple #1
0
class TerminalPdb(Pdb):
    def __init__(self, *args, **kwargs):
        Pdb.__init__(self, *args, **kwargs)
        self._ptcomp = None
        self.pt_init()

    def pt_init(self):
        def get_prompt_tokens():
            return [(Token.Prompt, self.prompt)]

        if self._ptcomp is None:
            compl = IPCompleter(shell=self.shell,
                                        namespace={},
                                        global_namespace={},
                                        parent=self.shell,
                                       )
            self._ptcomp = IPythonPTCompleter(compl)

        kb = KeyBindings()
        supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
        kb.add('c-z', filter=supports_suspend)(suspend_to_bg)

        if self.shell.display_completions == 'readlinelike':
            kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
                                  & ~has_selection
                                  & vi_insert_mode | emacs_insert_mode
                                  & ~cursor_in_leading_ws
                              ))(display_completions_like_readline)

        self.pt_app = PromptSession(
                            message=(lambda: PygmentsTokens(get_prompt_tokens())),
                            editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
                            key_bindings=kb,
                            history=self.shell.debugger_history,
                            completer=self._ptcomp,
                            enable_history_search=True,
                            mouse_support=self.shell.mouse_support,
                            complete_style=self.shell.pt_complete_style,
                            style=self.shell.style,
                            inputhook=self.shell.inputhook,
        )

    def cmdloop(self, intro=None):
        """Repeatedly issue a prompt, accept input, parse an initial prefix
        off the received input, and dispatch to action methods, passing them
        the remainder of the line as argument.

        override the same methods from cmd.Cmd to provide prompt toolkit replacement.
        """
        if not self.use_rawinput:
            raise ValueError('Sorry ipdb does not support use_rawinput=False')

        self.preloop()

        try:
            if intro is not None:
                self.intro = intro
            if self.intro:
                self.stdout.write(str(self.intro)+"\n")
            stop = None
            while not stop:
                if self.cmdqueue:
                    line = self.cmdqueue.pop(0)
                else:
                    self._ptcomp.ipy_completer.namespace = self.curframe_locals
                    self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
                    try:
                        line = self.pt_app.prompt() # reset_current_buffer=True)
                    except EOFError:
                        line = 'EOF'
                line = self.precmd(line)
                stop = self.onecmd(line)
                stop = self.postcmd(stop, line)
            self.postloop()
        except Exception:
            raise
Exemple #2
0
class TerminalPdb(Pdb):
    """Standalone IPython debugger."""
    def __init__(self, *args, pt_session_options=None, **kwargs):
        Pdb.__init__(self, *args, **kwargs)
        self._ptcomp = None
        self.pt_init(pt_session_options)

    def pt_init(self, pt_session_options=None):
        """Initialize the prompt session and the prompt loop
        and store them in self.pt_app and self.pt_loop.

        Additional keyword arguments for the PromptSession class
        can be specified in pt_session_options.
        """
        if pt_session_options is None:
            pt_session_options = {}

        def get_prompt_tokens():
            return [(Token.Prompt, self.prompt)]

        if self._ptcomp is None:
            compl = IPCompleter(shell=self.shell,
                                namespace={},
                                global_namespace={},
                                parent=self.shell)
            # add a completer for all the do_ methods
            methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]

            def gen_comp(self, text):
                return [m for m in methods_names if m.startswith(text)]

            import types
            newcomp = types.MethodType(gen_comp, compl)
            compl.custom_matchers.insert(0, newcomp)
            # end add completer.

            self._ptcomp = IPythonPTCompleter(compl)

        options = dict(
            message=(lambda: PygmentsTokens(get_prompt_tokens())),
            editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
            key_bindings=create_ipython_shortcuts(self.shell),
            history=self.shell.debugger_history,
            completer=self._ptcomp,
            enable_history_search=True,
            mouse_support=self.shell.mouse_support,
            complete_style=self.shell.pt_complete_style,
            style=self.shell.style,
            color_depth=self.shell.color_depth,
        )

        if not PTK3:
            options['inputhook'] = self.shell.inputhook
        options.update(pt_session_options)
        self.pt_loop = asyncio.new_event_loop()
        self.pt_app = PromptSession(**options)

    def cmdloop(self, intro=None):
        """Repeatedly issue a prompt, accept input, parse an initial prefix
        off the received input, and dispatch to action methods, passing them
        the remainder of the line as argument.

        override the same methods from cmd.Cmd to provide prompt toolkit replacement.
        """
        if not self.use_rawinput:
            raise ValueError('Sorry ipdb does not support use_rawinput=False')

        # In order to make sure that prompt, which uses asyncio doesn't
        # interfere with applications in which it's used, we always run the
        # prompt itself in a different thread (we can't start an event loop
        # within an event loop). This new thread won't have any event loop
        # running, and here we run our prompt-loop.

        self.preloop()

        try:
            if intro is not None:
                self.intro = intro
            if self.intro:
                self.stdout.write(str(self.intro) + "\n")
            stop = None
            while not stop:
                if self.cmdqueue:
                    line = self.cmdqueue.pop(0)
                else:
                    self._ptcomp.ipy_completer.namespace = self.curframe_locals
                    self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals

                    # Run the prompt in a different thread.
                    line = ''
                    keyboard_interrupt = False

                    def in_thread():
                        nonlocal line, keyboard_interrupt
                        try:
                            line = self.pt_app.prompt()
                        except EOFError:
                            line = 'EOF'
                        except KeyboardInterrupt:
                            keyboard_interrupt = True

                    th = threading.Thread(target=in_thread)
                    th.start()
                    th.join()

                    if keyboard_interrupt:
                        raise KeyboardInterrupt

                line = self.precmd(line)
                stop = self.onecmd(line)
                stop = self.postcmd(stop, line)
            self.postloop()
        except Exception:
            raise
Exemple #3
0
class PromptToolkit2Shell(BaseShell):
    """The xonsh shell for prompt_toolkit v2."""

    completion_displays_to_styles = {
        "multi": CompleteStyle.MULTI_COLUMN,
        "single": CompleteStyle.COLUMN,
        "readline": CompleteStyle.READLINE_LIKE,
        "none": None,
    }

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if ON_WINDOWS:
            winutils.enable_virtual_terminal_processing()
        self._first_prompt = True
        self.history = ThreadedHistory(PromptToolkitHistory())
        self.prompter = PromptSession(history=self.history)
        self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx,
                                                   self)
        self.key_bindings = KeyBindings()
        load_xonsh_bindings(self.key_bindings)
        # Store original `_history_matches` in case we need to restore it
        self._history_matches_orig = self.prompter.default_buffer._history_matches
        # This assumes that PromptToolkit2Shell is a singleton
        events.on_ptk_create.fire(
            prompter=self.prompter,
            history=self.history,
            completer=self.pt_completer,
            bindings=self.key_bindings,
        )

    def singleline(self,
                   auto_suggest=None,
                   enable_history_search=True,
                   multiline=True,
                   **kwargs):
        """Reads a single line of input from the shell. The store_in_history
        kwarg flags whether the input should be stored in PTK's in-memory
        history.
        """
        events.on_pre_prompt.fire()
        env = builtins.__xonsh__.env
        mouse_support = env.get("MOUSE_SUPPORT")
        auto_suggest = auto_suggest if env.get("AUTO_SUGGEST") else None
        completions_display = env.get("COMPLETIONS_DISPLAY")
        complete_style = self.completion_displays_to_styles[
            completions_display]

        complete_while_typing = env.get("UPDATE_COMPLETIONS_ON_KEYPRESS")
        if complete_while_typing:
            # PTK requires history search to be none when completing while typing
            enable_history_search = False
        if HAS_PYGMENTS:
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
        completer = None if completions_display == "none" else self.pt_completer

        if env.get("UPDATE_PROMPT_ON_KEYPRESS"):
            get_prompt_tokens = self.prompt_tokens
            get_rprompt_tokens = self.rprompt_tokens
            get_bottom_toolbar_tokens = self.bottom_toolbar_tokens
        else:
            get_prompt_tokens = self.prompt_tokens()
            get_rprompt_tokens = self.rprompt_tokens()
            get_bottom_toolbar_tokens = self.bottom_toolbar_tokens()

        if env.get("VI_MODE"):
            editing_mode = EditingMode.VI
        else:
            editing_mode = EditingMode.EMACS

        if env.get("XONSH_HISTORY_MATCH_ANYWHERE"):
            self.prompter.default_buffer._history_matches = MethodType(
                _cust_history_matches, self.prompter.default_buffer)
        elif (self.prompter.default_buffer._history_matches
              is not self._history_matches_orig):
            self.prompter.default_buffer._history_matches = self._history_matches_orig

        prompt_args = {
            "mouse_support": mouse_support,
            "auto_suggest": auto_suggest,
            "message": get_prompt_tokens,
            "rprompt": get_rprompt_tokens,
            "bottom_toolbar": get_bottom_toolbar_tokens,
            "completer": completer,
            "multiline": multiline,
            "editing_mode": editing_mode,
            "prompt_continuation": self.continuation_tokens,
            "enable_history_search": enable_history_search,
            "reserve_space_for_menu": 10,
            "key_bindings": self.key_bindings,
            "complete_style": complete_style,
            "complete_while_typing": complete_while_typing,
            "include_default_pygments_style": False,
        }
        if builtins.__xonsh__.env.get("COLOR_INPUT"):
            style = style_from_pygments_dict(DEFAULT_STYLE_DICT)

            prompt_args["style"] = style

            style_overrides_env = env.get("PTK_STYLE_OVERRIDES")
            if style_overrides_env:
                try:
                    style_overrides = Style.from_dict(style_overrides_env)
                    prompt_args["style"] = merge_styles(
                        [style, style_overrides])
                except (AttributeError, TypeError, ValueError):
                    print_exception()

        line = self.prompter.prompt(**prompt_args)
        events.on_post_prompt.fire()
        return line

    def _push(self, line):
        """Pushes a line onto the buffer and compiles the code in a way that
        enables multiline input.
        """
        code = None
        self.buffer.append(line)
        if self.need_more_lines:
            return None, code
        src = "".join(self.buffer)
        src = transform_command(src)
        try:
            code = self.execer.compile(src,
                                       mode="single",
                                       glbs=self.ctx,
                                       locs=None)
            self.reset_buffer()
        except Exception:  # pylint: disable=broad-except
            self.reset_buffer()
            print_exception()
            return src, None
        return src, code

    def cmdloop(self, intro=None):
        """Enters a loop that reads and execute input from user."""
        if intro:
            print(intro)
        auto_suggest = AutoSuggestFromHistory()
        self.push = self._push
        while not builtins.__xonsh__.exit:
            try:
                line = self.singleline(auto_suggest=auto_suggest)
                if not line:
                    self.emptyline()
                else:
                    line = self.precmd(line)
                    self.default(line)
            except (KeyboardInterrupt, SystemExit):
                self.reset_buffer()
            except EOFError:
                if builtins.__xonsh__.env.get("IGNOREEOF"):
                    print('Use "exit" to leave the shell.', file=sys.stderr)
                else:
                    break

    def prompt_tokens(self):
        """Returns a list of (token, str) tuples for the current prompt."""
        p = builtins.__xonsh__.env.get("PROMPT")
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        if self._first_prompt:
            carriage_return()
            self._first_prompt = False
        self.settitle()
        return PygmentsTokens(toks)

    def rprompt_tokens(self):
        """Returns a list of (token, str) tuples for the current right
        prompt.
        """
        p = builtins.__xonsh__.env.get("RIGHT_PROMPT")
        # self.prompt_formatter does handle empty strings properly,
        # but this avoids descending into it in the common case of
        # $RIGHT_PROMPT == ''.
        if isinstance(p, str) and len(p) == 0:
            return []
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        return PygmentsTokens(toks)

    def bottom_toolbar_tokens(self):
        """Returns a list of (token, str) tuples for the current bottom
        toolbar.
        """
        p = builtins.__xonsh__.env.get("BOTTOM_TOOLBAR")
        if not p:
            return
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        return PygmentsTokens(toks)

    def continuation_tokens(self, width, line_number, is_soft_wrap=False):
        """Displays dots in multiline prompt"""
        if is_soft_wrap:
            return ""
        width = width - 1
        dots = builtins.__xonsh__.env.get("MULTILINE_PROMPT")
        dots = dots() if callable(dots) else dots
        if dots is None:
            return [(Token, " " * (width + 1))]
        basetoks = self.format_color(dots)
        baselen = sum(len(t[1]) for t in basetoks)
        if baselen == 0:
            return [(Token, " " * (width + 1))]
        toks = basetoks * (width // baselen)
        n = width % baselen
        count = 0
        for tok in basetoks:
            slen = len(tok[1])
            newcount = slen + count
            if slen == 0:
                continue
            elif newcount <= n:
                toks.append(tok)
            else:
                toks.append((tok[0], tok[1][:n - count]))
            count = newcount
            if n <= count:
                break
        toks.append((Token, " "))  # final space
        return PygmentsTokens(toks)

    def format_color(self, string, hide=False, force_string=False, **kwargs):
        """Formats a color string using Pygments. This, therefore, returns
        a list of (Token, str) tuples. If force_string is set to true, though,
        this will return a color formatted string.
        """
        tokens = partial_color_tokenize(string)
        if force_string and HAS_PYGMENTS:
            env = builtins.__xonsh__.env
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
            proxy_style = pyghooks.xonsh_style_proxy(self.styler)
            formatter = pyghooks.XonshTerminal256Formatter(style=proxy_style)
            s = pygments.format(tokens, formatter)
            return s
        elif force_string:
            print("To force colorization of string, install Pygments")
            return tokens
        else:
            return tokens

    def print_color(self, string, end="\n", **kwargs):
        """Prints a color string using prompt-toolkit color management."""
        if isinstance(string, str):
            tokens = partial_color_tokenize(string)
        else:
            # assume this is a list of (Token, str) tuples and just print
            tokens = string
        tokens = PygmentsTokens(tokens)
        if HAS_PYGMENTS:
            env = builtins.__xonsh__.env
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
            proxy_style = style_from_pygments_cls(
                pyghooks.xonsh_style_proxy(self.styler))
        else:
            proxy_style = style_from_pygments_dict(DEFAULT_STYLE_DICT)
        ptk_print(tokens,
                  style=proxy_style,
                  end=end,
                  include_default_pygments_style=False)

    def color_style_names(self):
        """Returns an iterable of all available style names."""
        if not HAS_PYGMENTS:
            return ["For other xonsh styles, please install pygments"]
        return get_all_styles()

    def color_style(self):
        """Returns the current color map."""
        if not HAS_PYGMENTS:
            return DEFAULT_STYLE_DICT
        env = builtins.__xonsh__.env
        self.styler.style_name = env.get("XONSH_COLOR_STYLE")
        return self.styler.styles

    def restore_tty_sanity(self):
        """An interface for resetting the TTY stdin mode. This is highly
Exemple #4
0
class PromptToolkitShell(BaseShell):
    """The xonsh shell for prompt_toolkit v2 and later."""

    completion_displays_to_styles = {
        "multi": CompleteStyle.MULTI_COLUMN,
        "single": CompleteStyle.COLUMN,
        "readline": CompleteStyle.READLINE_LIKE,
        "none": None,
    }

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if ON_WINDOWS:
            winutils.enable_virtual_terminal_processing()
        self._first_prompt = True
        self.history = ThreadedHistory(PromptToolkitHistory())
        self.prompter = PromptSession(history=self.history)
        self.prompt_formatter = PTKPromptFormatter(self.prompter)
        self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx,
                                                   self)
        self.key_bindings = load_xonsh_bindings()

        # Store original `_history_matches` in case we need to restore it
        self._history_matches_orig = self.prompter.default_buffer._history_matches
        # This assumes that PromptToolkitShell is a singleton
        events.on_ptk_create.fire(
            prompter=self.prompter,
            history=self.history,
            completer=self.pt_completer,
            bindings=self.key_bindings,
        )
        # Goes at the end, since _MergedKeyBindings objects do not have
        # an add() function, which is necessary for on_ptk_create events
        self.key_bindings = merge_key_bindings(
            [self.key_bindings,
             load_emacs_shift_selection_bindings()])

    def singleline(self,
                   auto_suggest=None,
                   enable_history_search=True,
                   multiline=True,
                   **kwargs):
        """Reads a single line of input from the shell. The store_in_history
        kwarg flags whether the input should be stored in PTK's in-memory
        history.
        """
        events.on_pre_prompt_format.fire()
        env = builtins.__xonsh__.env
        mouse_support = env.get("MOUSE_SUPPORT")
        auto_suggest = auto_suggest if env.get("AUTO_SUGGEST") else None
        refresh_interval = env.get("PROMPT_REFRESH_INTERVAL")
        refresh_interval = refresh_interval if refresh_interval > 0 else None
        complete_in_thread = env.get("COMPLETION_IN_THREAD")
        completions_display = env.get("COMPLETIONS_DISPLAY")
        complete_style = self.completion_displays_to_styles[
            completions_display]

        complete_while_typing = env.get("UPDATE_COMPLETIONS_ON_KEYPRESS")
        if complete_while_typing:
            # PTK requires history search to be none when completing while typing
            enable_history_search = False
        if HAS_PYGMENTS:
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
        completer = None if completions_display == "none" else self.pt_completer

        events.on_timingprobe.fire(name="on_pre_prompt_tokenize")
        get_bottom_toolbar_tokens = self.bottom_toolbar_tokens
        if env.get("UPDATE_PROMPT_ON_KEYPRESS"):
            get_prompt_tokens = self.prompt_tokens
            get_rprompt_tokens = self.rprompt_tokens
        else:
            get_prompt_tokens = self.prompt_tokens()
            get_rprompt_tokens = self.rprompt_tokens()
            if get_bottom_toolbar_tokens:
                get_bottom_toolbar_tokens = get_bottom_toolbar_tokens()
        events.on_timingprobe.fire(name="on_post_prompt_tokenize")

        if env.get("VI_MODE"):
            editing_mode = EditingMode.VI
        else:
            editing_mode = EditingMode.EMACS

        if env.get("XONSH_HISTORY_MATCH_ANYWHERE"):
            self.prompter.default_buffer._history_matches = MethodType(
                _cust_history_matches, self.prompter.default_buffer)
        elif (self.prompter.default_buffer._history_matches
              is not self._history_matches_orig):
            self.prompter.default_buffer._history_matches = self._history_matches_orig

        prompt_args = {
            "mouse_support": mouse_support,
            "auto_suggest": auto_suggest,
            "message": get_prompt_tokens,
            "rprompt": get_rprompt_tokens,
            "bottom_toolbar": get_bottom_toolbar_tokens,
            "completer": completer,
            "multiline": multiline,
            "editing_mode": editing_mode,
            "prompt_continuation": self.continuation_tokens,
            "enable_history_search": enable_history_search,
            "reserve_space_for_menu": 0,
            "key_bindings": self.key_bindings,
            "complete_style": complete_style,
            "complete_while_typing": complete_while_typing,
            "include_default_pygments_style": False,
            "refresh_interval": refresh_interval,
            "complete_in_thread": complete_in_thread,
        }

        if env.get("COLOR_INPUT"):
            events.on_timingprobe.fire(name="on_pre_prompt_style")
            if HAS_PYGMENTS:
                prompt_args["lexer"] = PygmentsLexer(pyghooks.XonshLexer)
                style = style_from_pygments_cls(
                    pyghooks.xonsh_style_proxy(self.styler))
            else:
                style = style_from_pygments_dict(DEFAULT_STYLE_DICT)
            prompt_args["style"] = style
            events.on_timingprobe.fire(name="on_post_prompt_style")

            style_overrides_env = env.get("PTK_STYLE_OVERRIDES")
            if style_overrides_env:
                try:
                    style_overrides = Style.from_dict(style_overrides_env)
                    prompt_args["style"] = merge_styles(
                        [style, style_overrides])
                except (AttributeError, TypeError, ValueError):
                    print_exception()

        if env["ENABLE_ASYNC_PROMPT"]:
            # once the prompt is done, update it in background as each future is completed
            prompt_args["pre_run"] = self.prompt_formatter.start_update

        events.on_pre_prompt.fire()
        line = self.prompter.prompt(**prompt_args)
        events.on_post_prompt.fire()
        return line

    def _push(self, line):
        """Pushes a line onto the buffer and compiles the code in a way that
        enables multiline input.
        """
        code = None
        self.buffer.append(line)
        if self.need_more_lines:
            return None, code
        src = "".join(self.buffer)
        src = transform_command(src)
        try:
            code = self.execer.compile(src,
                                       mode="single",
                                       glbs=self.ctx,
                                       locs=None)
            self.reset_buffer()
        except Exception:  # pylint: disable=broad-except
            self.reset_buffer()
            print_exception()
            return src, None
        return src, code

    def cmdloop(self, intro=None):
        """Enters a loop that reads and execute input from user."""
        if intro:
            print(intro)
        auto_suggest = AutoSuggestFromHistory()
        self.push = self._push
        while not builtins.__xonsh__.exit:
            try:
                line = self.singleline(auto_suggest=auto_suggest)
                if not line:
                    self.emptyline()
                else:
                    line = self.precmd(line)
                    self.default(line)
            except (KeyboardInterrupt, SystemExit):
                self.reset_buffer()
            except EOFError:
                if builtins.__xonsh__.env.get("IGNOREEOF"):
                    print('Use "exit" to leave the shell.', file=sys.stderr)
                else:
                    break

    def _get_prompt_tokens(self, env_name: str, prompt_name: str, **kwargs):
        env = builtins.__xonsh__.env  # type:ignore
        p = env.get(env_name)

        if not p and "default" in kwargs:
            return kwargs.pop("default")

        try:
            p = self.prompt_formatter(template=p,
                                      threaded=env["ENABLE_ASYNC_PROMPT"],
                                      prompt_name=prompt_name)
        except Exception:  # pylint: disable=broad-except
            print_exception()

        p, osc_tokens = remove_ansi_osc(p)

        if kwargs.get("handle_osc_tokens"):
            # handle OSC tokens
            for osc in osc_tokens:
                if osc[2:4] == "0;":
                    env["TITLE"] = osc[4:-1]
                else:
                    print(osc, file=sys.__stdout__, flush=True)

        toks = partial_color_tokenize(p)

        return tokenize_ansi(PygmentsTokens(toks))

    def prompt_tokens(self):
        """Returns a list of (token, str) tuples for the current prompt."""
        if self._first_prompt:
            carriage_return()
            self._first_prompt = False

        tokens = self._get_prompt_tokens("PROMPT",
                                         "message",
                                         handle_osc_tokens=True)
        self.settitle()
        return tokens

    def rprompt_tokens(self):
        """Returns a list of (token, str) tuples for the current right
        prompt.
        """
        return self._get_prompt_tokens("RIGHT_PROMPT", "rprompt", default=[])

    def _bottom_toolbar_tokens(self):
        """Returns a list of (token, str) tuples for the current bottom
        toolbar.
        """
        return self._get_prompt_tokens("BOTTOM_TOOLBAR",
                                       "bottom_toolbar",
                                       default=None)

    @property
    def bottom_toolbar_tokens(self):
        """Returns self._bottom_toolbar_tokens if it would yield a result"""
        if builtins.__xonsh__.env.get("BOTTOM_TOOLBAR"):
            return self._bottom_toolbar_tokens

    def continuation_tokens(self, width, line_number, is_soft_wrap=False):
        """Displays dots in multiline prompt"""
        if is_soft_wrap:
            return ""
        width = width - 1
        dots = builtins.__xonsh__.env.get("MULTILINE_PROMPT")
        dots = dots() if callable(dots) else dots
        if not dots:
            return ""
        basetoks = self.format_color(dots)
        baselen = sum(len(t[1]) for t in basetoks)
        if baselen == 0:
            return [(Token, " " * (width + 1))]
        toks = basetoks * (width // baselen)
        n = width % baselen
        count = 0
        for tok in basetoks:
            slen = len(tok[1])
            newcount = slen + count
            if slen == 0:
                continue
            elif newcount <= n:
                toks.append(tok)
            else:
                toks.append((tok[0], tok[1][:n - count]))
            count = newcount
            if n <= count:
                break
        toks.append((Token, " "))  # final space
        return PygmentsTokens(toks)

    def format_color(self, string, hide=False, force_string=False, **kwargs):
        """Formats a color string using Pygments. This, therefore, returns
        a list of (Token, str) tuples. If force_string is set to true, though,
        this will return a color formatted string.
        """
        tokens = partial_color_tokenize(string)
        if force_string and HAS_PYGMENTS:
            env = builtins.__xonsh__.env
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
            proxy_style = pyghooks.xonsh_style_proxy(self.styler)
            formatter = pyghooks.XonshTerminal256Formatter(style=proxy_style)
            s = pygments.format(tokens, formatter)
            return s
        elif force_string:
            print("To force colorization of string, install Pygments")
            return tokens
        else:
            return tokens

    def print_color(self, string, end="\n", **kwargs):
        """Prints a color string using prompt-toolkit color management."""
        if isinstance(string, str):
            tokens = partial_color_tokenize(string)
        else:
            # assume this is a list of (Token, str) tuples and just print
            tokens = string
        tokens = PygmentsTokens(tokens)
        if HAS_PYGMENTS:
            env = builtins.__xonsh__.env
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
            proxy_style = style_from_pygments_cls(
                pyghooks.xonsh_style_proxy(self.styler))
        else:
            proxy_style = style_from_pygments_dict(DEFAULT_STYLE_DICT)
        ptk_print(tokens,
                  style=proxy_style,
                  end=end,
                  include_default_pygments_style=False)

    def color_style_names(self):
        """Returns an iterable of all available style names."""
        if not HAS_PYGMENTS:
            return ["For other xonsh styles, please install pygments"]
        return get_all_styles()

    def color_style(self):
        """Returns the current color map."""
        if not HAS_PYGMENTS:
            return DEFAULT_STYLE_DICT
        env = builtins.__xonsh__.env
        self.styler.style_name = env.get("XONSH_COLOR_STYLE")
        return self.styler.styles

    def restore_tty_sanity(self):
        """An interface for resetting the TTY stdin mode. This is highly
        dependent on the shell backend. Also it is mostly optional since
        it only affects ^Z backgrounding behaviour.
        """
        # PTK does not seem to need any specialization here. However,
        # if it does for some reason in the future...
        # The following writes an ANSI escape sequence that sends the cursor
        # to the end of the line. This has the effect of restoring ECHO mode.
        # See http://unix.stackexchange.com/a/108014/129048 for more details.
        # This line can also be replaced by os.system("stty sane"), as per
        # http://stackoverflow.com/questions/19777129/interactive-python-interpreter-run-in-background#comment29421919_19778355
        # However, it is important to note that not termios-based solution
        # seems to work. My guess is that this is because termios restoration
        # needs to be performed by the subprocess itself. This fix is important
        # when subprocesses don't properly restore the terminal attributes,
        # like Python in interactive mode. Also note that the sequences "\033M"
        # and "\033E" seem to work too, but these are technically VT100 codes.
        # I used the more primitive ANSI sequence to maximize compatibility.
        # -scopatz 2017-01-28
        #   if not ON_POSIX:
        #       return
        #   sys.stdout.write('\033[9999999C\n')
        if not ON_POSIX:
            return
        stty, _ = builtins.__xonsh__.commands_cache.lazyget(
            "stty", (None, None))
        if stty is None:
            return
        os.system(stty + " sane")
Exemple #5
0
class ZMQTerminalInteractiveShell(SingletonConfigurable):
    readline_use = False

    pt_cli = None

    _executing = False
    _execution_state = Unicode('')
    _pending_clearoutput = False
    _eventloop = None
    own_kernel = False  # Changed by ZMQTerminalIPythonApp

    editing_mode = Unicode('emacs', config=True,
        help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
    )

    highlighting_style = Unicode('', config=True,
        help="The name of a Pygments style to use for syntax highlighting"
    )

    highlighting_style_overrides = Dict(config=True,
        help="Override highlighting format for specific tokens"
    )

    true_color = Bool(False, config=True,
        help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
              "If your terminal supports true color, the following command "
              "should print 'TRUECOLOR' in orange: "
              "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
    )

    history_load_length = Integer(1000, config=True,
        help="How many history items to load into memory"
    )

    banner = Unicode('Jupyter console {version}\n\n{kernel_banner}', config=True,
        help=("Text to display before the first prompt. Will be formatted with "
              "variables {version} and {kernel_banner}.")
    )

    kernel_timeout = Float(60, config=True,
        help="""Timeout for giving up on a kernel (in seconds).

        On first connect and restart, the console tests whether the
        kernel is running and responsive by sending kernel_info_requests.
        This sets the timeout in seconds for how long the kernel can take
        before being presumed dead.
        """
    )

    image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
                         'PIL', config=True, allow_none=True, help=
        """
        Handler for image type output.  This is useful, for example,
        when connecting to the kernel in which pylab inline backend is
        activated.  There are four handlers defined.  'PIL': Use
        Python Imaging Library to popup image; 'stream': Use an
        external program to show the image.  Image will be fed into
        the STDIN of the program.  You will need to configure
        `stream_image_handler`; 'tempfile': Use an external program to
        show the image.  Image will be saved in a temporally file and
        the program is called with the temporally file.  You will need
        to configure `tempfile_image_handler`; 'callable': You can set
        any Python callable which is called with the image data.  You
        will need to configure `callable_image_handler`.
        """
    )

    stream_image_handler = List(config=True, help=
        """
        Command to invoke an image viewer program when you are using
        'stream' image handler.  This option is a list of string where
        the first element is the command itself and reminders are the
        options for the command.  Raw image data is given as STDIN to
        the program.
        """
    )

    tempfile_image_handler = List(config=True, help=
        """
        Command to invoke an image viewer program when you are using
        'tempfile' image handler.  This option is a list of string
        where the first element is the command itself and reminders
        are the options for the command.  You can use {file} and
        {format} in the string to represent the location of the
        generated image file and image format.
        """
    )

    callable_image_handler = Any(config=True, help=
        """
        Callable object called via 'callable' image handler with one
        argument, `data`, which is `msg["content"]["data"]` where
        `msg` is the message from iopub channel.  For example, you can
        find base64 encoded PNG data as `data['image/png']`. If your function
        can't handle the data supplied, it should return `False` to indicate
        this.
        """
    )

    mime_preference = List(
        default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
        config=True, help=
        """
        Preferred object representation MIME type in order.  First
        matched MIME type will be used.
        """
    )

    use_kernel_is_complete = Bool(True, config=True,
        help="""Whether to use the kernel's is_complete message
        handling. If False, then the frontend will use its
        own is_complete handler.
        """
    )
    kernel_is_complete_timeout = Float(1, config=True,
        help="""Timeout (in seconds) for giving up on a kernel's is_complete
        response.

        If the kernel does not respond at any point within this time,
        the kernel will no longer be asked if code is complete, and the
        console will default to the built-in is_complete test.
        """
    )

    # This is configurable on JupyterConsoleApp; this copy is not configurable
    # to avoid a duplicate config option.
    confirm_exit = Bool(True,
        help="""Set to display confirmation dialog on exit.
        You can always use 'exit' or 'quit', to force a
        direct exit without any confirmation.
        """
    )

    highlight_matching_brackets = Bool(True,
        help="Highlight matching brackets.",
    ).tag(config=True)

    manager = Instance('jupyter_client.KernelManager', allow_none=True)
    client = Instance('jupyter_client.KernelClient', allow_none=True)

    def _client_changed(self, name, old, new):
        self.session_id = new.session.session
    session_id = Unicode()

    def _banner1_default(self):
        return "Jupyter Console {version}\n".format(version=__version__)

    simple_prompt = Bool(False,
         help="""Use simple fallback prompt. Features may be limited."""
    ).tag(config=True)

    def __init__(self, **kwargs):
        # This is where traits with a config_key argument are updated
        # from the values on config.
        super(ZMQTerminalInteractiveShell, self).__init__(**kwargs)
        self.configurables = [self]

        self.init_history()
        self.init_completer()
        self.init_io()

        self.init_kernel_info()
        self.init_prompt_toolkit_cli()
        self.keep_running = True
        self.execution_count = 1

    def init_completer(self):
        """Initialize the completion machinery.

        This creates completion machinery that can be used by client code,
        either interactively in-process (typically triggered by the readline
        library), programmatically (such as in test suites) or out-of-process
        (typically over the network by remote frontends).
        """
        self.Completer = ZMQCompleter(self, self.client, config=self.config)

    def init_history(self):
        """Sets up the command history. """
        self.history_manager = ZMQHistoryManager(client=self.client)
        self.configurables.append(self.history_manager)

    def get_prompt_tokens(self):
        return [
            (Token.Prompt, 'In ['),
            (Token.PromptNum, str(self.execution_count)),
            (Token.Prompt, ']: '),
        ]

    def get_continuation_tokens(self, width):
        return [
            (Token.Prompt, (' ' * (width - 2)) + ': '),
        ]

    def get_out_prompt_tokens(self):
        return [
            (Token.OutPrompt, 'Out['),
            (Token.OutPromptNum, str(self.execution_count)),
            (Token.OutPrompt, ']: ')
        ]

    def print_out_prompt(self):
        tokens = self.get_out_prompt_tokens()
        print_formatted_text(PygmentsTokens(tokens), end='',
                             style = self.pt_cli.app.style)

    kernel_info = {}

    def init_kernel_info(self):
        """Wait for a kernel to be ready, and store kernel info"""
        timeout = self.kernel_timeout
        tic = time.time()
        self.client.hb_channel.unpause()
        msg_id = self.client.kernel_info()
        while True:
            try:
                reply = self.client.get_shell_msg(timeout=1)
            except Empty:
                if (time.time() - tic) > timeout:
                    raise RuntimeError("Kernel didn't respond to kernel_info_request")
            else:
                if reply['parent_header'].get('msg_id') == msg_id:
                    self.kernel_info = reply['content']
                    return

    def show_banner(self):
        print(self.banner.format(version=__version__,
                         kernel_banner=self.kernel_info.get('banner', '')))

    def init_prompt_toolkit_cli(self):
        if self.simple_prompt or ('JUPYTER_CONSOLE_TEST' in os.environ):
            # Simple restricted interface for tests so we can find prompts with
            # pexpect. Multi-line input not supported.
            def prompt():
                return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
            self.prompt_for_code = prompt
            self.print_out_prompt = \
                lambda: print('Out[%d]: ' % self.execution_count, end='')
            return

        kb = KeyBindings()
        insert_mode = vi_insert_mode | emacs_insert_mode

        @kb.add("enter", filter=(has_focus(DEFAULT_BUFFER)
                                 & ~has_selection
                                 & insert_mode
                                 ))
        def _(event):
            b = event.current_buffer
            d = b.document
            if not (d.on_last_line or d.cursor_position_row >= d.line_count
                                           - d.empty_line_count_at_the_end()):
                b.newline()
                return

            # Pressing enter flushes any pending display. This also ensures
            # the displayed execution_count is correct.
            self.handle_iopub()

            more, indent = self.check_complete(d.text)

            if (not more) and b.accept_handler:
                b.validate_and_handle()
            else:
                b.insert_text('\n' + indent)

        @kb.add("c-c", filter=has_focus(DEFAULT_BUFFER))
        def _(event):
            event.current_buffer.reset()

        @kb.add("c-\\", filter=has_focus(DEFAULT_BUFFER))
        def _(event):
            raise EOFError

        @kb.add("c-z", filter=Condition(lambda: suspend_to_background_supported()))
        def _(event):
            event.cli.suspend_to_background()

        # Pre-populate history from IPython's history database
        history = InMemoryHistory()
        last_cell = u""
        for _, _, cell in self.history_manager.get_tail(self.history_load_length,
                                                        include_latest=True):
            # Ignore blank lines and consecutive duplicates
            cell = cast_unicode_py2(cell.rstrip())
            if cell and (cell != last_cell):
                history.append_string(cell)

        style_overrides = {
            Token.Prompt: '#009900',
            Token.PromptNum: '#00ff00 bold',
            Token.OutPrompt: '#ff2200',
            Token.OutPromptNum: '#ff0000 bold',
        }
        if self.highlighting_style:
            style_cls = get_style_by_name(self.highlighting_style)
        else:
            style_cls = get_style_by_name('default')
            # The default theme needs to be visible on both a dark background
            # and a light background, because we can't tell what the terminal
            # looks like. These tweaks to the default theme help with that.
            style_overrides.update({
                Token.Number: '#007700',
                Token.Operator: 'noinherit',
                Token.String: '#BB6622',
                Token.Name.Function: '#2080D0',
                Token.Name.Class: 'bold #2080D0',
                Token.Name.Namespace: 'bold #2080D0',
            })
        style_overrides.update(self.highlighting_style_overrides)
        style = merge_styles([
            style_from_pygments_cls(style_cls),
            style_from_pygments_dict(style_overrides),
        ])

        editing_mode = getattr(EditingMode, self.editing_mode.upper())
        langinfo = self.kernel_info.get('language_info', {})
        lexer = langinfo.get('pygments_lexer', langinfo.get('name', 'text'))

        # If enabled in the settings, highlight matching brackets
        # when the DEFAULT_BUFFER has the focus
        input_processors = [ConditionalProcessor(
            processor=HighlightMatchingBracketProcessor(chars='[](){}'),
            filter=has_focus(DEFAULT_BUFFER) & ~is_done &
            Condition(lambda: self.highlight_matching_brackets))
        ]

        self.pt_cli = PromptSession(
            message=(lambda: PygmentsTokens(self.get_prompt_tokens())),
            multiline=True,
            editing_mode=editing_mode,
            lexer=PygmentsLexer(get_pygments_lexer(lexer)),
            prompt_continuation=(
                lambda width, lineno, is_soft_wrap:
                PygmentsTokens(self.get_continuation_tokens(width))
            ),
            key_bindings=kb,
            history=history,
            completer=JupyterPTCompleter(self.Completer),
            enable_history_search=True,
            style=style,
            input_processors=input_processors,
            color_depth=(ColorDepth.TRUE_COLOR if self.true_color else None),

        )

    def prompt_for_code(self):
        if self.next_input:
            default = self.next_input
            self.next_input = None
        else:
            default = ''

        with patch_stdout(raw=True):
            text = self.pt_cli.prompt(
                default=default,
#                pre_run=self.pre_prompt,# reset_current_buffer=True,
            )
        return text

    def init_io(self):
        if sys.platform not in {'win32', 'cli'}:
            return

        import colorama
        colorama.init()

    def check_complete(self, code):
        if self.use_kernel_is_complete:
            msg_id = self.client.is_complete(code)
            try:
                return self.handle_is_complete_reply(msg_id,
                                                     timeout=self.kernel_is_complete_timeout)
            except SyntaxError:
                return False, ""
        else:
            lines = code.splitlines()
            if len(lines):
                more = (lines[-1] != "")
                return more, ""
            else:
                return False, ""

    def ask_exit(self):
        self.keep_running = False

    # This is set from payloads in handle_execute_reply
    next_input = None

    def pre_prompt(self):
        if self.next_input:
            # We can't set the buffer here, because it will be reset just after
            # this. Adding a callable to pre_run_callables does what we need
            # after the buffer is reset.
            s = cast_unicode_py2(self.next_input)

            def set_doc():
                self.pt_cli.app.buffer.document = Document(s)
            if hasattr(self.pt_cli, 'pre_run_callables'):
                self.pt_cli.app.pre_run_callables.append(set_doc)
            else:
                # Older version of prompt_toolkit; it's OK to set the document
                # directly here.
                set_doc()
            self.next_input = None

    def interact(self, display_banner=None):
        while self.keep_running:
            print('\n', end='')

            try:
                code = self.prompt_for_code()
            except EOFError:
                if (not self.confirm_exit) or \
                        ask_yes_no('Do you really want to exit ([y]/n)?', 'y', 'n'):
                    self.ask_exit()

            else:
                if code:
                    self.run_cell(code, store_history=True)

    def mainloop(self):
        self.keepkernel = not self.own_kernel
        # An extra layer of protection in case someone mashing Ctrl-C breaks
        # out of our internal code.
        while True:
            try:
                self.interact()
                break
            except KeyboardInterrupt:
                print("\nKeyboardInterrupt escaped interact()\n")

        if self._eventloop:
            self._eventloop.close()
        if self.keepkernel and not self.own_kernel:
            print('keeping kernel alive')
        elif self.keepkernel and self.own_kernel:
            print("owning kernel, cannot keep it alive")
            self.client.shutdown()
        else:
            print("Shutting down kernel")
            self.client.shutdown()

    def run_cell(self, cell, store_history=True):
        """Run a complete IPython cell.

        Parameters
        ----------
        cell : str
          The code (including IPython code such as %magic functions) to run.
        store_history : bool
          If True, the raw and translated cell will be stored in IPython's
          history. For user code calling back into IPython's machinery, this
          should be set to False.
        """
        if (not cell) or cell.isspace():
            # pressing enter flushes any pending display
            self.handle_iopub()
            return

        # flush stale replies, which could have been ignored, due to missed heartbeats
        while self.client.shell_channel.msg_ready():
            self.client.shell_channel.get_msg()
        # execute takes 'hidden', which is the inverse of store_hist
        msg_id = self.client.execute(cell, not store_history)

        # first thing is wait for any side effects (output, stdin, etc.)
        self._executing = True
        self._execution_state = "busy"
        while self._execution_state != 'idle' and self.client.is_alive():
            try:
                self.handle_input_request(msg_id, timeout=0.05)
            except Empty:
                # display intermediate print statements, etc.
                self.handle_iopub(msg_id)
            except ZMQError as e:
                # Carry on if polling was interrupted by a signal
                if e.errno != errno.EINTR:
                    raise

        # after all of that is done, wait for the execute reply
        while self.client.is_alive():
            try:
                self.handle_execute_reply(msg_id, timeout=0.05)
            except Empty:
                pass
            else:
                break
        self._executing = False

    #-----------------
    # message handlers
    #-----------------

    def handle_execute_reply(self, msg_id, timeout=None):
        msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
        if msg["parent_header"].get("msg_id", None) == msg_id:

            self.handle_iopub(msg_id)

            content = msg["content"]
            status = content['status']

            if status == 'aborted':
                self.write('Aborted\n')
                return
            elif status == 'ok':
                # handle payloads
                for item in content.get("payload", []):
                    source = item['source']
                    if source == 'page':
                        page.page(item['data']['text/plain'])
                    elif source == 'set_next_input':
                        self.next_input = item['text']
                    elif source == 'ask_exit':
                        self.keepkernel = item.get('keepkernel', False)
                        self.ask_exit()

            elif status == 'error':
                pass

            self.execution_count = int(content["execution_count"] + 1)

    def handle_is_complete_reply(self, msg_id, timeout=None):
        """
        Wait for a repsonse from the kernel, and return two values:
            more? - (boolean) should the frontend ask for more input
            indent - an indent string to prefix the input
        Overloaded methods may want to examine the comeplete source. Its is
        in the self._source_lines_buffered list.
        """
        ## Get the is_complete response:
        msg = None
        try:
            msg = self.client.shell_channel.get_msg(block=True, timeout=timeout)
        except Empty:
            warn('The kernel did not respond to an is_complete_request. '
                 'Setting `use_kernel_is_complete` to False.')
            self.use_kernel_is_complete = False
            return False, ""
        ## Handle response:
        if msg["parent_header"].get("msg_id", None) != msg_id:
            warn('The kernel did not respond properly to an is_complete_request: %s.' % str(msg))
            return False, ""
        else:
            status = msg["content"].get("status", None)
            indent = msg["content"].get("indent", "")
        ## Return more? and indent string
        if status == "complete":
            return False, indent
        elif status == "incomplete":
            return True, indent
        elif status == "invalid":
            raise SyntaxError()
        elif status == "unknown":
            return False, indent
        else:
            warn('The kernel sent an invalid is_complete_reply status: "%s".' % status)
            return False, indent

    include_other_output = Bool(False, config=True,
        help="""Whether to include output from clients
        other than this one sharing the same kernel.

        Outputs are not displayed until enter is pressed.
        """
    )
    other_output_prefix = Unicode("[remote] ", config=True,
        help="""Prefix to add to outputs coming from clients other than this one.

        Only relevant if include_other_output is True.
        """
    )

    def from_here(self, msg):
        """Return whether a message is from this session"""
        return msg['parent_header'].get("session", self.session_id) == self.session_id

    def include_output(self, msg):
        """Return whether we should include a given output message"""
        from_here = self.from_here(msg)
        if msg['msg_type'] == 'execute_input':
            # only echo inputs not from here
            return self.include_other_output and not from_here

        if self.include_other_output:
            return True
        else:
            return from_here

    def handle_iopub(self, msg_id=''):
        """Process messages on the IOPub channel

           This method consumes and processes messages on the IOPub channel,
           such as stdout, stderr, execute_result and status.

           It only displays output that is caused by this session.
        """
        while self.client.iopub_channel.msg_ready():
            sub_msg = self.client.iopub_channel.get_msg()
            msg_type = sub_msg['header']['msg_type']
            parent = sub_msg["parent_header"]

            # Update execution_count in case it changed in another session
            if msg_type == "execute_input":
                self.execution_count = int(sub_msg["content"]["execution_count"]) + 1

            if self.include_output(sub_msg):
                if msg_type == 'status':
                    self._execution_state = sub_msg["content"]["execution_state"]
                elif msg_type == 'stream':
                    if sub_msg["content"]["name"] == "stdout":
                        if self._pending_clearoutput:
                            print("\r", end="")
                            self._pending_clearoutput = False
                        print(sub_msg["content"]["text"], end="")
                        sys.stdout.flush()
                    elif sub_msg["content"]["name"] == "stderr":
                        if self._pending_clearoutput:
                            print("\r", file=sys.stderr, end="")
                            self._pending_clearoutput = False
                        print(sub_msg["content"]["text"], file=sys.stderr, end="")
                        sys.stderr.flush()

                elif msg_type == 'execute_result':
                    if self._pending_clearoutput:
                        print("\r", end="")
                        self._pending_clearoutput = False
                    self.execution_count = int(sub_msg["content"]["execution_count"])
                    if not self.from_here(sub_msg):
                        sys.stdout.write(self.other_output_prefix)
                    format_dict = sub_msg["content"]["data"]
                    self.handle_rich_data(format_dict)

                    if 'text/plain' not in format_dict:
                        continue

                    # prompt_toolkit writes the prompt at a slightly lower level,
                    # so flush streams first to ensure correct ordering.
                    sys.stdout.flush()
                    sys.stderr.flush()
                    self.print_out_prompt()
                    text_repr = format_dict['text/plain']
                    if '\n' in text_repr:
                        # For multi-line results, start a new line after prompt
                        print()
                    print(text_repr)

                elif msg_type == 'display_data':
                    data = sub_msg["content"]["data"]
                    handled = self.handle_rich_data(data)
                    if not handled:
                        if not self.from_here(sub_msg):
                            sys.stdout.write(self.other_output_prefix)
                        # if it was an image, we handled it by now
                        if 'text/plain' in data:
                            print(data['text/plain'])

                elif msg_type == 'execute_input':
                    content = sub_msg['content']
                    if not self.from_here(sub_msg):
                        sys.stdout.write(self.other_output_prefix)
                    sys.stdout.write('In [{}]: '.format(content['execution_count']))
                    sys.stdout.write(content['code'] + '\n')

                elif msg_type == 'clear_output':
                    if sub_msg["content"]["wait"]:
                        self._pending_clearoutput = True
                    else:
                        print("\r", end="")

                elif msg_type == 'error':
                    for frame in sub_msg["content"]["traceback"]:
                        print(frame, file=sys.stderr)

    _imagemime = {
        'image/png': 'png',
        'image/jpeg': 'jpeg',
        'image/svg+xml': 'svg',
    }

    def handle_rich_data(self, data):
        for mime in self.mime_preference:
            if mime in data and mime in self._imagemime:
                if self.handle_image(data, mime):
                    return True
        return False

    def handle_image(self, data, mime):
        handler = getattr(
            self, 'handle_image_{0}'.format(self.image_handler), None)
        if handler:
            return handler(data, mime)

    def handle_image_PIL(self, data, mime):
        if mime not in ('image/png', 'image/jpeg'):
            return False
        try:
            from PIL import Image, ImageShow
        except ImportError:
            return False
        raw = base64.decodestring(data[mime].encode('ascii'))
        img = Image.open(BytesIO(raw))
        return ImageShow.show(img)

    def handle_image_stream(self, data, mime):
        raw = base64.decodestring(data[mime].encode('ascii'))
        imageformat = self._imagemime[mime]
        fmt = dict(format=imageformat)
        args = [s.format(**fmt) for s in self.stream_image_handler]
        with open(os.devnull, 'w') as devnull:
            proc = subprocess.Popen(
                args, stdin=subprocess.PIPE,
                stdout=devnull, stderr=devnull)
            proc.communicate(raw)
        return (proc.returncode == 0)

    def handle_image_tempfile(self, data, mime):
        raw = base64.decodestring(data[mime].encode('ascii'))
        imageformat = self._imagemime[mime]
        filename = 'tmp.{0}'.format(imageformat)
        with NamedFileInTemporaryDirectory(filename) as f, \
                open(os.devnull, 'w') as devnull:
            f.write(raw)
            f.flush()
            fmt = dict(file=f.name, format=imageformat)
            args = [s.format(**fmt) for s in self.tempfile_image_handler]
            rc = subprocess.call(args, stdout=devnull, stderr=devnull)
        return (rc == 0)

    def handle_image_callable(self, data, mime):
        res = self.callable_image_handler(data)
        if res is not False:
            # If handler func returns e.g. None, assume it has handled the data.
            res = True
        return res

    def handle_input_request(self, msg_id, timeout=0.1):
        """ Method to capture raw_input
        """
        req = self.client.stdin_channel.get_msg(timeout=timeout)
        # in case any iopub came while we were waiting:
        self.handle_iopub(msg_id)
        if msg_id == req["parent_header"].get("msg_id"):
            # wrap SIGINT handler
            real_handler = signal.getsignal(signal.SIGINT)

            def double_int(sig, frame):
                # call real handler (forwards sigint to kernel),
                # then raise local interrupt, stopping local raw_input
                real_handler(sig, frame)
                raise KeyboardInterrupt
            signal.signal(signal.SIGINT, double_int)
            content = req['content']
            read = getpass if content.get('password', False) else input
            try:
                raw_data = read(content["prompt"])
            except EOFError:
                # turn EOFError into EOF character
                raw_data = '\x04'
            except KeyboardInterrupt:
                sys.stdout.write('\n')
                return
            finally:
                # restore SIGINT handler
                signal.signal(signal.SIGINT, real_handler)

            # only send stdin reply if there *was not* another request
            # or execution finished while we were reading.
            if not (self.client.stdin_channel.msg_ready() or
                    self.client.shell_channel.msg_ready()):
                self.client.input(raw_data)
Exemple #6
0
if __name__ == '__main__':
    default_config()
    style = Style.from_dict({
        # User input (default text).
        '': '#ffffff',
        # Prompt.
        'pound': '#caa9fa',
    })
    message = [
        ('class:pound', '>>> '),
    ]
    session = PromptSession(history=FileHistory(HISTORY_PATH))
    while True:
        user_input = session.prompt(message,
                                    style=style,
                                    auto_suggest=AutoSuggestFromHistory(),
                                    completer=generate_completer())
        blocks = trim(user_input).split(' ')

        if blocks[0] == 'all':
            connect_node.print_node()
        elif blocks[0] == 'info':
            json2vmess.show_info(blocks[1:])
        elif blocks[0] == 'disconnect':
            connect_node.disconnect()
        elif blocks[0] == 'connect':
            connect_node.connect(blocks[1:])
        elif blocks[0] == 'delete':
            del_func.delete_func(blocks[1:])
        elif blocks[0] == 'port':
            port_path.port_about(blocks[1:])
Exemple #7
0
class PromptToolkit2Shell(BaseShell):
    """The xonsh shell for prompt_toolkit v2."""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if ON_WINDOWS:
            winutils.enable_virtual_terminal_processing()
        self._first_prompt = True
        self.history = ThreadedHistory(PromptToolkitHistory())
        self.prompter = PromptSession(history=self.history)
        self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx, self)
        self.key_bindings = KeyBindings()
        load_xonsh_bindings(self.key_bindings)
        # This assumes that PromptToolkit2Shell is a singleton
        events.on_ptk_create.fire(
            prompter=self.prompter,
            history=self.history,
            completer=self.pt_completer,
            bindings=self.key_bindings,
        )

    def singleline(self, auto_suggest=None, enable_history_search=True,
                   multiline=True, **kwargs):
        """Reads a single line of input from the shell. The store_in_history
        kwarg flags whether the input should be stored in PTK's in-memory
        history.
        """
        events.on_pre_prompt.fire()
        env = builtins.__xonsh_env__
        mouse_support = env.get('MOUSE_SUPPORT')
        auto_suggest = auto_suggest if env.get('AUTO_SUGGEST') else None
        completions_display = env.get('COMPLETIONS_DISPLAY')
        if completions_display == 'multi':
            complete_style = CompleteStyle.MULTI_COLUMN
        complete_while_typing = env.get('UPDATE_COMPLETIONS_ON_KEYPRESS')
        if complete_while_typing:
            # PTK requires history search to be none when completing while typing
            enable_history_search = False
        if HAS_PYGMENTS:
            self.styler.style_name = env.get('XONSH_COLOR_STYLE')
        completer = None if completions_display == 'none' else self.pt_completer

        if env.get('UPDATE_PROMPT_ON_KEYPRESS'):
            get_prompt_tokens = self.prompt_tokens
            get_rprompt_tokens = self.rprompt_tokens
            get_bottom_toolbar_tokens = self.bottom_toolbar_tokens
        else:
            get_prompt_tokens = self.prompt_tokens()
            get_rprompt_tokens = self.rprompt_tokens()
            get_bottom_toolbar_tokens = self.bottom_toolbar_tokens()

        if env.get('VI_MODE'):
            editing_mode = EditingMode.VI
        else:
            editing_mode = EditingMode.EMACS

        prompt_args = {
            'mouse_support': mouse_support,
            'auto_suggest': auto_suggest,
            'message': get_prompt_tokens,
            'rprompt': get_rprompt_tokens,
            'bottom_toolbar': get_bottom_toolbar_tokens,
            'completer': completer,
            'multiline': multiline,
            'editing_mode': editing_mode,
            'prompt_continuation': self.continuation_tokens,
            'enable_history_search': enable_history_search,
            'reserve_space_for_menu': 0,
            'key_bindings': self.key_bindings,
            'complete_style': complete_style,
            'complete_while_typing': complete_while_typing,
            'include_default_pygments_style': False,
        }
        if builtins.__xonsh_env__.get('COLOR_INPUT'):
            if HAS_PYGMENTS:
                prompt_args['lexer'] = PygmentsLexer(pyghooks.XonshLexer)
                style = style_from_pygments_cls(
                    pyghooks.xonsh_style_proxy(self.styler))
            else:
                style = style_from_pygments_dict(DEFAULT_STYLE_DICT)

            prompt_args['style'] = style

        line = self.prompter.prompt(**prompt_args)
        events.on_post_prompt.fire()
        return line

    def _push(self, line):
        """Pushes a line onto the buffer and compiles the code in a way that
        enables multiline input.
        """
        code = None
        self.buffer.append(line)
        if self.need_more_lines:
            return None, code
        src = ''.join(self.buffer)
        src = transform_command(src)
        try:
            code = self.execer.compile(src,
                                       mode='single',
                                       glbs=self.ctx,
                                       locs=None)
            self.reset_buffer()
        except Exception:  # pylint: disable=broad-except
            self.reset_buffer()
            print_exception()
            return src, None
        return src, code

    def cmdloop(self, intro=None):
        """Enters a loop that reads and execute input from user."""
        if intro:
            print(intro)
        auto_suggest = AutoSuggestFromHistory()
        self.push = self._push
        while not builtins.__xonsh_exit__:
            try:
                line = self.singleline(auto_suggest=auto_suggest)
                if not line:
                    self.emptyline()
                else:
                    line = self.precmd(line)
                    self.default(line)
            except (KeyboardInterrupt, SystemExit):
                self.reset_buffer()
            except EOFError:
                if builtins.__xonsh_env__.get("IGNOREEOF"):
                    print('Use "exit" to leave the shell.', file=sys.stderr)
                else:
                    break

    def prompt_tokens(self):
        """Returns a list of (token, str) tuples for the current prompt."""
        p = builtins.__xonsh_env__.get('PROMPT')
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        if self._first_prompt:
            carriage_return()
            self._first_prompt = False
        self.settitle()
        return PygmentsTokens(toks)

    def rprompt_tokens(self):
        """Returns a list of (token, str) tuples for the current right
        prompt.
        """
        p = builtins.__xonsh_env__.get('RIGHT_PROMPT')
        # self.prompt_formatter does handle empty strings properly,
        # but this avoids descending into it in the common case of
        # $RIGHT_PROMPT == ''.
        if isinstance(p, str) and len(p) == 0:
            return []
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        return PygmentsTokens(toks)

    def bottom_toolbar_tokens(self):
        """Returns a list of (token, str) tuples for the current bottom
        toolbar.
        """
        p = builtins.__xonsh_env__.get('BOTTOM_TOOLBAR')
        if not p:
            return
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        return PygmentsTokens(toks)

    def continuation_tokens(self, width, line_number, is_soft_wrap=False):
        """Displays dots in multiline prompt"""
        if is_soft_wrap:
            return ''
        width = width - 1
        dots = builtins.__xonsh_env__.get('MULTILINE_PROMPT')
        dots = dots() if callable(dots) else dots
        if dots is None:
            return [(Token, ' ' * (width + 1))]
        basetoks = self.format_color(dots)
        baselen = sum(len(t[1]) for t in basetoks)
        if baselen == 0:
            return [(Token, ' ' * (width + 1))]
        toks = basetoks * (width // baselen)
        n = width % baselen
        count = 0
        for tok in basetoks:
            slen = len(tok[1])
            newcount = slen + count
            if slen == 0:
                continue
            elif newcount <= n:
                toks.append(tok)
            else:
                toks.append((tok[0], tok[1][:n - count]))
            count = newcount
            if n <= count:
                break
        toks.append((Token, ' '))  # final space
        return PygmentsTokens(toks)

    def format_color(self, string, hide=False, force_string=False, **kwargs):
        """Formats a color string using Pygments. This, therefore, returns
        a list of (Token, str) tuples. If force_string is set to true, though,
        this will return a color formatted string.
        """
        tokens = partial_color_tokenize(string)
        if force_string and HAS_PYGMENTS:
            env = builtins.__xonsh_env__
            self.styler.style_name = env.get('XONSH_COLOR_STYLE')
            proxy_style = pyghooks.xonsh_style_proxy(self.styler)
            formatter = pyghooks.XonshTerminal256Formatter(style=proxy_style)
            s = pygments.format(tokens, formatter)
            return s
        elif force_string:
            print("To force colorization of string, install Pygments")
            return tokens
        else:
            return tokens

    def print_color(self, string, end='\n', **kwargs):
        """Prints a color string using prompt-toolkit color management."""
        if isinstance(string, str):
            tokens = partial_color_tokenize(string)
        else:
            # assume this is a list of (Token, str) tuples and just print
            tokens = string
        tokens = PygmentsTokens(tokens)
        if HAS_PYGMENTS:
            env = builtins.__xonsh_env__
            self.styler.style_name = env.get('XONSH_COLOR_STYLE')
            proxy_style = style_from_pygments_cls(pyghooks.xonsh_style_proxy(self.styler))
        else:
            proxy_style = style_from_pygments_dict(DEFAULT_STYLE_DICT)
        ptk_print(tokens, style=proxy_style, end=end, include_default_pygments_style=False)

    def color_style_names(self):
        """Returns an iterable of all available style names."""
        if not HAS_PYGMENTS:
            return ['For other xonsh styles, please install pygments']
        return get_all_styles()

    def color_style(self):
        """Returns the current color map."""
        if not HAS_PYGMENTS:
            return DEFAULT_STYLE_DICT
        env = builtins.__xonsh_env__
        self.styler.style_name = env.get('XONSH_COLOR_STYLE')
        return self.styler.styles

    def restore_tty_sanity(self):
        """An interface for resetting the TTY stdin mode. This is highly
Exemple #8
0
class PromptToolkit2Shell(BaseShell):
    """The xonsh shell for prompt_toolkit v2."""

    completion_displays_to_styles = {
        "multi": CompleteStyle.MULTI_COLUMN,
        "single": CompleteStyle.COLUMN,
        "readline": CompleteStyle.READLINE_LIKE,
        "none": None,
    }

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if ON_WINDOWS:
            winutils.enable_virtual_terminal_processing()
        self._first_prompt = True
        self.history = ThreadedHistory(PromptToolkitHistory())
        self.prompter = PromptSession(history=self.history)
        self.pt_completer = PromptToolkitCompleter(self.completer, self.ctx, self)
        self.key_bindings = KeyBindings()
        load_xonsh_bindings(self.key_bindings)
        # Store original `_history_matches` in case we need to restore it
        self._history_matches_orig = self.prompter.default_buffer._history_matches
        # This assumes that PromptToolkit2Shell is a singleton
        events.on_ptk_create.fire(
            prompter=self.prompter,
            history=self.history,
            completer=self.pt_completer,
            bindings=self.key_bindings,
        )

    def singleline(
        self, auto_suggest=None, enable_history_search=True, multiline=True, **kwargs
    ):
        """Reads a single line of input from the shell. The store_in_history
        kwarg flags whether the input should be stored in PTK's in-memory
        history.
        """
        events.on_pre_prompt.fire()
        env = builtins.__xonsh__.env
        mouse_support = env.get("MOUSE_SUPPORT")
        auto_suggest = auto_suggest if env.get("AUTO_SUGGEST") else None
        completions_display = env.get("COMPLETIONS_DISPLAY")
        complete_style = self.completion_displays_to_styles[completions_display]

        complete_while_typing = env.get("UPDATE_COMPLETIONS_ON_KEYPRESS")
        if complete_while_typing:
            # PTK requires history search to be none when completing while typing
            enable_history_search = False
        if HAS_PYGMENTS:
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
        completer = None if completions_display == "none" else self.pt_completer

        if env.get("UPDATE_PROMPT_ON_KEYPRESS"):
            get_prompt_tokens = self.prompt_tokens
            get_rprompt_tokens = self.rprompt_tokens
            get_bottom_toolbar_tokens = self.bottom_toolbar_tokens
        else:
            get_prompt_tokens = self.prompt_tokens()
            get_rprompt_tokens = self.rprompt_tokens()
            get_bottom_toolbar_tokens = self.bottom_toolbar_tokens()

        if env.get("VI_MODE"):
            editing_mode = EditingMode.VI
        else:
            editing_mode = EditingMode.EMACS

        if env.get("XONSH_HISTORY_MATCH_ANYWHERE"):
            self.prompter.default_buffer._history_matches = MethodType(
                _cust_history_matches, self.prompter.default_buffer
            )
        elif (
            self.prompter.default_buffer._history_matches
            is not self._history_matches_orig
        ):
            self.prompter.default_buffer._history_matches = self._history_matches_orig

        prompt_args = {
            "mouse_support": mouse_support,
            "auto_suggest": auto_suggest,
            "message": get_prompt_tokens,
            "rprompt": get_rprompt_tokens,
            "bottom_toolbar": get_bottom_toolbar_tokens,
            "completer": completer,
            "multiline": multiline,
            "editing_mode": editing_mode,
            "prompt_continuation": self.continuation_tokens,
            "enable_history_search": enable_history_search,
            "reserve_space_for_menu": 0,
            "key_bindings": self.key_bindings,
            "complete_style": complete_style,
            "complete_while_typing": complete_while_typing,
            "include_default_pygments_style": False,
        }
        if builtins.__xonsh__.env.get("COLOR_INPUT"):
            if HAS_PYGMENTS:
                prompt_args["lexer"] = PygmentsLexer(pyghooks.XonshLexer)
                style = style_from_pygments_cls(pyghooks.xonsh_style_proxy(self.styler))
            else:
                style = style_from_pygments_dict(DEFAULT_STYLE_DICT)

            prompt_args["style"] = style

            style_overrides_env = env.get("PTK_STYLE_OVERRIDES")
            if style_overrides_env:
                try:
                    style_overrides = Style.from_dict(style_overrides_env)
                    prompt_args["style"] = merge_styles([style, style_overrides])
                except (AttributeError, TypeError, ValueError):
                    print_exception()

        line = self.prompter.prompt(**prompt_args)
        events.on_post_prompt.fire()
        return line

    def _push(self, line):
        """Pushes a line onto the buffer and compiles the code in a way that
        enables multiline input.
        """
        code = None
        self.buffer.append(line)
        if self.need_more_lines:
            return None, code
        src = "".join(self.buffer)
        src = transform_command(src)
        try:
            code = self.execer.compile(src, mode="single", glbs=self.ctx, locs=None)
            self.reset_buffer()
        except Exception:  # pylint: disable=broad-except
            self.reset_buffer()
            print_exception()
            return src, None
        return src, code

    def cmdloop(self, intro=None):
        """Enters a loop that reads and execute input from user."""
        if intro:
            print(intro)
        auto_suggest = AutoSuggestFromHistory()
        self.push = self._push
        while not builtins.__xonsh__.exit:
            try:
                line = self.singleline(auto_suggest=auto_suggest)
                if not line:
                    self.emptyline()
                else:
                    line = self.precmd(line)
                    self.default(line)
            except (KeyboardInterrupt, SystemExit):
                self.reset_buffer()
            except EOFError:
                if builtins.__xonsh__.env.get("IGNOREEOF"):
                    print('Use "exit" to leave the shell.', file=sys.stderr)
                else:
                    break

    def prompt_tokens(self):
        """Returns a list of (token, str) tuples for the current prompt."""
        p = builtins.__xonsh__.env.get("PROMPT")
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        if self._first_prompt:
            carriage_return()
            self._first_prompt = False
        self.settitle()
        return PygmentsTokens(toks)

    def rprompt_tokens(self):
        """Returns a list of (token, str) tuples for the current right
        prompt.
        """
        p = builtins.__xonsh__.env.get("RIGHT_PROMPT")
        # self.prompt_formatter does handle empty strings properly,
        # but this avoids descending into it in the common case of
        # $RIGHT_PROMPT == ''.
        if isinstance(p, str) and len(p) == 0:
            return []
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        return PygmentsTokens(toks)

    def bottom_toolbar_tokens(self):
        """Returns a list of (token, str) tuples for the current bottom
        toolbar.
        """
        p = builtins.__xonsh__.env.get("BOTTOM_TOOLBAR")
        if not p:
            return
        try:
            p = self.prompt_formatter(p)
        except Exception:  # pylint: disable=broad-except
            print_exception()
        toks = partial_color_tokenize(p)
        return PygmentsTokens(toks)

    def continuation_tokens(self, width, line_number, is_soft_wrap=False):
        """Displays dots in multiline prompt"""
        if is_soft_wrap:
            return ""
        width = width - 1
        dots = builtins.__xonsh__.env.get("MULTILINE_PROMPT")
        dots = dots() if callable(dots) else dots
        if dots is None:
            return [(Token, " " * (width + 1))]
        basetoks = self.format_color(dots)
        baselen = sum(len(t[1]) for t in basetoks)
        if baselen == 0:
            return [(Token, " " * (width + 1))]
        toks = basetoks * (width // baselen)
        n = width % baselen
        count = 0
        for tok in basetoks:
            slen = len(tok[1])
            newcount = slen + count
            if slen == 0:
                continue
            elif newcount <= n:
                toks.append(tok)
            else:
                toks.append((tok[0], tok[1][: n - count]))
            count = newcount
            if n <= count:
                break
        toks.append((Token, " "))  # final space
        return PygmentsTokens(toks)

    def format_color(self, string, hide=False, force_string=False, **kwargs):
        """Formats a color string using Pygments. This, therefore, returns
        a list of (Token, str) tuples. If force_string is set to true, though,
        this will return a color formatted string.
        """
        tokens = partial_color_tokenize(string)
        if force_string and HAS_PYGMENTS:
            env = builtins.__xonsh__.env
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
            proxy_style = pyghooks.xonsh_style_proxy(self.styler)
            formatter = pyghooks.XonshTerminal256Formatter(style=proxy_style)
            s = pygments.format(tokens, formatter)
            return s
        elif force_string:
            print("To force colorization of string, install Pygments")
            return tokens
        else:
            return tokens

    def print_color(self, string, end="\n", **kwargs):
        """Prints a color string using prompt-toolkit color management."""
        if isinstance(string, str):
            tokens = partial_color_tokenize(string)
        else:
            # assume this is a list of (Token, str) tuples and just print
            tokens = string
        tokens = PygmentsTokens(tokens)
        if HAS_PYGMENTS:
            env = builtins.__xonsh__.env
            self.styler.style_name = env.get("XONSH_COLOR_STYLE")
            proxy_style = style_from_pygments_cls(
                pyghooks.xonsh_style_proxy(self.styler)
            )
        else:
            proxy_style = style_from_pygments_dict(DEFAULT_STYLE_DICT)
        ptk_print(
            tokens, style=proxy_style, end=end, include_default_pygments_style=False
        )

    def color_style_names(self):
        """Returns an iterable of all available style names."""
        if not HAS_PYGMENTS:
            return ["For other xonsh styles, please install pygments"]
        return get_all_styles()

    def color_style(self):
        """Returns the current color map."""
        if not HAS_PYGMENTS:
            return DEFAULT_STYLE_DICT
        env = builtins.__xonsh__.env
        self.styler.style_name = env.get("XONSH_COLOR_STYLE")
        return self.styler.styles

    def restore_tty_sanity(self):
        """An interface for resetting the TTY stdin mode. This is highly
Exemple #9
0
class TerminalPdb(Pdb):
    """Standalone IPython debugger."""

    def __init__(self, *args, **kwargs):
        Pdb.__init__(self, *args, **kwargs)
        self._ptcomp = None
        self.pt_init()

    def pt_init(self):
        def get_prompt_tokens():
            return [(Token.Prompt, self.prompt)]

        if self._ptcomp is None:
            compl = IPCompleter(shell=self.shell,
                                        namespace={},
                                        global_namespace={},
                                        parent=self.shell,
                                       )
            self._ptcomp = IPythonPTCompleter(compl)

        kb = KeyBindings()
        supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
        kb.add('c-z', filter=supports_suspend)(suspend_to_bg)

        if self.shell.display_completions == 'readlinelike':
            kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
                                  & ~has_selection
                                  & vi_insert_mode | emacs_insert_mode
                                  & ~cursor_in_leading_ws
                              ))(display_completions_like_readline)

        options = dict(
            message=(lambda: PygmentsTokens(get_prompt_tokens())),
            editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
            key_bindings=kb,
            history=self.shell.debugger_history,
            completer=self._ptcomp,
            enable_history_search=True,
            mouse_support=self.shell.mouse_support,
            complete_style=self.shell.pt_complete_style,
            style=self.shell.style,
            color_depth=self.shell.color_depth,
        )

        if not PTK3:
            options['inputhook'] = self.shell.inputhook
        self.pt_loop = asyncio.new_event_loop()
        self.pt_app = PromptSession(**options)

    def cmdloop(self, intro=None):
        """Repeatedly issue a prompt, accept input, parse an initial prefix
        off the received input, and dispatch to action methods, passing them
        the remainder of the line as argument.

        override the same methods from cmd.Cmd to provide prompt toolkit replacement.
        """
        if not self.use_rawinput:
            raise ValueError('Sorry ipdb does not support use_rawinput=False')

        # In order to make sure that asyncio code written in the
        # interactive shell doesn't interfere with the prompt, we run the
        # prompt in a different event loop.
        # If we don't do this, people could spawn coroutine with a
        # while/true inside which will freeze the prompt.

        try:
            old_loop = asyncio.get_event_loop()
        except RuntimeError:
            # This happens when the user used `asyncio.run()`.
            old_loop = None


        self.preloop()

        try:
            if intro is not None:
                self.intro = intro
            if self.intro:
                self.stdout.write(str(self.intro)+"\n")
            stop = None
            while not stop:
                if self.cmdqueue:
                    line = self.cmdqueue.pop(0)
                else:
                    self._ptcomp.ipy_completer.namespace = self.curframe_locals
                    self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals

                    asyncio.set_event_loop(self.pt_loop)
                    try:
                        line = self.pt_app.prompt()
                    except EOFError:
                        line = 'EOF'
                    finally:
                        # Restore the original event loop.
                        asyncio.set_event_loop(old_loop)

                line = self.precmd(line)
                stop = self.onecmd(line)
                stop = self.postcmd(stop, line)
            self.postloop()
        except Exception:
            raise
Exemple #10
0
class InputPrompt(BaseSimplePrompt):
    """Create a text prompt that accepts user input.

    A wrapper class around :class:`~prompt_toolkit.shortcuts.PromptSession`.

    Args:
        message: The question to ask the user.
            Refer to :ref:`pages/dynamic:message` documentation for more details.
        style: An :class:`InquirerPyStyle` instance.
            Refer to :ref:`Style <pages/style:Alternate Syntax>` documentation for more details.
        vi_mode: Use vim keybinding for the prompt.
            Refer to :ref:`pages/kb:Keybindings` documentation for more details.
        default: Set the default text value of the prompt.
            Refer to :ref:`pages/dynamic:default` documentation for more details.
        qmark: Question mark symbol. Custom symbol that will be displayed infront of the question before its answered.
        amark: Answer mark symbol. Custom symbol that will be displayed infront of the question after its answered.
        instruction: Short instruction to display next to the question.
        long_instruction: Long instructions to display at the bottom of the prompt.
        completer: Add auto completion to the prompt.
            Refer to :ref:`pages/prompts/input:Auto Completion` documentation for more details.
        multicolumn_complete: Change the auto-completion UI to a multi column display.
        multiline: Enable multiline edit. While multiline edit is active, pressing `enter` won't complete the answer.
            and will create a new line. Use `esc` followd by `enter` to complete the question.
        validate: Add validation to user input.
            Refer to :ref:`pages/validator:Validator` documentation for more details.
        invalid_message: Error message to display when user input is invalid.
            Refer to :ref:`pages/validator:Validator` documentation for more details.
        transformer: A function which performs additional transformation on the value that gets printed to the terminal.
            Different than `filter` parameter, this is only visual effect and won’t affect the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
            Refer to :ref:`pages/dynamic:transformer` documentation for more details.
        filter: A function which performs additional transformation on the result.
            This affects the actual value returned by :meth:`~InquirerPy.base.simple.BaseSimplePrompt.execute`.
            Refer to :ref:`pages/dynamic:filter` documentation for more details.
        keybindings: Customise the builtin keybindings.
            Refer to :ref:`pages/kb:Keybindings` for more details.
        wrap_lines: Soft wrap question lines when question exceeds the terminal width.
        raise_keyboard_interrupt: Raise the :class:`KeyboardInterrupt` exception when `ctrl-c` is pressed. If false, the result
            will be `None` and the question is skiped.
        is_password: Used internally for :class:`~InquirerPy.prompts.secret.SecretPrompt`.
        mandatory: Indicate if the prompt is mandatory. If True, then the question cannot be skipped.
        mandatory_message: Error message to show when user attempts to skip mandatory prompt.
        session_result: Used internally for :ref:`index:Classic Syntax (PyInquirer)`.
        input: Used internally and will be removed in future updates.
        output: Used internally and will be removed in future updates.

    Examples:
        >>> from InquirerPy import inquirer
        >>> result = inquirer.text(message="Enter your name:").execute()
        >>> print(f"Name: {result}")
        Name: Michael
    """
    def __init__(
        self,
        message: InquirerPyMessage,
        style: InquirerPyStyle = None,
        vi_mode: bool = False,
        default: InquirerPyDefault = "",
        qmark: str = "?",
        amark: str = "?",
        instruction: str = "",
        long_instruction: str = "",
        completer: Union[Dict[str, Optional[str]], "Completer"] = None,
        multicolumn_complete: bool = False,
        multiline: bool = False,
        validate: InquirerPyValidate = None,
        invalid_message: str = "Invalid input",
        transformer: Callable[[str], Any] = None,
        filter: Callable[[str], Any] = None,
        keybindings: InquirerPyKeybindings = None,
        wrap_lines: bool = True,
        raise_keyboard_interrupt: bool = True,
        is_password: bool = False,
        mandatory: bool = True,
        mandatory_message: str = "Mandatory prompt",
        session_result: InquirerPySessionResult = None,
        input: "Input" = None,
        output: "Output" = None,
    ) -> None:
        super().__init__(
            message,
            style,
            vi_mode=vi_mode,
            qmark=qmark,
            amark=amark,
            instruction=instruction,
            validate=validate,
            invalid_message=invalid_message,
            transformer=transformer,
            filter=filter,
            session_result=session_result,
            default=default,
            wrap_lines=wrap_lines,
            mandatory=mandatory,
            mandatory_message=mandatory_message,
            raise_keyboard_interrupt=raise_keyboard_interrupt,
        )
        if not isinstance(self._default, str):
            raise InvalidArgument(
                f"{type(self).__name__} argument 'default' should be type of str"
            )
        self._completer = None
        if isinstance(completer, dict):
            self._completer = NestedCompleter.from_nested_dict(completer)
        elif isinstance(completer, Completer):
            self._completer = completer
        self._multiline = multiline
        self._complete_style = (CompleteStyle.COLUMN
                                if not multicolumn_complete else
                                CompleteStyle.MULTI_COLUMN)

        @Condition
        def is_multiline():
            return self._multiline

        if not keybindings:
            keybindings = {}
        self.kb_maps = {
            "answer": [
                {
                    "key": Keys.Enter,
                    "filter": ~is_multiline
                },
                {
                    "key": [Keys.Escape, Keys.Enter],
                    "filter": is_multiline
                },
            ],
            "completion": [{
                "key": "c-space"
            }],
            **keybindings,
        }
        self.kb_func_lookup = {
            "completion": [{
                "func": self._handle_completion
            }]
        }
        self._keybinding_factory()

        self._session = PromptSession(
            message=self._get_prompt_message,
            key_bindings=self._kb,
            style=self._style,
            completer=self._completer,
            validator=self._validator,
            validate_while_typing=False,
            input=input,
            output=output,
            editing_mode=self._editing_mode,
            lexer=SimpleLexer(self._lexer),
            is_password=is_password,
            multiline=self._multiline,
            complete_style=self._complete_style,
            wrap_lines=wrap_lines,
            bottom_toolbar=[("class:long_instruction",
                             long_instruction)] if long_instruction else None,
        )

    def _set_error(self, message: str) -> None:
        self._session.default_buffer.validation_state = ValidationState.INVALID
        self._session.default_buffer.validation_error = ValidationError(
            message=message)

    def _handle_enter(self, event) -> None:
        try:
            self._session.validator.validate(
                self._session.default_buffer)  # type: ignore
        except ValidationError:
            self._session.default_buffer.validate_and_handle()
        else:
            self.status["answered"] = True
            self.status["result"] = self._session.default_buffer.text
            self._session.default_buffer.text = ""
            event.app.exit(result=self.status["result"])

    def _handle_completion(self, event) -> None:
        if self._completer is None:
            return
        buff = event.app.current_buffer
        if buff.complete_state:
            buff.complete_next()
        else:
            buff.start_completion(select_first=False)

    def _get_prompt_message(
        self,
        pre_answer: Optional[Tuple[str, str]] = None,
        post_answer: Optional[Tuple[str, str]] = None,
    ) -> List[Tuple[str, str]]:
        """Get message to display infront of the input buffer.

        Args:
            pre_answer: The formatted text to display before answering the question.
            post_answer: The formatted text to display after answering the question.

        Returns:
            Formatted text in list of tuple format.
        """
        if not pre_answer:
            if self._multiline and not self._instruction:
                pre_answer = ("class:instruction",
                              " ESC + Enter to finish input")
            else:
                pre_answer = (
                    "class:instruction",
                    " %s " % self.instruction if self.instruction else " ",
                )
        if not post_answer:
            if self._multiline and self.status["result"]:
                lines = self.status["result"].split("\n")
                if len(lines) > 1:
                    number_of_chars = len("".join(lines[1:]))
                    lines[0] += "...[%s char%s]" % (
                        number_of_chars,
                        "s" if number_of_chars > 1 else "",
                    )
                post_answer = ("class:answer", " %s" % lines[0])
            else:
                post_answer = ("class:answer", " %s" % self.status["result"])

        formatted_message = super()._get_prompt_message(
            pre_answer, post_answer)
        if not self.status["answered"] and self._multiline:
            formatted_message.append(
                ("class:questionmark", "\n%s " % INQUIRERPY_POINTER_SEQUENCE))
        return formatted_message

    def _run(self) -> str:
        return self._session.prompt(default=self._default)

    async def _run_async(self) -> Any:
        return await self._session.prompt_async(default=self._default)