defaults.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. from __future__ import annotations
  2. import io
  3. import sys
  4. from typing import ContextManager, TextIO
  5. from .base import DummyInput, Input, PipeInput
  6. __all__ = [
  7. "create_input",
  8. "create_pipe_input",
  9. ]
  10. def create_input(stdin: TextIO | None = None, always_prefer_tty: bool = False) -> Input:
  11. """
  12. Create the appropriate `Input` object for the current os/environment.
  13. :param always_prefer_tty: When set, if `sys.stdin` is connected to a Unix
  14. `pipe`, check whether `sys.stdout` or `sys.stderr` are connected to a
  15. pseudo terminal. If so, open the tty for reading instead of reading for
  16. `sys.stdin`. (We can open `stdout` or `stderr` for reading, this is how
  17. a `$PAGER` works.)
  18. """
  19. if sys.platform == "win32":
  20. from .win32 import Win32Input
  21. # If `stdin` was assigned `None` (which happens with pythonw.exe), use
  22. # a `DummyInput`. This triggers `EOFError` in the application code.
  23. if stdin is None and sys.stdin is None:
  24. return DummyInput()
  25. return Win32Input(stdin or sys.stdin)
  26. else:
  27. from .vt100 import Vt100Input
  28. # If no input TextIO is given, use stdin/stdout.
  29. if stdin is None:
  30. stdin = sys.stdin
  31. if always_prefer_tty:
  32. for obj in [sys.stdin, sys.stdout, sys.stderr]:
  33. if obj.isatty():
  34. stdin = obj
  35. break
  36. # If we can't access the file descriptor for the selected stdin, return
  37. # a `DummyInput` instead. This can happen for instance in unit tests,
  38. # when `sys.stdin` is patched by something that's not an actual file.
  39. # (Instantiating `Vt100Input` would fail in this case.)
  40. try:
  41. stdin.fileno()
  42. except io.UnsupportedOperation:
  43. return DummyInput()
  44. return Vt100Input(stdin)
  45. def create_pipe_input() -> ContextManager[PipeInput]:
  46. """
  47. Create an input pipe.
  48. This is mostly useful for unit testing.
  49. Usage::
  50. with create_pipe_input() as input:
  51. input.send_text('inputdata')
  52. Breaking change: In prompt_toolkit 3.0.28 and earlier, this was returning
  53. the `PipeInput` directly, rather than through a context manager.
  54. """
  55. if sys.platform == "win32":
  56. from .win32_pipe import Win32PipeInput
  57. return Win32PipeInput.create()
  58. else:
  59. from .posix_pipe import PosixPipeInput
  60. return PosixPipeInput.create()