efficient vim config
[dotfiles/.git] / .local / lib / python2.7 / site-packages / trollius / selectors.py
1 """Selectors module.
2
3 This module allows high-level and efficient I/O multiplexing, built upon the
4 `select` module primitives.
5 """
6
7
8 from abc import ABCMeta, abstractmethod
9 from collections import namedtuple, Mapping
10 import math
11 import select
12 import sys
13
14 from .py33_exceptions import wrap_error, InterruptedError
15 from .compat import integer_types
16
17
18 # generic events, that must be mapped to implementation-specific ones
19 EVENT_READ = (1 << 0)
20 EVENT_WRITE = (1 << 1)
21
22
23 def _fileobj_to_fd(fileobj):
24     """Return a file descriptor from a file object.
25
26     Parameters:
27     fileobj -- file object or file descriptor
28
29     Returns:
30     corresponding file descriptor
31
32     Raises:
33     ValueError if the object is invalid
34     """
35     if isinstance(fileobj, integer_types):
36         fd = fileobj
37     else:
38         try:
39             fd = int(fileobj.fileno())
40         except (AttributeError, TypeError, ValueError):
41             raise ValueError("Invalid file object: "
42                              "{0!r}".format(fileobj))
43     if fd < 0:
44         raise ValueError("Invalid file descriptor: {0}".format(fd))
45     return fd
46
47
48 SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
49 """Object used to associate a file object to its backing file descriptor,
50 selected event mask and attached data."""
51
52
53 class _SelectorMapping(Mapping):
54     """Mapping of file objects to selector keys."""
55
56     def __init__(self, selector):
57         self._selector = selector
58
59     def __len__(self):
60         return len(self._selector._fd_to_key)
61
62     def __getitem__(self, fileobj):
63         try:
64             fd = self._selector._fileobj_lookup(fileobj)
65             return self._selector._fd_to_key[fd]
66         except KeyError:
67             raise KeyError("{0!r} is not registered".format(fileobj))
68
69     def __iter__(self):
70         return iter(self._selector._fd_to_key)
71
72
73 class BaseSelector(object):
74     """Selector abstract base class.
75
76     A selector supports registering file objects to be monitored for specific
77     I/O events.
78
79     A file object is a file descriptor or any object with a `fileno()` method.
80     An arbitrary object can be attached to the file object, which can be used
81     for example to store context information, a callback, etc.
82
83     A selector can use various implementations (select(), poll(), epoll()...)
84     depending on the platform. The default `Selector` class uses the most
85     efficient implementation on the current platform.
86     """
87     __metaclass__ = ABCMeta
88
89     @abstractmethod
90     def register(self, fileobj, events, data=None):
91         """Register a file object.
92
93         Parameters:
94         fileobj -- file object or file descriptor
95         events  -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
96         data    -- attached data
97
98         Returns:
99         SelectorKey instance
100
101         Raises:
102         ValueError if events is invalid
103         KeyError if fileobj is already registered
104         OSError if fileobj is closed or otherwise is unacceptable to
105                 the underlying system call (if a system call is made)
106
107         Note:
108         OSError may or may not be raised
109         """
110         raise NotImplementedError
111
112     @abstractmethod
113     def unregister(self, fileobj):
114         """Unregister a file object.
115
116         Parameters:
117         fileobj -- file object or file descriptor
118
119         Returns:
120         SelectorKey instance
121
122         Raises:
123         KeyError if fileobj is not registered
124
125         Note:
126         If fileobj is registered but has since been closed this does
127         *not* raise OSError (even if the wrapped syscall does)
128         """
129         raise NotImplementedError
130
131     def modify(self, fileobj, events, data=None):
132         """Change a registered file object monitored events or attached data.
133
134         Parameters:
135         fileobj -- file object or file descriptor
136         events  -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
137         data    -- attached data
138
139         Returns:
140         SelectorKey instance
141
142         Raises:
143         Anything that unregister() or register() raises
144         """
145         self.unregister(fileobj)
146         return self.register(fileobj, events, data)
147
148     @abstractmethod
149     def select(self, timeout=None):
150         """Perform the actual selection, until some monitored file objects are
151         ready or a timeout expires.
152
153         Parameters:
154         timeout -- if timeout > 0, this specifies the maximum wait time, in
155                    seconds
156                    if timeout <= 0, the select() call won't block, and will
157                    report the currently ready file objects
158                    if timeout is None, select() will block until a monitored
159                    file object becomes ready
160
161         Returns:
162         list of (key, events) for ready file objects
163         `events` is a bitwise mask of EVENT_READ|EVENT_WRITE
164         """
165         raise NotImplementedError
166
167     def close(self):
168         """Close the selector.
169
170         This must be called to make sure that any underlying resource is freed.
171         """
172         pass
173
174     def get_key(self, fileobj):
175         """Return the key associated to a registered file object.
176
177         Returns:
178         SelectorKey for this file object
179         """
180         mapping = self.get_map()
181         if mapping is None:
182             raise RuntimeError('Selector is closed')
183         try:
184             return mapping[fileobj]
185         except KeyError:
186             raise KeyError("{0!r} is not registered".format(fileobj))
187
188     @abstractmethod
189     def get_map(self):
190         """Return a mapping of file objects to selector keys."""
191         raise NotImplementedError
192
193     def __enter__(self):
194         return self
195
196     def __exit__(self, *args):
197         self.close()
198
199
200 class _BaseSelectorImpl(BaseSelector):
201     """Base selector implementation."""
202
203     def __init__(self):
204         # this maps file descriptors to keys
205         self._fd_to_key = {}
206         # read-only mapping returned by get_map()
207         self._map = _SelectorMapping(self)
208
209     def _fileobj_lookup(self, fileobj):
210         """Return a file descriptor from a file object.
211
212         This wraps _fileobj_to_fd() to do an exhaustive search in case
213         the object is invalid but we still have it in our map.  This
214         is used by unregister() so we can unregister an object that
215         was previously registered even if it is closed.  It is also
216         used by _SelectorMapping.
217         """
218         try:
219             return _fileobj_to_fd(fileobj)
220         except ValueError:
221             # Do an exhaustive search.
222             for key in self._fd_to_key.values():
223                 if key.fileobj is fileobj:
224                     return key.fd
225             # Raise ValueError after all.
226             raise
227
228     def register(self, fileobj, events, data=None):
229         if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
230             raise ValueError("Invalid events: {0!r}".format(events))
231
232         key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
233
234         if key.fd in self._fd_to_key:
235             raise KeyError("{0!r} (FD {1}) is already registered"
236                            .format(fileobj, key.fd))
237
238         self._fd_to_key[key.fd] = key
239         return key
240
241     def unregister(self, fileobj):
242         try:
243             key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
244         except KeyError:
245             raise KeyError("{0!r} is not registered".format(fileobj))
246         return key
247
248     def modify(self, fileobj, events, data=None):
249         # TODO: Subclasses can probably optimize this even further.
250         try:
251             key = self._fd_to_key[self._fileobj_lookup(fileobj)]
252         except KeyError:
253             raise KeyError("{0!r} is not registered".format(fileobj))
254         if events != key.events:
255             self.unregister(fileobj)
256             key = self.register(fileobj, events, data)
257         elif data != key.data:
258             # Use a shortcut to update the data.
259             key = key._replace(data=data)
260             self._fd_to_key[key.fd] = key
261         return key
262
263     def close(self):
264         self._fd_to_key.clear()
265         self._map = None
266
267     def get_map(self):
268         return self._map
269
270     def _key_from_fd(self, fd):
271         """Return the key associated to a given file descriptor.
272
273         Parameters:
274         fd -- file descriptor
275
276         Returns:
277         corresponding key, or None if not found
278         """
279         try:
280             return self._fd_to_key[fd]
281         except KeyError:
282             return None
283
284
285 class SelectSelector(_BaseSelectorImpl):
286     """Select-based selector."""
287
288     def __init__(self):
289         super(SelectSelector, self).__init__()
290         self._readers = set()
291         self._writers = set()
292
293     def register(self, fileobj, events, data=None):
294         key = super(SelectSelector, self).register(fileobj, events, data)
295         if events & EVENT_READ:
296             self._readers.add(key.fd)
297         if events & EVENT_WRITE:
298             self._writers.add(key.fd)
299         return key
300
301     def unregister(self, fileobj):
302         key = super(SelectSelector, self).unregister(fileobj)
303         self._readers.discard(key.fd)
304         self._writers.discard(key.fd)
305         return key
306
307     if sys.platform == 'win32':
308         def _select(self, r, w, _, timeout=None):
309             r, w, x = select.select(r, w, w, timeout)
310             return r, w + x, []
311     else:
312         def _select(self, r, w, x, timeout=None):
313             return select.select(r, w, x, timeout)
314
315     def select(self, timeout=None):
316         timeout = None if timeout is None else max(timeout, 0)
317         ready = []
318         try:
319             r, w, _ = wrap_error(self._select,
320                                  self._readers, self._writers, [], timeout)
321         except InterruptedError:
322             return ready
323         r = set(r)
324         w = set(w)
325         for fd in r | w:
326             events = 0
327             if fd in r:
328                 events |= EVENT_READ
329             if fd in w:
330                 events |= EVENT_WRITE
331
332             key = self._key_from_fd(fd)
333             if key:
334                 ready.append((key, events & key.events))
335         return ready
336
337
338 if hasattr(select, 'poll'):
339
340     class PollSelector(_BaseSelectorImpl):
341         """Poll-based selector."""
342
343         def __init__(self):
344             super(PollSelector, self).__init__()
345             self._poll = select.poll()
346
347         def register(self, fileobj, events, data=None):
348             key = super(PollSelector, self).register(fileobj, events, data)
349             poll_events = 0
350             if events & EVENT_READ:
351                 poll_events |= select.POLLIN
352             if events & EVENT_WRITE:
353                 poll_events |= select.POLLOUT
354             self._poll.register(key.fd, poll_events)
355             return key
356
357         def unregister(self, fileobj):
358             key = super(PollSelector, self).unregister(fileobj)
359             self._poll.unregister(key.fd)
360             return key
361
362         def select(self, timeout=None):
363             if timeout is None:
364                 timeout = None
365             elif timeout <= 0:
366                 timeout = 0
367             else:
368                 # poll() has a resolution of 1 millisecond, round away from
369                 # zero to wait *at least* timeout seconds.
370                 timeout = int(math.ceil(timeout * 1e3))
371             ready = []
372             try:
373                 fd_event_list = wrap_error(self._poll.poll, timeout)
374             except InterruptedError:
375                 return ready
376             for fd, event in fd_event_list:
377                 events = 0
378                 if event & ~select.POLLIN:
379                     events |= EVENT_WRITE
380                 if event & ~select.POLLOUT:
381                     events |= EVENT_READ
382
383                 key = self._key_from_fd(fd)
384                 if key:
385                     ready.append((key, events & key.events))
386             return ready
387
388
389 if hasattr(select, 'epoll'):
390
391     class EpollSelector(_BaseSelectorImpl):
392         """Epoll-based selector."""
393
394         def __init__(self):
395             super(EpollSelector, self).__init__()
396             self._epoll = select.epoll()
397
398         def fileno(self):
399             return self._epoll.fileno()
400
401         def register(self, fileobj, events, data=None):
402             key = super(EpollSelector, self).register(fileobj, events, data)
403             epoll_events = 0
404             if events & EVENT_READ:
405                 epoll_events |= select.EPOLLIN
406             if events & EVENT_WRITE:
407                 epoll_events |= select.EPOLLOUT
408             self._epoll.register(key.fd, epoll_events)
409             return key
410
411         def unregister(self, fileobj):
412             key = super(EpollSelector, self).unregister(fileobj)
413             try:
414                 self._epoll.unregister(key.fd)
415             except IOError:
416                 # This can happen if the FD was closed since it
417                 # was registered.
418                 pass
419             return key
420
421         def select(self, timeout=None):
422             if timeout is None:
423                 timeout = -1
424             elif timeout <= 0:
425                 timeout = 0
426             else:
427                 # epoll_wait() has a resolution of 1 millisecond, round away
428                 # from zero to wait *at least* timeout seconds.
429                 timeout = math.ceil(timeout * 1e3) * 1e-3
430
431             # epoll_wait() expects `maxevents` to be greater than zero;
432             # we want to make sure that `select()` can be called when no
433             # FD is registered.
434             max_ev = max(len(self._fd_to_key), 1)
435
436             ready = []
437             try:
438                 fd_event_list = wrap_error(self._epoll.poll, timeout, max_ev)
439             except InterruptedError:
440                 return ready
441             for fd, event in fd_event_list:
442                 events = 0
443                 if event & ~select.EPOLLIN:
444                     events |= EVENT_WRITE
445                 if event & ~select.EPOLLOUT:
446                     events |= EVENT_READ
447
448                 key = self._key_from_fd(fd)
449                 if key:
450                     ready.append((key, events & key.events))
451             return ready
452
453         def close(self):
454             self._epoll.close()
455             super(EpollSelector, self).close()
456
457
458 if hasattr(select, 'devpoll'):
459
460     class DevpollSelector(_BaseSelectorImpl):
461         """Solaris /dev/poll selector."""
462
463         def __init__(self):
464             super(DevpollSelector, self).__init__()
465             self._devpoll = select.devpoll()
466
467         def fileno(self):
468             return self._devpoll.fileno()
469
470         def register(self, fileobj, events, data=None):
471             key = super(DevpollSelector, self).register(fileobj, events, data)
472             poll_events = 0
473             if events & EVENT_READ:
474                 poll_events |= select.POLLIN
475             if events & EVENT_WRITE:
476                 poll_events |= select.POLLOUT
477             self._devpoll.register(key.fd, poll_events)
478             return key
479
480         def unregister(self, fileobj):
481             key = super(DevpollSelector, self).unregister(fileobj)
482             self._devpoll.unregister(key.fd)
483             return key
484
485         def select(self, timeout=None):
486             if timeout is None:
487                 timeout = None
488             elif timeout <= 0:
489                 timeout = 0
490             else:
491                 # devpoll() has a resolution of 1 millisecond, round away from
492                 # zero to wait *at least* timeout seconds.
493                 timeout = math.ceil(timeout * 1e3)
494             ready = []
495             try:
496                 fd_event_list = self._devpoll.poll(timeout)
497             except InterruptedError:
498                 return ready
499             for fd, event in fd_event_list:
500                 events = 0
501                 if event & ~select.POLLIN:
502                     events |= EVENT_WRITE
503                 if event & ~select.POLLOUT:
504                     events |= EVENT_READ
505
506                 key = self._key_from_fd(fd)
507                 if key:
508                     ready.append((key, events & key.events))
509             return ready
510
511         def close(self):
512             self._devpoll.close()
513             super(DevpollSelector, self).close()
514
515
516 if hasattr(select, 'kqueue'):
517
518     class KqueueSelector(_BaseSelectorImpl):
519         """Kqueue-based selector."""
520
521         def __init__(self):
522             super(KqueueSelector, self).__init__()
523             self._kqueue = select.kqueue()
524
525         def fileno(self):
526             return self._kqueue.fileno()
527
528         def register(self, fileobj, events, data=None):
529             key = super(KqueueSelector, self).register(fileobj, events, data)
530             if events & EVENT_READ:
531                 kev = select.kevent(key.fd, select.KQ_FILTER_READ,
532                                     select.KQ_EV_ADD)
533                 self._kqueue.control([kev], 0, 0)
534             if events & EVENT_WRITE:
535                 kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
536                                     select.KQ_EV_ADD)
537                 self._kqueue.control([kev], 0, 0)
538             return key
539
540         def unregister(self, fileobj):
541             key = super(KqueueSelector, self).unregister(fileobj)
542             if key.events & EVENT_READ:
543                 kev = select.kevent(key.fd, select.KQ_FILTER_READ,
544                                     select.KQ_EV_DELETE)
545                 try:
546                     self._kqueue.control([kev], 0, 0)
547                 except OSError:
548                     # This can happen if the FD was closed since it
549                     # was registered.
550                     pass
551             if key.events & EVENT_WRITE:
552                 kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
553                                     select.KQ_EV_DELETE)
554                 try:
555                     self._kqueue.control([kev], 0, 0)
556                 except OSError:
557                     # See comment above.
558                     pass
559             return key
560
561         def select(self, timeout=None):
562             timeout = None if timeout is None else max(timeout, 0)
563             max_ev = len(self._fd_to_key)
564             ready = []
565             try:
566                 kev_list = wrap_error(self._kqueue.control,
567                                       None, max_ev, timeout)
568             except InterruptedError:
569                 return ready
570             for kev in kev_list:
571                 fd = kev.ident
572                 flag = kev.filter
573                 events = 0
574                 if flag == select.KQ_FILTER_READ:
575                     events |= EVENT_READ
576                 if flag == select.KQ_FILTER_WRITE:
577                     events |= EVENT_WRITE
578
579                 key = self._key_from_fd(fd)
580                 if key:
581                     ready.append((key, events & key.events))
582             return ready
583
584         def close(self):
585             self._kqueue.close()
586             super(KqueueSelector, self).close()
587
588
589 # Choose the best implementation, roughly:
590 #    epoll|kqueue|devpoll > poll > select.
591 # select() also can't accept a FD > FD_SETSIZE (usually around 1024)
592 if 'KqueueSelector' in globals():
593     DefaultSelector = KqueueSelector
594 elif 'EpollSelector' in globals():
595     DefaultSelector = EpollSelector
596 elif 'DevpollSelector' in globals():
597     DefaultSelector = DevpollSelector
598 elif 'PollSelector' in globals():
599     DefaultSelector = PollSelector
600 else:
601     DefaultSelector = SelectSelector