diff options
author | Nikita Slyusarev <nslus@yandex-team.com> | 2022-02-10 16:46:52 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:46:52 +0300 |
commit | cd77cecfc03a3eaf87816af28a33067c4f0cdb59 (patch) | |
tree | 1308e0bae862d52e0020d881fe758080437fe389 /contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop | |
parent | cdae02d225fb5b3afbb28990e79a7ac6c9125327 (diff) | |
download | ydb-cd77cecfc03a3eaf87816af28a33067c4f0cdb59.tar.gz |
Restoring authorship annotation for Nikita Slyusarev <nslus@yandex-team.com>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop')
8 files changed, 347 insertions, 347 deletions
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 426ed96f67..737ca0ec00 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 @@ -67,10 +67,10 @@ class PosixAsyncioEventLoop(EventLoop): inputstream.feed(data) timeout.reset() - # Quit when the input stream was closed. - if stdin_reader.closed: - self.stop() - + # 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. 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 45f5f52679..6e4e2ca9e7 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 @@ -66,8 +66,8 @@ class Win32AsyncioEventLoop(EventLoop): # was not created here. self.closed = True - self._console_input_reader.close() - + self._console_input_reader.close() + def run_in_executor(self, callback): self.loop.run_in_executor(None, callback) 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 db86face66..520ab9e366 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/base.py @@ -74,12 +74,12 @@ class EventLoop(with_metaclass(ABCMeta, object)): Call this function in the main event loop. Similar to Twisted's ``callFromThread``. - :param _max_postpone_until: `None` or `time.time` value. For interal + :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.) + + 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/inputhook.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py index bab1f4c003..44e0a60591 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/inputhook.py @@ -25,8 +25,8 @@ controls everything. from __future__ import unicode_literals import os import threading -from prompt_toolkit.utils import is_windows -from .select import select_fds +from prompt_toolkit.utils import is_windows +from .select import select_fds __all__ = ( 'InputHookContext', @@ -75,19 +75,19 @@ class InputHookContext(object): # 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) - + # 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. 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 f631dbd891..be70d61f19 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/posix.py @@ -3,7 +3,7 @@ import fcntl import os import signal import threading -import time +import time from prompt_toolkit.terminal.vt100_input import InputStream from prompt_toolkit.utils import DummyContext, in_main_thread @@ -13,22 +13,22 @@ 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 +from .select import AutoSelector, Selector, fd_to_int __all__ = ( 'PosixEventLoop', ) -_now = time.time +_now = time.time class PosixEventLoop(EventLoop): """ Event loop for posix systems (Linux, Mac os X). """ - def __init__(self, inputhook=None, selector=AutoSelector): + def __init__(self, inputhook=None, selector=AutoSelector): assert inputhook is None or callable(inputhook) - assert issubclass(selector, Selector) + assert issubclass(selector, Selector) self.running = False self.closed = False @@ -37,7 +37,7 @@ class PosixEventLoop(EventLoop): self._calls_from_executor = [] self._read_fds = {} # Maps fd to handler. - self.selector = selector() + self.selector = selector() # Create a pipe for inter thread communication. self._schedule_pipe = os.pipe() @@ -84,31 +84,31 @@ class PosixEventLoop(EventLoop): # Set timeout again. current_timeout[0] = INPUT_TIMEOUT - # Quit when the input stream was closed. - if stdin_reader.closed: - self.stop() - + # 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: + 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 + 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) + remaining_timeout = max(0, current_timeout[0] - inputhook_duration) # Wait until input is ready. fds = self._ready_for_reading(remaining_timeout) @@ -126,23 +126,23 @@ class PosixEventLoop(EventLoop): # case. tasks = [] low_priority_tasks = [] - now = None # Lazy load time. (Fewer system calls.) + 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. + 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)) + # 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. @@ -185,8 +185,8 @@ class PosixEventLoop(EventLoop): """ Return the file descriptors that are ready for reading. """ - fds = self.selector.select(timeout) - return fds + fds = self.selector.select(timeout) + return fds def received_winch(self): """ @@ -230,23 +230,23 @@ class PosixEventLoop(EventLoop): Call this function in the main event loop. Similar to Twisted's ``callFromThread``. - :param _max_postpone_until: `None` or `time.time` value. For interal + :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) + 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 + 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): """ @@ -270,18 +270,18 @@ class PosixEventLoop(EventLoop): def add_reader(self, fd, callback): " Add read file descriptor to the event loop. " - fd = fd_to_int(fd) + fd = fd_to_int(fd) self._read_fds[fd] = callback - self.selector.register(fd) + self.selector.register(fd) def remove_reader(self, fd): " Remove read file descriptor from the event loop. " - fd = fd_to_int(fd) - + fd = fd_to_int(fd) + if fd in self._read_fds: del self._read_fds[fd] - self.selector.unregister(fd) + self.selector.unregister(fd) class call_on_sigwinch(object): 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 320df438ca..bb675ba9aa 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 @@ -2,7 +2,7 @@ from __future__ import unicode_literals from codecs import getincrementaldecoder import os -import six +import six __all__ = ( 'PosixStdinReader', @@ -13,42 +13,42 @@ 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. + + 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')): + # 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 + 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 + 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 @@ -56,27 +56,27 @@ class PosixStdinReader(object): # 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. + + 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'' - + 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 '' + + # 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) + 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 index f678f84c55..66e8e346b8 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/select.py @@ -1,216 +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() +""" +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/win32.py b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py index 18e356f088..0a3f63620c 100644 --- a/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py +++ b/contrib/python/prompt-toolkit/py2/prompt_toolkit/eventloop/win32.py @@ -15,7 +15,7 @@ from .utils import TimeIt from ctypes import windll, pointer from ctypes.wintypes import DWORD, BOOL, HANDLE -import msvcrt +import msvcrt import threading __all__ = ( @@ -29,23 +29,23 @@ 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. + + :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): + 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._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. - + # Additional readers. + self._read_fds = {} # Maps fd to handler. + # Create inputhook context. self._inputhook_context = InputHookContext(inputhook) if inputhook else None @@ -86,9 +86,9 @@ class Win32EventLoop(EventLoop): windll.kernel32.ResetEvent(self._event) self._process_queued_calls_from_executor() - elif handle in self._read_fds: - callback = self._read_fds[handle] - callback() + elif handle in self._read_fds: + callback = self._read_fds[handle] + callback() else: # Fire input timeout event. callbacks.input_timeout() @@ -98,9 +98,9 @@ class Win32EventLoop(EventLoop): """ 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) + 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 @@ -114,8 +114,8 @@ class Win32EventLoop(EventLoop): if self._inputhook_context: self._inputhook_context.close() - self._console_input_reader.close() - + self._console_input_reader.close() + def run_in_executor(self, callback): """ Run a long running function in a background thread. @@ -148,14 +148,14 @@ class Win32EventLoop(EventLoop): def add_reader(self, fd, callback): " Start watching the file descriptor for read availability. " - h = msvcrt.get_osfhandle(fd) - self._read_fds[h] = callback + 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] + h = msvcrt.get_osfhandle(fd) + if h in self._read_fds: + del self._read_fds[h] def _wait_for_handles(handles, timeout=-1): |