Ejemplo n.º 1
0
Archivo: main.py Proyecto: ashang/pymux
    def run_server(self):
        # Ignore keyboard. (When people run "pymux server" and press Ctrl-C.)
        # Pymux has to be terminated by termining all the processes running in
        # its panes.
        def handle_sigint(*a):
            print('Ignoring keyboard interrupt.')

        signal.signal(signal.SIGINT, handle_sigint)

        # Start background threads.
        self._start_auto_refresh_thread()

        # Run eventloop.
        try:
            get_event_loop().run_until_complete(self.done_f)
        except:
            # When something bad happens, always dump the traceback.
            # (Otherwise, when running as a daemon, and stdout/stderr are not
            # available, it's hard to see what went wrong.)
            fd, path = tempfile.mkstemp(prefix='pymux.crash-')
            logger.fatal(
                'Pymux has crashed, dumping traceback to {0}'.format(path))
            os.write(fd, traceback.format_exc().encode('utf-8'))
            os.close(fd)
            raise

        finally:
            # Clean up socket.
            os.remove(self.socket_name)
Ejemplo n.º 2
0
def bind_and_listen_on_posix_socket(socket_name, accept_callback):
    """
    :param accept_callback: Called with `PosixSocketConnection` when a new
        connection is established.
    """
    assert socket_name is None or isinstance(socket_name, six.text_type)
    assert callable(accept_callback)

    # Py2 uses 0027 and Py3 uses 0o027, but both know
    # how to create the right value from the string '0027'.
    old_umask = os.umask(int('0027', 8))

    # Bind socket.
    socket_name, socket = _bind_posix_socket(socket_name)

    _ = os.umask(old_umask)

    # Listen on socket.
    socket.listen(0)

    def _accept_cb():
        connection, client_address = socket.accept()
        # Note: We don't have to put this socket in non blocking mode.
        #       This can cause crashes when sending big packets on OS X.

        posix_connection = PosixSocketConnection(connection)

        accept_callback(posix_connection)

    get_event_loop().add_reader(socket.fileno(), _accept_cb)

    logger.info('Listening on %r.' % socket_name)
    return socket_name
Ejemplo n.º 3
0
def bind_and_listen_on_posix_socket(socket_name, accept_callback):
    """
    :param accept_callback: Called with `PosixSocketConnection` when a new
        connection is established.
    """
    assert socket_name is None or isinstance(socket_name, six.text_type)
    assert callable(accept_callback)

    # Py2 uses 0027 and Py3 uses 0o027, but both know
    # how to create the right value from the string '0027'.
    old_umask = os.umask(int('0027', 8))

    # Bind socket.
    socket_name, socket = _bind_posix_socket(socket_name)

    _ = os.umask(old_umask)

    # Listen on socket.
    socket.listen(0)

    def _accept_cb():
        connection, client_address = socket.accept()
        # Note: We don't have to put this socket in non blocking mode.
        #       This can cause crashes when sending big packets on OS X.

        posix_connection = PosixSocketConnection(connection)

        accept_callback(posix_connection)

    get_event_loop().add_reader(socket.fileno(), _accept_cb)

    logger.info('Listening on %r.' % socket_name)
    return socket_name
Ejemplo n.º 4
0
def _read_chunk_from_socket(socket):
    """
    (coroutine)
    Turn socket reading into coroutine.
    """
    fd = socket.fileno()
    f = Future()

    def read_callback():
        get_event_loop().remove_reader(fd)

        # Read next chunk.
        try:
            data = socket.recv(1024)
        except OSError as e:
            # On OSX, when we try to create a new window by typing "pymux
            # new-window" in a centain pane, very often we get the following
            # error: "OSError: [Errno 9] Bad file descriptor."
            # This doesn't seem very harmful, and we can just try again.
            logger.warning('Got OSError while reading data from client: %s. '
                           'Trying again.', e)
            f.set_result('')
            return

        if data:
            f.set_result(data)
        else:
            f.set_exception(BrokenPipeError)

    get_event_loop().add_reader(fd, read_callback)

    return f
Ejemplo n.º 5
0
def _read_chunk_from_socket(socket):
    """
    (coroutine)
    Turn socket reading into coroutine.
    """
    fd = socket.fileno()
    f = Future()

    def read_callback():
        get_event_loop().remove_reader(fd)

        # Read next chunk.
        try:
            data = socket.recv(1024)
        except OSError as e:
            # On OSX, when we try to create a new window by typing "pymux
            # new-window" in a centain pane, very often we get the following
            # error: "OSError: [Errno 9] Bad file descriptor."
            # This doesn't seem very harmful, and we can just try again.
            logger.warning(
                'Got OSError while reading data from client: %s. '
                'Trying again.', e)
            f.set_result('')
            return

        if data:
            f.set_result(data)
        else:
            f.set_exception(BrokenPipeError)

    get_event_loop().add_reader(fd, read_callback)

    return f
