base.py 3.9 KB

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