select.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. """
  2. Selectors for the Posix event loop.
  3. """
  4. from __future__ import unicode_literals, absolute_import
  5. import sys
  6. import abc
  7. import errno
  8. import select
  9. import six
  10. __all__ = (
  11. 'AutoSelector',
  12. 'PollSelector',
  13. 'SelectSelector',
  14. 'Selector',
  15. 'fd_to_int',
  16. )
  17. def fd_to_int(fd):
  18. assert isinstance(fd, int) or hasattr(fd, 'fileno')
  19. if isinstance(fd, int):
  20. return fd
  21. else:
  22. return fd.fileno()
  23. class Selector(six.with_metaclass(abc.ABCMeta, object)):
  24. @abc.abstractmethod
  25. def register(self, fd):
  26. assert isinstance(fd, int)
  27. @abc.abstractmethod
  28. def unregister(self, fd):
  29. assert isinstance(fd, int)
  30. @abc.abstractmethod
  31. def select(self, timeout):
  32. pass
  33. @abc.abstractmethod
  34. def close(self):
  35. pass
  36. class AutoSelector(Selector):
  37. def __init__(self):
  38. self._fds = []
  39. self._select_selector = SelectSelector()
  40. self._selectors = [self._select_selector]
  41. # When 'select.poll' exists, create a PollSelector.
  42. if hasattr(select, 'poll'):
  43. self._poll_selector = PollSelector()
  44. self._selectors.append(self._poll_selector)
  45. else:
  46. self._poll_selector = None
  47. # Use of the 'select' module, that was introduced in Python3.4. We don't
  48. # use it before 3.5 however, because this is the point where this module
  49. # retries interrupted system calls.
  50. if sys.version_info >= (3, 5):
  51. self._py3_selector = Python3Selector()
  52. self._selectors.append(self._py3_selector)
  53. else:
  54. self._py3_selector = None
  55. def register(self, fd):
  56. assert isinstance(fd, int)
  57. self._fds.append(fd)
  58. for sel in self._selectors:
  59. sel.register(fd)
  60. def unregister(self, fd):
  61. assert isinstance(fd, int)
  62. self._fds.remove(fd)
  63. for sel in self._selectors:
  64. sel.unregister(fd)
  65. def select(self, timeout):
  66. # Try Python 3 selector first.
  67. if self._py3_selector:
  68. try:
  69. return self._py3_selector.select(timeout)
  70. except PermissionError:
  71. # We had a situation (in pypager) where epoll raised a
  72. # PermissionError when a local file descriptor was registered,
  73. # however poll and select worked fine. So, in that case, just
  74. # try using select below.
  75. pass
  76. try:
  77. # Prefer 'select.select', if we don't have much file descriptors.
  78. # This is more universal.
  79. return self._select_selector.select(timeout)
  80. except ValueError:
  81. # When we have more than 1024 open file descriptors, we'll always
  82. # get a "ValueError: filedescriptor out of range in select()" for
  83. # 'select'. In this case, try, using 'poll' instead.
  84. if self._poll_selector is not None:
  85. return self._poll_selector.select(timeout)
  86. else:
  87. raise
  88. def close(self):
  89. for sel in self._selectors:
  90. sel.close()
  91. class Python3Selector(Selector):
  92. """
  93. Use of the Python3 'selectors' module.
  94. NOTE: Only use on Python 3.5 or newer!
  95. """
  96. def __init__(self):
  97. assert sys.version_info >= (3, 5)
  98. import selectors # Inline import: Python3 only!
  99. self._sel = selectors.DefaultSelector()
  100. def register(self, fd):
  101. assert isinstance(fd, int)
  102. import selectors # Inline import: Python3 only!
  103. self._sel.register(fd, selectors.EVENT_READ, None)
  104. def unregister(self, fd):
  105. assert isinstance(fd, int)
  106. self._sel.unregister(fd)
  107. def select(self, timeout):
  108. events = self._sel.select(timeout=timeout)
  109. return [key.fileobj for key, mask in events]
  110. def close(self):
  111. self._sel.close()
  112. class PollSelector(Selector):
  113. def __init__(self):
  114. self._poll = select.poll()
  115. def register(self, fd):
  116. assert isinstance(fd, int)
  117. self._poll.register(fd, select.POLLIN)
  118. def unregister(self, fd):
  119. assert isinstance(fd, int)
  120. def select(self, timeout):
  121. tuples = self._poll.poll(timeout) # Returns (fd, event) tuples.
  122. return [t[0] for t in tuples]
  123. def close(self):
  124. pass # XXX
  125. class SelectSelector(Selector):
  126. """
  127. Wrapper around select.select.
  128. When the SIGWINCH signal is handled, other system calls, like select
  129. are aborted in Python. This wrapper will retry the system call.
  130. """
  131. def __init__(self):
  132. self._fds = []
  133. def register(self, fd):
  134. self._fds.append(fd)
  135. def unregister(self, fd):
  136. self._fds.remove(fd)
  137. def select(self, timeout):
  138. while True:
  139. try:
  140. return select.select(self._fds, [], [], timeout)[0]
  141. except select.error as e:
  142. # Retry select call when EINTR
  143. if e.args and e.args[0] == errno.EINTR:
  144. continue
  145. else:
  146. raise
  147. def close(self):
  148. pass
  149. def select_fds(read_fds, timeout, selector=AutoSelector):
  150. """
  151. Wait for a list of file descriptors (`read_fds`) to become ready for
  152. reading. This chooses the most appropriate select-tool for use in
  153. prompt-toolkit.
  154. """
  155. # Map to ensure that we return the objects that were passed in originally.
  156. # Whether they are a fd integer or an object that has a fileno().
  157. # (The 'poll' implementation for instance, returns always integers.)
  158. fd_map = dict((fd_to_int(fd), fd) for fd in read_fds)
  159. # Wait, using selector.
  160. sel = selector()
  161. try:
  162. for fd in read_fds:
  163. sel.register(fd)
  164. result = sel.select(timeout)
  165. if result is not None:
  166. return [fd_map[fd_to_int(fd)] for fd in result]
  167. finally:
  168. sel.close()