Ejemplo n.º 6
0
    def run_server(self):
        # Ignore keyboard. (When people run "pymux server" and press Ctrl-C.)
        # Pymux has to be terminated by termining all the processes running in
        # its panes.
        def handle_sigint(*a):
            print('Ignoring keyboard interrupt.')

        signal.signal(signal.SIGINT, handle_sigint)

        # Start background threads.
        self._start_auto_refresh_thread()

        # Run eventloop.
        try:
            get_event_loop().run_until_complete(self.done_f)
        except:
            # When something bad happens, always dump the traceback.
            # (Otherwise, when running as a daemon, and stdout/stderr are not
            # available, it's hard to see what went wrong.)
            fd, path = tempfile.mkstemp(prefix='pymux.crash-')
            logger.fatal(
                'Pymux has crashed, dumping traceback to {0}'.format(path))
            os.write(fd, traceback.format_exc().encode('utf-8'))
            os.close(fd)
            raise

        finally:
            # Clean up socket.
            os.remove(self.socket_name)
Ejemplo n.º 7
0
    def close(self):
        """
        Close connection.
        """
        self.socket.close()

        # Make sure to remove the reader from the event loop.
        get_event_loop().remove_reader(self._fd)
Ejemplo n.º 8
0
 def pre_run():
     if accept_default:
         # Validate and handle input. We use `call_from_executor` in
         # order to run it "soon" (during the next iteration of the
         # event loop), instead of right now. Otherwise, it won't
         # display the default value.
         get_event_loop().call_from_executor(
             self.default_buffer.validate_and_handle)
    def _write_and_flush(self, text):
        if not text:
            return

        def write_and_flush():
            self.log_field.log(text)

        get_event_loop().call_from_executor(write_and_flush)
Ejemplo n.º 10
0
    def close(self):
        """
        Close connection.
        """
        self.socket.close()

        # Make sure to remove the reader from the event loop.
        get_event_loop().remove_reader(self._fd)
Ejemplo n.º 11
0
def screen_exit():
    def application_exit():
        data.application.exit()
        data.screen_down_event.set()

    eventloop.get_event_loop().call_from_executor(application_exit)
    data.screen_down_event.wait()
    while data.application.is_running:
        pass
Ejemplo n.º 12
0
                def timer():
                    time.sleep(self.CPR_TIMEOUT)

                    # Not set in the meantime -> not supported.
                    if self.cpr_support == CPR_Support.UNKNOWN:
                        self.cpr_support = CPR_Support.NOT_SUPPORTED

                        if self.cpr_not_supported_callback:
                            # Make sure to call this callback in the main thread.
                            get_event_loop().call_from_executor(self.cpr_not_supported_callback)
                def timer():
                    time.sleep(self.CPR_TIMEOUT)

                    # Not set in the meantime -> not supported.
                    if self.cpr_support == CPR_Support.UNKNOWN:
                        self.cpr_support = CPR_Support.NOT_SUPPORTED

                        if self.cpr_not_supported_callback:
                            # Make sure to call this callback in the main thread.
                            get_event_loop().call_from_executor(self.cpr_not_supported_callback)
Ejemplo n.º 14
0
    def start(self):
        """
        Start the telnet server.
        Don't forget to call `loop.run_forever()` after doing this.
        """
        self._listen_socket = self._create_socket(self.host, self.port)
        logger.info('Listening for telnet connections on %s port %r',
                    self.host, self.port)

        get_event_loop().add_reader(self._listen_socket, self._accept)
Ejemplo n.º 15
0
    def close(self):
        """
        Closed by client.
        """
        if not self._closed:
            self._closed = True

            self.vt100_input.close()
            get_event_loop().remove_reader(self.conn)
            self.conn.close()
Ejemplo n.º 16
0
def wait_for_event(event):
    """
    Wraps a win32 event into a `Future` and wait for it.
    """
    f = Future()
    def ready():
        get_event_loop().remove_win32_handle(event)
        f.set_result(None)
    get_event_loop().add_win32_handle(event, ready)
    return f
        def pre_run2():
            if pre_run:
                pre_run()

            if accept_default:
                # Validate and handle input. We use `call_from_executor` in
                # order to run it "soon" (during the next iteration of the
                # event loop), instead of right now. Otherwise, it won't
                # display the default value.
                get_event_loop().call_from_executor(
                    self.default_buffer.validate_and_handle)
