_utils.py 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637
  1. from __future__ import annotations
  2. import select
  3. import socket
  4. import sys
  5. def is_socket_readable(sock: socket.socket | None) -> bool:
  6. """
  7. Return whether a socket, as identifed by its file descriptor, is readable.
  8. "A socket is readable" means that the read buffer isn't empty, i.e. that calling
  9. .recv() on it would immediately return some data.
  10. """
  11. # NOTE: we want check for readability without actually attempting to read, because
  12. # we don't want to block forever if it's not readable.
  13. # In the case that the socket no longer exists, or cannot return a file
  14. # descriptor, we treat it as being readable, as if it the next read operation
  15. # on it is ready to return the terminating `b""`.
  16. sock_fd = None if sock is None else sock.fileno()
  17. if sock_fd is None or sock_fd < 0: # pragma: nocover
  18. return True
  19. # The implementation below was stolen from:
  20. # https://github.com/python-trio/trio/blob/20ee2b1b7376db637435d80e266212a35837ddcc/trio/_socket.py#L471-L478
  21. # See also: https://github.com/encode/httpcore/pull/193#issuecomment-703129316
  22. # Use select.select on Windows, and when poll is unavailable and select.poll
  23. # everywhere else. (E.g. When eventlet is in use. See #327)
  24. if (
  25. sys.platform == "win32" or getattr(select, "poll", None) is None
  26. ): # pragma: nocover
  27. rready, _, _ = select.select([sock_fd], [], [], 0)
  28. return bool(rready)
  29. p = select.poll()
  30. p.register(sock_fd, select.POLLIN)
  31. return bool(p.poll(0))