def interactive_shell():
    """
    Coroutine that shows the interactive command line.
    """
    # Create an asyncio `EventLoop` object. This is a wrapper around the
    # asyncio loop that can be passed into prompt_toolkit.
    eventloop = create_asyncio_eventloop()

    # Create interface.
    cli = CommandLineInterface(application=create_default_application(
        'Say something inside the event loop: '),
                               eventloop=eventloop)

    # Patch stdout in something that will always print *above* the prompt when
    # something is written to stdout.
    sys.stdout = cli.stdout_proxy()

    # Run echo loop. Read text from stdin, and reply it back.
    while True:
        try:
            result = yield from cli.run_async()
            print('You said: "%s"\n' % result.text)
        except (EOFError, KeyboardInterrupt):
            loop.stop()
            print('Qutting event loop. Bye.')
            return
def interactive_shell():
    """
    Coroutine that shows the interactive command line.
    """
    # Create an asyncio `EventLoop` object. This is a wrapper around the
    # asyncio loop that can be passed into prompt_toolkit.
    eventloop = create_asyncio_eventloop()

    # Create interface.
    cli = CommandLineInterface(
        application=create_default_application('Say something inside the event loop: '),
        eventloop=eventloop)

    # Patch stdout in something that will always print *above* the prompt when
    # something is written to stdout.
    sys.stdout = cli.stdout_proxy()

    # Run echo loop. Read text from stdin, and reply it back.
    while True:
        try:
            result = yield from cli.run_async()
            print('You said: "%s"\n' % result.text)
        except (EOFError, KeyboardInterrupt):
            loop.stop()
            print('Qutting event loop. Bye.')
            return
Example #3
0
class Tosh:
    def __init__(self, base_dir, config):
        for module in config.get('modules'):
            import_module(module)

        self.tasks = TaskManager(self)
        self.window = MainWindow(self)
        self.style = ToshStyle(config.get('ui', 'style'))
        self._parser = CommandLineParser(self, base_dir)
        self.config = config
        self.variables = {}

        application = Application(
            layout=self.window,
            buffer=Buffer(
                enable_history_search=True,
                complete_while_typing=False,
                is_multiline=False,
                history=FileHistory(base_dir + "/history"),
                # validator=validator,
                completer=CommandLineCompleter(self),
                auto_suggest=AutoSuggestFromHistory(),
                accept_action=AcceptAction(self.run_command),
            ),
            mouse_support=config.get('ui', 'mouse'),
            style=self.style,
            key_bindings_registry=get_key_bindings(self),
            use_alternate_screen=True)

        self._cli = CommandLineInterface(application=application,
                                         eventloop=create_asyncio_eventloop(),
                                         output=create_output(true_color=True))

    def refresh(self):
        self._cli.request_redraw()

    def _exception_handler(self, loop, context):
        self._cli.reset()
        print(context['message'])
        if 'exception' in context:
            traceback.print_exc()
        sys.exit(1)

    def run(self):
        for cmd in self.config.get('autostart'):
            cmd_task = self._parser.parse(cmd)
            cmd_task.set_cmdline(cmd)
            self.tasks._tasks.append(cmd_task)
            asyncio.ensure_future(cmd_task.run())

        asyncio.get_event_loop().set_exception_handler(self._exception_handler)
        try:
            asyncio.get_event_loop().run_until_complete(self._cli.run_async())
        except EOFError:
            pass

    def run_command(self, _, document):
        if not document.text:
            return
        try:
            cmd_task = self._parser.parse(document.text)
            if isinstance(cmd_task, Statement):
                cmd_task.set_cmdline(document.text)
                document.reset(append_to_history=True)
                self.tasks._tasks.append(cmd_task)
                asyncio.ensure_future(cmd_task.run())
            else:
                self.tasks._tasks.append(
                    ErrorStatement(self, document.text,
                                   "Parser returned no task"))
                document.reset(append_to_history=False)
        except BaseException as e:
            # Last resort error handler
            self.tasks._tasks.append(
                ErrorStatement(self, document.text, traceback.format_exc()))
            document.reset(append_to_history=False)