Ejemplo n.º 18
0
    async def stop(self) -> None:
        if self._listen_socket:
            get_event_loop().remove_reader(self._listen_socket)
            self._listen_socket.close()

        # Wait for all applications to finish.
        for t in self._application_tasks:
            t.cancel()

        for t in self._application_tasks:
            await t
Ejemplo n.º 19
0
def _call_history_load(history):
    """
    Helper: Call the history "load" method and return the result as a list of strings.
    """
    result = []

    async def call_load():
        async for item in history.load():
            result.append(item)

    get_event_loop().run_until_complete(call_load())
    return result
Ejemplo n.º 20
0
def wait_for_event(event):
    """
    Wraps a win32 event into a `Future` and wait for it.
    """
    f = Future()

    def ready():
        get_event_loop().remove_win32_handle(event)
        f.set_result(None)

    get_event_loop().add_win32_handle(event, ready)
    return f
Ejemplo n.º 21
0
    def _write_and_flush(self, text):
        if not text:
            return

        def write_and_flush():
            self.log_lines.extend(text.split("\n"))
            while len(self.log_lines) > MAXIMUM_LOG_PANE_LINE_COUNT:
                self.log_lines.popleft()
            new_text: str = "\n".join(self.log_lines)
            self.log_field.buffer.document = Document(text=new_text, cursor_position=len(new_text))

        get_event_loop().call_from_executor(write_and_flush)
Ejemplo n.º 22
0
def test_empty_async_generator():
    " Test asynchronous generator. "
    items = []
    f = ensure_future(consume_async_generator(
        _empty_async_generator(), lambda: False, items.append))

    # Run the event loop until all items are collected.
    get_event_loop().run_until_complete(f)
    assert items == []

    # Check that `consume_async_generator` didn't fail.
    assert f.result() is None
Ejemplo n.º 23
0
def run_telnet(host, port):
    # Import it here, because the import causes an error in Windows;
    #  and I want to be able to run the local version in Windows.
    from telnet.server import TelnetServer

    logging.basicConfig()
    logging.getLogger().setLevel(logging.INFO)

    server = TelnetServer(interact=launch_telnet_session, host=host, port=port)
    server.start()
    get_event_loop().set_exception_handler(exception_handler)
    get_event_loop().run_forever()
Ejemplo n.º 24
0
    async def stop(self) -> None:
        if self._listen_socket:
            get_event_loop().remove_reader(self._listen_socket)
            self._listen_socket.close()

        # Wait for all applications to finish.
        for t in self._application_tasks:
            t.cancel()

        for t in self._application_tasks:
            try:
                await t
            except asyncio.CancelledError:
                logger.debug("Task %s cancelled", str(t))
Ejemplo n.º 25
0
    def attach(self, detach_other_clients=False, color_depth=ColorDepth.DEPTH_8_BIT):
        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': ''
        })

        f = ensure_future(self._start_reader())
        with self._input.attach(self._input_ready):
            # Run as long as we have a connection with the server.
            get_event_loop().run_until_complete(f)  # Run forever.
Ejemplo n.º 26
0
    def run(self):
        """
        Run the event loop for the interface.
        This starts the interaction.
        """
        # Make sure everything is in sync, before starting.
        self.sync_with_prompt_toolkit()

        def pre_run():
            # Start in navigation mode.
            self.application.vi_state.input_mode = InputMode.NAVIGATION

        # Run eventloop of prompt_toolkit.
        def run():
            with _auto_refresh_context(self.application, .3):
                try:
                    self.application.run(pre_run=pre_run)
                except BaseException as e:
                    import traceback
                    traceback.print_exc()
                    print(e)
        self.thread = threading.Thread(target=run)
        self.thread.start()

        has_sigwinch = hasattr(signal, 'SIGWINCH') and in_main_thread()
        if has_sigwinch:
            loop = get_event_loop()
            loop.add_signal_handler(signal.SIGWINCH,
                                    self.application.invalidate)
Ejemplo n.º 27
0
def main():
    loop = get_event_loop()
    f = ensure_future(my_coroutine())

    # Run the event loop, until the coroutine is done.
    loop.run_until_complete(f)
    print(f.result())
