base.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. """
  2. Abstraction of CLI Input.
  3. """
  4. from abc import ABCMeta, abstractmethod, abstractproperty
  5. from contextlib import contextmanager
  6. from typing import Callable, ContextManager, Generator, List
  7. from prompt_toolkit.key_binding import KeyPress
  8. __all__ = [
  9. "Input",
  10. "PipeInput",
  11. "DummyInput",
  12. ]
  13. class Input(metaclass=ABCMeta):
  14. """
  15. Abstraction for any input.
  16. An instance of this class can be given to the constructor of a
  17. :class:`~prompt_toolkit.application.Application` and will also be
  18. passed to the :class:`~prompt_toolkit.eventloop.base.EventLoop`.
  19. """
  20. @abstractmethod
  21. def fileno(self) -> int:
  22. """
  23. Fileno for putting this in an event loop.
  24. """
  25. @abstractmethod
  26. def typeahead_hash(self) -> str:
  27. """
  28. Identifier for storing type ahead key presses.
  29. """
  30. @abstractmethod
  31. def read_keys(self) -> List[KeyPress]:
  32. """
  33. Return a list of Key objects which are read/parsed from the input.
  34. """
  35. def flush_keys(self) -> List[KeyPress]:
  36. """
  37. Flush the underlying parser. and return the pending keys.
  38. (Used for vt100 input.)
  39. """
  40. return []
  41. def flush(self) -> None:
  42. "The event loop can call this when the input has to be flushed."
  43. pass
  44. @abstractproperty
  45. def closed(self) -> bool:
  46. "Should be true when the input stream is closed."
  47. return False
  48. @abstractmethod
  49. def raw_mode(self) -> ContextManager[None]:
  50. """
  51. Context manager that turns the input into raw mode.
  52. """
  53. @abstractmethod
  54. def cooked_mode(self) -> ContextManager[None]:
  55. """
  56. Context manager that turns the input into cooked mode.
  57. """
  58. @abstractmethod
  59. def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]:
  60. """
  61. Return a context manager that makes this input active in the current
  62. event loop.
  63. """
  64. @abstractmethod
  65. def detach(self) -> ContextManager[None]:
  66. """
  67. Return a context manager that makes sure that this input is not active
  68. in the current event loop.
  69. """
  70. def close(self) -> None:
  71. "Close input."
  72. pass
  73. class PipeInput(Input):
  74. """
  75. Abstraction for pipe input.
  76. """
  77. @abstractmethod
  78. def send_bytes(self, data: bytes) -> None:
  79. """Feed byte string into the pipe"""
  80. @abstractmethod
  81. def send_text(self, data: str) -> None:
  82. """Feed a text string into the pipe"""
  83. class DummyInput(Input):
  84. """
  85. Input for use in a `DummyApplication`
  86. If used in an actual application, it will make the application render
  87. itself once and exit immediately, due to an `EOFError`.
  88. """
  89. def fileno(self) -> int:
  90. raise NotImplementedError
  91. def typeahead_hash(self) -> str:
  92. return "dummy-%s" % id(self)
  93. def read_keys(self) -> List[KeyPress]:
  94. return []
  95. @property
  96. def closed(self) -> bool:
  97. # This needs to be true, so that the dummy input will trigger an
  98. # `EOFError` immediately in the application.
  99. return True
  100. def raw_mode(self) -> ContextManager[None]:
  101. return _dummy_context_manager()
  102. def cooked_mode(self) -> ContextManager[None]:
  103. return _dummy_context_manager()
  104. def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]:
  105. # Call the callback immediately once after attaching.
  106. # This tells the callback to call `read_keys` and check the
  107. # `input.closed` flag, after which it won't receive any keys, but knows
  108. # that `EOFError` should be raised. This unblocks `read_from_input` in
  109. # `application.py`.
  110. input_ready_callback()
  111. return _dummy_context_manager()
  112. def detach(self) -> ContextManager[None]:
  113. return _dummy_context_manager()
  114. @contextmanager
  115. def _dummy_context_manager() -> Generator[None, None, None]:
  116. yield