Example #4
0
class ReplSSHServerSession(asyncssh.SSHServerSession):

    """
    SSH server session that runs a Python REPL.

    :param get_globals: callable that returns the current globals.
    :param get_locals: (optional) callable that returns the current locals.
    """

    def __init__(self, get_globals, get_locals=None):
        assert callable(get_globals)
        assert get_locals is None or callable(get_locals)

        self._chan = None

        def _globals():
            data = get_globals()
            data.setdefault('print', self._print)
            return data

        repl = PythonRepl(get_globals=_globals,
                          get_locals=get_locals or _globals)

        # Disable open-in-editor and system prompt. Because it would run and
        # display these commands on the server side, rather than in the SSH
        # client.
        repl.enable_open_in_editor = False
        repl.enable_system_bindings = False

        # PipInput object, for sending input in the CLI.
        # (This is something that we can use in the prompt_toolkit event loop,
        # but still write date in manually.)
        self._input_pipe = PipeInput()

        # Output object. Don't render to the real stdout, but write everything
        # in the SSH channel.
        class Stdout(object):

            def write(s, data):
                if self._chan is not None:
                    self._chan.write(data.replace('\n', '\r\n'))

            def flush(s):
                pass

        # Create command line interface.
        self.cli = CommandLineInterface(
            application=repl.create_application(),
            eventloop=create_asyncio_eventloop(),
            input=self._input_pipe,
            output=Vt100_Output(Stdout(), self._get_size))

        self._callbacks = self.cli.create_eventloop_callbacks()

    def _get_size(self):
        """
        Callable that returns the current `Size`, required by Vt100_Output.
        """
        if self._chan is None:
            return Size(rows=20, columns=79)
        else:
            width, height, pixwidth, pixheight = self._chan.get_terminal_size()
            return Size(rows=height, columns=width)

    def connection_made(self, chan):
        """
        Client connected, run repl in coroutine.
        """
        self._chan = chan

        # Run REPL interface.
        f = asyncio.async(self.cli.run_async())

        # Close channel when done.
        def done(_):
            chan.close()
            self._chan = None
        f.add_done_callback(done)

    def shell_requested(self):
        return True

    def terminal_size_changed(self, width, height, pixwidth, pixheight):
        """
        When the terminal size changes, report back to CLI.
        """
        self._callbacks.terminal_size_changed()

    def data_received(self, data, datatype):
        """
        When data is received, send to inputstream of the CLI and repaint.
        """
        self._input_pipe.send(data)

    def _print(self, *data, **kw):
        """
        _print(self, *data, sep=' ', end='\n', file=None)

        Alternative 'print' function that prints back into the SSH channel.
        """
        # Pop keyword-only arguments. (We cannot use the syntax from the
        # signature. Otherwise, Python2 will give a syntax error message when
        # installing.)
        sep = kw.pop('sep', ' ')
        end = kw.pop('end', '\n')
        file = kw.pop('end', None)
        assert not kw, 'Too many keyword-only arguments'

        data = sep.join(map(str, data))
        self._chan.write(data + end)
Example #5
0
class ChromeConsoleApplication(object):
    ''' Main application object. '''
    def __init__(self, loop):
        self.is_connecting = False
        self.loop = loop
        self.layout = Layout(self)
        self.bindings = Keybindings()
        self.buffers = {
            DEFAULT_BUFFER: Buffer(is_multiline=True),
            COMMAND_BUFFER: Buffer(
                accept_action=AcceptAction(handler=self.handle_action),
            ),
            TAB_SELECT_BUFFER: Buffer(is_multiline=True),
        }
        self.application = Application(
            layout=self.layout.layout,
            buffers=self.buffers,
            key_bindings_registry=self.bindings.registry,
            use_alternate_screen=True,
            on_abort=AbortAction.RAISE_EXCEPTION,
            on_exit=AbortAction.RAISE_EXCEPTION,
        )
        self.cli = None

    def handle_action(self, cli, buffer):
        ''' Executes commands received from command prompt. '''
        # pylint: disable=unused-argument
        handle_command(self, buffer.text)
        # clears the buffer
        buffer.reset()

    def _display_tab_list(self, future):
        ''' Callback that outputs list of tabs to default buffer. '''
        response = future.result()
        tabs = response.json()
        text = ''
        self.is_connecting = True
        for idx, tab in enumerate(tabs):
            text += '{:02d}:  {}\n'.format(idx, tab['title'])
        self.buffers[TAB_SELECT_BUFFER].text = text
        self.cli.focus(TAB_SELECT_BUFFER)
        self.cli.invalidate()

    def load_tab_list(self, host='127.0.0.1', port=9222):
        '''
        Loads the list of Chrome tabs from the given host.
        Chrome remote debugging must have been enabled using the
        --remote-debugging-port command line option.
        '''
        url = 'http://{}:{}/json'.format(host, port)
        future = self.loop.run_in_executor(None, requests.get, url)
        future.add_done_callback(self._display_tab_list)
        # pylint: disable=deprecated-method
        asyncio.async(future)

    @asyncio.coroutine
    def run_async(self):
        ''' Runs the user interface as an async task. '''
        eventloop = create_asyncio_eventloop()
        self.cli = CommandLineInterface(application=self.application,
                                        eventloop=eventloop)
        self.cli.focus(COMMAND_BUFFER)
        try:
            while True:
                result = yield from self.cli.run_async()
                if result is None:
                    print('Exiting...')
                    return
        except (EOFError, KeyboardInterrupt):
            return
        finally:
            eventloop.close()
