Mercurial > hg
annotate mercurial/selectors2.py @ 34968:3649c3f2cd90
revert: do not reverse hunks in interactive when REV is not parent (issue5096)
And introduce a new "apply" operation verb for this case as suggested in
issue5096. This replaces the no longer used "revert" operation.
In interactive revert, when reverting to something else that the parent
revision, display an "apply this change" message with a diff that is not
reversed.
The rationale is that `hg revert -i -r REV` will show hunks of the diff from
the working directory to REV and prompt the user to select them for applying
(to working directory). This contradicts dcc56e10c23b in which it was
decided to have the "direction" of prompted hunks reversed. Later on
[1], there was a broad consensus (but no decision) towards the "as to
be applied direction". Now that --interactive is no longer experimental
(5910db5d1913), it's time to switch and thus we drop no longer used
"experimental.revertalternateinteractivemode" configuration option.
[1]: https://www.mercurial-scm.org/pipermail/mercurial-devel/2016-November/090142.html
.. feature::
When interactive revert is run against a revision other than the working
directory parent, the diff shown is the diff to *apply* to the working directory,
rather than the diff to *discard* from the working copy. This is in line with
related user experiences with `git` and appears to be less confusing with
`ui.interface=curses`.
author | Denis Laxalde <denis.laxalde@logilab.fr> |
---|---|
date | Fri, 03 Nov 2017 14:47:37 +0100 |
parents | a568a46751b6 |
children |
rev | line source |
---|---|
33502 | 1 """ Back-ported, durable, and portable selectors """ |
2 | |
3 # MIT License | |
4 # | |
5 # Copyright (c) 2017 Seth Michael Larson | |
6 # | |
7 # Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 # of this software and associated documentation files (the "Software"), to deal | |
9 # in the Software without restriction, including without limitation the rights | |
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 # copies of the Software, and to permit persons to whom the Software is | |
12 # furnished to do so, subject to the following conditions: | |
13 # | |
14 # The above copyright notice and this permission notice shall be included in all | |
15 # copies or substantial portions of the Software. | |
16 # | |
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
23 # SOFTWARE. | |
24 | |
25 # no-check-code | |
26 | |
27 from __future__ import absolute_import | |
28 | |
29 import collections | |
30 import errno | |
31 import math | |
32 import select | |
33 import socket | |
34 import sys | |
35 import time | |
36 | |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
37 from . import pycompat |
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
38 |
33502 | 39 namedtuple = collections.namedtuple |
40 Mapping = collections.Mapping | |
41 | |
42 try: | |
43 monotonic = time.monotonic | |
44 except AttributeError: | |
45 monotonic = time.time | |
46 | |
47 __author__ = 'Seth Michael Larson' | |
48 __email__ = 'sethmichaellarson@protonmail.com' | |
49 __version__ = '2.0.0' | |
50 __license__ = 'MIT' | |
51 __url__ = 'https://www.github.com/SethMichaelLarson/selectors2' | |
52 | |
53 __all__ = ['EVENT_READ', | |
54 'EVENT_WRITE', | |
55 'SelectorKey', | |
56 'DefaultSelector', | |
57 'BaseSelector'] | |
58 | |
59 EVENT_READ = (1 << 0) | |
60 EVENT_WRITE = (1 << 1) | |
61 _DEFAULT_SELECTOR = None | |
62 _SYSCALL_SENTINEL = object() # Sentinel in case a system call returns None. | |
63 _ERROR_TYPES = (OSError, IOError, socket.error) | |
64 | |
65 | |
66 SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data']) | |
67 | |
68 | |
69 class _SelectorMapping(Mapping): | |
70 """ Mapping of file objects to selector keys """ | |
71 | |
72 def __init__(self, selector): | |
73 self._selector = selector | |
74 | |
75 def __len__(self): | |
76 return len(self._selector._fd_to_key) | |
77 | |
78 def __getitem__(self, fileobj): | |
79 try: | |
80 fd = self._selector._fileobj_lookup(fileobj) | |
81 return self._selector._fd_to_key[fd] | |
82 except KeyError: | |
83 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
84 | |
85 def __iter__(self): | |
86 return iter(self._selector._fd_to_key) | |
87 | |
88 | |
89 def _fileobj_to_fd(fileobj): | |
90 """ Return a file descriptor from a file object. If | |
91 given an integer will simply return that integer back. """ | |
92 if isinstance(fileobj, int): | |
93 fd = fileobj | |
94 else: | |
95 try: | |
96 fd = int(fileobj.fileno()) | |
97 except (AttributeError, TypeError, ValueError): | |
98 raise ValueError("Invalid file object: {0!r}".format(fileobj)) | |
99 if fd < 0: | |
100 raise ValueError("Invalid file descriptor: {0}".format(fd)) | |
101 return fd | |
102 | |
103 | |
104 class BaseSelector(object): | |
105 """ Abstract Selector class | |
106 | |
107 A selector supports registering file objects to be monitored | |
108 for specific I/O events. | |
109 | |
110 A file object is a file descriptor or any object with a | |
111 `fileno()` method. An arbitrary object can be attached to the | |
112 file object which can be used for example to store context info, | |
113 a callback, etc. | |
114 | |
115 A selector can use various implementations (select(), poll(), epoll(), | |
116 and kqueue()) depending on the platform. The 'DefaultSelector' class uses | |
117 the most efficient implementation for the current platform. | |
118 """ | |
119 def __init__(self): | |
120 # Maps file descriptors to keys. | |
121 self._fd_to_key = {} | |
122 | |
123 # Read-only mapping returned by get_map() | |
124 self._map = _SelectorMapping(self) | |
125 | |
126 def _fileobj_lookup(self, fileobj): | |
127 """ Return a file descriptor from a file object. | |
128 This wraps _fileobj_to_fd() to do an exhaustive | |
129 search in case the object is invalid but we still | |
130 have it in our map. Used by unregister() so we can | |
131 unregister an object that was previously registered | |
132 even if it is closed. It is also used by _SelectorMapping | |
133 """ | |
134 try: | |
135 return _fileobj_to_fd(fileobj) | |
136 except ValueError: | |
137 | |
138 # Search through all our mapped keys. | |
139 for key in self._fd_to_key.values(): | |
140 if key.fileobj is fileobj: | |
141 return key.fd | |
142 | |
143 # Raise ValueError after all. | |
144 raise | |
145 | |
146 def register(self, fileobj, events, data=None): | |
147 """ Register a file object for a set of events to monitor. """ | |
148 if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)): | |
149 raise ValueError("Invalid events: {0!r}".format(events)) | |
150 | |
151 key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data) | |
152 | |
153 if key.fd in self._fd_to_key: | |
154 raise KeyError("{0!r} (FD {1}) is already registered" | |
155 .format(fileobj, key.fd)) | |
156 | |
157 self._fd_to_key[key.fd] = key | |
158 return key | |
159 | |
160 def unregister(self, fileobj): | |
161 """ Unregister a file object from being monitored. """ | |
162 try: | |
163 key = self._fd_to_key.pop(self._fileobj_lookup(fileobj)) | |
164 except KeyError: | |
165 raise KeyError("{0!r} is not registered".format(fileobj)) | |
166 | |
167 # Getting the fileno of a closed socket on Windows errors with EBADF. | |
168 except socket.error as err: | |
169 if err.errno != errno.EBADF: | |
170 raise | |
171 else: | |
172 for key in self._fd_to_key.values(): | |
173 if key.fileobj is fileobj: | |
174 self._fd_to_key.pop(key.fd) | |
175 break | |
176 else: | |
177 raise KeyError("{0!r} is not registered".format(fileobj)) | |
178 return key | |
179 | |
180 def modify(self, fileobj, events, data=None): | |
181 """ Change a registered file object monitored events and data. """ | |
182 # NOTE: Some subclasses optimize this operation even further. | |
183 try: | |
184 key = self._fd_to_key[self._fileobj_lookup(fileobj)] | |
185 except KeyError: | |
186 raise KeyError("{0!r} is not registered".format(fileobj)) | |
187 | |
188 if events != key.events: | |
189 self.unregister(fileobj) | |
190 key = self.register(fileobj, events, data) | |
191 | |
192 elif data != key.data: | |
193 # Use a shortcut to update the data. | |
194 key = key._replace(data=data) | |
195 self._fd_to_key[key.fd] = key | |
196 | |
197 return key | |
198 | |
199 def select(self, timeout=None): | |
200 """ Perform the actual selection until some monitored file objects | |
201 are ready or the timeout expires. """ | |
202 raise NotImplementedError() | |
203 | |
204 def close(self): | |
205 """ Close the selector. This must be called to ensure that all | |
206 underlying resources are freed. """ | |
207 self._fd_to_key.clear() | |
208 self._map = None | |
209 | |
210 def get_key(self, fileobj): | |
211 """ Return the key associated with a registered file object. """ | |
212 mapping = self.get_map() | |
213 if mapping is None: | |
214 raise RuntimeError("Selector is closed") | |
215 try: | |
216 return mapping[fileobj] | |
217 except KeyError: | |
218 raise KeyError("{0!r} is not registered".format(fileobj)) | |
219 | |
220 def get_map(self): | |
221 """ Return a mapping of file objects to selector keys """ | |
222 return self._map | |
223 | |
224 def _key_from_fd(self, fd): | |
225 """ Return the key associated to a given file descriptor | |
226 Return None if it is not found. """ | |
227 try: | |
228 return self._fd_to_key[fd] | |
229 except KeyError: | |
230 return None | |
231 | |
232 def __enter__(self): | |
233 return self | |
234 | |
235 def __exit__(self, *_): | |
236 self.close() | |
237 | |
238 | |
239 # Almost all platforms have select.select() | |
240 if hasattr(select, "select"): | |
241 class SelectSelector(BaseSelector): | |
242 """ Select-based selector. """ | |
243 def __init__(self): | |
244 super(SelectSelector, self).__init__() | |
245 self._readers = set() | |
246 self._writers = set() | |
247 | |
248 def register(self, fileobj, events, data=None): | |
249 key = super(SelectSelector, self).register(fileobj, events, data) | |
250 if events & EVENT_READ: | |
251 self._readers.add(key.fd) | |
252 if events & EVENT_WRITE: | |
253 self._writers.add(key.fd) | |
254 return key | |
255 | |
256 def unregister(self, fileobj): | |
257 key = super(SelectSelector, self).unregister(fileobj) | |
258 self._readers.discard(key.fd) | |
259 self._writers.discard(key.fd) | |
260 return key | |
261 | |
262 def select(self, timeout=None): | |
263 # Selecting on empty lists on Windows errors out. | |
264 if not len(self._readers) and not len(self._writers): | |
265 return [] | |
266 | |
267 timeout = None if timeout is None else max(timeout, 0.0) | |
268 ready = [] | |
269 r, w, _ = _syscall_wrapper(self._wrap_select, True, self._readers, | |
270 self._writers, timeout) | |
271 r = set(r) | |
272 w = set(w) | |
273 for fd in r | w: | |
274 events = 0 | |
275 if fd in r: | |
276 events |= EVENT_READ | |
277 if fd in w: | |
278 events |= EVENT_WRITE | |
279 | |
280 key = self._key_from_fd(fd) | |
281 if key: | |
282 ready.append((key, events & key.events)) | |
283 return ready | |
284 | |
285 def _wrap_select(self, r, w, timeout=None): | |
286 """ Wrapper for select.select because timeout is a positional arg """ | |
287 return select.select(r, w, [], timeout) | |
288 | |
289 __all__.append('SelectSelector') | |
290 | |
291 # Jython has a different implementation of .fileno() for socket objects. | |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
292 if pycompat.isjython: |
33502 | 293 class _JythonSelectorMapping(object): |
294 """ This is an implementation of _SelectorMapping that is built | |
295 for use specifically with Jython, which does not provide a hashable | |
296 value from socket.socket.fileno(). """ | |
297 | |
298 def __init__(self, selector): | |
299 assert isinstance(selector, JythonSelectSelector) | |
300 self._selector = selector | |
301 | |
302 def __len__(self): | |
303 return len(self._selector._sockets) | |
304 | |
305 def __getitem__(self, fileobj): | |
306 for sock, key in self._selector._sockets: | |
307 if sock is fileobj: | |
308 return key | |
309 else: | |
310 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
311 | |
312 class JythonSelectSelector(SelectSelector): | |
313 """ This is an implementation of SelectSelector that is for Jython | |
314 which works around that Jython's socket.socket.fileno() does not | |
315 return an integer fd value. All SelectorKey.fd will be equal to -1 | |
316 and should not be used. This instead uses object id to compare fileobj | |
317 and will only use select.select as it's the only selector that allows | |
318 directly passing in socket objects rather than registering fds. | |
319 See: http://bugs.jython.org/issue1678 | |
320 https://wiki.python.org/jython/NewSocketModule#socket.fileno.28.29_does_not_return_an_integer | |
321 """ | |
322 | |
323 def __init__(self): | |
324 super(JythonSelectSelector, self).__init__() | |
325 | |
326 self._sockets = [] # Uses a list of tuples instead of dictionary. | |
327 self._map = _JythonSelectorMapping(self) | |
328 self._readers = [] | |
329 self._writers = [] | |
330 | |
331 # Jython has a select.cpython_compatible_select function in older versions. | |
332 self._select_func = getattr(select, 'cpython_compatible_select', select.select) | |
333 | |
334 def register(self, fileobj, events, data=None): | |
335 for sock, _ in self._sockets: | |
336 if sock is fileobj: | |
337 raise KeyError("{0!r} is already registered" | |
338 .format(fileobj, sock)) | |
339 | |
340 key = SelectorKey(fileobj, -1, events, data) | |
341 self._sockets.append((fileobj, key)) | |
342 | |
343 if events & EVENT_READ: | |
344 self._readers.append(fileobj) | |
345 if events & EVENT_WRITE: | |
346 self._writers.append(fileobj) | |
347 return key | |
348 | |
349 def unregister(self, fileobj): | |
350 for i, (sock, key) in enumerate(self._sockets): | |
351 if sock is fileobj: | |
352 break | |
353 else: | |
354 raise KeyError("{0!r} is not registered.".format(fileobj)) | |
355 | |
356 if key.events & EVENT_READ: | |
357 self._readers.remove(fileobj) | |
358 if key.events & EVENT_WRITE: | |
359 self._writers.remove(fileobj) | |
360 | |
361 del self._sockets[i] | |
362 return key | |
363 | |
364 def _wrap_select(self, r, w, timeout=None): | |
365 """ Wrapper for select.select because timeout is a positional arg """ | |
366 return self._select_func(r, w, [], timeout) | |
367 | |
368 __all__.append('JythonSelectSelector') | |
369 SelectSelector = JythonSelectSelector # Override so the wrong selector isn't used. | |
370 | |
371 | |
372 if hasattr(select, "poll"): | |
373 class PollSelector(BaseSelector): | |
374 """ Poll-based selector """ | |
375 def __init__(self): | |
376 super(PollSelector, self).__init__() | |
377 self._poll = select.poll() | |
378 | |
379 def register(self, fileobj, events, data=None): | |
380 key = super(PollSelector, self).register(fileobj, events, data) | |
381 event_mask = 0 | |
382 if events & EVENT_READ: | |
383 event_mask |= select.POLLIN | |
384 if events & EVENT_WRITE: | |
385 event_mask |= select.POLLOUT | |
386 self._poll.register(key.fd, event_mask) | |
387 return key | |
388 | |
389 def unregister(self, fileobj): | |
390 key = super(PollSelector, self).unregister(fileobj) | |
391 self._poll.unregister(key.fd) | |
392 return key | |
393 | |
394 def _wrap_poll(self, timeout=None): | |
395 """ Wrapper function for select.poll.poll() so that | |
396 _syscall_wrapper can work with only seconds. """ | |
397 if timeout is not None: | |
398 if timeout <= 0: | |
399 timeout = 0 | |
400 else: | |
401 # select.poll.poll() has a resolution of 1 millisecond, | |
402 # round away from zero to wait *at least* timeout seconds. | |
403 timeout = math.ceil(timeout * 1000) | |
404 | |
405 result = self._poll.poll(timeout) | |
406 return result | |
407 | |
408 def select(self, timeout=None): | |
409 ready = [] | |
410 fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout) | |
411 for fd, event_mask in fd_events: | |
412 events = 0 | |
413 if event_mask & ~select.POLLIN: | |
414 events |= EVENT_WRITE | |
415 if event_mask & ~select.POLLOUT: | |
416 events |= EVENT_READ | |
417 | |
418 key = self._key_from_fd(fd) | |
419 if key: | |
420 ready.append((key, events & key.events)) | |
421 | |
422 return ready | |
423 | |
424 __all__.append('PollSelector') | |
425 | |
426 if hasattr(select, "epoll"): | |
427 class EpollSelector(BaseSelector): | |
428 """ Epoll-based selector """ | |
429 def __init__(self): | |
430 super(EpollSelector, self).__init__() | |
431 self._epoll = select.epoll() | |
432 | |
433 def fileno(self): | |
434 return self._epoll.fileno() | |
435 | |
436 def register(self, fileobj, events, data=None): | |
437 key = super(EpollSelector, self).register(fileobj, events, data) | |
438 events_mask = 0 | |
439 if events & EVENT_READ: | |
440 events_mask |= select.EPOLLIN | |
441 if events & EVENT_WRITE: | |
442 events_mask |= select.EPOLLOUT | |
443 _syscall_wrapper(self._epoll.register, False, key.fd, events_mask) | |
444 return key | |
445 | |
446 def unregister(self, fileobj): | |
447 key = super(EpollSelector, self).unregister(fileobj) | |
448 try: | |
449 _syscall_wrapper(self._epoll.unregister, False, key.fd) | |
450 except _ERROR_TYPES: | |
451 # This can occur when the fd was closed since registry. | |
452 pass | |
453 return key | |
454 | |
455 def select(self, timeout=None): | |
456 if timeout is not None: | |
457 if timeout <= 0: | |
458 timeout = 0.0 | |
459 else: | |
460 # select.epoll.poll() has a resolution of 1 millisecond | |
461 # but luckily takes seconds so we don't need a wrapper | |
462 # like PollSelector. Just for better rounding. | |
463 timeout = math.ceil(timeout * 1000) * 0.001 | |
464 timeout = float(timeout) | |
465 else: | |
466 timeout = -1.0 # epoll.poll() must have a float. | |
467 | |
468 # We always want at least 1 to ensure that select can be called | |
469 # with no file descriptors registered. Otherwise will fail. | |
470 max_events = max(len(self._fd_to_key), 1) | |
471 | |
472 ready = [] | |
473 fd_events = _syscall_wrapper(self._epoll.poll, True, | |
474 timeout=timeout, | |
475 maxevents=max_events) | |
476 for fd, event_mask in fd_events: | |
477 events = 0 | |
478 if event_mask & ~select.EPOLLIN: | |
479 events |= EVENT_WRITE | |
480 if event_mask & ~select.EPOLLOUT: | |
481 events |= EVENT_READ | |
482 | |
483 key = self._key_from_fd(fd) | |
484 if key: | |
485 ready.append((key, events & key.events)) | |
486 return ready | |
487 | |
488 def close(self): | |
489 self._epoll.close() | |
490 super(EpollSelector, self).close() | |
491 | |
492 __all__.append('EpollSelector') | |
493 | |
494 | |
495 if hasattr(select, "devpoll"): | |
496 class DevpollSelector(BaseSelector): | |
497 """Solaris /dev/poll selector.""" | |
498 | |
499 def __init__(self): | |
500 super(DevpollSelector, self).__init__() | |
501 self._devpoll = select.devpoll() | |
502 | |
503 def fileno(self): | |
504 return self._devpoll.fileno() | |
505 | |
506 def register(self, fileobj, events, data=None): | |
507 key = super(DevpollSelector, self).register(fileobj, events, data) | |
508 poll_events = 0 | |
509 if events & EVENT_READ: | |
510 poll_events |= select.POLLIN | |
511 if events & EVENT_WRITE: | |
512 poll_events |= select.POLLOUT | |
513 self._devpoll.register(key.fd, poll_events) | |
514 return key | |
515 | |
516 def unregister(self, fileobj): | |
517 key = super(DevpollSelector, self).unregister(fileobj) | |
518 self._devpoll.unregister(key.fd) | |
519 return key | |
520 | |
521 def _wrap_poll(self, timeout=None): | |
522 """ Wrapper function for select.poll.poll() so that | |
523 _syscall_wrapper can work with only seconds. """ | |
524 if timeout is not None: | |
525 if timeout <= 0: | |
526 timeout = 0 | |
527 else: | |
528 # select.devpoll.poll() has a resolution of 1 millisecond, | |
529 # round away from zero to wait *at least* timeout seconds. | |
530 timeout = math.ceil(timeout * 1000) | |
531 | |
532 result = self._devpoll.poll(timeout) | |
533 return result | |
534 | |
535 def select(self, timeout=None): | |
536 ready = [] | |
537 fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout) | |
538 for fd, event_mask in fd_events: | |
539 events = 0 | |
540 if event_mask & ~select.POLLIN: | |
541 events |= EVENT_WRITE | |
542 if event_mask & ~select.POLLOUT: | |
543 events |= EVENT_READ | |
544 | |
545 key = self._key_from_fd(fd) | |
546 if key: | |
547 ready.append((key, events & key.events)) | |
548 | |
549 return ready | |
550 | |
551 def close(self): | |
552 self._devpoll.close() | |
553 super(DevpollSelector, self).close() | |
554 | |
555 __all__.append('DevpollSelector') | |
556 | |
557 | |
558 if hasattr(select, "kqueue"): | |
559 class KqueueSelector(BaseSelector): | |
560 """ Kqueue / Kevent-based selector """ | |
561 def __init__(self): | |
562 super(KqueueSelector, self).__init__() | |
563 self._kqueue = select.kqueue() | |
564 | |
565 def fileno(self): | |
566 return self._kqueue.fileno() | |
567 | |
568 def register(self, fileobj, events, data=None): | |
569 key = super(KqueueSelector, self).register(fileobj, events, data) | |
570 if events & EVENT_READ: | |
571 kevent = select.kevent(key.fd, | |
572 select.KQ_FILTER_READ, | |
573 select.KQ_EV_ADD) | |
574 | |
575 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
576 | |
577 if events & EVENT_WRITE: | |
578 kevent = select.kevent(key.fd, | |
579 select.KQ_FILTER_WRITE, | |
580 select.KQ_EV_ADD) | |
581 | |
582 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
583 | |
584 return key | |
585 | |
586 def unregister(self, fileobj): | |
587 key = super(KqueueSelector, self).unregister(fileobj) | |
588 if key.events & EVENT_READ: | |
589 kevent = select.kevent(key.fd, | |
590 select.KQ_FILTER_READ, | |
591 select.KQ_EV_DELETE) | |
592 try: | |
593 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
594 except _ERROR_TYPES: | |
595 pass | |
596 if key.events & EVENT_WRITE: | |
597 kevent = select.kevent(key.fd, | |
598 select.KQ_FILTER_WRITE, | |
599 select.KQ_EV_DELETE) | |
600 try: | |
601 _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) | |
602 except _ERROR_TYPES: | |
603 pass | |
604 | |
605 return key | |
606 | |
607 def select(self, timeout=None): | |
608 if timeout is not None: | |
609 timeout = max(timeout, 0) | |
610 | |
611 max_events = len(self._fd_to_key) * 2 | |
612 ready_fds = {} | |
613 | |
614 kevent_list = _syscall_wrapper(self._kqueue.control, True, | |
615 None, max_events, timeout) | |
616 | |
617 for kevent in kevent_list: | |
618 fd = kevent.ident | |
619 event_mask = kevent.filter | |
620 events = 0 | |
621 if event_mask == select.KQ_FILTER_READ: | |
622 events |= EVENT_READ | |
623 if event_mask == select.KQ_FILTER_WRITE: | |
624 events |= EVENT_WRITE | |
625 | |
626 key = self._key_from_fd(fd) | |
627 if key: | |
628 if key.fd not in ready_fds: | |
629 ready_fds[key.fd] = (key, events & key.events) | |
630 else: | |
631 old_events = ready_fds[key.fd][1] | |
632 ready_fds[key.fd] = (key, (events | old_events) & key.events) | |
633 | |
634 return list(ready_fds.values()) | |
635 | |
636 def close(self): | |
637 self._kqueue.close() | |
638 super(KqueueSelector, self).close() | |
639 | |
640 __all__.append('KqueueSelector') | |
641 | |
642 | |
643 def _can_allocate(struct): | |
644 """ Checks that select structs can be allocated by the underlying | |
645 operating system, not just advertised by the select module. We don't | |
646 check select() because we'll be hopeful that most platforms that | |
647 don't have it available will not advertise it. (ie: GAE) """ | |
648 try: | |
649 # select.poll() objects won't fail until used. | |
650 if struct == 'poll': | |
651 p = select.poll() | |
652 p.poll(0) | |
653 | |
654 # All others will fail on allocation. | |
655 else: | |
656 getattr(select, struct)().close() | |
657 return True | |
658 except (OSError, AttributeError): | |
659 return False | |
660 | |
661 | |
662 # Python 3.5 uses a more direct route to wrap system calls to increase speed. | |
663 if sys.version_info >= (3, 5): | |
664 def _syscall_wrapper(func, _, *args, **kwargs): | |
665 """ This is the short-circuit version of the below logic | |
666 because in Python 3.5+ all selectors restart system calls. """ | |
667 return func(*args, **kwargs) | |
668 else: | |
669 def _syscall_wrapper(func, recalc_timeout, *args, **kwargs): | |
670 """ Wrapper function for syscalls that could fail due to EINTR. | |
671 All functions should be retried if there is time left in the timeout | |
672 in accordance with PEP 475. """ | |
673 timeout = kwargs.get("timeout", None) | |
674 if timeout is None: | |
675 expires = None | |
676 recalc_timeout = False | |
677 else: | |
678 timeout = float(timeout) | |
679 if timeout < 0.0: # Timeout less than 0 treated as no timeout. | |
680 expires = None | |
681 else: | |
682 expires = monotonic() + timeout | |
683 | |
684 args = list(args) | |
685 if recalc_timeout and "timeout" not in kwargs: | |
686 raise ValueError( | |
687 "Timeout must be in args or kwargs to be recalculated") | |
688 | |
689 result = _SYSCALL_SENTINEL | |
690 while result is _SYSCALL_SENTINEL: | |
691 try: | |
692 result = func(*args, **kwargs) | |
693 # OSError is thrown by select.select | |
694 # IOError is thrown by select.epoll.poll | |
695 # select.error is thrown by select.poll.poll | |
696 # Aren't we thankful for Python 3.x rework for exceptions? | |
697 except (OSError, IOError, select.error) as e: | |
698 # select.error wasn't a subclass of OSError in the past. | |
699 errcode = None | |
700 if hasattr(e, "errno"): | |
701 errcode = e.errno | |
702 elif hasattr(e, "args"): | |
703 errcode = e.args[0] | |
704 | |
705 # Also test for the Windows equivalent of EINTR. | |
706 is_interrupt = (errcode == errno.EINTR or (hasattr(errno, "WSAEINTR") and | |
707 errcode == errno.WSAEINTR)) | |
708 | |
709 if is_interrupt: | |
710 if expires is not None: | |
711 current_time = monotonic() | |
712 if current_time > expires: | |
713 raise OSError(errno=errno.ETIMEDOUT) | |
714 if recalc_timeout: | |
715 if "timeout" in kwargs: | |
716 kwargs["timeout"] = expires - current_time | |
717 continue | |
718 raise | |
719 return result | |
720 | |
721 | |
722 # Choose the best implementation, roughly: | |
723 # kqueue == devpoll == epoll > poll > select | |
724 # select() also can't accept a FD > FD_SETSIZE (usually around 1024) | |
725 def DefaultSelector(): | |
726 """ This function serves as a first call for DefaultSelector to | |
727 detect if the select module is being monkey-patched incorrectly | |
728 by eventlet, greenlet, and preserve proper behavior. """ | |
729 global _DEFAULT_SELECTOR | |
730 if _DEFAULT_SELECTOR is None: | |
34639
a568a46751b6
selectors2: do not use platform.system()
Jun Wu <quark@fb.com>
parents:
33502
diff
changeset
|
731 if pycompat.isjython: |
33502 | 732 _DEFAULT_SELECTOR = JythonSelectSelector |
733 elif _can_allocate('kqueue'): | |
734 _DEFAULT_SELECTOR = KqueueSelector | |
735 elif _can_allocate('devpoll'): | |
736 _DEFAULT_SELECTOR = DevpollSelector | |
737 elif _can_allocate('epoll'): | |
738 _DEFAULT_SELECTOR = EpollSelector | |
739 elif _can_allocate('poll'): | |
740 _DEFAULT_SELECTOR = PollSelector | |
741 elif hasattr(select, 'select'): | |
742 _DEFAULT_SELECTOR = SelectSelector | |
743 else: # Platform-specific: AppEngine | |
744 raise RuntimeError('Platform does not have a selector.') | |
745 return _DEFAULT_SELECTOR() |