1# This file is from the selectors2.py package.  It backports the PSF Licensed
2# selectors module from the Python-3.5 stdlib to older versions of Python.
3# The author, Seth Michael Larson, dual licenses his modifications under the
4# PSF License and MIT License:
5# https://github.com/SethMichaelLarson/selectors2#license
6#
7# Copyright (c) 2016 Seth Michael Larson
8#
9# PSF License (see licenses/PSF-license.txt or https://opensource.org/licenses/Python-2.0)
10# MIT License (see licenses/MIT-license.txt or https://opensource.org/licenses/MIT)
11#
12
13
14# Backport of selectors.py from Python 3.5+ to support Python < 3.4
15# Also has the behavior specified in PEP 475 which is to retry syscalls
16# in the case of an EINTR error. This module is required because selectors34
17# does not follow this behavior and instead returns that no file descriptor
18# events have occurred rather than retry the syscall. The decision to drop
19# support for select.devpoll is made to maintain 100% test coverage.
20
21import errno
22import math
23import select
24import socket
25import sys
26import time
27from collections import namedtuple
28from ansible.module_utils.common._collections_compat import Mapping
29
30try:
31    monotonic = time.monotonic
32except (AttributeError, ImportError):  # Python 3.3<
33    monotonic = time.time
34
35__author__ = 'Seth Michael Larson'
36__email__ = 'sethmichaellarson@protonmail.com'
37__version__ = '1.1.1'
38__license__ = 'MIT'
39
40__all__ = [
41    'EVENT_READ',
42    'EVENT_WRITE',
43    'SelectorError',
44    'SelectorKey',
45    'DefaultSelector'
46]
47
48EVENT_READ = (1 << 0)
49EVENT_WRITE = (1 << 1)
50
51HAS_SELECT = True  # Variable that shows whether the platform has a selector.
52_SYSCALL_SENTINEL = object()  # Sentinel in case a system call returns None.
53
54
55class SelectorError(Exception):
56    def __init__(self, errcode):
57        super(SelectorError, self).__init__()
58        self.errno = errcode
59
60    def __repr__(self):
61        return "<SelectorError errno={0}>".format(self.errno)
62
63    def __str__(self):
64        return self.__repr__()
65
66
67def _fileobj_to_fd(fileobj):
68    """ Return a file descriptor from a file object. If
69    given an integer will simply return that integer back. """
70    if isinstance(fileobj, int):
71        fd = fileobj
72    else:
73        try:
74            fd = int(fileobj.fileno())
75        except (AttributeError, TypeError, ValueError):
76            raise ValueError("Invalid file object: {0!r}".format(fileobj))
77    if fd < 0:
78        raise ValueError("Invalid file descriptor: {0}".format(fd))
79    return fd
80
81
82# Python 3.5 uses a more direct route to wrap system calls to increase speed.
83if sys.version_info >= (3, 5):
84    def _syscall_wrapper(func, _, *args, **kwargs):
85        """ This is the short-circuit version of the below logic
86        because in Python 3.5+ all selectors restart system calls. """
87        try:
88            return func(*args, **kwargs)
89        except (OSError, IOError, select.error) as e:
90            errcode = None
91            if hasattr(e, "errno"):
92                errcode = e.errno
93            elif hasattr(e, "args"):
94                errcode = e.args[0]
95            raise SelectorError(errcode)
96else:
97    def _syscall_wrapper(func, recalc_timeout, *args, **kwargs):
98        """ Wrapper function for syscalls that could fail due to EINTR.
99        All functions should be retried if there is time left in the timeout
100        in accordance with PEP 475. """
101        timeout = kwargs.get("timeout", None)
102        if timeout is None:
103            expires = None
104            recalc_timeout = False
105        else:
106            timeout = float(timeout)
107            if timeout < 0.0:  # Timeout less than 0 treated as no timeout.
108                expires = None
109            else:
110                expires = monotonic() + timeout
111
112        args = list(args)
113        if recalc_timeout and "timeout" not in kwargs:
114            raise ValueError(
115                "Timeout must be in args or kwargs to be recalculated")
116
117        result = _SYSCALL_SENTINEL
118        while result is _SYSCALL_SENTINEL:
119            try:
120                result = func(*args, **kwargs)
121            # OSError is thrown by select.select
122            # IOError is thrown by select.epoll.poll
123            # select.error is thrown by select.poll.poll
124            # Aren't we thankful for Python 3.x rework for exceptions?
125            except (OSError, IOError, select.error) as e:
126                # select.error wasn't a subclass of OSError in the past.
127                errcode = None
128                if hasattr(e, "errno"):
129                    errcode = e.errno
130                elif hasattr(e, "args"):
131                    errcode = e.args[0]
132
133                # Also test for the Windows equivalent of EINTR.
134                is_interrupt = (errcode == errno.EINTR or (hasattr(errno, "WSAEINTR") and
135                                                           errcode == errno.WSAEINTR))
136
137                if is_interrupt:
138                    if expires is not None:
139                        current_time = monotonic()
140                        if current_time > expires:
141                            raise OSError(errno.ETIMEDOUT)
142                        if recalc_timeout:
143                            if "timeout" in kwargs:
144                                kwargs["timeout"] = expires - current_time
145                    continue
146                if errcode:
147                    raise SelectorError(errcode)
148                else:
149                    raise
150        return result
151
152
153SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
154
155
156class _SelectorMapping(Mapping):
157    """ Mapping of file objects to selector keys """
158
159    def __init__(self, selector):
160        self._selector = selector
161
162    def __len__(self):
163        return len(self._selector._fd_to_key)
164
165    def __getitem__(self, fileobj):
166        try:
167            fd = self._selector._fileobj_lookup(fileobj)
168            return self._selector._fd_to_key[fd]
169        except KeyError:
170            raise KeyError("{0!r} is not registered.".format(fileobj))
171
172    def __iter__(self):
173        return iter(self._selector._fd_to_key)
174
175
176class BaseSelector(object):
177    """ Abstract Selector class
178
179    A selector supports registering file objects to be monitored
180    for specific I/O events.
181
182    A file object is a file descriptor or any object with a
183    `fileno()` method. An arbitrary object can be attached to the
184    file object which can be used for example to store context info,
185    a callback, etc.
186
187    A selector can use various implementations (select(), poll(), epoll(),
188    and kqueue()) depending on the platform. The 'DefaultSelector' class uses
189    the most efficient implementation for the current platform.
190    """
191    def __init__(self):
192        # Maps file descriptors to keys.
193        self._fd_to_key = {}
194
195        # Read-only mapping returned by get_map()
196        self._map = _SelectorMapping(self)
197
198    def _fileobj_lookup(self, fileobj):
199        """ Return a file descriptor from a file object.
200        This wraps _fileobj_to_fd() to do an exhaustive
201        search in case the object is invalid but we still
202        have it in our map. Used by unregister() so we can
203        unregister an object that was previously registered
204        even if it is closed. It is also used by _SelectorMapping
205        """
206        try:
207            return _fileobj_to_fd(fileobj)
208        except ValueError:
209
210            # Search through all our mapped keys.
211            for key in self._fd_to_key.values():
212                if key.fileobj is fileobj:
213                    return key.fd
214
215            # Raise ValueError after all.
216            raise
217
218    def register(self, fileobj, events, data=None):
219        """ Register a file object for a set of events to monitor. """
220        if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
221            raise ValueError("Invalid events: {0!r}".format(events))
222
223        key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
224
225        if key.fd in self._fd_to_key:
226            raise KeyError("{0!r} (FD {1}) is already registered"
227                           .format(fileobj, key.fd))
228
229        self._fd_to_key[key.fd] = key
230        return key
231
232    def unregister(self, fileobj):
233        """ Unregister a file object from being monitored. """
234        try:
235            key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
236        except KeyError:
237            raise KeyError("{0!r} is not registered".format(fileobj))
238
239        # Getting the fileno of a closed socket on Windows errors with EBADF.
240        except socket.error as err:
241            if err.errno != errno.EBADF:
242                raise
243            else:
244                for key in self._fd_to_key.values():
245                    if key.fileobj is fileobj:
246                        self._fd_to_key.pop(key.fd)
247                        break
248                else:
249                    raise KeyError("{0!r} is not registered".format(fileobj))
250        return key
251
252    def modify(self, fileobj, events, data=None):
253        """ Change a registered file object monitored events and data. """
254        # NOTE: Some subclasses optimize this operation even further.
255        try:
256            key = self._fd_to_key[self._fileobj_lookup(fileobj)]
257        except KeyError:
258            raise KeyError("{0!r} is not registered".format(fileobj))
259
260        if events != key.events:
261            self.unregister(fileobj)
262            key = self.register(fileobj, events, data)
263
264        elif data != key.data:
265            # Use a shortcut to update the data.
266            key = key._replace(data=data)
267            self._fd_to_key[key.fd] = key
268
269        return key
270
271    def select(self, timeout=None):
272        """ Perform the actual selection until some monitored file objects
273        are ready or the timeout expires. """
274        raise NotImplementedError()
275
276    def close(self):
277        """ Close the selector. This must be called to ensure that all
278        underlying resources are freed. """
279        self._fd_to_key.clear()
280        self._map = None
281
282    def get_key(self, fileobj):
283        """ Return the key associated with a registered file object. """
284        mapping = self.get_map()
285        if mapping is None:
286            raise RuntimeError("Selector is closed")
287        try:
288            return mapping[fileobj]
289        except KeyError:
290            raise KeyError("{0!r} is not registered".format(fileobj))
291
292    def get_map(self):
293        """ Return a mapping of file objects to selector keys """
294        return self._map
295
296    def _key_from_fd(self, fd):
297        """ Return the key associated to a given file descriptor
298         Return None if it is not found. """
299        try:
300            return self._fd_to_key[fd]
301        except KeyError:
302            return None
303
304    def __enter__(self):
305        return self
306
307    def __exit__(self, *args):
308        self.close()
309
310
311# Almost all platforms have select.select()
312if hasattr(select, "select"):
313    class SelectSelector(BaseSelector):
314        """ Select-based selector. """
315        def __init__(self):
316            super(SelectSelector, self).__init__()
317            self._readers = set()
318            self._writers = set()
319
320        def register(self, fileobj, events, data=None):
321            key = super(SelectSelector, self).register(fileobj, events, data)
322            if events & EVENT_READ:
323                self._readers.add(key.fd)
324            if events & EVENT_WRITE:
325                self._writers.add(key.fd)
326            return key
327
328        def unregister(self, fileobj):
329            key = super(SelectSelector, self).unregister(fileobj)
330            self._readers.discard(key.fd)
331            self._writers.discard(key.fd)
332            return key
333
334        def _select(self, r, w, timeout=None):
335            """ Wrapper for select.select because timeout is a positional arg """
336            return select.select(r, w, [], timeout)
337
338        def select(self, timeout=None):
339            # Selecting on empty lists on Windows errors out.
340            if not len(self._readers) and not len(self._writers):
341                return []
342
343            timeout = None if timeout is None else max(timeout, 0.0)
344            ready = []
345            r, w, _ = _syscall_wrapper(self._select, True, self._readers,
346                                       self._writers, timeout=timeout)
347            r = set(r)
348            w = set(w)
349            for fd in r | w:
350                events = 0
351                if fd in r:
352                    events |= EVENT_READ
353                if fd in w:
354                    events |= EVENT_WRITE
355
356                key = self._key_from_fd(fd)
357                if key:
358                    ready.append((key, events & key.events))
359            return ready
360
361    __all__.append('SelectSelector')
362
363
364if hasattr(select, "poll"):
365    class PollSelector(BaseSelector):
366        """ Poll-based selector """
367        def __init__(self):
368            super(PollSelector, self).__init__()
369            self._poll = select.poll()
370
371        def register(self, fileobj, events, data=None):
372            key = super(PollSelector, self).register(fileobj, events, data)
373            event_mask = 0
374            if events & EVENT_READ:
375                event_mask |= select.POLLIN
376            if events & EVENT_WRITE:
377                event_mask |= select.POLLOUT
378            self._poll.register(key.fd, event_mask)
379            return key
380
381        def unregister(self, fileobj):
382            key = super(PollSelector, self).unregister(fileobj)
383            self._poll.unregister(key.fd)
384            return key
385
386        def _wrap_poll(self, timeout=None):
387            """ Wrapper function for select.poll.poll() so that
388            _syscall_wrapper can work with only seconds. """
389            if timeout is not None:
390                if timeout <= 0:
391                    timeout = 0
392                else:
393                    # select.poll.poll() has a resolution of 1 millisecond,
394                    # round away from zero to wait *at least* timeout seconds.
395                    timeout = math.ceil(timeout * 1e3)
396
397            result = self._poll.poll(timeout)
398            return result
399
400        def select(self, timeout=None):
401            ready = []
402            fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout)
403            for fd, event_mask in fd_events:
404                events = 0
405                if event_mask & ~select.POLLIN:
406                    events |= EVENT_WRITE
407                if event_mask & ~select.POLLOUT:
408                    events |= EVENT_READ
409
410                key = self._key_from_fd(fd)
411                if key:
412                    ready.append((key, events & key.events))
413
414            return ready
415
416    __all__.append('PollSelector')
417
418if hasattr(select, "epoll"):
419    class EpollSelector(BaseSelector):
420        """ Epoll-based selector """
421        def __init__(self):
422            super(EpollSelector, self).__init__()
423            self._epoll = select.epoll()
424
425        def fileno(self):
426            return self._epoll.fileno()
427
428        def register(self, fileobj, events, data=None):
429            key = super(EpollSelector, self).register(fileobj, events, data)
430            events_mask = 0
431            if events & EVENT_READ:
432                events_mask |= select.EPOLLIN
433            if events & EVENT_WRITE:
434                events_mask |= select.EPOLLOUT
435            _syscall_wrapper(self._epoll.register, False, key.fd, events_mask)
436            return key
437
438        def unregister(self, fileobj):
439            key = super(EpollSelector, self).unregister(fileobj)
440            try:
441                _syscall_wrapper(self._epoll.unregister, False, key.fd)
442            except SelectorError:
443                # This can occur when the fd was closed since registry.
444                pass
445            return key
446
447        def select(self, timeout=None):
448            if timeout is not None:
449                if timeout <= 0:
450                    timeout = 0.0
451                else:
452                    # select.epoll.poll() has a resolution of 1 millisecond
453                    # but luckily takes seconds so we don't need a wrapper
454                    # like PollSelector. Just for better rounding.
455                    timeout = math.ceil(timeout * 1e3) * 1e-3
456                timeout = float(timeout)
457            else:
458                timeout = -1.0  # epoll.poll() must have a float.
459
460            # We always want at least 1 to ensure that select can be called
461            # with no file descriptors registered. Otherwise will fail.
462            max_events = max(len(self._fd_to_key), 1)
463
464            ready = []
465            fd_events = _syscall_wrapper(self._epoll.poll, True,
466                                         timeout=timeout,
467                                         maxevents=max_events)
468            for fd, event_mask in fd_events:
469                events = 0
470                if event_mask & ~select.EPOLLIN:
471                    events |= EVENT_WRITE
472                if event_mask & ~select.EPOLLOUT:
473                    events |= EVENT_READ
474
475                key = self._key_from_fd(fd)
476                if key:
477                    ready.append((key, events & key.events))
478            return ready
479
480        def close(self):
481            self._epoll.close()
482            super(EpollSelector, self).close()
483
484    __all__.append('EpollSelector')
485
486
487if hasattr(select, "devpoll"):
488    class DevpollSelector(BaseSelector):
489        """Solaris /dev/poll selector."""
490
491        def __init__(self):
492            super(DevpollSelector, self).__init__()
493            self._devpoll = select.devpoll()
494
495        def fileno(self):
496            return self._devpoll.fileno()
497
498        def register(self, fileobj, events, data=None):
499            key = super(DevpollSelector, self).register(fileobj, events, data)
500            poll_events = 0
501            if events & EVENT_READ:
502                poll_events |= select.POLLIN
503            if events & EVENT_WRITE:
504                poll_events |= select.POLLOUT
505            self._devpoll.register(key.fd, poll_events)
506            return key
507
508        def unregister(self, fileobj):
509            key = super(DevpollSelector, self).unregister(fileobj)
510            self._devpoll.unregister(key.fd)
511            return key
512
513        def _wrap_poll(self, timeout=None):
514            """ Wrapper function for select.poll.poll() so that
515            _syscall_wrapper can work with only seconds. """
516            if timeout is not None:
517                if timeout <= 0:
518                    timeout = 0
519                else:
520                    # select.devpoll.poll() has a resolution of 1 millisecond,
521                    # round away from zero to wait *at least* timeout seconds.
522                    timeout = math.ceil(timeout * 1e3)
523
524            result = self._devpoll.poll(timeout)
525            return result
526
527        def select(self, timeout=None):
528            ready = []
529            fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout)
530            for fd, event_mask in fd_events:
531                events = 0
532                if event_mask & ~select.POLLIN:
533                    events |= EVENT_WRITE
534                if event_mask & ~select.POLLOUT:
535                    events |= EVENT_READ
536
537                key = self._key_from_fd(fd)
538                if key:
539                    ready.append((key, events & key.events))
540
541            return ready
542
543        def close(self):
544            self._devpoll.close()
545            super(DevpollSelector, self).close()
546
547    __all__.append('DevpollSelector')
548
549
550if hasattr(select, "kqueue"):
551    class KqueueSelector(BaseSelector):
552        """ Kqueue / Kevent-based selector """
553        def __init__(self):
554            super(KqueueSelector, self).__init__()
555            self._kqueue = select.kqueue()
556
557        def fileno(self):
558            return self._kqueue.fileno()
559
560        def register(self, fileobj, events, data=None):
561            key = super(KqueueSelector, self).register(fileobj, events, data)
562            if events & EVENT_READ:
563                kevent = select.kevent(key.fd,
564                                       select.KQ_FILTER_READ,
565                                       select.KQ_EV_ADD)
566
567                _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
568
569            if events & EVENT_WRITE:
570                kevent = select.kevent(key.fd,
571                                       select.KQ_FILTER_WRITE,
572                                       select.KQ_EV_ADD)
573
574                _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
575
576            return key
577
578        def unregister(self, fileobj):
579            key = super(KqueueSelector, self).unregister(fileobj)
580            if key.events & EVENT_READ:
581                kevent = select.kevent(key.fd,
582                                       select.KQ_FILTER_READ,
583                                       select.KQ_EV_DELETE)
584                try:
585                    _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
586                except SelectorError:
587                    pass
588            if key.events & EVENT_WRITE:
589                kevent = select.kevent(key.fd,
590                                       select.KQ_FILTER_WRITE,
591                                       select.KQ_EV_DELETE)
592                try:
593                    _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0)
594                except SelectorError:
595                    pass
596
597            return key
598
599        def select(self, timeout=None):
600            if timeout is not None:
601                timeout = max(timeout, 0)
602
603            max_events = len(self._fd_to_key) * 2
604            ready_fds = {}
605
606            kevent_list = _syscall_wrapper(self._wrap_control, True,
607                                           None, max_events, timeout=timeout)
608
609            for kevent in kevent_list:
610                fd = kevent.ident
611                event_mask = kevent.filter
612                events = 0
613                if event_mask == select.KQ_FILTER_READ:
614                    events |= EVENT_READ
615                if event_mask == select.KQ_FILTER_WRITE:
616                    events |= EVENT_WRITE
617
618                key = self._key_from_fd(fd)
619                if key:
620                    if key.fd not in ready_fds:
621                        ready_fds[key.fd] = (key, events & key.events)
622                    else:
623                        old_events = ready_fds[key.fd][1]
624                        ready_fds[key.fd] = (key, (events | old_events) & key.events)
625
626            return list(ready_fds.values())
627
628        def close(self):
629            self._kqueue.close()
630            super(KqueueSelector, self).close()
631
632        def _wrap_control(self, changelist, max_events, timeout):
633            return self._kqueue.control(changelist, max_events, timeout)
634
635    __all__.append('KqueueSelector')
636
637
638# Choose the best implementation, roughly:
639# kqueue == epoll == devpoll > poll > select.
640# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
641if 'KqueueSelector' in globals():  # Platform-specific: Mac OS and BSD
642    DefaultSelector = KqueueSelector
643elif 'DevpollSelector' in globals():
644    DefaultSelector = DevpollSelector
645elif 'EpollSelector' in globals():  # Platform-specific: Linux
646    DefaultSelector = EpollSelector
647elif 'PollSelector' in globals():  # Platform-specific: Linux
648    DefaultSelector = PollSelector
649elif 'SelectSelector' in globals():  # Platform-specific: Windows
650    DefaultSelector = SelectSelector
651else:  # Platform-specific: AppEngine
652    def no_selector(_):
653        raise ValueError("Platform does not have a selector")
654    DefaultSelector = no_selector
655    HAS_SELECT = False
656