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.

228 lines
6.5KB

  1. import errno
  2. import os
  3. from eventlet.greenio.base import (
  4. _operation_on_closed_file,
  5. greenpipe_doc,
  6. set_nonblocking,
  7. socket,
  8. SOCKET_BLOCKING,
  9. )
  10. from eventlet.hubs import trampoline, notify_close, notify_opened, IOClosed
  11. from eventlet.support import get_errno
  12. import six
  13. __all__ = ['_fileobject', 'GreenPipe']
  14. _fileobject = socket._fileobject
  15. class GreenPipe(_fileobject):
  16. __doc__ = greenpipe_doc
  17. def __init__(self, f, mode='r', bufsize=-1):
  18. if not isinstance(f, six.string_types + (int, file)):
  19. raise TypeError('f(ile) should be int, str, unicode or file, not %r' % f)
  20. if isinstance(f, six.string_types):
  21. f = open(f, mode, 0)
  22. if isinstance(f, int):
  23. fileno = f
  24. self._name = "<fd:%d>" % fileno
  25. else:
  26. fileno = os.dup(f.fileno())
  27. self._name = f.name
  28. if f.mode != mode:
  29. raise ValueError('file.mode %r does not match mode parameter %r' % (f.mode, mode))
  30. self._name = f.name
  31. f.close()
  32. super(GreenPipe, self).__init__(_SocketDuckForFd(fileno), mode, bufsize)
  33. set_nonblocking(self)
  34. self.softspace = 0
  35. @property
  36. def name(self):
  37. return self._name
  38. def __repr__(self):
  39. return "<%s %s %r, mode %r at 0x%x>" % (
  40. self.closed and 'closed' or 'open',
  41. self.__class__.__name__,
  42. self.name,
  43. self.mode,
  44. (id(self) < 0) and (sys.maxint + id(self)) or id(self))
  45. def close(self):
  46. super(GreenPipe, self).close()
  47. for method in [
  48. 'fileno', 'flush', 'isatty', 'next', 'read', 'readinto',
  49. 'readline', 'readlines', 'seek', 'tell', 'truncate',
  50. 'write', 'xreadlines', '__iter__', '__next__', 'writelines']:
  51. setattr(self, method, _operation_on_closed_file)
  52. def __enter__(self):
  53. return self
  54. def __exit__(self, *args):
  55. self.close()
  56. def _get_readahead_len(self):
  57. return len(self._rbuf.getvalue())
  58. def _clear_readahead_buf(self):
  59. len = self._get_readahead_len()
  60. if len > 0:
  61. self.read(len)
  62. def tell(self):
  63. self.flush()
  64. try:
  65. return os.lseek(self.fileno(), 0, 1) - self._get_readahead_len()
  66. except OSError as e:
  67. raise IOError(*e.args)
  68. def seek(self, offset, whence=0):
  69. self.flush()
  70. if whence == 1 and offset == 0: # tell synonym
  71. return self.tell()
  72. if whence == 1: # adjust offset by what is read ahead
  73. offset -= self._get_readahead_len()
  74. try:
  75. rv = os.lseek(self.fileno(), offset, whence)
  76. except OSError as e:
  77. raise IOError(*e.args)
  78. else:
  79. self._clear_readahead_buf()
  80. return rv
  81. if getattr(file, "truncate", None): # not all OSes implement truncate
  82. def truncate(self, size=-1):
  83. self.flush()
  84. if size == -1:
  85. size = self.tell()
  86. try:
  87. rv = os.ftruncate(self.fileno(), size)
  88. except OSError as e:
  89. raise IOError(*e.args)
  90. else:
  91. self.seek(size) # move position&clear buffer
  92. return rv
  93. def isatty(self):
  94. try:
  95. return os.isatty(self.fileno())
  96. except OSError as e:
  97. raise IOError(*e.args)
  98. class _SocketDuckForFd(object):
  99. """Class implementing all socket method used by _fileobject
  100. in cooperative manner using low level os I/O calls.
  101. """
  102. _refcount = 0
  103. def __init__(self, fileno):
  104. self._fileno = fileno
  105. notify_opened(fileno)
  106. self._closed = False
  107. def _trampoline(self, fd, read=False, write=False, timeout=None, timeout_exc=None):
  108. if self._closed:
  109. # Don't trampoline if we're already closed.
  110. raise IOClosed()
  111. try:
  112. return trampoline(fd, read=read, write=write, timeout=timeout,
  113. timeout_exc=timeout_exc,
  114. mark_as_closed=self._mark_as_closed)
  115. except IOClosed:
  116. # Our fileno has been obsoleted. Defang ourselves to
  117. # prevent spurious closes.
  118. self._mark_as_closed()
  119. raise
  120. def _mark_as_closed(self):
  121. current = self._closed
  122. self._closed = True
  123. return current
  124. @property
  125. def _sock(self):
  126. return self
  127. def fileno(self):
  128. return self._fileno
  129. def recv(self, buflen):
  130. while True:
  131. try:
  132. data = os.read(self._fileno, buflen)
  133. return data
  134. except OSError as e:
  135. if get_errno(e) not in SOCKET_BLOCKING:
  136. raise IOError(*e.args)
  137. self._trampoline(self, read=True)
  138. def recv_into(self, buf, nbytes=0, flags=0):
  139. if nbytes == 0:
  140. nbytes = len(buf)
  141. data = self.recv(nbytes)
  142. buf[:nbytes] = data
  143. return len(data)
  144. def send(self, data):
  145. while True:
  146. try:
  147. return os.write(self._fileno, data)
  148. except OSError as e:
  149. if get_errno(e) not in SOCKET_BLOCKING:
  150. raise IOError(*e.args)
  151. else:
  152. trampoline(self, write=True)
  153. def sendall(self, data):
  154. len_data = len(data)
  155. os_write = os.write
  156. fileno = self._fileno
  157. try:
  158. total_sent = os_write(fileno, data)
  159. except OSError as e:
  160. if get_errno(e) != errno.EAGAIN:
  161. raise IOError(*e.args)
  162. total_sent = 0
  163. while total_sent < len_data:
  164. self._trampoline(self, write=True)
  165. try:
  166. total_sent += os_write(fileno, data[total_sent:])
  167. except OSError as e:
  168. if get_errno(e) != errno. EAGAIN:
  169. raise IOError(*e.args)
  170. def __del__(self):
  171. self._close()
  172. def _close(self):
  173. was_closed = self._mark_as_closed()
  174. if was_closed:
  175. return
  176. notify_close(self._fileno)
  177. try:
  178. os.close(self._fileno)
  179. except:
  180. # os.close may fail if __init__ didn't complete
  181. # (i.e file dscriptor passed to popen was invalid
  182. pass
  183. def __repr__(self):
  184. return "%s:%d" % (self.__class__.__name__, self._fileno)
  185. def _reuse(self):
  186. self._refcount += 1
  187. def _drop(self):
  188. self._refcount -= 1
  189. if self._refcount == 0:
  190. self._close()