Ejemplo n.º 28
0
Archivo: base.py Proyecto: Arrendi/rice
    def __init__(self,
                 title=None,
                 formatters=None,
                 bottom_toolbar=None,
                 style=None,
                 key_bindings=None,
                 file=None,
                 output=None,
                 input=None):
        assert formatters is None or (isinstance(formatters, list) and all(
            isinstance(fo, f.Formatter) for fo in formatters))
        assert style is None or isinstance(style, BaseStyle)
        assert key_bindings is None or isinstance(key_bindings, KeyBindings)

        self.title = title
        self.formatters = formatters or create_default_formatters()
        self.bottom_toolbar = bottom_toolbar
        self.counters = []
        self.style = style
        self.key_bindings = key_bindings

        self.output = output or create_output(stdout=file or sys.stderr)
        self.input = input or get_default_input()

        self._thread = None

        self._loop = get_event_loop()
        self._previous_winch_handler = None
        self._has_sigwinch = False
Ejemplo n.º 29
0
    def __init__(self, title=None, formatters=None, bottom_toolbar=None,
                 style=None, key_bindings=None, file=None, color_depth=None,
                 output=None, input=None):
        assert formatters is None or (
            isinstance(formatters, list) and all(isinstance(fo, Formatter) for fo in formatters))
        assert style is None or isinstance(style, BaseStyle)
        assert key_bindings is None or isinstance(key_bindings, KeyBindings)

        self.title = title
        self.formatters = formatters or create_default_formatters()
        self.bottom_toolbar = bottom_toolbar
        self.counters = []
        self.style = style
        self.key_bindings = key_bindings

        # Note that we use __stderr__ as default error output, because that
        # works best with `patch_stdout`.
        self.color_depth = color_depth
        self.output = output or create_output(stdout=file or sys.__stderr__)
        self.input = input or get_default_input()

        self._thread = None

        self._loop = get_event_loop()
        self._previous_winch_handler = None
        self._has_sigwinch = False
def main():
    loop = get_event_loop()
    f = ensure_future(my_coroutine())

    # Run the event loop, until the coroutine is done.
    loop.run_until_complete(f)
    print(f.result())
Ejemplo n.º 31
0
def test_generator_to_async_generator():
    """
    Test conversion of sync to asycn generator.
    This should run the synchronous parts in a background thread.
    """
    async_gen = generator_to_async_generator(_sync_generator)

    items = []

    async def consume_async_generator():
        async for item in async_gen:
            items.append(item)

    # Run the event loop until all items are collected.
    get_event_loop().run_until_complete(consume_async_generator())
    assert items == [1, 10]
Ejemplo n.º 32
0
def attach_win32_input(input, callback):
    """
    Context manager that makes this input active in the current event loop.

    :param input: :class:`~prompt_toolkit.input.Input` object.
    :param input_ready_callback: Called when the input is ready to read.
    """
    assert isinstance(input, Input)
    assert callable(callback)

    loop = get_event_loop()
    previous_callback = _current_callbacks.get(loop)

    # Add reader.
    loop.add_win32_handle(input.handle, callback)
    _current_callbacks[loop] = callback

    try:
        yield
    finally:
        loop.remove_win32_handle(input.handle)

        if previous_callback:
            loop.add_win32_handle(input.handle, previous_callback)
            _current_callbacks[loop] = previous_callback
        else:
            del _current_callbacks[loop]
Ejemplo n.º 33
0
        def run():
            with context() as ctx_id:
                self._context_id = ctx_id

                # Set input/output for all application running in this context.
                set_default_input(self.vt100_input)
                set_default_output(self.vt100_output)

                # Add reader.
                loop = get_event_loop()
                loop.add_reader(self.conn, handle_incoming_data)

                try:
                    obj = self.interact(self)
                    if _is_coroutine(obj):
                        # Got an asyncio coroutine.
                        import asyncio
                        f = asyncio.ensure_future(obj)
                        yield From(Future.from_asyncio_future(f))
                    else:
                        # Got a prompt_toolkit coroutine.
                        yield From(obj)
                except Exception as e:
                    print('Got %s' % type(e).__name__, e)
                    import traceback
                    traceback.print_exc()
                    raise
                finally:
                    self.close()
Ejemplo n.º 34
0
    def __init__(self,
                 title=None,
                 formatters=None,
                 bottom_toolbar=None,
                 style=None,
                 key_bindings=None,
                 file=None,
                 color_depth=None,
                 output=None,
                 input=None):
        assert formatters is None or (isinstance(formatters, list) and all(
            isinstance(fo, Formatter) for fo in formatters))
        assert style is None or isinstance(style, BaseStyle)
        assert key_bindings is None or isinstance(key_bindings, KeyBindings)

        self.title = title
        self.formatters = formatters or create_default_formatters()
        self.bottom_toolbar = bottom_toolbar
        self.counters = []
        self.style = style
        self.key_bindings = key_bindings

        # Note that we use __stderr__ as default error output, because that
        # works best with `patch_stdout`.
        self.color_depth = color_depth
        self.output = output or create_output(stdout=file or sys.__stderr__)
        self.input = input or get_default_input()

        self._thread = None

        self._loop = get_event_loop()
        self._previous_winch_handler = None
        self._has_sigwinch = False
