diff -r 07cfe9f5561b Doc/library/selectors.rst --- a/Doc/library/selectors.rst Fri Dec 06 12:57:40 2013 -0800 +++ b/Doc/library/selectors.rst Fri Dec 06 14:00:43 2013 -0800 @@ -102,7 +102,8 @@ Register a file object for selection, monitoring it for I/O events. - *fileobj* is the file object to monitor. + *fileobj* is the file object to monitor. It may either be an integer + file descriptor or an object with a ``fileno()`` method. *events* is a bitwise mask of events to monitor. *data* is an opaque object. @@ -118,7 +119,9 @@ *fileobj* must be a file object previously registered. This returns the associated :class:`SelectorKey` instance, or raises a - :exc:`KeyError` if the file object is not registered. + :exc:`KeyError` if *fileobj* is not registered. It will raise + :exc:`ValueError` if *fileobj* is an object with a ``fileno()`` method + that has been closed. .. method:: modify(fileobj, events, data=None) diff -r 07cfe9f5561b Lib/selectors.py --- a/Lib/selectors.py Fri Dec 06 12:57:40 2013 -0800 +++ b/Lib/selectors.py Fri Dec 06 14:00:43 2013 -0800 @@ -89,6 +89,15 @@ Returns: SelectorKey instance + + Raises: + ValueError if events is invalid + KeyError if fileobj is already registered + OSError if fileobj is closed or otherwise is unacceptable to + the underlying system call (if a system call is made) + + Note: + OSError may or may not be raised """ raise NotImplementedError @@ -101,6 +110,13 @@ Returns: SelectorKey instance + + Raises: + KeyError if fileobj is not registered + + Note: + If fileobj is registered but has since been closed this does + *not* raise OSError (even if the wrapped syscall does) """ raise NotImplementedError @@ -114,6 +130,9 @@ Returns: SelectorKey instance + + Raises: + Anything that unregister() or register() raises """ self.unregister(fileobj) return self.register(fileobj, events, data) @@ -409,11 +428,20 @@ if key.events & EVENT_READ: kev = select.kevent(key.fd, select.KQ_FILTER_READ, select.KQ_EV_DELETE) - self._kqueue.control([kev], 0, 0) + try: + self._kqueue.control([kev], 0, 0) + except OSError: + # This can happen if the FD was closed since it + # was registered. + pass if key.events & EVENT_WRITE: kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, select.KQ_EV_DELETE) - self._kqueue.control([kev], 0, 0) + try: + self._kqueue.control([kev], 0, 0) + except OSError: + # See comment above. + pass return key def select(self, timeout=None): diff -r 07cfe9f5561b Lib/test/test_selectors.py --- a/Lib/test/test_selectors.py Fri Dec 06 12:57:40 2013 -0800 +++ b/Lib/test/test_selectors.py Fri Dec 06 14:00:43 2013 -0800 @@ -94,6 +94,20 @@ # unregister twice self.assertRaises(KeyError, s.unregister, rd) + def test_unregister_after_fd_close(self): + s = self.SELECTOR() + self.addCleanup(s.close) + rd, wr = socketpair() + r, w = rd.fileno(), wr.fileno() + self.addCleanup(rd.close) + self.addCleanup(wr.close) + s.register(r, selectors.EVENT_READ) + s.register(w, selectors.EVENT_WRITE) + rd.close() + wr.close() + s.unregister(r) + s.unregister(w) + def test_modify(self): s = self.SELECTOR() self.addCleanup(s.close)