Skip to content

Commit

Permalink
Move "responds_to_cpr" logic from Input to Output class.
Browse files Browse the repository at this point in the history
This does clean up the code quite a bit in a couple of places.
  • Loading branch information
jonathanslenders committed Aug 23, 2020
1 parent 946e383 commit 32f65da
Show file tree
Hide file tree
Showing 13 changed files with 51 additions and 48 deletions.
5 changes: 2 additions & 3 deletions prompt_toolkit/application/application.py
Expand Up @@ -310,7 +310,6 @@ def __init__(
self.renderer = Renderer(
self._merged_style,
self.output,
self.input,
full_screen=full_screen,
mouse_support=mouse_support,
cpr_not_supported_callback=self.cpr_not_supported_callback,
Expand Down Expand Up @@ -729,7 +728,7 @@ def flush_input() -> None:
self._invalidate_events = []

# Wait for CPR responses.
if self.input.responds_to_cpr:
if self.output.responds_to_cpr:
await self.renderer.wait_for_cpr_responses()

# Wait for the run-in-terminals to terminate.
Expand Down Expand Up @@ -876,7 +875,7 @@ def cpr_not_supported_callback(self) -> None:
"""
Called when we don't receive the cursor position response in time.
"""
if not self.input.responds_to_cpr:
if not self.output.responds_to_cpr:
return # We know about this already.

def in_terminal() -> None:
Expand Down
2 changes: 1 addition & 1 deletion prompt_toolkit/application/run_in_terminal.py
Expand Up @@ -88,7 +88,7 @@ async def f():
# Wait for all CPRs to arrive. We don't want to detach the input until
# all cursor position responses have been arrived. Otherwise, the tty
# will echo its input and can show stuff like ^[[39;1R.
if app.input.responds_to_cpr:
if app.output.responds_to_cpr:
await app.renderer.wait_for_cpr_responses()

# Draw interface in 'done' state, or erase.
Expand Down
8 changes: 0 additions & 8 deletions prompt_toolkit/input/base.py
Expand Up @@ -51,14 +51,6 @@ def flush(self) -> None:
" The event loop can call this when the input has to be flushed. "
pass

@property
def responds_to_cpr(self) -> bool:
"""
`True` if the `Application` can expect to receive a CPR response from
here.
"""
return False

@abstractproperty
def closed(self) -> bool:
" Should be true when the input stream is closed. "
Expand Down
6 changes: 3 additions & 3 deletions prompt_toolkit/input/defaults.py
Expand Up @@ -43,16 +43,16 @@ def create_input(
return Vt100Input(stdin)


def create_pipe_input(responds_to_cpr: bool = True) -> PipeInput:
def create_pipe_input() -> PipeInput:
"""
Create an input pipe.
This is mostly useful for unit testing.
"""
if is_windows():
from .win32_pipe import Win32PipeInput

return Win32PipeInput(responds_to_cpr=responds_to_cpr)
return Win32PipeInput()
else:
from .posix_pipe import PosixPipeInput

return PosixPipeInput(responds_to_cpr=responds_to_cpr)
return PosixPipeInput()
7 changes: 1 addition & 6 deletions prompt_toolkit/input/posix_pipe.py
Expand Up @@ -24,8 +24,7 @@ class PosixPipeInput(Vt100Input, PipeInput):

_id = 0

def __init__(self, text: str = "", responds_to_cpr: bool = True) -> None:
self._responds_to_cpr = True
def __init__(self, text: str = "") -> None:
self._r, self._w = os.pipe()

class Stdin:
Expand All @@ -44,10 +43,6 @@ def fileno(stdin) -> int:
self.__class__._id += 1
self._id = self.__class__._id

@property
def responds_to_cpr(self) -> bool:
return self._responds_to_cpr

def send_bytes(self, data: bytes) -> None:
os.write(self._w, data)

Expand Down
15 changes: 0 additions & 15 deletions prompt_toolkit/input/vt100.py
Expand Up @@ -18,8 +18,6 @@
Union,
)

from prompt_toolkit.utils import is_dumb_terminal

from ..key_binding import KeyPress
from .base import Input
from .posix_utils import PosixStdinReader
Expand Down Expand Up @@ -83,19 +81,6 @@ def __init__(self, stdin: TextIO) -> None:
lambda key_press: self._buffer.append(key_press)
)

@property
def responds_to_cpr(self) -> bool:
# When the input is a tty, we assume that CPR is supported.
# It's not when the input is piped from Pexpect.
if os.environ.get("PROMPT_TOOLKIT_NO_CPR", "") == "1":
return False
if is_dumb_terminal():
return False
try:
return self.stdin.isatty()
except ValueError:
return False # ValueError: I/O operation on closed file

def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]:
"""
Return a context manager that makes this input active in the current
Expand Down
7 changes: 1 addition & 6 deletions prompt_toolkit/input/win32_pipe.py
Expand Up @@ -30,7 +30,7 @@ class Win32PipeInput(_Win32InputBase, PipeInput):

_id = 0

def __init__(self, responds_to_cpr: bool = True) -> None:
def __init__(self) -> None:
super().__init__()
# Event (handle) for registering this input in the event loop.
# This event is set when there is data available to read from the pipe.
Expand All @@ -40,7 +40,6 @@ def __init__(self, responds_to_cpr: bool = True) -> None:
self._event = create_win32_event()

self._closed = False
self._responds_to_cpr = responds_to_cpr

# Parser for incoming keys.
self._buffer: List[KeyPress] = [] # Buffer to collect the Key objects.
Expand Down Expand Up @@ -105,10 +104,6 @@ def flush_keys(self) -> List[KeyPress]:
self._buffer = []
return result

@property
def responds_to_cpr(self) -> bool:
return self._responds_to_cpr

def send_bytes(self, data: bytes) -> None:
" Send bytes to the input. "
self.send_text(data.decode("utf-8", "ignore"))
Expand Down
16 changes: 16 additions & 0 deletions prompt_toolkit/output/base.py
Expand Up @@ -143,6 +143,22 @@ def ask_for_cpr(self) -> None:
(VT100 only.)
"""