Ejemplo n.º 35
0
def attach_win32_input(input, callback):
    """
    Context manager that makes this input active in the current event loop.

    :param input: :class:`~prompt_toolkit.input.Input` object.
    :param input_ready_callback: Called when the input is ready to read.
    """
    assert isinstance(input, Input)
    assert callable(callback)

    loop = get_event_loop()
    previous_callback = _current_callbacks.get(loop)

    # Add reader.
    loop.add_win32_handle(input.handle, callback)
    _current_callbacks[loop] = callback

    try:
        yield
    finally:
        loop.remove_win32_handle(input.handle)

        if previous_callback:
            loop.add_win32_handle(input.handle, previous_callback)
            _current_callbacks[loop] = previous_callback
        else:
            del _current_callbacks[loop]
Ejemplo n.º 36
0
    def _accept(self) -> None:
        """
        Accept new incoming connection.
        """
        if self._listen_socket is None:
            return  # Should not happen. `_accept` is called after `start`.

        conn, addr = self._listen_socket.accept()
        logger.info("New connection %r %r", *addr)

        connection = TelnetConnection(conn,
                                      addr,
                                      self.interact,
                                      self,
                                      encoding=self.encoding,
                                      style=self.style)
        self.connections.add(connection)

        # Run application for this connection.
        async def run() -> None:
            logger.info("Starting interaction %r %r", *addr)
            try:
                await connection.run_application()
            except Exception as e:
                print(e)
            finally:
                self.connections.remove(connection)
                self._application_tasks.remove(task)
                logger.info("Stopping interaction %r %r", *addr)

        task = get_event_loop().create_task(run())
        self._application_tasks.append(task)
Ejemplo n.º 37
0
    async def run_application(self) -> None:
        """
        Run application.
        """
        def handle_incoming_data() -> None:
            data = self.conn.recv(1024)
            if data:
                self.feed(data)
            else:
                # Connection closed by client.
                logger.info("Connection closed by client. %r %r" % self.addr)
                self.close()

        # Add reader.
        loop = get_event_loop()
        loop.add_reader(self.conn, handle_incoming_data)

        try:
            # Wait for v100_output to be properly instantiated
            await self._ready.wait()
            with create_app_session(input=self.vt100_input,
                                    output=self.vt100_output):
                self.context = contextvars.copy_context()
                await self.interact(self)
        except Exception as e:
            print("Got %s" % type(e).__name__, e)
            import traceback

            traceback.print_exc()
            raise
        finally:
            self.close()
Ejemplo n.º 38
0
    def __init__(
        self,
        title: AnyFormattedText = None,
        formatters: Optional[Sequence[Formatter]] = None,
        bottom_toolbar: AnyFormattedText = None,
        style: Optional[BaseStyle] = None,
        key_bindings: Optional[KeyBindings] = None,
        file: Optional[TextIO] = None,
        color_depth: Optional[ColorDepth] = None,
        output: Optional[Output] = None,
        input: Optional[Input] = None,
    ) -> None:

        self.title = title
        self.formatters = formatters or create_default_formatters()
        self.bottom_toolbar = bottom_toolbar
        self.counters: List[ProgressBarCounter[object]] = []
        self.style = style
        self.key_bindings = key_bindings

        # Note that we use __stderr__ as default error output, because that
        # works best with `patch_stdout`.
        self.color_depth = color_depth
        self.output = output or get_app_session().output
        self.input = input or get_app_session().input

        self._thread: Optional[threading.Thread] = None

        self._loop = get_event_loop()
        self._app_loop = new_event_loop()
        self._has_sigwinch = False
        self._app_started = threading.Event()
