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