async def _interact(self) -> None: if self._chan is None: # Should not happen. raise Exception("`_interact` called before `connection_made`.") if hasattr(self._chan, "set_line_mode") and self._chan._editor is not None: # Disable the line editing provided by asyncssh. Prompt_toolkit # provides the line editing. self._chan.set_line_mode(False) term = self._chan.get_terminal_type() self._output = Vt100_Output(self.stdout, self._get_size, term=term, write_binary=False) with create_app_session(input=self._input, output=self._output) as session: self.app_session = session try: await self.interact(self) except BaseException: traceback.print_exc() finally: # Close the connection. self._chan.close() self._input.close()
def ttype_received(ttype: str) -> None: """ TelnetProtocolParser 'ttype_received' callback """ self.vt100_output = Vt100_Output(self.stdout, get_size, term=ttype, write_binary=False) self._ready.set()
def __init__(self, stdin, stdout, term_type): # A patch until https://github.com/ipython/ipython/issues/11745 is solved TerminalInteractiveShell.simple_prompt = False term_input = Vt100Input(stdin) term_output = Vt100_Output.from_pty(stdout, term_type) super().__init__(pt_session_options=dict(input=term_input, output=term_output), stdin=stdin, stdout=stdout) self.use_rawinput = True
async def create_full_prompt(process): # Mandatory from prompt_toolkit context_id = None use_asyncio_event_loop() # Size getter def get_size(): width, height, _, _ = process.get_terminal_size() return Size(rows=height, columns=width) # Set up resize event def size_changed(*_): with context(context_id): get_app()._on_resize() process.terminal_size_changed = size_changed # Prepare input stream vt100_input = create_pipe_input() type(vt100_input).responds_to_cpr = True await process.redirect_stdin(vt100_input._w) # Prepare output stream process.stdout.encoding = "utf-8" process.stdout.flush = lambda: None vt100_output = Vt100_Output(process.stdout, get_size, term=process.get_terminal_type()) # Define local print def sprint(*args, **kwargs): kwargs.setdefault("output", vt100_output) print_formatted_text(*args, **kwargs) async def aprint(*args, **kwargs): sprint(*args, **kwargs) await process.stdout.drain() # Define local prompt async def aprompt(*args, **kwargs): nonlocal context_id kwargs['async_'] = True kwargs['input'] = vt100_input kwargs['output'] = vt100_output with context() as context_id: return await prompt(*args, **kwargs) aprompt.get_size = vt100_output.get_size aprint.sprint = sprint return aprint, aprompt
def _ready(self): self.terminal = self.get_node(self.terminal_path) self.terminal.connect("data_sent", self, "_on_Terminal_data_sent") self.terminal.connect("size_changed", self, "_on_Terminal_size_changed") if self.focused: self.terminal.grab_focus() self.stdout = Stdout(writefunc=self._terminal_write) self.output = Vt100_Output(stdout=self.stdout, get_size=self._get_terminal_size, write_binary=False) self.thread = Thread(target=self._main_wrapped) self.thread.start()
def __init__(self, conn, addr, interact, server, encoding, style): assert isinstance(addr, tuple) # (addr, port) tuple assert callable(interact) assert isinstance(server, TelnetServer) assert isinstance(encoding, text_type) # e.g. 'utf-8' assert isinstance(style, BaseStyle) self.conn = conn self.addr = addr self.interact = interact self.server = server self.encoding = encoding self.style = style self._closed = False # Execution context. self._context_id = None # Create "Output" object. self.size = Size(rows=40, columns=79) # Initialize. _initialize_telnet(conn) # Create input. self.vt100_input = PipeInput() # Create output. def get_size(): return self.size self.stdout = _ConnectionStdout(conn, encoding=encoding) self.vt100_output = Vt100_Output(self.stdout, get_size, write_binary=False) def data_received(data): """ TelnetProtocolParser 'data_received' callback """ assert isinstance(data, binary_type) self.vt100_input.send_bytes(data) def size_received(rows, columns): """ TelnetProtocolParser 'size_received' callback """ self.size = Size(rows=rows, columns=columns) get_app()._on_resize() self.parser = TelnetProtocolParser(data_received, size_received)
def __init__( self, conn: socket.socket, addr: Tuple[str, int], interact: Callable[["TelnetConnection"], Awaitable[None]], server: "TelnetServer", encoding: str, style: Optional[BaseStyle], ) -> None: self.conn = conn self.addr = addr self.interact = interact self.server = server self.encoding = encoding self.style = style self._closed = False # Create "Output" object. self.size = Size(rows=40, columns=79) # Initialize. _initialize_telnet(conn) # Create input. self.vt100_input = PosixPipeInput() # Create output. def get_size() -> Size: return self.size self.stdout = cast(TextIO, _ConnectionStdout(conn, encoding=encoding)) self.vt100_output = Vt100_Output(self.stdout, get_size, write_binary=False) def data_received(data: bytes) -> None: """ TelnetProtocolParser 'data_received' callback """ self.vt100_input.send_bytes(data) def size_received(rows: int, columns: int) -> None: """ TelnetProtocolParser 'size_received' callback """ self.size = Size(rows=rows, columns=columns) get_app()._on_resize() self.parser = TelnetProtocolParser(data_received, size_received) self.context: Optional[contextvars.Context] = None
def __init__(self, get_globals: _GetNamespace, get_locals: Optional[_GetNamespace] = None) -> None: self._chan = None def _globals() -> dict: data = get_globals() data.setdefault("print", self._print) return data # 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 = create_pipe_input() # Output object. Don't render to the real stdout, but write everything # in the SSH channel. class Stdout: def write(s, data: str) -> None: if self._chan is not None: data = data.replace("\n", "\r\n") self._chan.write(data) def flush(s) -> None: pass self.repl = PythonRepl( get_globals=_globals, get_locals=get_locals or _globals, input=self._input_pipe, output=Vt100_Output(cast(TextIO, Stdout()), self._get_size), ) # 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. self.repl.enable_open_in_editor = False self.repl.enable_system_bindings = False
def __init__(self, interact: Callable[[], Awaitable[None]]) -> None: self.interact = interact self._chan = None self.app_session: Optional[AppSession] = None # 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 = PosixPipeInput() # Output object. Don't render to the real stdout, but write everything # in the SSH channel. class Stdout: def write(s, data): if self._chan is not None: self._chan.write(data.replace('\n', '\r\n')) def flush(s): pass self._output = Vt100_Output(cast(TextIO, Stdout()), self._get_size, write_binary=False)
def _create_app(self, color_depth, term='xterm'): """ Create CommandLineInterface for this client. Called when the client wants to attach the UI to the server. """ output = Vt100_Output(_SocketStdout(self._send_packet), lambda: self.size, term=term, write_binary=False) self.client_state = self.pymux.add_client(input=self._pipeinput, output=output, connection=self, color_depth=color_depth) print('Start running app...') future = self.client_state.app.run_async() print('Start running app got future...', future) @future.add_done_callback def done(_): print('APP DONE.........') print(future.result()) self._close_connection()
def fmt(text): s = io.StringIO() o = Vt100_Output(s, lambda: 80, write_binary=False) print_formatted_text(HTML(text), end='', output=o) return s.getvalue()
def attach(self, detach_other_clients=False, color_depth=ColorDepth.DEPTH_8_BIT): """ Attach client user interface. """ assert isinstance(detach_other_clients, bool) self._send_size() self._send_packet({ 'cmd': 'start-gui', 'detach-others': detach_other_clients, 'color-depth': color_depth, 'term': os.environ.get('TERM', ''), 'data': '' }) with raw_mode(sys.stdin.fileno()): data_buffer = b'' stdin_fd = sys.stdin.fileno() socket_fd = self.socket.fileno() current_timeout = INPUT_TIMEOUT # Timeout, used to flush escape sequences. try: def winch_handler(signum, frame): self._send_size() signal.signal(signal.SIGWINCH, winch_handler) while True: r = select_fds([stdin_fd, socket_fd], current_timeout) if socket_fd in r: # Received packet from server. data = self.socket.recv(1024) if data == b'': # End of file. Connection closed. # Reset terminal o = Vt100_Output.from_pty(sys.stdout) o.quit_alternate_screen() o.disable_mouse_support() o.disable_bracketed_paste() o.reset_attributes() o.flush() return else: data_buffer += data while b'\0' in data_buffer: pos = data_buffer.index(b'\0') self._process(data_buffer[:pos]) data_buffer = data_buffer[pos + 1:] elif stdin_fd in r: # Got user input. self._process_stdin() current_timeout = INPUT_TIMEOUT else: # Timeout. (Tell the server to flush the vt100 Escape.) self._send_packet({'cmd': 'flush-input'}) current_timeout = None finally: signal.signal(signal.SIGWINCH, signal.SIG_IGN)