Ejemplo n.º 39
0
def run_coroutine_in_terminal(async_func, render_cli_done=False):
    """
    Suspend the current application and run this coroutine instead.
    `async_func` can be a coroutine or a function that returns a Future.

    :param async_func: A function that returns either a Future or coroutine
        when called.
    :returns: A `Future`.
    """
    assert callable(async_func)
    loop = get_event_loop()

    # Make sure to run this function in the current `Application`, or if no
    # application is active, run it normally.
    app = get_app(return_none=True)

    if app is None:
        return ensure_future(async_func())
    assert app._is_running

    # When a previous `run_in_terminal` call was in progress. Wait for that
    # to finish, before starting this one. Chain to previous call.
    previous_run_in_terminal_f = app._running_in_terminal_f
    new_run_in_terminal_f = loop.create_future()
    app._running_in_terminal_f = new_run_in_terminal_f

    def _run_in_t():
        " Coroutine. "
        # Wait for the previous `run_in_terminal` to finish.
        if previous_run_in_terminal_f is not None:
            yield previous_run_in_terminal_f

        # Draw interface in 'done' state, or erase.
        if render_cli_done:
            app._redraw(render_as_done=True)
        else:
            app.renderer.erase()

        # Disable rendering.
        app._running_in_terminal = True

        # Detach input.
        try:
            with app.input.detach():
                with app.input.cooked_mode():
                    result = yield From(async_func())

            raise Return(result)  # Same as: "return result"
        finally:
            # Redraw interface again.
            try:
                app._running_in_terminal = False
                app.renderer.reset()
                app._request_absolute_cursor_position()
                app._redraw()
            finally:
                new_run_in_terminal_f.set_result(None)

    return ensure_future(_run_in_t())
Ejemplo n.º 40
0
def test_generator_to_async_generator():
    """
    Test conversion of sync to asycn generator.
    This should run the synchronous parts in a background thread.
    """
    async_gen = generator_to_async_generator(_sync_generator)

    items = []
    f = ensure_future(consume_async_generator(
        async_gen, lambda: False, items.append))

    # Run the event loop until all items are collected.
    get_event_loop().run_until_complete(f)
    assert items == [1, 10]

    # Check that `consume_async_generator` didn't fail.
    assert f.result() is None
Ejemplo n.º 41
0
                def receive_content_from_fd():
                    # Read data from the source.
                    tokens = source.read_chunk()
                    data = handle_content(tokens)

                    # Set document.
                    insert_text(data)

                    # Remove the reader when we received another whole page.
                    # or when there is nothing more to read.
                    if lines[0] <= 0 or source.eof():
                        if fd is not None:
                            get_event_loop().remove_reader(fd)
                        source_info.waiting_for_input_stream = False

                    # Redraw.
                    self.application.invalidate()
Ejemplo n.º 42
0
def detach_win32_input(input):
    assert isinstance(input, Input)

    loop = get_event_loop()
    previous = _current_callbacks.get(loop)

    if previous:
        loop.remove_win32_handle(input.handle)
        _current_callbacks[loop] = None

    try:
        yield
    finally:
        if previous:
            loop.add_win32_handle(input.handle, previous)
            _current_callbacks[loop] = previous
    def run(self, pre_run=None, set_exception_handler=True, inputhook=None):
        """
        A blocking 'run' call that waits until the UI is finished.

        :param set_exception_handler: When set, in case of an exception, go out
            of the alternate screen and hide the application, display the
            exception, and wait for the user to press ENTER.
        :param inputhook: None or a callable that takes an `InputHookContext`.
        """
        loop = get_event_loop()

        def run():
            f = self.run_async(pre_run=pre_run)
            run_until_complete(f, inputhook=inputhook)
            return f.result()

        def handle_exception(context):
            " Print the exception, using run_in_terminal. "
            # For Python 2: we have to get traceback at this point, because
            # we're still in the 'except:' block of the event loop where the
            # traceback is still available. Moving this code in the
            # 'print_exception' coroutine will loose the exception.
            tb = get_traceback_from_context(context)
            formatted_tb = ''.join(format_tb(tb))

            def print_exception():
                # Print output. Similar to 'loop.default_exception_handler',
                # but don't use logger. (This works better on Python 2.)
                print('\nUnhandled exception in event loop:')
                print(formatted_tb)
                print('Exception %s' % (context.get('exception'), ))

                yield From(_do_wait_for_enter('Press ENTER to continue...'))
            run_coroutine_in_terminal(print_exception)

        if set_exception_handler:
            # Run with patched exception handler.
            previous_exc_handler = loop.get_exception_handler()
            loop.set_exception_handler(handle_exception)
            try:
                return run()
            finally:
                loop.set_exception_handler(previous_exc_handler)
        else:
            run()
Ejemplo n.º 44
0
def main():
    server = TelnetServer(interact=interact, port=2323)
    server.start()
    get_event_loop().run_forever()
Ejemplo n.º 45
0
 def ready():
     get_event_loop().remove_win32_handle(event)
     f.set_result(None)
