aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-04-06 18:18:01 +0300
committerarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-04-06 18:18:01 +0300
commit01fbacb386809436dfa331780875aed72cb76118 (patch)
tree04c911ad96ff0523bd4d3e7a45c23cf2f2d7607d
parent48fb997d7f820a474b9094a72d9798a95ec612b7 (diff)
downloadydb-01fbacb386809436dfa331780875aed72cb76118.tar.gz
intermediate changes
ref:b4f892f3c2b06a356c155f73c27efc5661a7fb89
-rw-r--r--contrib/python/prompt-toolkit/py3/.dist-info/METADATA4
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py2
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/application/application.py9
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/ssh/server.py29
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/telnet/server.py68
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/inputhook.py5
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/win32.py4
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/input/__init__.py3
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/input/base.py6
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/input/defaults.py22
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/input/posix_pipe.py74
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/input/vt100.py5
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32.py2
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32_pipe.py29
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/key_binding/bindings/mouse.py73
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py4
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py3
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py4
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py7
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/prompt.py14
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/utils.py11
-rw-r--r--contrib/python/prompt-toolkit/py3/prompt_toolkit/utils.py13
-rw-r--r--contrib/python/prompt-toolkit/py3/tests/test_cli.py30
-rw-r--r--contrib/python/prompt-toolkit/py3/tests/test_key_binding.py15
-rw-r--r--contrib/python/prompt-toolkit/py3/tests/test_shortcuts.py16
25 files changed, 300 insertions, 152 deletions
diff --git a/contrib/python/prompt-toolkit/py3/.dist-info/METADATA b/contrib/python/prompt-toolkit/py3/.dist-info/METADATA
index cdf27c97ff..bc64b6812b 100644
--- a/contrib/python/prompt-toolkit/py3/.dist-info/METADATA
+++ b/contrib/python/prompt-toolkit/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: prompt-toolkit
-Version: 3.0.28
+Version: 3.0.29
Summary: Library for building powerful interactive command lines in Python
Home-page: https://github.com/prompt-toolkit/python-prompt-toolkit
Author: Jonathan Slenders
@@ -21,6 +21,8 @@ Classifier: Programming Language :: Python
Classifier: Topic :: Software Development
Requires-Python: >=3.6.2
Description-Content-Type: text/x-rst
+License-File: LICENSE
+License-File: AUTHORS.rst
Requires-Dist: wcwidth
Python Prompt Toolkit
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py
index f3423df38c..4b36db1a64 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/__init__.py
@@ -18,7 +18,7 @@ from .formatted_text import ANSI, HTML
from .shortcuts import PromptSession, print_formatted_text, prompt
# Don't forget to update in `docs/conf.py`!
-__version__ = "3.0.28"
+__version__ = "3.0.29"
# Version tuple.
VERSION = tuple(__version__.split("."))
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/application/application.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/application/application.py
index 07b81d5ec1..b00c2c2a73 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/application/application.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/application/application.py
@@ -89,7 +89,6 @@ from prompt_toolkit.styles import (
)
from prompt_toolkit.utils import Event, in_main_thread
-from ..utils import is_windows
from .current import get_app_session, set_app
from .run_in_terminal import in_terminal, run_in_terminal
@@ -663,7 +662,7 @@ class Application(Generic[_AppResult]):
"""
assert not self._is_running, "Application is already running."
- if not in_main_thread() or is_windows():
+ if not in_main_thread() or sys.platform == "win32":
# Handling signals in other threads is not supported.
# Also on Windows, `add_signal_handler(signal.SIGINT, ...)` raises
# `NotImplementedError`.
@@ -935,7 +934,11 @@ class Application(Generic[_AppResult]):
set_event_loop(loop)
return loop.run_until_complete(
- self.run_async(pre_run=pre_run, set_exception_handler=set_exception_handler)
+ self.run_async(
+ pre_run=pre_run,
+ set_exception_handler=set_exception_handler,
+ handle_sigint=handle_sigint,
+ )
)
def _handle_exception(
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/ssh/server.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/ssh/server.py
index ba11036fc2..2b5935557d 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/ssh/server.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/ssh/server.py
@@ -10,7 +10,7 @@ import asyncssh
from prompt_toolkit.application.current import AppSession, create_app_session
from prompt_toolkit.data_structures import Size
from prompt_toolkit.eventloop import get_event_loop
-from prompt_toolkit.input import create_pipe_input
+from prompt_toolkit.input import PipeInput, create_pipe_input
from prompt_toolkit.output.vt100 import Vt100_Output
__all__ = ["PromptToolkitSSHSession", "PromptToolkitSSHServer"]
@@ -28,7 +28,7 @@ class PromptToolkitSSHSession(asyncssh.SSHServerSession): # type: ignore
# PipInput object, for sending input in the CLI.
# (This is something that we can use in the prompt_toolkit event loop,
# but still write date in manually.)
- self._input = create_pipe_input()
+ self._input: Optional[PipeInput] = None
self._output: Optional[Vt100_Output] = None
# Output object. Don't render to the real stdout, but write everything
@@ -88,16 +88,17 @@ class PromptToolkitSSHSession(asyncssh.SSHServerSession): # type: ignore
self._output = Vt100_Output(
self.stdout, self._get_size, term=term, write_binary=False
)
- with create_app_session(input=self._input, output=self._output) as session:
- self.app_session = session
- try:
- await self.interact(self)
- except BaseException:
- traceback.print_exc()
- finally:
- # Close the connection.
- self._chan.close()
- self._input.close()
+ with create_pipe_input() as self._input:
+ with create_app_session(input=self._input, output=self._output) as session:
+ self.app_session = session
+ try:
+ await self.interact(self)
+ except BaseException:
+ traceback.print_exc()
+ finally:
+ # Close the connection.
+ self._chan.close()
+ self._input.close()
def terminal_size_changed(
self, width: int, height: int, pixwidth: object, pixheight: object
@@ -107,6 +108,10 @@ class PromptToolkitSSHSession(asyncssh.SSHServerSession): # type: ignore
self.app_session.app._on_resize()
def data_received(self, data: str, datatype: object) -> None:
+ if self._input is None:
+ # Should not happen.
+ return
+
self._input.send_text(data)
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/telnet/server.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/telnet/server.py
index 2e042c95d1..4dfeb7fe05 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/telnet/server.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/contrib/telnet/server.py
@@ -11,7 +11,7 @@ from prompt_toolkit.application.run_in_terminal import run_in_terminal
from prompt_toolkit.data_structures import Size
from prompt_toolkit.eventloop import get_event_loop
from prompt_toolkit.formatted_text import AnyFormattedText, to_formatted_text
-from prompt_toolkit.input import create_pipe_input
+from prompt_toolkit.input import PipeInput, create_pipe_input
from prompt_toolkit.output.vt100 import Vt100_Output
from prompt_toolkit.renderer import print_formatted_text as print_formatted_text
from prompt_toolkit.styles import BaseStyle, DummyStyle
@@ -87,6 +87,7 @@ class _ConnectionStdout:
self._connection = connection
self._errors = "strict"
self._buffer: List[bytes] = []
+ self._closed = False
def write(self, data: str) -> None:
data = data.replace("\n", "\r\n")
@@ -98,12 +99,16 @@ class _ConnectionStdout:
def flush(self) -> None:
try:
- self._connection.send(b"".join(self._buffer))
+ if not self._closed:
+ self._connection.send(b"".join(self._buffer))
except OSError as e:
logger.warning("Couldn't send data over socket: %s" % e)
self._buffer = []
+ def close(self) -> None:
+ self._closed = True
+
@property
def encoding(self) -> str:
return self._encoding
@@ -126,6 +131,7 @@ class TelnetConnection:
server: "TelnetServer",
encoding: str,
style: Optional[BaseStyle],
+ vt100_input: PipeInput,
) -> None:
self.conn = conn
@@ -136,6 +142,7 @@ class TelnetConnection:
self.style = style
self._closed = False
self._ready = asyncio.Event()
+ self.vt100_input = vt100_input
self.vt100_output = None
# Create "Output" object.
@@ -144,9 +151,6 @@ class TelnetConnection:
# Initialize.
_initialize_telnet(conn)
- # Create input.
- self.vt100_input = create_pipe_input()
-
# Create output.
def get_size() -> Size:
return self.size
@@ -160,8 +164,8 @@ class TelnetConnection:
def size_received(rows: int, columns: int) -> None:
"""TelnetProtocolParser 'size_received' callback"""
self.size = Size(rows=rows, columns=columns)
- if self.vt100_output is not None:
- get_app()._on_resize()
+ if self.vt100_output is not None and self.context:
+ self.context.run(lambda: get_app()._on_resize())
def ttype_received(ttype: str) -> None:
"""TelnetProtocolParser 'ttype_received' callback"""
@@ -197,12 +201,6 @@ class TelnetConnection:
with create_app_session(input=self.vt100_input, output=self.vt100_output):
self.context = contextvars.copy_context()
await self.interact(self)
- except Exception as e:
- print("Got %s" % type(e).__name__, e)
- import traceback
-
- traceback.print_exc()
- raise
finally:
self.close()
@@ -222,6 +220,7 @@ class TelnetConnection:
self.vt100_input.close()
get_event_loop().remove_reader(self.conn)
self.conn.close()
+ self.stdout.close()
def send(self, formatted_text: AnyFormattedText) -> None:
"""
@@ -336,22 +335,43 @@ class TelnetServer:
conn, addr = self._listen_socket.accept()
logger.info("New connection %r %r", *addr)
- connection = TelnetConnection(
- conn, addr, self.interact, self, encoding=self.encoding, style=self.style
- )
- self.connections.add(connection)
-
# Run application for this connection.
async def run() -> None:
- logger.info("Starting interaction %r %r", *addr)
try:
- await connection.run_application()
- except Exception as e:
- print(e)
+ with create_pipe_input() as vt100_input:
+ connection = TelnetConnection(
+ conn,
+ addr,
+ self.interact,
+ self,
+ encoding=self.encoding,
+ style=self.style,
+ vt100_input=vt100_input,
+ )
+ self.connections.add(connection)
+
+ logger.info("Starting interaction %r %r", *addr)
+ try:
+ await connection.run_application()
+ finally:
+ self.connections.remove(connection)
+ logger.info("Stopping interaction %r %r", *addr)
+ except EOFError:
+ # Happens either when the connection is closed by the client
+ # (e.g., when the user types 'control-]', then 'quit' in the
+ # telnet client) or when the user types control-d in a prompt
+ # and this is not handled by the interact function.
+ logger.info("Unhandled EOFError in telnet application.")
+ except KeyboardInterrupt:
+ # Unhandled control-c propagated by a prompt.
+ logger.info("Unhandled KeyboardInterrupt in telnet application.")
+ except BaseException as e:
+ print("Got %s" % type(e).__name__, e)
+ import traceback
+
+ traceback.print_exc()
finally:
- self.connections.remove(connection)
self._application_tasks.remove(task)
- logger.info("Stopping interaction %r %r", *addr)
task = get_event_loop().create_task(run())
self._application_tasks.append(task)
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/inputhook.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/inputhook.py
index 26228a2af3..05d298117e 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/inputhook.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/inputhook.py
@@ -26,13 +26,12 @@ import asyncio
import os
import select
import selectors
+import sys
import threading
from asyncio import AbstractEventLoop
from selectors import BaseSelector, SelectorKey
from typing import TYPE_CHECKING, Any, Callable, List, Mapping, Optional, Tuple
-from prompt_toolkit.utils import is_windows
-
from .utils import get_event_loop
__all__ = [
@@ -141,7 +140,7 @@ class InputHookSelector(BaseSelector):
# However, if we would ever want to add a select call, it
# should use `windll.kernel32.WaitForMultipleObjects`,
# because `select.select` can't wait for a pipe on Windows.
- if not is_windows():
+ if sys.platform != "win32":
select.select([self._r], [], [], None)
os.read(self._r, 1024)
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/win32.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/win32.py
index a53632e0e8..fbc02d493a 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/win32.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/eventloop/win32.py
@@ -1,3 +1,7 @@
+import sys
+
+assert sys.platform == "win32"
+
from ctypes import pointer
from ..utils import SPHINX_AUTODOC_RUNNING
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/__init__.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/__init__.py
index 421d4ccdf4..dc319769ef 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/__init__.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/__init__.py
@@ -1,9 +1,10 @@
-from .base import DummyInput, Input
+from .base import DummyInput, Input, PipeInput
from .defaults import create_input, create_pipe_input
__all__ = [
# Base.
"Input",
+ "PipeInput",
"DummyInput",
# Defaults.
"create_input",
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/base.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/base.py
index 9885a37bc2..313622de5a 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/base.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/base.py
@@ -9,6 +9,7 @@ from prompt_toolkit.key_binding import KeyPress
__all__ = [
"Input",
+ "PipeInput",
"DummyInput",
]
@@ -104,6 +105,9 @@ class PipeInput(Input):
class DummyInput(Input):
"""
Input for use in a `DummyApplication`
+
+ If used in an actual application, it will make the application render
+ itself once and exit immediately, due to an `EOFError`.
"""
def fileno(self) -> int:
@@ -117,6 +121,8 @@ class DummyInput(Input):
@property
def closed(self) -> bool:
+ # This needs to be true, so that the dummy input will trigger an
+ # `EOFError` immediately in the application.
return True
def raw_mode(self) -> ContextManager[None]:
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/defaults.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/defaults.py
index 347f8c6ad3..197dcb9a60 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/defaults.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/defaults.py
@@ -1,7 +1,5 @@
import sys
-from typing import Optional, TextIO
-
-from prompt_toolkit.utils import is_windows
+from typing import ContextManager, Optional, TextIO
from .base import DummyInput, Input, PipeInput
@@ -23,7 +21,7 @@ def create_input(
`sys.stdin`. (We can open `stdout` or `stderr` for reading, this is how
a `$PAGER` works.)
"""
- if is_windows():
+ if sys.platform == "win32":
from .win32 import Win32Input
# If `stdin` was assigned `None` (which happens with pythonw.exe), use
@@ -48,16 +46,24 @@ def create_input(
return Vt100Input(stdin)
-def create_pipe_input() -> PipeInput:
+def create_pipe_input() -> ContextManager[PipeInput]:
"""
Create an input pipe.
This is mostly useful for unit testing.
+
+ Usage::
+
+ with create_pipe_input() as input:
+ input.send_text('inputdata')
+
+ Breaking change: In prompt_toolkit 3.0.28 and earlier, this was returning
+ the `PipeInput` directly, rather than through a context manager.
"""
- if is_windows():
+ if sys.platform == "win32":
from .win32_pipe import Win32PipeInput
- return Win32PipeInput()
+ return Win32PipeInput.create()
else:
from .posix_pipe import PosixPipeInput
- return PosixPipeInput()
+ return PosixPipeInput.create()
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/posix_pipe.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/posix_pipe.py
index 22dd7be6b5..1e7dec77fd 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/posix_pipe.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/posix_pipe.py
@@ -1,5 +1,10 @@
+import sys
+
+assert sys.platform != "win32"
+
import os
-from typing import ContextManager, TextIO, cast
+from contextlib import contextmanager
+from typing import ContextManager, Iterator, TextIO, cast
from ..utils import DummyContext
from .base import PipeInput
@@ -10,6 +15,36 @@ __all__ = [
]
+class _Pipe:
+ "Wrapper around os.pipe, that ensures we don't double close any end."
+
+ def __init__(self) -> None:
+ self.read_fd, self.write_fd = os.pipe()
+ self._read_closed = False
+ self._write_closed = False
+
+ def close_read(self) -> None:
+ "Close read-end if not yet closed."
+ if self._read_closed:
+ return
+
+ os.close(self.read_fd)
+ self._read_closed = True
+
+ def close_write(self) -> None:
+ "Close write-end if not yet closed."
+ if self._write_closed:
+ return
+
+ os.close(self.write_fd)
+ self._write_closed = True
+
+ def close(self) -> None:
+ "Close both read and write ends."
+ self.close_read()
+ self.close_write()
+
+
class PosixPipeInput(Vt100Input, PipeInput):
"""
Input that is send through a pipe.
@@ -18,14 +53,15 @@ class PosixPipeInput(Vt100Input, PipeInput):
Usage::
- input = PosixPipeInput()
- input.send_text('inputdata')
+ with PosixPipeInput.create() as input:
+ input.send_text('inputdata')
"""
_id = 0
- def __init__(self, text: str = "") -> None:
- self._r, self._w = os.pipe()
+ def __init__(self, _pipe: _Pipe, _text: str = "") -> None:
+ # Private constructor. Users should use the public `.create()` method.
+ self.pipe = _pipe
class Stdin:
encoding = "utf-8"
@@ -34,21 +70,30 @@ class PosixPipeInput(Vt100Input, PipeInput):
return True
def fileno(stdin) -> int:
- return self._r
+ return self.pipe.read_fd
super().__init__(cast(TextIO, Stdin()))
- self.send_text(text)
+ self.send_text(_text)
# Identifier for every PipeInput for the hash.
self.__class__._id += 1
self._id = self.__class__._id
+ @classmethod
+ @contextmanager
+ def create(cls, text: str = "") -> Iterator["PosixPipeInput"]:
+ pipe = _Pipe()
+ try:
+ yield PosixPipeInput(_pipe=pipe, _text=text)
+ finally:
+ pipe.close()
+
def send_bytes(self, data: bytes) -> None:
- os.write(self._w, data)
+ os.write(self.pipe.write_fd, data)
def send_text(self, data: str) -> None:
"Send text to the input."
- os.write(self._w, data.encode("utf-8"))
+ os.write(self.pipe.write_fd, data.encode("utf-8"))
def raw_mode(self) -> ContextManager[None]:
return DummyContext()
@@ -58,12 +103,11 @@ class PosixPipeInput(Vt100Input, PipeInput):
def close(self) -> None:
"Close pipe fds."
- os.close(self._r)
- os.close(self._w)
-
- # We should assign `None` to 'self._r` and 'self._w',
- # The event loop still needs to know the the fileno for this input in order
- # to properly remove it from the selectors.
+ # Only close the write-end of the pipe. This will unblock the reader
+ # callback (in vt100.py > _attached_input), which eventually will raise
+ # `EOFError`. If we'd also close the read-end, then the event loop
+ # won't wake up the corresponding callback because of this.
+ self.pipe.close_write()
def typeahead_hash(self) -> str:
"""
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/vt100.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/vt100.py
index 639d372609..45ce37208a 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/vt100.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/vt100.py
@@ -1,6 +1,9 @@
+import sys
+
+assert sys.platform != "win32"
+
import contextlib
import io
-import sys
import termios
import tty
from asyncio import AbstractEventLoop
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32.py
index c59375b3d4..db3fa2badd 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32.py
@@ -7,6 +7,8 @@ from prompt_toolkit.eventloop import get_event_loop
from ..utils import SPHINX_AUTODOC_RUNNING
+assert sys.platform == "win32"
+
# Do not import win32-specific stuff when generating documentation.
# Otherwise RTD would be unable to generate docs for this module.
if not SPHINX_AUTODOC_RUNNING:
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32_pipe.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32_pipe.py
index fdbcb8ee83..ebee2075ed 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32_pipe.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/input/win32_pipe.py
@@ -1,6 +1,11 @@
+import sys
+
+assert sys.platform == "win32"
+
+from contextlib import contextmanager
from ctypes import windll
from ctypes.wintypes import HANDLE
-from typing import Callable, ContextManager, List
+from typing import Callable, ContextManager, Iterator, List
from prompt_toolkit.eventloop.win32 import create_win32_event
@@ -31,7 +36,7 @@ class Win32PipeInput(_Win32InputBase, PipeInput):
_id = 0
- def __init__(self) -> None:
+ def __init__(self, _event: HANDLE) -> 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.
@@ -50,6 +55,15 @@ class Win32PipeInput(_Win32InputBase, PipeInput):
self.__class__._id += 1
self._id = self.__class__._id
+ @classmethod
+ @contextmanager
+ def create(cls) -> Iterator["Win32PipeInput"]:
+ event = create_win32_event()
+ try:
+ yield Win32PipeInput(_event=event)
+ finally:
+ windll.kernel32.CloseHandle(event)
+
@property
def closed(self) -> bool:
return self._closed
@@ -87,7 +101,9 @@ class Win32PipeInput(_Win32InputBase, PipeInput):
self._buffer = []
# Reset event.
- windll.kernel32.ResetEvent(self._event)
+ if not self._closed:
+ # (If closed, the event should not reset.)
+ windll.kernel32.ResetEvent(self._event)
return result
@@ -111,6 +127,9 @@ class Win32PipeInput(_Win32InputBase, PipeInput):
def send_text(self, text: str) -> None:
"Send text to the input."
+ if self._closed:
+ raise ValueError("Attempt to write into a closed pipe.")
+
# Pass it through our vt100 parser.
self.vt100_parser.feed(text)
@@ -124,9 +143,9 @@ class Win32PipeInput(_Win32InputBase, PipeInput):
return DummyContext()
def close(self) -> None:
- "Close pipe handles."
- windll.kernel32.CloseHandle(self._event)
+ "Close write-end of the pipe."
self._closed = True
+ windll.kernel32.SetEvent(self._event)
def typeahead_hash(self) -> str:
"""
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/key_binding/bindings/mouse.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/key_binding/bindings/mouse.py
index 949c33f72c..916cd41132 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/key_binding/bindings/mouse.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/key_binding/bindings/mouse.py
@@ -1,3 +1,4 @@
+import sys
from typing import TYPE_CHECKING, FrozenSet
from prompt_toolkit.data_structures import Point
@@ -9,7 +10,6 @@ from prompt_toolkit.mouse_events import (
MouseEventType,
MouseModifier,
)
-from prompt_toolkit.utils import is_windows
from ..key_bindings import KeyBindings
@@ -202,7 +202,7 @@ def load_mouse_bindings() -> KeyBindings:
mouse_event, x, y = map(ord, event.data[3:])
# TODO: Is it possible to add modifiers here?
- mouse_button, mouse_event_type, mouse_modifier = typical_mouse_events[
+ mouse_button, mouse_event_type, mouse_modifiers = typical_mouse_events[
mouse_event
]
@@ -303,41 +303,42 @@ def load_mouse_bindings() -> KeyBindings:
"""
Handling of mouse events for Windows.
"""
- assert is_windows() # This key binding should only exist for Windows.
-
- # Parse data.
- pieces = event.data.split(";")
-
- button = MouseButton(pieces[0])
- event_type = MouseEventType(pieces[1])
- x = int(pieces[2])
- y = int(pieces[3])
-
- # Make coordinates absolute to the visible part of the terminal.
- output = event.app.renderer.output
-
- from prompt_toolkit.output.win32 import Win32Output
- from prompt_toolkit.output.windows10 import Windows10_Output
-
- if isinstance(output, (Win32Output, Windows10_Output)):
- screen_buffer_info = output.get_win32_screen_buffer_info()
- rows_above_cursor = (
- screen_buffer_info.dwCursorPosition.Y - event.app.renderer._cursor_pos.y
- )
- y -= rows_above_cursor
-
- # Call the mouse event handler.
- # (Can return `NotImplemented`.)
- handler = event.app.renderer.mouse_handlers.mouse_handlers[y][x]
-
- return handler(
- MouseEvent(
- position=Point(x=x, y=y),
- event_type=event_type,
- button=button,
- modifiers=UNKNOWN_MODIFIER,
+ # This key binding should only exist for Windows.
+ if sys.platform == "win32":
+ # Parse data.
+ pieces = event.data.split(";")
+
+ button = MouseButton(pieces[0])
+ event_type = MouseEventType(pieces[1])
+ x = int(pieces[2])
+ y = int(pieces[3])
+
+ # Make coordinates absolute to the visible part of the terminal.
+ output = event.app.renderer.output
+
+ from prompt_toolkit.output.win32 import Win32Output
+ from prompt_toolkit.output.windows10 import Windows10_Output
+
+ if isinstance(output, (Win32Output, Windows10_Output)):
+ screen_buffer_info = output.get_win32_screen_buffer_info()
+ rows_above_cursor = (
+ screen_buffer_info.dwCursorPosition.Y
+ - event.app.renderer._cursor_pos.y
+ )
+ y -= rows_above_cursor
+
+ # Call the mouse event handler.
+ # (Can return `NotImplemented`.)
+ handler = event.app.renderer.mouse_handlers.mouse_handlers[y][x]
+
+ return handler(
+ MouseEvent(
+ position=Point(x=x, y=y),
+ event_type=event_type,
+ button=button,
+ modifiers=UNKNOWN_MODIFIER,
+ )
)
- )
# No mouse handler found. Return `NotImplemented` so that we don't
# invalidate the UI.
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py
index ee1ac41d4d..fc46cc4afd 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/conemu.py
@@ -1,3 +1,7 @@
+import sys
+
+assert sys.platform == "win32"
+
from typing import Any, Optional, TextIO
from prompt_toolkit.data_structures import Size
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py
index bd4bf950c4..7fb0f8931d 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/defaults.py
@@ -5,7 +5,6 @@ from prompt_toolkit.utils import (
get_bell_environment_variable,
get_term_environment_variable,
is_conemu_ansi,
- is_windows,
)
from .base import DummyOutput, Output
@@ -68,7 +67,7 @@ def create_output(
while isinstance(stdout, StdoutProxy):
stdout = stdout.original_stdout
- if is_windows():
+ if sys.platform == "win32":
from .conemu import ConEmuOutput
from .win32 import Win32Output
from .windows10 import Windows10_Output, is_win_vt100_enabled
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py
index a73978c3ef..1724eae5da 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/win32.py
@@ -1,3 +1,7 @@
+import sys
+
+assert sys.platform == "win32"
+
import os
from ctypes import ArgumentError, byref, c_char, c_long, c_uint, c_ulong, pointer
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py
index 933f54a28f..d5d55f18ca 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/output/windows10.py
@@ -1,9 +1,12 @@
+import sys
+
+assert sys.platform == "win32"
+
from ctypes import byref, windll
from ctypes.wintypes import DWORD, HANDLE
from typing import Any, Optional, TextIO
from prompt_toolkit.data_structures import Size
-from prompt_toolkit.utils import is_windows
from prompt_toolkit.win32_types import STD_OUTPUT_HANDLE
from .base import Output
@@ -84,7 +87,7 @@ def is_win_vt100_enabled() -> bool:
Returns True when we're running Windows and VT100 escape sequences are
supported.
"""
- if not is_windows():
+ if sys.platform != "win32":
return False
hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/prompt.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/prompt.py
index a8d8a58555..4dc1b18d1c 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/prompt.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/prompt.py
@@ -900,6 +900,7 @@ class PromptSession(Generic[_T]):
accept_default: bool = False,
pre_run: Optional[Callable[[], None]] = None,
set_exception_handler: bool = True,
+ handle_sigint: bool = True,
in_thread: bool = False,
) -> _T:
"""
@@ -1028,10 +1029,12 @@ class PromptSession(Generic[_T]):
# dumb prompt.
if self._output is None and is_dumb_terminal():
with self._dumb_prompt(self.message) as dump_app:
- return dump_app.run(in_thread=in_thread)
+ return dump_app.run(in_thread=in_thread, handle_sigint=handle_sigint)
return self.app.run(
- set_exception_handler=set_exception_handler, in_thread=in_thread
+ set_exception_handler=set_exception_handler,
+ in_thread=in_thread,
+ handle_sigint=handle_sigint,
)
@contextmanager
@@ -1132,6 +1135,7 @@ class PromptSession(Generic[_T]):
accept_default: bool = False,
pre_run: Optional[Callable[[], None]] = None,
set_exception_handler: bool = True,
+ handle_sigint: bool = True,
) -> _T:
if message is not None:
@@ -1219,9 +1223,11 @@ class PromptSession(Generic[_T]):
# dumb prompt.
if self._output is None and is_dumb_terminal():
with self._dumb_prompt(self.message) as dump_app:
- return await dump_app.run_async()
+ return await dump_app.run_async(handle_sigint=handle_sigint)
- return await self.app.run_async(set_exception_handler=set_exception_handler)
+ return await self.app.run_async(
+ set_exception_handler=set_exception_handler, handle_sigint=handle_sigint
+ )
def _add_pre_run_callables(
self, pre_run: Optional[Callable[[], None]], accept_default: bool
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/utils.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/utils.py
index c7ce74e627..a628f4b6ae 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/utils.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/shortcuts/utils.py
@@ -183,20 +183,19 @@ def print_container(
else:
output = get_app_session().output
- def exit_immediately() -> None:
- # Use `call_from_executor` to exit "soon", so that we still render one
- # initial time, before exiting the application.
- get_event_loop().call_soon(lambda: app.exit())
-
app: Application[None] = Application(
layout=Layout(container=container),
output=output,
+ # `DummyInput` will cause the application to terminate immediately.
input=DummyInput(),
style=_create_merged_style(
style, include_default_pygments_style=include_default_pygments_style
),
)
- app.run(pre_run=exit_immediately, in_thread=True)
+ try:
+ app.run(in_thread=True)
+ except EOFError:
+ pass
def _create_merged_style(
diff --git a/contrib/python/prompt-toolkit/py3/prompt_toolkit/utils.py b/contrib/python/prompt-toolkit/py3/prompt_toolkit/utils.py
index 8b501e5b23..4ceded34c4 100644
--- a/contrib/python/prompt-toolkit/py3/prompt_toolkit/utils.py
+++ b/contrib/python/prompt-toolkit/py3/prompt_toolkit/utils.py
@@ -188,24 +188,27 @@ def is_windows() -> bool:
"""
True when we are using Windows.
"""
- return sys.platform.startswith("win") # E.g. 'win32', not 'darwin' or 'linux2'
+ return sys.platform == "win32" # Not 'darwin' or 'linux2'
def is_windows_vt100_supported() -> bool:
"""
True when we are using Windows, but VT100 escape sequences are supported.
"""
- # Import needs to be inline. Windows libraries are not always available.
- from prompt_toolkit.output.windows10 import is_win_vt100_enabled
+ if sys.platform == "win32":
+ # Import needs to be inline. Windows libraries are not always available.
+ from prompt_toolkit.output.windows10 import is_win_vt100_enabled
- return is_windows() and is_win_vt100_enabled()
+ return is_win_vt100_enabled()
+
+ return False
def is_conemu_ansi() -> bool:
"""
True when the ConEmu Windows console is used.
"""
- return is_windows() and os.environ.get("ConEmuANSI", "OFF") == "ON"
+ return sys.platform == "win32" and os.environ.get("ConEmuANSI", "OFF") == "ON"
def in_main_thread() -> bool:
diff --git a/contrib/python/prompt-toolkit/py3/tests/test_cli.py b/contrib/python/prompt-toolkit/py3/tests/test_cli.py
index 678bc52636..53d1e4f284 100644
--- a/contrib/python/prompt-toolkit/py3/tests/test_cli.py
+++ b/contrib/python/prompt-toolkit/py3/tests/test_cli.py
@@ -45,9 +45,7 @@ def _feed_cli_with_input(
if check_line_ending:
assert text.endswith("\r")
- inp = create_pipe_input()
-
- try:
+ with create_pipe_input() as inp:
inp.send_text(text)
session = PromptSession(
input=inp,
@@ -59,12 +57,9 @@ def _feed_cli_with_input(
key_bindings=key_bindings,
)
- result = session.prompt()
+ _ = session.prompt()
return session.default_buffer.document, session.app
- finally:
- inp.close()
-
def test_simple_text_input():
# Simple text input, followed by enter.
@@ -933,15 +928,12 @@ def test_accept_default():
"""
Test `prompt(accept_default=True)`.
"""
- inp = create_pipe_input()
-
- session = PromptSession(input=inp, output=DummyOutput())
- result = session.prompt(default="hello", accept_default=True)
- assert result == "hello"
-
- # Test calling prompt() for a second time. (We had an issue where the
- # prompt reset between calls happened at the wrong time, breaking this.)
- result = session.prompt(default="world", accept_default=True)
- assert result == "world"
-
- inp.close()
+ with create_pipe_input() as inp:
+ session = PromptSession(input=inp, output=DummyOutput())
+ result = session.prompt(default="hello", accept_default=True)
+ assert result == "hello"
+
+ # Test calling prompt() for a second time. (We had an issue where the
+ # prompt reset between calls happened at the wrong time, breaking this.)
+ result = session.prompt(default="world", accept_default=True)
+ assert result == "world"
diff --git a/contrib/python/prompt-toolkit/py3/tests/test_key_binding.py b/contrib/python/prompt-toolkit/py3/tests/test_key_binding.py
index 617e71c1e1..6f03f2deab 100644
--- a/contrib/python/prompt-toolkit/py3/tests/test_key_binding.py
+++ b/contrib/python/prompt-toolkit/py3/tests/test_key_binding.py
@@ -1,3 +1,5 @@
+from contextlib import contextmanager
+
import pytest
from prompt_toolkit.application import Application
@@ -21,16 +23,21 @@ class Handlers:
return func
+@contextmanager
def set_dummy_app():
"""
Return a context manager that makes sure that this dummy application is
active. This is important, because we need an `Application` with
`is_done=False` flag, otherwise no keys will be processed.
"""
- app = Application(
- layout=Layout(Window()), output=DummyOutput(), input=create_pipe_input()
- )
- return set_app(app)
+ with create_pipe_input() as pipe_input:
+ app = Application(
+ layout=Layout(Window()),
+ output=DummyOutput(),
+ input=pipe_input,
+ )
+ with set_app(app):
+ yield
@pytest.fixture
diff --git a/contrib/python/prompt-toolkit/py3/tests/test_shortcuts.py b/contrib/python/prompt-toolkit/py3/tests/test_shortcuts.py
index dc4d65b272..10ee73a20e 100644
--- a/contrib/python/prompt-toolkit/py3/tests/test_shortcuts.py
+++ b/contrib/python/prompt-toolkit/py3/tests/test_shortcuts.py
@@ -1,4 +1,7 @@
+from prompt_toolkit.shortcuts import print_container
from prompt_toolkit.shortcuts.prompt import _split_multiline_prompt
+from prompt_toolkit.shortcuts.utils import print_container
+from prompt_toolkit.widgets import Frame, TextArea
def test_split_multiline_prompt():
@@ -49,3 +52,16 @@ def test_split_multiline_prompt():
assert has_before_tokens() is True
assert before() == [("class:testclass", "\n")]
assert first_input_line() == [("class:testclass", "a"), ("class:testclass", "b")]
+
+
+def test_print_container(tmpdir):
+ # Call `print_container`, render to a dummy file.
+ f = tmpdir.join("output")
+ with open(f, "w") as fd:
+ print_container(Frame(TextArea(text="Hello world!\n"), title="Title"), file=fd)
+
+ # Verify rendered output.
+ with open(f, "r") as fd:
+ text = fd.read()
+ assert "Hello world" in text
+ assert "Title" in text