_fileobjectposix.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. from __future__ import absolute_import
  2. import os
  3. import io
  4. from io import BufferedReader
  5. from io import BufferedWriter
  6. from io import BytesIO
  7. from io import DEFAULT_BUFFER_SIZE
  8. from io import RawIOBase
  9. from io import UnsupportedOperation
  10. from gevent._fileobjectcommon import cancel_wait_ex
  11. from gevent._fileobjectcommon import FileObjectBase
  12. from gevent.hub import get_hub
  13. from gevent.os import _read
  14. from gevent.os import _write
  15. from gevent.os import ignored_errors
  16. from gevent.os import make_nonblocking
  17. class GreenFileDescriptorIO(RawIOBase):
  18. # Note that RawIOBase has a __del__ method that calls
  19. # self.close(). (In C implementations like CPython, this is
  20. # the type's tp_dealloc slot; prior to Python 3, the object doesn't
  21. # appear to have a __del__ method, even though it functionally does)
  22. _read_event = None
  23. _write_event = None
  24. def __init__(self, fileno, mode='r', closefd=True):
  25. RawIOBase.__init__(self) # Python 2: pylint:disable=no-member,non-parent-init-called
  26. self._closed = False
  27. self._closefd = closefd
  28. self._fileno = fileno
  29. make_nonblocking(fileno)
  30. self._readable = 'r' in mode
  31. self._writable = 'w' in mode
  32. self.hub = get_hub()
  33. io_watcher = self.hub.loop.io
  34. if self._readable:
  35. self._read_event = io_watcher(fileno, 1)
  36. if self._writable:
  37. self._write_event = io_watcher(fileno, 2)
  38. self._seekable = None
  39. def readable(self):
  40. return self._readable
  41. def writable(self):
  42. return self._writable
  43. def seekable(self):
  44. if self._seekable is None:
  45. try:
  46. os.lseek(self._fileno, 0, os.SEEK_CUR)
  47. except OSError:
  48. self._seekable = False
  49. else:
  50. self._seekable = True
  51. return self._seekable
  52. def fileno(self):
  53. return self._fileno
  54. @property
  55. def closed(self):
  56. return self._closed
  57. def close(self):
  58. if self._closed:
  59. return
  60. self.flush()
  61. self._closed = True
  62. if self._readable:
  63. self.hub.cancel_wait(self._read_event, cancel_wait_ex)
  64. if self._writable:
  65. self.hub.cancel_wait(self._write_event, cancel_wait_ex)
  66. fileno = self._fileno
  67. if self._closefd:
  68. self._fileno = None
  69. os.close(fileno)
  70. # RawIOBase provides a 'read' method that will call readall() if
  71. # the `size` was missing or -1 and otherwise call readinto(). We
  72. # want to take advantage of this to avoid single byte reads when
  73. # possible. This is highlighted by a bug in BufferedIOReader that
  74. # calls read() in a loop when its readall() method is invoked;
  75. # this was fixed in Python 3.3. See
  76. # https://github.com/gevent/gevent/issues/675)
  77. def __read(self, n):
  78. if not self._readable:
  79. raise UnsupportedOperation('read')
  80. while True:
  81. try:
  82. return _read(self._fileno, n)
  83. except (IOError, OSError) as ex:
  84. if ex.args[0] not in ignored_errors:
  85. raise
  86. self.hub.wait(self._read_event)
  87. def readall(self):
  88. ret = BytesIO()
  89. while True:
  90. data = self.__read(DEFAULT_BUFFER_SIZE)
  91. if not data:
  92. break
  93. ret.write(data)
  94. return ret.getvalue()
  95. def readinto(self, b):
  96. data = self.__read(len(b))
  97. n = len(data)
  98. try:
  99. b[:n] = data
  100. except TypeError as err:
  101. import array
  102. if not isinstance(b, array.array):
  103. raise err
  104. b[:n] = array.array(b'b', data)
  105. return n
  106. def write(self, b):
  107. if not self._writable:
  108. raise UnsupportedOperation('write')
  109. while True:
  110. try:
  111. return _write(self._fileno, b)
  112. except (IOError, OSError) as ex:
  113. if ex.args[0] not in ignored_errors:
  114. raise
  115. self.hub.wait(self._write_event)
  116. def seek(self, offset, whence=0):
  117. return os.lseek(self._fileno, offset, whence)
  118. class FlushingBufferedWriter(BufferedWriter):
  119. def write(self, b):
  120. ret = BufferedWriter.write(self, b)
  121. self.flush()
  122. return ret
  123. class FileObjectPosix(FileObjectBase):
  124. """
  125. A file-like object that operates on non-blocking files but
  126. provides a synchronous, cooperative interface.
  127. .. caution::
  128. This object is only effective wrapping files that can be used meaningfully
  129. with :func:`select.select` such as sockets and pipes.
  130. In general, on most platforms, operations on regular files
  131. (e.g., ``open('a_file.txt')``) are considered non-blocking
  132. already, even though they can take some time to complete as
  133. data is copied to the kernel and flushed to disk: this time
  134. is relatively bounded compared to sockets or pipes, though.
  135. A :func:`~os.read` or :func:`~os.write` call on such a file
  136. will still effectively block for some small period of time.
  137. Therefore, wrapping this class around a regular file is
  138. unlikely to make IO gevent-friendly: reading or writing large
  139. amounts of data could still block the event loop.
  140. If you'll be working with regular files and doing IO in large
  141. chunks, you may consider using
  142. :class:`~gevent.fileobject.FileObjectThread` or
  143. :func:`~gevent.os.tp_read` and :func:`~gevent.os.tp_write` to bypass this
  144. concern.
  145. .. note::
  146. Random read/write (e.g., ``mode='rwb'``) is not supported.
  147. For that, use :class:`io.BufferedRWPair` around two instance of this
  148. class.
  149. .. tip::
  150. Although this object provides a :meth:`fileno` method and so
  151. can itself be passed to :func:`fcntl.fcntl`, setting the
  152. :data:`os.O_NONBLOCK` flag will have no effect (reads will
  153. still block the greenlet, although other greenlets can run).
  154. However, removing that flag *will cause this object to no
  155. longer be cooperative* (other greenlets will no longer run).
  156. You can use the internal ``fileio`` attribute of this object
  157. (a :class:`io.RawIOBase`) to perform non-blocking byte reads.
  158. Note, however, that once you begin directly using this
  159. attribute, the results from using methods of *this* object
  160. are undefined, especially in text mode. (See :issue:`222`.)
  161. .. versionchanged:: 1.1
  162. Now uses the :mod:`io` package internally. Under Python 2, previously
  163. used the undocumented class :class:`socket._fileobject`. This provides
  164. better file-like semantics (and portability to Python 3).
  165. .. versionchanged:: 1.2a1
  166. Document the ``fileio`` attribute for non-blocking reads.
  167. """
  168. #: platform specific default for the *bufsize* parameter
  169. default_bufsize = io.DEFAULT_BUFFER_SIZE
  170. def __init__(self, fobj, mode='rb', bufsize=-1, close=True):
  171. """
  172. :param fobj: Either an integer fileno, or an object supporting the
  173. usual :meth:`socket.fileno` method. The file *will* be
  174. put in non-blocking mode using :func:`gevent.os.make_nonblocking`.
  175. :keyword str mode: The manner of access to the file, one of "rb", "rU" or "wb"
  176. (where the "b" or "U" can be omitted).
  177. If "U" is part of the mode, IO will be done on text, otherwise bytes.
  178. :keyword int bufsize: If given, the size of the buffer to use. The default
  179. value means to use a platform-specific default
  180. Other values are interpreted as for the :mod:`io` package.
  181. Buffering is ignored in text mode.
  182. .. versionchanged:: 1.2a1
  183. A bufsize of 0 in write mode is no longer forced to be 1.
  184. Instead, the underlying buffer is flushed after every write
  185. operation to simulate a bufsize of 0. In gevent 1.0, a
  186. bufsize of 0 was flushed when a newline was written, while
  187. in gevent 1.1 it was flushed when more than one byte was
  188. written. Note that this may have performance impacts.
  189. """
  190. if isinstance(fobj, int):
  191. fileno = fobj
  192. fobj = None
  193. else:
  194. fileno = fobj.fileno()
  195. if not isinstance(fileno, int):
  196. raise TypeError('fileno must be int: %r' % fileno)
  197. orig_mode = mode
  198. mode = (mode or 'rb').replace('b', '')
  199. if 'U' in mode:
  200. self._translate = True
  201. mode = mode.replace('U', '')
  202. else:
  203. self._translate = False
  204. if len(mode) != 1 and mode not in 'rw': # pragma: no cover
  205. # Python 3 builtin `open` raises a ValueError for invalid modes;
  206. # Python 2 ignores it. In the past, we raised an AssertionError, if __debug__ was
  207. # enabled (which it usually was). Match Python 3 because it makes more sense
  208. # and because __debug__ may not be enabled.
  209. # NOTE: This is preventing a mode like 'rwb' for binary random access;
  210. # that code was never tested and was explicitly marked as "not used"
  211. raise ValueError('mode can only be [rb, rU, wb], not %r' % (orig_mode,))
  212. self._fobj = fobj
  213. # This attribute is documented as available for non-blocking reads.
  214. self.fileio = GreenFileDescriptorIO(fileno, mode, closefd=close)
  215. self._orig_bufsize = bufsize
  216. if bufsize < 0 or bufsize == 1:
  217. bufsize = self.default_bufsize
  218. elif bufsize == 0:
  219. bufsize = 1
  220. if mode == 'r':
  221. IOFamily = BufferedReader
  222. else:
  223. assert mode == 'w'
  224. IOFamily = BufferedWriter
  225. if self._orig_bufsize == 0:
  226. # We could also simply pass self.fileio as *io*, but this way
  227. # we at least consistently expose a BufferedWriter in our *io*
  228. # attribute.
  229. IOFamily = FlushingBufferedWriter
  230. super(FileObjectPosix, self).__init__(IOFamily(self.fileio, bufsize), close)
  231. def _do_close(self, fobj, closefd):
  232. try:
  233. fobj.close()
  234. # self.fileio already knows whether or not to close the
  235. # file descriptor
  236. self.fileio.close()
  237. finally:
  238. self._fobj = None
  239. self.fileio = None
  240. def __iter__(self):
  241. return self._io