summaryrefslogtreecommitdiffstats
path: root/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop
diff options
context:
space:
mode:
authornkozlovskiy <[email protected]>2023-09-29 12:24:06 +0300
committernkozlovskiy <[email protected]>2023-09-29 12:41:34 +0300
commite0e3e1717e3d33762ce61950504f9637a6e669ed (patch)
treebca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop
parent38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff)
add ydb deps
Diffstat (limited to 'contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop')
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/__init__.py0
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py46
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py113
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py83
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py85
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py29
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py107
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py306
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py82
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py216
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py23
-rw-r--r--contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py187
12 files changed, 1277 insertions, 0 deletions
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/__init__.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/__init__.py
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py
new file mode 100644
index 00000000000..ace2b8db497
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py
@@ -0,0 +1,46 @@
+"""
+Eventloop for integration with Python3 asyncio.
+
+Note that we can't use "yield from", because the package should be installable
+under Python 2.6 as well, and it should contain syntactically valid Python 2.6
+code.
+"""
+from __future__ import unicode_literals
+
+__all__ = (
+ 'AsyncioTimeout',
+)
+
+
+class AsyncioTimeout(object):
+ """
+ Call the `timeout` function when the timeout expires.
+ Every call of the `reset` method, resets the timeout and starts a new
+ timer.
+ """
+ def __init__(self, timeout, callback, loop):
+ self.timeout = timeout
+ self.callback = callback
+ self.loop = loop
+
+ self.counter = 0
+ self.running = True
+
+ def reset(self):
+ """
+ Reset the timeout. Starts a new timer.
+ """
+ self.counter += 1
+ local_counter = self.counter
+
+ def timer_timeout():
+ if self.counter == local_counter and self.running:
+ self.callback()
+
+ self.loop.call_later(self.timeout, timer_timeout)
+
+ def stop(self):
+ """
+ Ignore timeout. Don't call the callback anymore.
+ """
+ self.running = False
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py
new file mode 100644
index 00000000000..426ed96f67d
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py
@@ -0,0 +1,113 @@
+"""
+Posix asyncio event loop.
+"""
+from __future__ import unicode_literals
+
+from ..terminal.vt100_input import InputStream
+from .asyncio_base import AsyncioTimeout
+from .base import EventLoop, INPUT_TIMEOUT
+from .callbacks import EventLoopCallbacks
+from .posix_utils import PosixStdinReader
+
+import asyncio
+import signal
+
+__all__ = (
+ 'PosixAsyncioEventLoop',
+)
+
+
+class PosixAsyncioEventLoop(EventLoop):
+ def __init__(self, loop=None):
+ self.loop = loop or asyncio.get_event_loop()
+ self.closed = False
+
+ self._stopped_f = asyncio.Future(loop=self.loop)
+
+ @asyncio.coroutine
+ def run_as_coroutine(self, stdin, callbacks):
+ """
+ The input 'event loop'.
+ """
+ assert isinstance(callbacks, EventLoopCallbacks)
+
+ # Create reader class.
+ stdin_reader = PosixStdinReader(stdin.fileno())
+
+ if self.closed:
+ raise Exception('Event loop already closed.')
+
+ inputstream = InputStream(callbacks.feed_key)
+
+ try:
+ # Create a new Future every time.
+ self._stopped_f = asyncio.Future(loop=self.loop)
+
+ # Handle input timouts
+ def timeout_handler():
+ """
+ When no input has been received for INPUT_TIMEOUT seconds,
+ flush the input stream and fire the timeout event.
+ """
+ inputstream.flush()
+
+ callbacks.input_timeout()
+
+ timeout = AsyncioTimeout(INPUT_TIMEOUT, timeout_handler, self.loop)
+
+ # Catch sigwinch
+ def received_winch():
+ self.call_from_executor(callbacks.terminal_size_changed)
+
+ self.loop.add_signal_handler(signal.SIGWINCH, received_winch)
+
+ # Read input data.
+ def stdin_ready():
+ data = stdin_reader.read()
+ inputstream.feed(data)
+ timeout.reset()
+
+ # Quit when the input stream was closed.
+ if stdin_reader.closed:
+ self.stop()
+
+ self.loop.add_reader(stdin.fileno(), stdin_ready)
+
+ # Block this coroutine until stop() has been called.
+ for f in self._stopped_f:
+ yield f
+
+ finally:
+ # Clean up.
+ self.loop.remove_reader(stdin.fileno())
+ self.loop.remove_signal_handler(signal.SIGWINCH)
+
+ # Don't trigger any timeout events anymore.
+ timeout.stop()
+
+ def stop(self):
+ # Trigger the 'Stop' future.
+ self._stopped_f.set_result(True)
+
+ def close(self):
+ # Note: we should not close the asyncio loop itself, because that one
+ # was not created here.
+ self.closed = True
+
+ def run_in_executor(self, callback):
+ self.loop.run_in_executor(None, callback)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ """
+ Call this function in the main event loop.
+ Similar to Twisted's ``callFromThread``.
+ """
+ self.loop.call_soon_threadsafe(callback)
+
+ def add_reader(self, fd, callback):
+ " Start watching the file descriptor for read availability. "
+ self.loop.add_reader(fd, callback)
+
+ def remove_reader(self, fd):
+ " Stop watching the file descriptor for read availability. "
+ self.loop.remove_reader(fd)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py
new file mode 100644
index 00000000000..45f5f526798
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py
@@ -0,0 +1,83 @@
+"""
+Win32 asyncio event loop.
+
+Windows notes:
+- Somehow it doesn't seem to work with the 'ProactorEventLoop'.
+"""
+from __future__ import unicode_literals
+
+from .base import EventLoop, INPUT_TIMEOUT
+from ..terminal.win32_input import ConsoleInputReader
+from .callbacks import EventLoopCallbacks
+from .asyncio_base import AsyncioTimeout
+
+import asyncio
+
+__all__ = (
+ 'Win32AsyncioEventLoop',
+)
+
+
+class Win32AsyncioEventLoop(EventLoop):
+ def __init__(self, loop=None):
+ self._console_input_reader = ConsoleInputReader()
+ self.running = False
+ self.closed = False
+ self.loop = loop or asyncio.get_event_loop()
+
+ @asyncio.coroutine
+ def run_as_coroutine(self, stdin, callbacks):
+ """
+ The input 'event loop'.
+ """
+ # Note: We cannot use "yield from", because this package also
+ # installs on Python 2.
+ assert isinstance(callbacks, EventLoopCallbacks)
+
+ if self.closed:
+ raise Exception('Event loop already closed.')
+
+ timeout = AsyncioTimeout(INPUT_TIMEOUT, callbacks.input_timeout, self.loop)
+ self.running = True
+
+ try:
+ while self.running:
+ timeout.reset()
+
+ # Get keys
+ try:
+ g = iter(self.loop.run_in_executor(None, self._console_input_reader.read))
+ while True:
+ yield next(g)
+ except StopIteration as e:
+ keys = e.args[0]
+
+ # Feed keys to input processor.
+ for k in keys:
+ callbacks.feed_key(k)
+ finally:
+ timeout.stop()
+
+ def stop(self):
+ self.running = False
+
+ def close(self):
+ # Note: we should not close the asyncio loop itself, because that one
+ # was not created here.
+ self.closed = True
+
+ self._console_input_reader.close()
+
+ def run_in_executor(self, callback):
+ self.loop.run_in_executor(None, callback)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ self.loop.call_soon_threadsafe(callback)
+
+ def add_reader(self, fd, callback):
+ " Start watching the file descriptor for read availability. "
+ self.loop.add_reader(fd, callback)
+
+ def remove_reader(self, fd):
+ " Stop watching the file descriptor for read availability. "
+ self.loop.remove_reader(fd)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py
new file mode 100644
index 00000000000..db86face668
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py
@@ -0,0 +1,85 @@
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+__all__ = (
+ 'EventLoop',
+ 'INPUT_TIMEOUT',
+)
+
+
+#: When to trigger the `onInputTimeout` event.
+INPUT_TIMEOUT = .5
+
+
+class EventLoop(with_metaclass(ABCMeta, object)):
+ """
+ Eventloop interface.
+ """
+ def run(self, stdin, callbacks):
+ """
+ Run the eventloop until stop() is called. Report all
+ input/timeout/terminal-resize events to the callbacks.
+
+ :param stdin: :class:`~prompt_toolkit.input.Input` instance.
+ :param callbacks: :class:`~prompt_toolkit.eventloop.callbacks.EventLoopCallbacks` instance.
+ """
+ raise NotImplementedError("This eventloop doesn't implement synchronous 'run()'.")
+
+ def run_as_coroutine(self, stdin, callbacks):
+ """
+ Similar to `run`, but this is a coroutine. (For asyncio integration.)
+ """
+ raise NotImplementedError("This eventloop doesn't implement 'run_as_coroutine()'.")
+
+ @abstractmethod
+ def stop(self):
+ """
+ Stop the `run` call. (Normally called by
+ :class:`~prompt_toolkit.interface.CommandLineInterface`, when a result
+ is available, or Abort/Quit has been called.)
+ """
+
+ @abstractmethod
+ def close(self):
+ """
+ Clean up of resources. Eventloop cannot be reused a second time after
+ this call.
+ """
+
+ @abstractmethod
+ def add_reader(self, fd, callback):
+ """
+ Start watching the file descriptor for read availability and then call
+ the callback.
+ """
+
+ @abstractmethod
+ def remove_reader(self, fd):
+ """
+ Stop watching the file descriptor for read availability.
+ """
+
+ @abstractmethod
+ def run_in_executor(self, callback):
+ """
+ Run a long running function in a background thread. (This is
+ recommended for code that could block the event loop.)
+ Similar to Twisted's ``deferToThread``.
+ """
+
+ @abstractmethod
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ """
+ Call this function in the main event loop. Similar to Twisted's
+ ``callFromThread``.
+
+ :param _max_postpone_until: `None` or `time.time` value. For interal
+ use. If the eventloop is saturated, consider this task to be low
+ priority and postpone maximum until this timestamp. (For instance,
+ repaint is done using low priority.)
+
+ Note: In the past, this used to be a datetime.datetime instance,
+ but apparently, executing `time.time` is more efficient: it
+ does fewer system calls. (It doesn't read /etc/localtime.)
+ """
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py
new file mode 100644
index 00000000000..04adab6fd49
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py
@@ -0,0 +1,29 @@
+from __future__ import unicode_literals
+from abc import ABCMeta, abstractmethod
+from six import with_metaclass
+
+__all__ = (
+ 'EventLoopCallbacks',
+)
+
+
+class EventLoopCallbacks(with_metaclass(ABCMeta, object)):
+ """
+ This is the glue between the :class:`~prompt_toolkit.eventloop.base.EventLoop`
+ and :class:`~prompt_toolkit.interface.CommandLineInterface`.
+
+ :meth:`~prompt_toolkit.eventloop.base.EventLoop.run` takes an
+ :class:`.EventLoopCallbacks` instance and operates on that one, driving the
+ interface.
+ """
+ @abstractmethod
+ def terminal_size_changed(self):
+ pass
+
+ @abstractmethod
+ def input_timeout(self):
+ pass
+
+ @abstractmethod
+ def feed_key(self, key):
+ pass
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py
new file mode 100644
index 00000000000..bab1f4c003a
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py
@@ -0,0 +1,107 @@
+"""
+Similar to `PyOS_InputHook` of the Python API. Some eventloops can have an
+inputhook to allow easy integration with other event loops.
+
+When the eventloop of prompt-toolkit is idle, it can call such a hook. This
+hook can call another eventloop that runs for a short while, for instance to
+keep a graphical user interface responsive.
+
+It's the responsibility of this hook to exit when there is input ready.
+There are two ways to detect when input is ready:
+
+- Call the `input_is_ready` method periodically. Quit when this returns `True`.
+
+- Add the `fileno` as a watch to the external eventloop. Quit when file descriptor
+ becomes readable. (But don't read from it.)
+
+ Note that this is not the same as checking for `sys.stdin.fileno()`. The
+ eventloop of prompt-toolkit allows thread-based executors, for example for
+ asynchronous autocompletion. When the completion for instance is ready, we
+ also want prompt-toolkit to gain control again in order to display that.
+
+An alternative to using input hooks, is to create a custom `EventLoop` class that
+controls everything.
+"""
+from __future__ import unicode_literals
+import os
+import threading
+from prompt_toolkit.utils import is_windows
+from .select import select_fds
+
+__all__ = (
+ 'InputHookContext',
+)
+
+
+class InputHookContext(object):
+ """
+ Given as a parameter to the inputhook.
+ """
+ def __init__(self, inputhook):
+ assert callable(inputhook)
+
+ self.inputhook = inputhook
+ self._input_is_ready = None
+
+ self._r, self._w = os.pipe()
+
+ def input_is_ready(self):
+ """
+ Return True when the input is ready.
+ """
+ return self._input_is_ready(wait=False)
+
+ def fileno(self):
+ """
+ File descriptor that will become ready when the event loop needs to go on.
+ """
+ return self._r
+
+ def call_inputhook(self, input_is_ready_func):
+ """
+ Call the inputhook. (Called by a prompt-toolkit eventloop.)
+ """
+ self._input_is_ready = input_is_ready_func
+
+ # Start thread that activates this pipe when there is input to process.
+ def thread():
+ input_is_ready_func(wait=True)
+ os.write(self._w, b'x')
+
+ threading.Thread(target=thread).start()
+
+ # Call inputhook.
+ self.inputhook(self)
+
+ # Flush the read end of the pipe.
+ try:
+ # Before calling 'os.read', call select.select. This is required
+ # when the gevent monkey patch has been applied. 'os.read' is never
+ # monkey patched and won't be cooperative, so that would block all
+ # other select() calls otherwise.
+ # See: http://www.gevent.org/gevent.os.html
+
+ # Note: On Windows, this is apparently not an issue.
+ # 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():
+ select_fds([self._r], timeout=None)
+
+ os.read(self._r, 1024)
+ except OSError:
+ # This happens when the window resizes and a SIGWINCH was received.
+ # We get 'Error: [Errno 4] Interrupted system call'
+ # Just ignore.
+ pass
+ self._input_is_ready = None
+
+ def close(self):
+ """
+ Clean up resources.
+ """
+ if self._r:
+ os.close(self._r)
+ os.close(self._w)
+
+ self._r = self._w = None
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py
new file mode 100644
index 00000000000..f631dbd8915
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py
@@ -0,0 +1,306 @@
+from __future__ import unicode_literals
+import fcntl
+import os
+import signal
+import threading
+import time
+
+from prompt_toolkit.terminal.vt100_input import InputStream
+from prompt_toolkit.utils import DummyContext, in_main_thread
+from prompt_toolkit.input import Input
+from .base import EventLoop, INPUT_TIMEOUT
+from .callbacks import EventLoopCallbacks
+from .inputhook import InputHookContext
+from .posix_utils import PosixStdinReader
+from .utils import TimeIt
+from .select import AutoSelector, Selector, fd_to_int
+
+__all__ = (
+ 'PosixEventLoop',
+)
+
+_now = time.time
+
+
+class PosixEventLoop(EventLoop):
+ """
+ Event loop for posix systems (Linux, Mac os X).
+ """
+ def __init__(self, inputhook=None, selector=AutoSelector):
+ assert inputhook is None or callable(inputhook)
+ assert issubclass(selector, Selector)
+
+ self.running = False
+ self.closed = False
+ self._running = False
+ self._callbacks = None
+
+ self._calls_from_executor = []
+ self._read_fds = {} # Maps fd to handler.
+ self.selector = selector()
+
+ # Create a pipe for inter thread communication.
+ self._schedule_pipe = os.pipe()
+ fcntl.fcntl(self._schedule_pipe[0], fcntl.F_SETFL, os.O_NONBLOCK)
+
+ # Create inputhook context.
+ self._inputhook_context = InputHookContext(inputhook) if inputhook else None
+
+ def run(self, stdin, callbacks):
+ """
+ The input 'event loop'.
+ """
+ assert isinstance(stdin, Input)
+ assert isinstance(callbacks, EventLoopCallbacks)
+ assert not self._running
+
+ if self.closed:
+ raise Exception('Event loop already closed.')
+
+ self._running = True
+ self._callbacks = callbacks
+
+ inputstream = InputStream(callbacks.feed_key)
+ current_timeout = [INPUT_TIMEOUT] # Nonlocal
+
+ # Create reader class.
+ stdin_reader = PosixStdinReader(stdin.fileno())
+
+ # Only attach SIGWINCH signal handler in main thread.
+ # (It's not possible to attach signal handlers in other threads. In
+ # that case we should rely on a the main thread to call this manually
+ # instead.)
+ if in_main_thread():
+ ctx = call_on_sigwinch(self.received_winch)
+ else:
+ ctx = DummyContext()
+
+ def read_from_stdin():
+ " Read user input. "
+ # Feed input text.
+ data = stdin_reader.read()
+ inputstream.feed(data)
+
+ # Set timeout again.
+ current_timeout[0] = INPUT_TIMEOUT
+
+ # Quit when the input stream was closed.
+ if stdin_reader.closed:
+ self.stop()
+
+ self.add_reader(stdin, read_from_stdin)
+ self.add_reader(self._schedule_pipe[0], None)
+
+ with ctx:
+ while self._running:
+ # Call inputhook.
+ if self._inputhook_context:
+ with TimeIt() as inputhook_timer:
+ def ready(wait):
+ " True when there is input ready. The inputhook should return control. "
+ return self._ready_for_reading(current_timeout[0] if wait else 0) != []
+ self._inputhook_context.call_inputhook(ready)
+ inputhook_duration = inputhook_timer.duration
+ else:
+ inputhook_duration = 0
+
+ # Calculate remaining timeout. (The inputhook consumed some of the time.)
+ if current_timeout[0] is None:
+ remaining_timeout = None
+ else:
+ remaining_timeout = max(0, current_timeout[0] - inputhook_duration)
+
+ # Wait until input is ready.
+ fds = self._ready_for_reading(remaining_timeout)
+
+ # When any of the FDs are ready. Call the appropriate callback.
+ if fds:
+ # Create lists of high/low priority tasks. The main reason
+ # for this is to allow painting the UI to happen as soon as
+ # possible, but when there are many events happening, we
+ # don't want to call the UI renderer 1000x per second. If
+ # the eventloop is completely saturated with many CPU
+ # intensive tasks (like processing input/output), we say
+ # that drawing the UI can be postponed a little, to make
+ # CPU available. This will be a low priority task in that
+ # case.
+ tasks = []
+ low_priority_tasks = []
+ now = None # Lazy load time. (Fewer system calls.)
+
+ for fd in fds:
+ # For the 'call_from_executor' fd, put each pending
+ # item on either the high or low priority queue.
+ if fd == self._schedule_pipe[0]:
+ for c, max_postpone_until in self._calls_from_executor:
+ if max_postpone_until is None:
+ # Execute now.
+ tasks.append(c)
+ else:
+ # Execute soon, if `max_postpone_until` is in the future.
+ now = now or _now()
+ if max_postpone_until < now:
+ tasks.append(c)
+ else:
+ low_priority_tasks.append((c, max_postpone_until))
+ self._calls_from_executor = []
+
+ # Flush all the pipe content.
+ os.read(self._schedule_pipe[0], 1024)
+ else:
+ handler = self._read_fds.get(fd)
+ if handler:
+ tasks.append(handler)
+
+ # When there are high priority tasks, run all these.
+ # Schedule low priority tasks for the next iteration.
+ if tasks:
+ for t in tasks:
+ t()
+
+ # Postpone low priority tasks.
+ for t, max_postpone_until in low_priority_tasks:
+ self.call_from_executor(t, _max_postpone_until=max_postpone_until)
+ else:
+ # Currently there are only low priority tasks -> run them right now.
+ for t, _ in low_priority_tasks:
+ t()
+
+ else:
+ # Flush all pending keys on a timeout. (This is most
+ # important to flush the vt100 'Escape' key early when
+ # nothing else follows.)
+ inputstream.flush()
+
+ # Fire input timeout event.
+ callbacks.input_timeout()
+ current_timeout[0] = None
+
+ self.remove_reader(stdin)
+ self.remove_reader(self._schedule_pipe[0])
+
+ self._callbacks = None
+
+ def _ready_for_reading(self, timeout=None):
+ """
+ Return the file descriptors that are ready for reading.
+ """
+ fds = self.selector.select(timeout)
+ return fds
+
+ def received_winch(self):
+ """
+ Notify the event loop that SIGWINCH has been received
+ """
+ # Process signal asynchronously, because this handler can write to the
+ # output, and doing this inside the signal handler causes easily
+ # reentrant calls, giving runtime errors..
+
+ # Furthur, this has to be thread safe. When the CommandLineInterface
+ # runs not in the main thread, this function still has to be called
+ # from the main thread. (The only place where we can install signal
+ # handlers.)
+ def process_winch():
+ if self._callbacks:
+ self._callbacks.terminal_size_changed()
+
+ self.call_from_executor(process_winch)
+
+ def run_in_executor(self, callback):
+ """
+ Run a long running function in a background thread.
+ (This is recommended for code that could block the event loop.)
+ Similar to Twisted's ``deferToThread``.
+ """
+ # Wait until the main thread is idle.
+ # We start the thread by using `call_from_executor`. The event loop
+ # favours processing input over `calls_from_executor`, so the thread
+ # will not start until there is no more input to process and the main
+ # thread becomes idle for an instant. This is good, because Python
+ # threading favours CPU over I/O -- an autocompletion thread in the
+ # background would cause a significantly slow down of the main thread.
+ # It is mostly noticable when pasting large portions of text while
+ # having real time autocompletion while typing on.
+ def start_executor():
+ threading.Thread(target=callback).start()
+ self.call_from_executor(start_executor)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ """
+ Call this function in the main event loop.
+ Similar to Twisted's ``callFromThread``.
+
+ :param _max_postpone_until: `None` or `time.time` value. For interal
+ use. If the eventloop is saturated, consider this task to be low
+ priority and postpone maximum until this timestamp. (For instance,
+ repaint is done using low priority.)
+ """
+ assert _max_postpone_until is None or isinstance(_max_postpone_until, float)
+ self._calls_from_executor.append((callback, _max_postpone_until))
+
+ if self._schedule_pipe:
+ try:
+ os.write(self._schedule_pipe[1], b'x')
+ except (AttributeError, IndexError, OSError):
+ # Handle race condition. We're in a different thread.
+ # - `_schedule_pipe` could have become None in the meantime.
+ # - We catch `OSError` (actually BrokenPipeError), because the
+ # main thread could have closed the pipe already.
+ pass
+
+ def stop(self):
+ """
+ Stop the event loop.
+ """
+ self._running = False
+
+ def close(self):
+ self.closed = True
+
+ # Close pipes.
+ schedule_pipe = self._schedule_pipe
+ self._schedule_pipe = None
+
+ if schedule_pipe:
+ os.close(schedule_pipe[0])
+ os.close(schedule_pipe[1])
+
+ if self._inputhook_context:
+ self._inputhook_context.close()
+
+ def add_reader(self, fd, callback):
+ " Add read file descriptor to the event loop. "
+ fd = fd_to_int(fd)
+ self._read_fds[fd] = callback
+ self.selector.register(fd)
+
+ def remove_reader(self, fd):
+ " Remove read file descriptor from the event loop. "
+ fd = fd_to_int(fd)
+
+ if fd in self._read_fds:
+ del self._read_fds[fd]
+
+ self.selector.unregister(fd)
+
+
+class call_on_sigwinch(object):
+ """
+ Context manager which Installs a SIGWINCH callback.
+ (This signal occurs when the terminal size changes.)
+ """
+ def __init__(self, callback):
+ self.callback = callback
+ self.previous_callback = None
+
+ def __enter__(self):
+ self.previous_callback = signal.signal(signal.SIGWINCH, lambda *a: self.callback())
+
+ def __exit__(self, *a, **kw):
+ if self.previous_callback is None:
+ # Normally, `signal.signal` should never return `None`.
+ # For some reason it happens here:
+ # https://github.com/jonathanslenders/python-prompt-toolkit/pull/174
+ signal.signal(signal.SIGWINCH, 0)
+ else:
+ signal.signal(signal.SIGWINCH, self.previous_callback)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py
new file mode 100644
index 00000000000..320df438ca2
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py
@@ -0,0 +1,82 @@
+from __future__ import unicode_literals
+
+from codecs import getincrementaldecoder
+import os
+import six
+
+__all__ = (
+ 'PosixStdinReader',
+)
+
+
+class PosixStdinReader(object):
+ """
+ Wrapper around stdin which reads (nonblocking) the next available 1024
+ bytes and decodes it.
+
+ Note that you can't be sure that the input file is closed if the ``read``
+ function returns an empty string. When ``errors=ignore`` is passed,
+ ``read`` can return an empty string if all malformed input was replaced by
+ an empty string. (We can't block here and wait for more input.) So, because
+ of that, check the ``closed`` attribute, to be sure that the file has been
+ closed.
+
+ :param stdin_fd: File descriptor from which we read.
+ :param errors: Can be 'ignore', 'strict' or 'replace'.
+ On Python3, this can be 'surrogateescape', which is the default.
+
+ 'surrogateescape' is preferred, because this allows us to transfer
+ unrecognised bytes to the key bindings. Some terminals, like lxterminal
+ and Guake, use the 'Mxx' notation to send mouse events, where each 'x'
+ can be any possible byte.
+ """
+ # By default, we want to 'ignore' errors here. The input stream can be full
+ # of junk. One occurrence of this that I had was when using iTerm2 on OS X,
+ # with "Option as Meta" checked (You should choose "Option as +Esc".)
+
+ def __init__(self, stdin_fd,
+ errors=('ignore' if six.PY2 else 'surrogateescape')):
+ assert isinstance(stdin_fd, int)
+ self.stdin_fd = stdin_fd
+ self.errors = errors
+
+ # Create incremental decoder for decoding stdin.
+ # We can not just do `os.read(stdin.fileno(), 1024).decode('utf-8')`, because
+ # it could be that we are in the middle of a utf-8 byte sequence.
+ self._stdin_decoder_cls = getincrementaldecoder('utf-8')
+ self._stdin_decoder = self._stdin_decoder_cls(errors=errors)
+
+ #: True when there is nothing anymore to read.
+ self.closed = False
+
+ def read(self, count=1024):
+ # By default we choose a rather small chunk size, because reading
+ # big amounts of input at once, causes the event loop to process
+ # all these key bindings also at once without going back to the
+ # loop. This will make the application feel unresponsive.
+ """
+ Read the input and return it as a string.
+
+ Return the text. Note that this can return an empty string, even when
+ the input stream was not yet closed. This means that something went
+ wrong during the decoding.
+ """
+ if self.closed:
+ return b''
+
+ # Note: the following works better than wrapping `self.stdin` like
+ # `codecs.getreader('utf-8')(stdin)` and doing `read(1)`.
+ # Somehow that causes some latency when the escape
+ # character is pressed. (Especially on combination with the `select`.)
+ try:
+ data = os.read(self.stdin_fd, count)
+
+ # Nothing more to read, stream is closed.
+ if data == b'':
+ self.closed = True
+ return ''
+ except OSError:
+ # In case of SIGWINCH
+ data = b''
+
+ return self._stdin_decoder.decode(data)
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py
new file mode 100644
index 00000000000..f678f84c557
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py
@@ -0,0 +1,216 @@
+"""
+Selectors for the Posix event loop.
+"""
+from __future__ import unicode_literals, absolute_import
+import sys
+import abc
+import errno
+import select
+import six
+
+__all__ = (
+ 'AutoSelector',
+ 'PollSelector',
+ 'SelectSelector',
+ 'Selector',
+ 'fd_to_int',
+)
+
+def fd_to_int(fd):
+ assert isinstance(fd, int) or hasattr(fd, 'fileno')
+
+ if isinstance(fd, int):
+ return fd
+ else:
+ return fd.fileno()
+
+
+class Selector(six.with_metaclass(abc.ABCMeta, object)):
+ @abc.abstractmethod
+ def register(self, fd):
+ assert isinstance(fd, int)
+
+ @abc.abstractmethod
+ def unregister(self, fd):
+ assert isinstance(fd, int)
+
+ @abc.abstractmethod
+ def select(self, timeout):
+ pass
+
+ @abc.abstractmethod
+ def close(self):
+ pass
+
+
+class AutoSelector(Selector):
+ def __init__(self):
+ self._fds = []
+
+ self._select_selector = SelectSelector()
+ self._selectors = [self._select_selector]
+
+ # When 'select.poll' exists, create a PollSelector.
+ if hasattr(select, 'poll'):
+ self._poll_selector = PollSelector()
+ self._selectors.append(self._poll_selector)
+ else:
+ self._poll_selector = None
+
+ # Use of the 'select' module, that was introduced in Python3.4. We don't
+ # use it before 3.5 however, because this is the point where this module
+ # retries interrupted system calls.
+ if sys.version_info >= (3, 5):
+ self._py3_selector = Python3Selector()
+ self._selectors.append(self._py3_selector)
+ else:
+ self._py3_selector = None
+
+ def register(self, fd):
+ assert isinstance(fd, int)
+
+ self._fds.append(fd)
+
+ for sel in self._selectors:
+ sel.register(fd)
+
+ def unregister(self, fd):
+ assert isinstance(fd, int)
+
+ self._fds.remove(fd)
+
+ for sel in self._selectors:
+ sel.unregister(fd)
+
+ def select(self, timeout):
+ # Try Python 3 selector first.
+ if self._py3_selector:
+ try:
+ return self._py3_selector.select(timeout)
+ except PermissionError:
+ # We had a situation (in pypager) where epoll raised a
+ # PermissionError when a local file descriptor was registered,
+ # however poll and select worked fine. So, in that case, just
+ # try using select below.
+ pass
+
+ try:
+ # Prefer 'select.select', if we don't have much file descriptors.
+ # This is more universal.
+ return self._select_selector.select(timeout)
+ except ValueError:
+ # When we have more than 1024 open file descriptors, we'll always
+ # get a "ValueError: filedescriptor out of range in select()" for
+ # 'select'. In this case, try, using 'poll' instead.
+ if self._poll_selector is not None:
+ return self._poll_selector.select(timeout)
+ else:
+ raise
+
+ def close(self):
+ for sel in self._selectors:
+ sel.close()
+
+
+class Python3Selector(Selector):
+ """
+ Use of the Python3 'selectors' module.
+
+ NOTE: Only use on Python 3.5 or newer!
+ """
+ def __init__(self):
+ assert sys.version_info >= (3, 5)
+
+ import selectors # Inline import: Python3 only!
+ self._sel = selectors.DefaultSelector()
+
+ def register(self, fd):
+ assert isinstance(fd, int)
+ import selectors # Inline import: Python3 only!
+ self._sel.register(fd, selectors.EVENT_READ, None)
+
+ def unregister(self, fd):
+ assert isinstance(fd, int)
+ self._sel.unregister(fd)
+
+ def select(self, timeout):
+ events = self._sel.select(timeout=timeout)
+ return [key.fileobj for key, mask in events]
+
+ def close(self):
+ self._sel.close()
+
+
+class PollSelector(Selector):
+ def __init__(self):
+ self._poll = select.poll()
+
+ def register(self, fd):
+ assert isinstance(fd, int)
+ self._poll.register(fd, select.POLLIN)
+
+ def unregister(self, fd):
+ assert isinstance(fd, int)
+
+ def select(self, timeout):
+ tuples = self._poll.poll(timeout) # Returns (fd, event) tuples.
+ return [t[0] for t in tuples]
+
+ def close(self):
+ pass # XXX
+
+
+class SelectSelector(Selector):
+ """
+ Wrapper around select.select.
+
+ When the SIGWINCH signal is handled, other system calls, like select
+ are aborted in Python. This wrapper will retry the system call.
+ """
+ def __init__(self):
+ self._fds = []
+
+ def register(self, fd):
+ self._fds.append(fd)
+
+ def unregister(self, fd):
+ self._fds.remove(fd)
+
+ def select(self, timeout):
+ while True:
+ try:
+ return select.select(self._fds, [], [], timeout)[0]
+ except select.error as e:
+ # Retry select call when EINTR
+ if e.args and e.args[0] == errno.EINTR:
+ continue
+ else:
+ raise
+
+ def close(self):
+ pass
+
+
+def select_fds(read_fds, timeout, selector=AutoSelector):
+ """
+ Wait for a list of file descriptors (`read_fds`) to become ready for
+ reading. This chooses the most appropriate select-tool for use in
+ prompt-toolkit.
+ """
+ # Map to ensure that we return the objects that were passed in originally.
+ # Whether they are a fd integer or an object that has a fileno().
+ # (The 'poll' implementation for instance, returns always integers.)
+ fd_map = dict((fd_to_int(fd), fd) for fd in read_fds)
+
+ # Wait, using selector.
+ sel = selector()
+ try:
+ for fd in read_fds:
+ sel.register(fd)
+
+ result = sel.select(timeout)
+
+ if result is not None:
+ return [fd_map[fd_to_int(fd)] for fd in result]
+ finally:
+ sel.close()
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py
new file mode 100644
index 00000000000..ff3a4cfd697
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py
@@ -0,0 +1,23 @@
+from __future__ import unicode_literals
+import time
+
+__all__ = (
+ 'TimeIt',
+)
+
+
+class TimeIt(object):
+ """
+ Context manager that times the duration of the code body.
+ The `duration` attribute will contain the execution time in seconds.
+ """
+ def __init__(self):
+ self.duration = None
+
+ def __enter__(self):
+ self.start = time.time()
+ return self
+
+ def __exit__(self, *args):
+ self.end = time.time()
+ self.duration = self.end - self.start
diff --git a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py
new file mode 100644
index 00000000000..18e356f0882
--- /dev/null
+++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py
@@ -0,0 +1,187 @@
+"""
+Win32 event loop.
+
+Windows notes:
+ - Somehow it doesn't seem to work with the 'ProactorEventLoop'.
+"""
+from __future__ import unicode_literals
+
+from ..terminal.win32_input import ConsoleInputReader
+from ..win32_types import SECURITY_ATTRIBUTES
+from .base import EventLoop, INPUT_TIMEOUT
+from .inputhook import InputHookContext
+from .utils import TimeIt
+
+from ctypes import windll, pointer
+from ctypes.wintypes import DWORD, BOOL, HANDLE
+
+import msvcrt
+import threading
+
+__all__ = (
+ 'Win32EventLoop',
+)
+
+WAIT_TIMEOUT = 0x00000102
+INPUT_TIMEOUT_MS = int(1000 * INPUT_TIMEOUT)
+
+
+class Win32EventLoop(EventLoop):
+ """
+ Event loop for Windows systems.
+
+ :param recognize_paste: When True, try to discover paste actions and turn
+ the event into a BracketedPaste.
+ """
+ def __init__(self, inputhook=None, recognize_paste=True):
+ assert inputhook is None or callable(inputhook)
+
+ self._event = HANDLE(_create_event())
+ self._console_input_reader = ConsoleInputReader(recognize_paste=recognize_paste)
+ self._calls_from_executor = []
+
+ self.closed = False
+ self._running = False
+
+ # Additional readers.
+ self._read_fds = {} # Maps fd to handler.
+
+ # Create inputhook context.
+ self._inputhook_context = InputHookContext(inputhook) if inputhook else None
+
+ def run(self, stdin, callbacks):
+ if self.closed:
+ raise Exception('Event loop already closed.')
+
+ current_timeout = INPUT_TIMEOUT_MS
+ self._running = True
+
+ while self._running:
+ # Call inputhook.
+ with TimeIt() as inputhook_timer:
+ if self._inputhook_context:
+ def ready(wait):
+ " True when there is input ready. The inputhook should return control. "
+ return bool(self._ready_for_reading(current_timeout if wait else 0))
+ self._inputhook_context.call_inputhook(ready)
+
+ # Calculate remaining timeout. (The inputhook consumed some of the time.)
+ if current_timeout == -1:
+ remaining_timeout = -1
+ else:
+ remaining_timeout = max(0, current_timeout - int(1000 * inputhook_timer.duration))
+
+ # Wait for the next event.
+ handle = self._ready_for_reading(remaining_timeout)
+
+ if handle == self._console_input_reader.handle.value:
+ # When stdin is ready, read input and reset timeout timer.
+ keys = self._console_input_reader.read()
+ for k in keys:
+ callbacks.feed_key(k)
+ current_timeout = INPUT_TIMEOUT_MS
+
+ elif handle == self._event.value:
+ # When the Windows Event has been trigger, process the messages in the queue.
+ windll.kernel32.ResetEvent(self._event)
+ self._process_queued_calls_from_executor()
+
+ elif handle in self._read_fds:
+ callback = self._read_fds[handle]
+ callback()
+ else:
+ # Fire input timeout event.
+ callbacks.input_timeout()
+ current_timeout = -1
+
+ def _ready_for_reading(self, timeout=None):
+ """
+ Return the handle that is ready for reading or `None` on timeout.
+ """
+ handles = [self._event, self._console_input_reader.handle]
+ handles.extend(self._read_fds.keys())
+ return _wait_for_handles(handles, timeout)
+
+ def stop(self):
+ self._running = False
+
+ def close(self):
+ self.closed = True
+
+ # Clean up Event object.
+ windll.kernel32.CloseHandle(self._event)
+
+ if self._inputhook_context:
+ self._inputhook_context.close()
+
+ self._console_input_reader.close()
+
+ def run_in_executor(self, callback):
+ """
+ Run a long running function in a background thread.
+ (This is recommended for code that could block the event loop.)
+ Similar to Twisted's ``deferToThread``.
+ """
+ # Wait until the main thread is idle for an instant before starting the
+ # executor. (Like in eventloop/posix.py, we start the executor using
+ # `call_from_executor`.)
+ def start_executor():
+ threading.Thread(target=callback).start()
+ self.call_from_executor(start_executor)
+
+ def call_from_executor(self, callback, _max_postpone_until=None):
+ """
+ Call this function in the main event loop.
+ Similar to Twisted's ``callFromThread``.
+ """
+ # Append to list of pending callbacks.
+ self._calls_from_executor.append(callback)
+
+ # Set Windows event.
+ windll.kernel32.SetEvent(self._event)
+
+ def _process_queued_calls_from_executor(self):
+ # Process calls from executor.
+ calls_from_executor, self._calls_from_executor = self._calls_from_executor, []
+ for c in calls_from_executor:
+ c()
+
+ def add_reader(self, fd, callback):
+ " Start watching the file descriptor for read availability. "
+ h = msvcrt.get_osfhandle(fd)
+ self._read_fds[h] = callback
+
+ def remove_reader(self, fd):
+ " Stop watching the file descriptor for read availability. "
+ h = msvcrt.get_osfhandle(fd)
+ if h in self._read_fds:
+ del self._read_fds[h]
+
+
+def _wait_for_handles(handles, timeout=-1):
+ """
+ Waits for multiple handles. (Similar to 'select') Returns the handle which is ready.
+ Returns `None` on timeout.
+
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx
+ """
+ arrtype = HANDLE * len(handles)
+ handle_array = arrtype(*handles)
+
+ ret = windll.kernel32.WaitForMultipleObjects(
+ len(handle_array), handle_array, BOOL(False), DWORD(timeout))
+
+ if ret == WAIT_TIMEOUT:
+ return None
+ else:
+ h = handle_array[ret]
+ return h
+
+
+def _create_event():
+ """
+ Creates a Win32 unnamed Event .
+
+ http://msdn.microsoft.com/en-us/library/windows/desktop/ms682396(v=vs.85).aspx
+ """
+ return windll.kernel32.CreateEventA(pointer(SECURITY_ATTRIBUTES()), BOOL(True), BOOL(False), None)