You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fdesc.py 3.1KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. # -*- test-case-name: twisted.test.test_fdesc -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Utility functions for dealing with POSIX file descriptors.
  6. """
  7. import os
  8. import errno
  9. try:
  10. import fcntl
  11. except ImportError:
  12. fcntl = None
  13. # twisted imports
  14. from twisted.internet.main import CONNECTION_LOST, CONNECTION_DONE
  15. def setNonBlocking(fd):
  16. """
  17. Set the file description of the given file descriptor to non-blocking.
  18. """
  19. flags = fcntl.fcntl(fd, fcntl.F_GETFL)
  20. flags = flags | os.O_NONBLOCK
  21. fcntl.fcntl(fd, fcntl.F_SETFL, flags)
  22. def setBlocking(fd):
  23. """
  24. Set the file description of the given file descriptor to blocking.
  25. """
  26. flags = fcntl.fcntl(fd, fcntl.F_GETFL)
  27. flags = flags & ~os.O_NONBLOCK
  28. fcntl.fcntl(fd, fcntl.F_SETFL, flags)
  29. if fcntl is None:
  30. # fcntl isn't available on Windows. By default, handles aren't
  31. # inherited on Windows, so we can do nothing here.
  32. _setCloseOnExec = _unsetCloseOnExec = lambda fd: None
  33. else:
  34. def _setCloseOnExec(fd):
  35. """
  36. Make a file descriptor close-on-exec.
  37. """
  38. flags = fcntl.fcntl(fd, fcntl.F_GETFD)
  39. flags = flags | fcntl.FD_CLOEXEC
  40. fcntl.fcntl(fd, fcntl.F_SETFD, flags)
  41. def _unsetCloseOnExec(fd):
  42. """
  43. Make a file descriptor close-on-exec.
  44. """
  45. flags = fcntl.fcntl(fd, fcntl.F_GETFD)
  46. flags = flags & ~fcntl.FD_CLOEXEC
  47. fcntl.fcntl(fd, fcntl.F_SETFD, flags)
  48. def readFromFD(fd, callback):
  49. """
  50. Read from file descriptor, calling callback with resulting data.
  51. If successful, call 'callback' with a single argument: the
  52. resulting data.
  53. Returns same thing FileDescriptor.doRead would: CONNECTION_LOST,
  54. CONNECTION_DONE, or None.
  55. @type fd: C{int}
  56. @param fd: non-blocking file descriptor to be read from.
  57. @param callback: a callable which accepts a single argument. If
  58. data is read from the file descriptor it will be called with this
  59. data. Handling exceptions from calling the callback is up to the
  60. caller.
  61. Note that if the descriptor is still connected but no data is read,
  62. None will be returned but callback will not be called.
  63. @return: CONNECTION_LOST on error, CONNECTION_DONE when fd is
  64. closed, otherwise None.
  65. """
  66. try:
  67. output = os.read(fd, 8192)
  68. except (OSError, IOError) as ioe:
  69. if ioe.args[0] in (errno.EAGAIN, errno.EINTR):
  70. return
  71. else:
  72. return CONNECTION_LOST
  73. if not output:
  74. return CONNECTION_DONE
  75. callback(output)
  76. def writeToFD(fd, data):
  77. """
  78. Write data to file descriptor.
  79. Returns same thing FileDescriptor.writeSomeData would.
  80. @type fd: C{int}
  81. @param fd: non-blocking file descriptor to be written to.
  82. @type data: C{str} or C{buffer}
  83. @param data: bytes to write to fd.
  84. @return: number of bytes written, or CONNECTION_LOST.
  85. """
  86. try:
  87. return os.write(fd, data)
  88. except (OSError, IOError) as io:
  89. if io.errno in (errno.EAGAIN, errno.EINTR):
  90. return 0
  91. return CONNECTION_LOST
  92. __all__ = ["setNonBlocking", "setBlocking", "readFromFD", "writeToFD"]