diff options
author | Ivan Blinkov <ivan@blinkov.ru> | 2022-02-10 16:47:11 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:47:11 +0300 |
commit | 5b283123c882433dafbaf6b338adeea16c1a0ea0 (patch) | |
tree | 339adc63bce23800021202ae4a8328a843dc447a /contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop | |
parent | 1aeb9a455974457866f78722ad98114bafc84e8a (diff) | |
download | ydb-5b283123c882433dafbaf6b338adeea16c1a0ea0.tar.gz |
Restoring authorship annotation for Ivan Blinkov <ivan@blinkov.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop')
10 files changed, 925 insertions, 925 deletions
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 index 1ea41dda60..ace2b8db49 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_base.py @@ -1,46 +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 +""" +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 index 26606e8493..426ed96f67 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_posix.py @@ -1,113 +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 - +""" +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. + + @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() - + + # 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) + 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 index adc538afe9..45f5f52679 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/asyncio_win32.py @@ -1,83 +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 - +""" +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) + 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 index 0be339ca7c..db86face66 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py @@ -1,85 +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``. - +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.) + 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 index 068d900f62..04adab6fd4 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/callbacks.py @@ -1,29 +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 +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 index c49d5876a1..bab1f4c003 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py @@ -1,80 +1,80 @@ -""" -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 +""" +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: + +__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 @@ -88,20 +88,20 @@ class InputHookContext(object): 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 + 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 index a2ef2ac3d8..f631dbd891 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py @@ -1,244 +1,244 @@ -from __future__ import unicode_literals -import fcntl -import os -import signal -import threading +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 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', -) - + +__all__ = ( + 'PosixEventLoop', +) + _now = time.time - - -class PosixEventLoop(EventLoop): - """ - Event loop for posix systems (Linux, Mac os X). - """ + + +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 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.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 - + + # 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. + 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) + 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: + + # 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 = [] + + # 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: + + 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: + 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. - """ + 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``. - + + 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.) - """ + 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: + 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): @@ -247,60 +247,60 @@ class PosixEventLoop(EventLoop): # - 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. " + + 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._read_fds[fd] = callback self.selector.register(fd) - - def remove_reader(self, fd): - " Remove read file descriptor from the event loop. " + + 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] - + 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) + + +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 index af040650f0..320df438ca 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix_utils.py @@ -1,18 +1,18 @@ -from __future__ import unicode_literals - -from codecs import getincrementaldecoder -import os +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. + +__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, @@ -29,54 +29,54 @@ class PosixStdinReader(object): 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 + 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') + + # 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. + 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) + # 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'' - + 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/utils.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py index 56b4646d1c..ff3a4cfd69 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/utils.py @@ -1,23 +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 +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 index 273c277de9..18e356f088 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py @@ -1,187 +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 - +""" +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. +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) - + 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 - + 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) - + # 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 - + # 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() - + # 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. - """ + 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() - + + 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. " + 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. " + + 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) + + +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) |