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
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)
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)
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()
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)