Пример #1
0
    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()
Пример #2
0
 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()
Пример #3
0
 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
Пример #4
0
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
Пример #5
0
    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()
Пример #6
0
    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)
Пример #7
0
    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
Пример #8
0
    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
Пример #9
0
    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)
Пример #10
0
    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()
Пример #11
0
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()
Пример #12
0
    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)
Пример #13
0
    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)