Mini Shell
"""
Abstraction of CLI Input.
"""
from __future__ import annotations
from abc import ABCMeta, abstractmethod, abstractproperty
from contextlib import contextmanager
from typing import Callable, ContextManager, Generator
from prompt_toolkit.key_binding import KeyPress
__all__ = [
"Input",
"PipeInput",
"DummyInput",
]
class Input(metaclass=ABCMeta):
"""
Abstraction for any input.
An instance of this class can be given to the constructor of a
:class:`~prompt_toolkit.application.Application` and will also be
passed to the :class:`~prompt_toolkit.eventloop.base.EventLoop`.
"""
@abstractmethod
def fileno(self) -> int:
"""
Fileno for putting this in an event loop.
"""
@abstractmethod
def typeahead_hash(self) -> str:
"""
Identifier for storing type ahead key presses.
"""
@abstractmethod
def read_keys(self) -> list[KeyPress]:
"""
Return a list of Key objects which are read/parsed from the input.
"""
def flush_keys(self) -> list[KeyPress]:
"""
Flush the underlying parser. and return the pending keys.
(Used for vt100 input.)
"""
return []
def flush(self) -> None:
"The event loop can call this when the input has to be flushed."
pass
@abstractproperty
def closed(self) -> bool:
"Should be true when the input stream is closed."
return False
@abstractmethod
def raw_mode(self) -> ContextManager[None]:
"""
Context manager that turns the input into raw mode.
"""
@abstractmethod
def cooked_mode(self) -> ContextManager[None]:
"""
Context manager that turns the input into cooked mode.
"""
@abstractmethod
def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]:
"""
Return a context manager that makes this input active in the current
event loop.
"""
@abstractmethod
def detach(self) -> ContextManager[None]:
"""
Return a context manager that makes sure that this input is not active
in the current event loop.
"""
def close(self) -> None:
"Close input."
pass
class PipeInput(Input):
"""
Abstraction for pipe input.
"""
@abstractmethod
def send_bytes(self, data: bytes) -> None:
"""Feed byte string into the pipe"""
@abstractmethod
def send_text(self, data: str) -> None:
"""Feed a text string into the pipe"""
class DummyInput(Input):
"""
Input for use in a `DummyApplication`
If used in an actual application, it will make the application render
itself once and exit immediately, due to an `EOFError`.
"""
def fileno(self) -> int:
raise NotImplementedError
def typeahead_hash(self) -> str:
return f"dummy-{id(self)}"
def read_keys(self) -> list[KeyPress]:
return []
@property
def closed(self) -> bool:
# This needs to be true, so that the dummy input will trigger an
# `EOFError` immediately in the application.
return True
def raw_mode(self) -> ContextManager[None]:
return _dummy_context_manager()
def cooked_mode(self) -> ContextManager[None]:
return _dummy_context_manager()
def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]:
# Call the callback immediately once after attaching.
# This tells the callback to call `read_keys` and check the
# `input.closed` flag, after which it won't receive any keys, but knows
# that `EOFError` should be raised. This unblocks `read_from_input` in
# `application.py`.
input_ready_callback()
return _dummy_context_manager()
def detach(self) -> ContextManager[None]:
return _dummy_context_manager()
@contextmanager
def _dummy_context_manager() -> Generator[None, None, None]:
yield
Zerion Mini Shell 1.0