@property
def responds_to_cpr(self) -> bool:
"""
`True` if the `Application` can expect to receive a CPR response after
calling `ask_for_cpr` (this will come back through the corresponding
`Input`).
This is used to determine the amount of available rows we have below
the cursor position. In the first place, we have this so that the drop
down autocompletion menus are sized according to the available space.
On Windows, we don't need this, there we have
`get_rows_below_cursor_position`.
"""
return False

@abstractmethod
def get_size(self) -> Size:
" Return the size of the output window. "
Expand Down
4 changes: 4 additions & 0 deletions prompt_toolkit/output/conemu.py
Expand Up @@ -36,6 +36,10 @@ def __init__(
stdout, lambda: Size(0, 0), default_color_depth=default_color_depth
)

@property
def responds_to_cpr(self) -> bool:
return False # We don't need this on Windows.

def __getattr__(self, name: str) -> Any:
if name in (
"get_size",
Expand Down
15 changes: 15 additions & 0 deletions prompt_toolkit/output/vt100.py
Expand Up @@ -8,6 +8,7 @@
"""
import array
import errno
import os
import sys
from typing import (
IO,
Expand Down Expand Up @@ -687,6 +688,20 @@ def ask_for_cpr(self) -> None:
self.write_raw("\x1b[6n")
self.flush()

@property
def responds_to_cpr(self) -> bool:
# When the input is a tty, we assume that CPR is supported.
# It's not when the input is piped from Pexpect.
if os.environ.get("PROMPT_TOOLKIT_NO_CPR", "") == "1":
return False

if is_dumb_terminal(self.term):
return False
try:
return self.stdout.isatty()
except ValueError:
return False # ValueError: I/O operation on closed file

def bell(self) -> None:
" Sound bell. "
self.write_raw("\a")
Expand Down
4 changes: 4 additions & 0 deletions prompt_toolkit/output/windows10.py
Expand Up @@ -55,6 +55,10 @@ def flush(self) -> None:
# Restore console mode.
windll.kernel32.SetConsoleMode(self._hconsole, original_mode)

@property
def responds_to_cpr(self) -> bool:
return False # We don't need this on Windows.

def __getattr__(self, name: str) -> Any:
if name in (
"get_size",
Expand Down
6 changes: 2 additions & 4 deletions prompt_toolkit/renderer.py
Expand Up @@ -11,7 +11,6 @@
from prompt_toolkit.data_structures import Point, Size
from prompt_toolkit.filters import FilterOrBool, to_filter
from prompt_toolkit.formatted_text import AnyFormattedText, to_formatted_text
from prompt_toolkit.input.base import Input
from prompt_toolkit.layout.mouse_handlers import MouseHandlers
from prompt_toolkit.layout.screen import Char, Screen, WritePosition
from prompt_toolkit.output import ColorDepth, Output
Expand Down Expand Up @@ -341,15 +340,13 @@ def __init__(
self,
style: BaseStyle,
output: Output,
input: Input,
full_screen: bool = False,
mouse_support: FilterOrBool = False,
cpr_not_supported_callback: Optional[Callable[[], None]] = None,
) -> None:

self.style = style
self.output = output
self.input = input
self.full_screen = full_screen
self.mouse_support = to_filter(mouse_support)
self.cpr_not_supported_callback = cpr_not_supported_callback
Expand All @@ -361,7 +358,8 @@ def __init__(
# Future set when we are waiting for a CPR flag.
self._waiting_for_cpr_futures: Deque[Future[None]] = deque()
self.cpr_support = CPR_Support.UNKNOWN
if not input.responds_to_cpr:

if not output.responds_to_cpr:
self.cpr_support = CPR_Support.NOT_SUPPORTED

# Cache for the style.
Expand Down
4 changes: 2 additions & 2 deletions prompt_toolkit/utils.py
Expand Up @@ -307,6 +307,6 @@ def is_dumb_terminal(term: Optional[str] = None) -> bool:
without cursor positioning and color support.
"""
if term is None:
term = os.environ.get("TERM", "")
return is_dumb_terminal(os.environ.get("TERM", ""))

return term in ["dumb", "unknown"]
return term.lower() in ["dumb", "unknown"]

0 comments on commit 32f65da

Please sign in to comment.