"""Selectors module.This module allows high-level and efficient I/O multiplexing, built upon the`select` module primitives."""from abc import ABCMeta, abstractmethodfrom collections import namedtuplefrom collections.abc import Mappingimport mathimport selectimport sys# generic events, that must be mapped to implementation-specific onesEVENT_READ = (1 << 0)EVENT_WRITE = (1 << 1)def _fileobj_to_fd(fileobj):"""Return a file descriptor from a file object.Parameters:fileobj -- file object or file descriptorReturns:corresponding file descriptorRaises:ValueError if the object is invalid"""if isinstance(fileobj, int):fd = fileobjelse:try:fd = int(fileobj.fileno())except (AttributeError, TypeError, ValueError):raise ValueError("Invalid file object: ""{!r}".format(fileobj)) from Noneif fd < 0:raise ValueError("Invalid file descriptor: {}".format(fd))return fdSelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])SelectorKey.__doc__ = """SelectorKey(fileobj, fd, events, data)Object used to associate a file object to its backingfile descriptor, selected event mask, and attached data."""if sys.version_info >= (3, 5):SelectorKey.fileobj.__doc__ = 'File object registered.'SelectorKey.fd.__doc__ = 'Underlying file descriptor.'SelectorKey.events.__doc__ = 'Events that must be waited for on this file object.'SelectorKey.data.__doc__ = ('''Optional opaque data associated to this file object.For example, this could be used to store a per-client session ID.''')class _SelectorMapping(Mapping):"""Mapping of file objects to selector keys."""def __init__(self, selector):self._selector = selectordef __len__(self):return len(self._selector._fd_to_key)def __getitem__(self, fileobj):try:fd = self._selector._fileobj_lookup(fileobj)return self._selector._fd_to_key[fd]except KeyError:raise KeyError("{!r} is not registered".format(fileobj)) from Nonedef __iter__(self):return iter(self._selector._fd_to_key)class BaseSelector(metaclass=ABCMeta):"""Selector abstract base class.A selector supports registering file objects to be monitored for specificI/O events.A file object is a file descriptor or any object with a `fileno()` method.An arbitrary object can be attached to the file object, which can be usedfor example to store context information, a callback, etc.A selector can use various implementations (select(), poll(), epoll()...)depending on the platform. The default `Selector` class uses the mostefficient implementation on the current platform."""@abstractmethoddef register(self, fileobj, events, data=None):"""Register a file object.Parameters:fileobj -- file object or file descriptorevents -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)data -- attached dataReturns:SelectorKey instanceRaises:ValueError if events is invalidKeyError if fileobj is already registeredOSError if fileobj is closed or otherwise is unacceptable tothe underlying system call (if a system call is made)Note:OSError may or may not be raised"""raise NotImplementedError@abstractmethoddef unregister(self, fileobj):"""Unregister a file object.Parameters:fileobj -- file object or file descriptorReturns:SelectorKey instanceRaises:KeyError if fileobj is not registeredNote:If fileobj is registered but has since been closed this does*not* raise OSError (even if the wrapped syscall does)"""raise NotImplementedErrordef modify(self, fileobj, events, data=None):"""Change a registered file object monitored events or attached data.Parameters:fileobj -- file object or file descriptorevents -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)data -- attached dataReturns:SelectorKey instanceRaises:Anything that unregister() or register() raises"""self.unregister(fileobj)return self.register(fileobj, events, data)@abstractmethoddef select(self, timeout=None):"""Perform the actual selection, until some monitored file objects areready or a timeout expires.Parameters:timeout -- if timeout > 0, this specifies the maximum wait time, insecondsif timeout <= 0, the select() call won't block, and willreport the currently ready file objectsif timeout is None, select() will block until a monitoredfile object becomes readyReturns:list of (key, events) for ready file objects`events` is a bitwise mask of EVENT_READ|EVENT_WRITE"""raise NotImplementedErrordef close(self):"""Close the selector.This must be called to make sure that any underlying resource is freed."""passdef get_key(self, fileobj):"""Return the key associated to a registered file object.Returns:SelectorKey for this file object"""mapping = self.get_map()if mapping is None:raise RuntimeError('Selector is closed')try:return mapping[fileobj]except KeyError:raise KeyError("{!r} is not registered".format(fileobj)) from None@abstractmethoddef get_map(self):"""Return a mapping of file objects to selector keys."""raise NotImplementedErrordef __enter__(self):return selfdef __exit__(self, *args):self.close()class _BaseSelectorImpl(BaseSelector):"""Base selector implementation."""def __init__(self):# this maps file descriptors to keysself._fd_to_key = {}# read-only mapping returned by get_map()self._map = _SelectorMapping(self)def _fileobj_lookup(self, fileobj):"""Return a file descriptor from a file object.This wraps _fileobj_to_fd() to do an exhaustive search in casethe object is invalid but we still have it in our map. Thisis used by unregister() so we can unregister an object thatwas previously registered even if it is closed. It is alsoused by _SelectorMapping."""try:return _fileobj_to_fd(fileobj)except ValueError:# Do an exhaustive search.for key in self._fd_to_key.values():if key.fileobj is fileobj:return key.fd# Raise ValueError after all.raisedef register(self, fileobj, events, data=None):if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):raise ValueError("Invalid events: {!r}".format(events))key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)if key.fd in self._fd_to_key:raise KeyError("{!r} (FD {}) is already registered".format(fileobj, key.fd))self._fd_to_key[key.fd] = keyreturn keydef unregister(self, fileobj):try:key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))except KeyError:raise KeyError("{!r} is not registered".format(fileobj)) from Nonereturn keydef modify(self, fileobj, events, data=None):try:key = self._fd_to_key[self._fileobj_lookup(fileobj)]except KeyError:raise KeyError("{!r} is not registered".format(fileobj)) from Noneif events != key.events:self.unregister(fileobj)key = self.register(fileobj, events, data)elif data != key.data:# Use a shortcut to update the data.key = key._replace(data=data)self._fd_to_key[key.fd] = keyreturn keydef close(self):self._fd_to_key.clear()self._map = Nonedef get_map(self):return self._mapdef _key_from_fd(self, fd):"""Return the key associated to a given file descriptor.Parameters:fd -- file descriptorReturns:corresponding key, or None if not found"""try:return self._fd_to_key[fd]except KeyError:return Noneclass SelectSelector(_BaseSelectorImpl):"""Select-based selector."""def __init__(self):super().__init__()self._readers = set()self._writers = set()def register(self, fileobj, events, data=None):key = super().register(fileobj, events, data)if events & EVENT_READ:self._readers.add(key.fd)if events & EVENT_WRITE:self._writers.add(key.fd)return keydef unregister(self, fileobj):key = super().unregister(fileobj)self._readers.discard(key.fd)self._writers.discard(key.fd)return keyif sys.platform == 'win32':def _select(self, r, w, _, timeout=None):r, w, x = select.select(r, w, w, timeout)return r, w + x, []else:_select = select.selectdef select(self, timeout=None):timeout = None if timeout is None else max(timeout, 0)ready = []try:r, w, _ = self._select(self._readers, self._writers, [], timeout)except InterruptedError:return readyr = set(r)w = set(w)for fd in r | w:events = 0if fd in r:events |= EVENT_READif fd in w:events |= EVENT_WRITEkey = self._key_from_fd(fd)if key:ready.append((key, events & key.events))return readyclass _PollLikeSelector(_BaseSelectorImpl):"""Base class shared between poll, epoll and devpoll selectors."""_selector_cls = None_EVENT_READ = None_EVENT_WRITE = Nonedef __init__(self):super().__init__()self._selector = self._selector_cls()def register(self, fileobj, events, data=None):key = super().register(fileobj, events, data)poller_events = 0if events & EVENT_READ:poller_events |= self._EVENT_READif events & EVENT_WRITE:poller_events |= self._EVENT_WRITEtry:self._selector.register(key.fd, poller_events)except:super().unregister(fileobj)raisereturn keydef unregister(self, fileobj):key = super().unregister(fileobj)try:self._selector.unregister(key.fd)except OSError:# This can happen if the FD was closed since it# was registered.passreturn keydef modify(self, fileobj, events, data=None):try:key = self._fd_to_key[self._fileobj_lookup(fileobj)]except KeyError:raise KeyError(f"{fileobj!r} is not registered") from Nonechanged = Falseif events != key.events:selector_events = 0if events & EVENT_READ:selector_events |= self._EVENT_READif events & EVENT_WRITE:selector_events |= self._EVENT_WRITEtry:self._selector.modify(key.fd, selector_events)except:super().unregister(fileobj)raisechanged = Trueif data != key.data:changed = Trueif changed:key = key._replace(events=events, data=data)self._fd_to_key[key.fd] = keyreturn keydef select(self, timeout=None):# This is shared between poll() and epoll().# epoll() has a different signature and handling of timeout parameter.if timeout is None:timeout = Noneelif timeout <= 0:timeout = 0else:# poll() has a resolution of 1 millisecond, round away from# zero to wait *at least* timeout seconds.timeout = math.ceil(timeout * 1e3)ready = []try:fd_event_list = self._selector.poll(timeout)except InterruptedError:return readyfor fd, event in fd_event_list:events = 0if event & ~self._EVENT_READ:events |= EVENT_WRITEif event & ~self._EVENT_WRITE:events |= EVENT_READkey = self._key_from_fd(fd)if key:ready.append((key, events & key.events))return readyif hasattr(select, 'poll'):class PollSelector(_PollLikeSelector):"""Poll-based selector."""_selector_cls = select.poll_EVENT_READ = select.POLLIN_EVENT_WRITE = select.POLLOUTif hasattr(select, 'epoll'):class EpollSelector(_PollLikeSelector):"""Epoll-based selector."""_selector_cls = select.epoll_EVENT_READ = select.EPOLLIN_EVENT_WRITE = select.EPOLLOUTdef fileno(self):return self._selector.fileno()def select(self, timeout=None):if timeout is None:timeout = -1elif timeout <= 0:timeout = 0else:# epoll_wait() has a resolution of 1 millisecond, round away# from zero to wait *at least* timeout seconds.timeout = math.ceil(timeout * 1e3) * 1e-3# epoll_wait() expects `maxevents` to be greater than zero;# we want to make sure that `select()` can be called when no# FD is registered.max_ev = max(len(self._fd_to_key), 1)ready = []try:fd_event_list = self._selector.poll(timeout, max_ev)except InterruptedError:return readyfor fd, event in fd_event_list:events = 0if event & ~select.EPOLLIN:events |= EVENT_WRITEif event & ~select.EPOLLOUT:events |= EVENT_READkey = self._key_from_fd(fd)if key:ready.append((key, events & key.events))return readydef close(self):self._selector.close()super().close()if hasattr(select, 'devpoll'):class DevpollSelector(_PollLikeSelector):"""Solaris /dev/poll selector."""_selector_cls = select.devpoll_EVENT_READ = select.POLLIN_EVENT_WRITE = select.POLLOUTdef fileno(self):return self._selector.fileno()def close(self):self._selector.close()super().close()if hasattr(select, 'kqueue'):class KqueueSelector(_BaseSelectorImpl):"""Kqueue-based selector."""def __init__(self):super().__init__()self._selector = select.kqueue()def fileno(self):return self._selector.fileno()def register(self, fileobj, events, data=None):key = super().register(fileobj, events, data)try:if events & EVENT_READ:kev = select.kevent(key.fd, select.KQ_FILTER_READ,select.KQ_EV_ADD)self._selector.control([kev], 0, 0)if events & EVENT_WRITE:kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,select.KQ_EV_ADD)self._selector.control([kev], 0, 0)except:super().unregister(fileobj)raisereturn keydef unregister(self, fileobj):key = super().unregister(fileobj)if key.events & EVENT_READ:kev = select.kevent(key.fd, select.KQ_FILTER_READ,select.KQ_EV_DELETE)try:self._selector.control([kev], 0, 0)except OSError:# This can happen if the FD was closed since it# was registered.passif key.events & EVENT_WRITE:kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,select.KQ_EV_DELETE)try:self._selector.control([kev], 0, 0)except OSError:# See comment above.passreturn keydef select(self, timeout=None):timeout = None if timeout is None else max(timeout, 0)max_ev = len(self._fd_to_key)ready = []try:kev_list = self._selector.control(None, max_ev, timeout)except InterruptedError:return readyfor kev in kev_list:fd = kev.identflag = kev.filterevents = 0if flag == select.KQ_FILTER_READ:events |= EVENT_READif flag == select.KQ_FILTER_WRITE:events |= EVENT_WRITEkey = self._key_from_fd(fd)if key:ready.append((key, events & key.events))return readydef close(self):self._selector.close()super().close()# Choose the best implementation, roughly:# epoll|kqueue|devpoll > poll > select.# select() also can't accept a FD > FD_SETSIZE (usually around 1024)if 'KqueueSelector' in globals():DefaultSelector = KqueueSelectorelif 'EpollSelector' in globals():DefaultSelector = EpollSelectorelif 'DevpollSelector' in globals():DefaultSelector = DevpollSelectorelif 'PollSelector' in globals():DefaultSelector = PollSelectorelse:DefaultSelector = SelectSelector
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。