def run_coroutine_in_terminal(async_func, render_cli_done=False):
    """
    Suspend the current application and run this coroutine instead.
    `async_func` can be a coroutine or a function that returns a Future.

    :param async_func: A function that returns either a Future or coroutine
        when called.
    :returns: A `Future`.
    """
    assert callable(async_func)
    loop = get_event_loop()

    # Make sure to run this function in the current `Application`, or if no
    # application is active, run it normally.
    app = get_app(return_none=True)

    if app is None:
        return ensure_future(async_func())
    assert app._is_running

    # When a previous `run_in_terminal` call was in progress. Wait for that
    # to finish, before starting this one. Chain to previous call.
    previous_run_in_terminal_f = app._running_in_terminal_f
    new_run_in_terminal_f = loop.create_future()
    app._running_in_terminal_f = new_run_in_terminal_f

    def _run_in_t():
        " Coroutine. "
        # Wait for the previous `run_in_terminal` to finish.
        if previous_run_in_terminal_f is not None:
            yield previous_run_in_terminal_f

        # Wait for all CPRs to arrive. We don't want to detach the input until
        # all cursor position responses have been arrived. Otherwise, the tty
        # will echo its input and can show stuff like ^[[39;1R.
        if app.input.responds_to_cpr:
            yield From(app.renderer.wait_for_cpr_responses())

        # Draw interface in 'done' state, or erase.
        if render_cli_done:
            app._redraw(render_as_done=True)
        else:
            app.renderer.erase()

        # Disable rendering.
        app._running_in_terminal = True

        # Detach input.
        try:
            with app.input.detach():
                with app.input.cooked_mode():
                    result = yield From(async_func())

            raise Return(result)  # Same as: "return result"
        finally:
            # Redraw interface again.
            try:
                app._running_in_terminal = False
                app.renderer.reset()
                app._request_absolute_cursor_position()
                app._redraw()
            finally:
                new_run_in_terminal_f.set_result(None)

    return ensure_future(_run_in_t())
        def _run_async():
            " Coroutine. "
            loop = get_event_loop()
            f = loop.create_future()
            self.future = f  # XXX: make sure to set this before calling '_redraw'.

            # Counter for cancelling 'flush' timeouts. Every time when a key is
            # pressed, we start a 'flush' timer for flushing our escape key. But
            # when any subsequent input is received, a new timer is started and
            # the current timer will be ignored.
            flush_counter = [0]  # Non local.

            # Reset.
            self.reset()
            self._pre_run(pre_run)

            # Feed type ahead input first.
            self.key_processor.feed_multiple(get_typeahead(self.input))
            self.key_processor.process_keys()

            def read_from_input():
                # Ignore when we aren't running anymore. This callback will
                # removed from the loop next time. (It could be that it was
                # still in the 'tasks' list of the loop.)
                # Except: if we need to process incoming CPRs.
                if not self._is_running and not self.renderer.waiting_for_cpr:
                    return

                # Get keys from the input object.
                keys = self.input.read_keys()

                # Feed to key processor.
                self.key_processor.feed_multiple(keys)
                self.key_processor.process_keys()

                # Quit when the input stream was closed.
                if self.input.closed:
                    f.set_exception(EOFError)
                else:
                    # Increase this flush counter.
                    flush_counter[0] += 1
                    counter = flush_counter[0]

                    # Automatically flush keys.
                    # (_daemon needs to be set, otherwise, this will hang the
                    # application for .5 seconds before exiting.)
                    run_in_executor(
                        lambda: auto_flush_input(counter), _daemon=True)

            def auto_flush_input(counter):
                # Flush input after timeout.
                # (Used for flushing the enter key.)
                time.sleep(self.ttimeoutlen)

                if flush_counter[0] == counter:
                    call_from_executor(flush_input)

            def flush_input():
                if not self.is_done:
                    # Get keys, and feed to key processor.
                    keys = self.input.flush_keys()
                    self.key_processor.feed_multiple(keys)
                    self.key_processor.process_keys()

                    if self.input.closed:
                        f.set_exception(EOFError)

            # Enter raw mode.
            with self.input.raw_mode():
                with self.input.attach(read_from_input):
                    # Draw UI.
                    self._request_absolute_cursor_position()
                    self._redraw()

                    has_sigwinch = hasattr(signal, 'SIGWINCH') and in_main_thread()
                    if has_sigwinch:
                        previous_winch_handler = loop.add_signal_handler(
                            signal.SIGWINCH, self._on_resize)

                    # Wait for UI to finish.
                    try:
                        result = yield From(f)
                    finally:
                        # In any case, when the application finishes. (Successful,
                        # or because of an error.)
                        try:
                            self._redraw(render_as_done=True)
                        finally:
                            # _redraw has a good chance to fail if it calls widgets
                            # with bad code. Make sure to reset the renderer anyway.
                            self.renderer.reset()

                            # Unset `is_running`, this ensures that possibly
                            # scheduled draws won't paint during the following
                            # yield.
                            self._is_running = False

                            # Detach event handlers for invalidate events.
                            # (Important when a UIControl is embedded in
                            # multiple applications, like ptterm in pymux. An
                            # invalidate should not trigger a repaint in
                            # terminated applications.)
                            for ev in self._invalidate_events:
                                ev -= self._invalidate_handler
                            self._invalidate_events = []

                            # Wait for CPR responses.
                            if self.input.responds_to_cpr:
                                yield From(self.renderer.wait_for_cpr_responses())

                            if has_sigwinch:
                                loop.add_signal_handler(signal.SIGWINCH, previous_winch_handler)

                            # Wait for the run-in-terminals to terminate.
                            previous_run_in_terminal_f = self._running_in_terminal_f

                            if previous_run_in_terminal_f:
                                yield From(previous_run_in_terminal_f)

                            # Store unprocessed input as typeahead for next time.
                            store_typeahead(self.input, self.key_processor.empty_queue())

                raise Return(result)
