ยปCore Development>Code coverage>Lib/selectors.py

Python code coverage for Lib/selectors.py

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