Example #6
0
class ReplSSHServerSession(asyncssh.SSHServerSession):
    """
    SSH server session that runs a Python REPL.

    :param get_globals: callable that returns the current globals.
    :param get_locals: (optional) callable that returns the current locals.
    """
    def __init__(self, get_globals, get_locals=None):
        assert callable(get_globals)
        assert get_locals is None or callable(get_locals)

        self._chan = None

        def _globals():
            data = get_globals()
            data.setdefault('print', self._print)
            return data

        repl = PythonRepl(get_globals=_globals,
                          get_locals=get_locals or _globals)

        # Disable open-in-editor and system prompt. Because it would run and
        # display these commands on the server side, rather than in the SSH
        # client.
        repl.enable_open_in_editor = False
        repl.enable_system_bindings = False

        # PipInput object, for sending input in the CLI.
        # (This is something that we can use in the prompt_toolkit event loop,
        # but still write date in manually.)
        self._input_pipe = PipeInput()

        # Output object. Don't render to the real stdout, but write everything
        # in the SSH channel.
        class Stdout(object):
            def write(s, data):
                if self._chan is not None:
                    self._chan.write(data.replace('\n', '\r\n'))

            def flush(s):
                pass

        # Create command line interface.
        self.cli = CommandLineInterface(application=repl.create_application(),
                                        eventloop=create_asyncio_eventloop(),
                                        input=self._input_pipe,
                                        output=Vt100_Output(
                                            Stdout(), self._get_size))

        self._callbacks = self.cli.create_eventloop_callbacks()

    def _get_size(self):
        """
        Callable that returns the current `Size`, required by Vt100_Output.
        """
        if self._chan is None:
            return Size(rows=20, columns=79)
        else:
            width, height, pixwidth, pixheight = self._chan.get_terminal_size()
            return Size(rows=height, columns=width)

    def connection_made(self, chan):
        """
        Client connected, run repl in coroutine.
        """
        self._chan = chan

        # Run REPL interface.
        f = asyncio.ensure_future(self.cli.run_async())

        # Close channel when done.
        def done(_):
            chan.close()
            self._chan = None

        f.add_done_callback(done)

    def shell_requested(self):
        return True

    def terminal_size_changed(self, width, height, pixwidth, pixheight):
        """
        When the terminal size changes, report back to CLI.
        """
        self._callbacks.terminal_size_changed()

    def data_received(self, data, datatype):
        """
        When data is received, send to inputstream of the CLI and repaint.
        """
        self._input_pipe.send(data)

    def _print(self, *data, **kw):
        """
        _print(self, *data, sep=' ', end='\n', file=None)

        Alternative 'print' function that prints back into the SSH channel.
        """
        # Pop keyword-only arguments. (We cannot use the syntax from the
        # signature. Otherwise, Python2 will give a syntax error message when
        # installing.)
        sep = kw.pop('sep', ' ')
        end = kw.pop('end', '\n')
        _ = kw.pop('file', None)
        assert not kw, 'Too many keyword-only arguments'

        data = sep.join(map(str, data))
        self._chan.write(data + end)