Ejemplo n.º 48
0
 def exit_immediately():
     # Use `call_from_executor` to exit "soon", so that we still render one
     # initial time, before exiting the application.
     get_event_loop().call_from_executor(
          lambda: app.exit())
Ejemplo n.º 49
0
    def _after_render(self, app):
        """
        Each time when the rendering is done, we should see whether we need to
        read more data from the input pipe.
        """
        # When the bottom is visible, read more input.
        # Try at least `info.window_height`, if this amount of data is
        # available.
        info = self.layout.dynamic_body.get_render_info()
        source = self.current_source
        source_info = self.source_info[source]
        b = source_info.buffer
        line_tokens = source_info.line_tokens

        if not source_info.waiting_for_input_stream and not source.eof() and info:
            lines_below_bottom = info.ui_content.line_count - info.last_visible_line()

            # Make sure to preload at least 2x the amount of lines on a page.
            if lines_below_bottom < info.window_height * 2 or self.forward_forever:
                # Lines to be loaded.
                lines = [info.window_height * 2 - lines_below_bottom]  # nonlocal

                fd = source.get_fd()

                def handle_content(tokens):
                    """ Handle tokens, update `line_tokens`, decrease
                    line count and return list of characters. """
                    data = []
                    for token_char in tokens:
                        char = token_char[1]
                        if char == '\n':
                            line_tokens.append([])

                            # Decrease line count.
                            lines[0] -= 1
                        else:
                            line_tokens[-1].append(token_char)
                        data.append(char)
                    return data

                def insert_text(list_of_fragments):
                    document = Document(b.text + ''.join(list_of_fragments), b.cursor_position)
                    b.set_document(document, bypass_readonly=True)

                    if self.forward_forever:
                        b.cursor_position = len(b.text)

                def receive_content_from_fd():
                    # Read data from the source.
                    tokens = source.read_chunk()
                    data = handle_content(tokens)

                    # Set document.
                    insert_text(data)

                    # Remove the reader when we received another whole page.
                    # or when there is nothing more to read.
                    if lines[0] <= 0 or source.eof():
                        if fd is not None:
                            get_event_loop().remove_reader(fd)
                        source_info.waiting_for_input_stream = False

                    # Redraw.
                    self.application.invalidate()

                def receive_content_from_generator():
                    " (in executor) Read data from generator. "
                    # Call `read_chunk` as long as we need more lines.
                    while lines[0] > 0 and not source.eof():
                        tokens = source.read_chunk()
                        data = handle_content(tokens)
                        insert_text(data)

                        # Schedule redraw.
                        self.application.invalidate()

                    source_info.waiting_for_input_stream = False

                # Set 'waiting_for_input_stream' and render.
                source_info.waiting_for_input_stream = True
                self.application.invalidate()

                # Add reader for stdin.
                if fd is not None:
                    get_event_loop().add_reader(fd, receive_content_from_fd)
                else:
                    # Execute receive_content_from_generator in thread.
                    # (Don't use 'run_in_executor', because we need a daemon.
                    t = threading.Thread(target=receive_content_from_generator)
                    t.daemon = True
                    t.start()