aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/_fileobjectposix.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/gevent/_fileobjectposix.py')
-rw-r--r--python/gevent/_fileobjectposix.py320
1 files changed, 0 insertions, 320 deletions
diff --git a/python/gevent/_fileobjectposix.py b/python/gevent/_fileobjectposix.py
deleted file mode 100644
index 93dd65f..0000000
--- a/python/gevent/_fileobjectposix.py
+++ /dev/null
@@ -1,320 +0,0 @@
-from __future__ import absolute_import
-import os
-import io
-from io import BufferedReader
-from io import BufferedWriter
-from io import BytesIO
-from io import DEFAULT_BUFFER_SIZE
-from io import RawIOBase
-from io import UnsupportedOperation
-
-from gevent._fileobjectcommon import cancel_wait_ex
-from gevent._fileobjectcommon import FileObjectBase
-from gevent.hub import get_hub
-from gevent.os import _read
-from gevent.os import _write
-from gevent.os import ignored_errors
-from gevent.os import make_nonblocking
-
-
-class GreenFileDescriptorIO(RawIOBase):
-
- # Note that RawIOBase has a __del__ method that calls
- # self.close(). (In C implementations like CPython, this is
- # the type's tp_dealloc slot; prior to Python 3, the object doesn't
- # appear to have a __del__ method, even though it functionally does)
-
- _read_event = None
- _write_event = None
- _closed = False
- _seekable = None
-
- def __init__(self, fileno, mode='r', closefd=True):
- RawIOBase.__init__(self) # Python 2: pylint:disable=no-member,non-parent-init-called
- self._closefd = closefd
- self._fileno = fileno
- make_nonblocking(fileno)
- readable = 'r' in mode
- writable = 'w' in mode
- self.hub = get_hub()
-
- io_watcher = self.hub.loop.io
- if readable:
- self._read_event = io_watcher(fileno, 1)
-
- if writable:
- self._write_event = io_watcher(fileno, 2)
-
- def readable(self):
- return self._read_event is not None
-
- def writable(self):
- return self._write_event is not None
-
- def seekable(self):
- if self._seekable is None:
- try:
- os.lseek(self._fileno, 0, os.SEEK_CUR)
- except OSError:
- self._seekable = False
- else:
- self._seekable = True
- return self._seekable
-
- def fileno(self):
- return self._fileno
-
- @property
- def closed(self):
- return self._closed
-
- def close(self):
- if self._closed:
- return
- self.flush()
- # TODO: Can we use 'read_event is not None and write_event is
- # not None' to mean _closed?
- self._closed = True
- read_event = self._read_event
- write_event = self._write_event
- self._read_event = self._write_event = None
-
- if read_event is not None:
- self.hub.cancel_wait(read_event, cancel_wait_ex, True)
- if write_event is not None:
- self.hub.cancel_wait(write_event, cancel_wait_ex, True)
-
- fileno = self._fileno
- if self._closefd:
- self._fileno = None
- os.close(fileno)
-
- # RawIOBase provides a 'read' method that will call readall() if
- # the `size` was missing or -1 and otherwise call readinto(). We
- # want to take advantage of this to avoid single byte reads when
- # possible. This is highlighted by a bug in BufferedIOReader that
- # calls read() in a loop when its readall() method is invoked;
- # this was fixed in Python 3.3, but we still need our workaround for 2.7. See
- # https://github.com/gevent/gevent/issues/675)
- def __read(self, n):
- if self._read_event is None:
- raise UnsupportedOperation('read')
- while True:
- try:
- return _read(self._fileno, n)
- except (IOError, OSError) as ex:
- if ex.args[0] not in ignored_errors:
- raise
- self.hub.wait(self._read_event)
-
- def readall(self):
- ret = BytesIO()
- while True:
- data = self.__read(DEFAULT_BUFFER_SIZE)
- if not data:
- break
- ret.write(data)
- return ret.getvalue()
-
- def readinto(self, b):
- data = self.__read(len(b))
- n = len(data)
- try:
- b[:n] = data
- except TypeError as err:
- import array
- if not isinstance(b, array.array):
- raise err
- b[:n] = array.array(b'b', data)
- return n
-
- def write(self, b):
- if self._write_event is None:
- raise UnsupportedOperation('write')
- while True:
- try:
- return _write(self._fileno, b)
- except (IOError, OSError) as ex:
- if ex.args[0] not in ignored_errors:
- raise
- self.hub.wait(self._write_event)
-
- def seek(self, offset, whence=0):
- return os.lseek(self._fileno, offset, whence)
-
-class FlushingBufferedWriter(BufferedWriter):
-
- def write(self, b):
- ret = BufferedWriter.write(self, b)
- self.flush()
- return ret
-
-class FileObjectPosix(FileObjectBase):
- """
- A file-like object that operates on non-blocking files but
- provides a synchronous, cooperative interface.
-
- .. caution::
- This object is only effective wrapping files that can be used meaningfully
- with :func:`select.select` such as sockets and pipes.
-
- In general, on most platforms, operations on regular files
- (e.g., ``open('a_file.txt')``) are considered non-blocking
- already, even though they can take some time to complete as
- data is copied to the kernel and flushed to disk: this time
- is relatively bounded compared to sockets or pipes, though.
- A :func:`~os.read` or :func:`~os.write` call on such a file
- will still effectively block for some small period of time.
- Therefore, wrapping this class around a regular file is
- unlikely to make IO gevent-friendly: reading or writing large
- amounts of data could still block the event loop.
-
- If you'll be working with regular files and doing IO in large
- chunks, you may consider using
- :class:`~gevent.fileobject.FileObjectThread` or
- :func:`~gevent.os.tp_read` and :func:`~gevent.os.tp_write` to bypass this
- concern.
-
- .. note::
- Random read/write (e.g., ``mode='rwb'``) is not supported.
- For that, use :class:`io.BufferedRWPair` around two instance of this
- class.
-
- .. tip::
- Although this object provides a :meth:`fileno` method and so
- can itself be passed to :func:`fcntl.fcntl`, setting the
- :data:`os.O_NONBLOCK` flag will have no effect (reads will
- still block the greenlet, although other greenlets can run).
- However, removing that flag *will cause this object to no
- longer be cooperative* (other greenlets will no longer run).
-
- You can use the internal ``fileio`` attribute of this object
- (a :class:`io.RawIOBase`) to perform non-blocking byte reads.
- Note, however, that once you begin directly using this
- attribute, the results from using methods of *this* object
- are undefined, especially in text mode. (See :issue:`222`.)
-
- .. versionchanged:: 1.1
- Now uses the :mod:`io` package internally. Under Python 2, previously
- used the undocumented class :class:`socket._fileobject`. This provides
- better file-like semantics (and portability to Python 3).
- .. versionchanged:: 1.2a1
- Document the ``fileio`` attribute for non-blocking reads.
- """
-
- #: platform specific default for the *bufsize* parameter
- default_bufsize = io.DEFAULT_BUFFER_SIZE
-
- def __init__(self, fobj, mode='rb', bufsize=-1, close=True):
- """
- :param fobj: Either an integer fileno, or an object supporting the
- usual :meth:`socket.fileno` method. The file *will* be
- put in non-blocking mode using :func:`gevent.os.make_nonblocking`.
- :keyword str mode: The manner of access to the file, one of "rb", "rU" or "wb"
- (where the "b" or "U" can be omitted).
- If "U" is part of the mode, universal newlines will be used. On Python 2,
- if 't' is not in the mode, this will result in returning byte (native) strings;
- putting 't' in the mode will return text strings. This may cause
- :exc:`UnicodeDecodeError` to be raised.
- :keyword int bufsize: If given, the size of the buffer to use. The default
- value means to use a platform-specific default
- Other values are interpreted as for the :mod:`io` package.
- Buffering is ignored in text mode.
-
- .. versionchanged:: 1.3a1
-
- On Python 2, enabling universal newlines no longer forces unicode
- IO.
-
- .. versionchanged:: 1.2a1
-
- A bufsize of 0 in write mode is no longer forced to be 1.
- Instead, the underlying buffer is flushed after every write
- operation to simulate a bufsize of 0. In gevent 1.0, a
- bufsize of 0 was flushed when a newline was written, while
- in gevent 1.1 it was flushed when more than one byte was
- written. Note that this may have performance impacts.
- """
-
- if isinstance(fobj, int):
- fileno = fobj
- fobj = None
- else:
- fileno = fobj.fileno()
- if not isinstance(fileno, int):
- raise TypeError('fileno must be int: %r' % fileno)
-
- orig_mode = mode
- mode = (mode or 'rb').replace('b', '')
- if 'U' in mode:
- self._translate = True
- if bytes is str and 't' not in mode:
- # We're going to be producing unicode objects, but
- # universal newlines doesn't do that in the stdlib,
- # so fix that to return str objects. The fix is two parts:
- # first, set an encoding on the stream that can round-trip
- # all bytes, and second, decode all bytes once they've been read.
- self._translate_encoding = 'latin-1'
- import functools
-
- def wrap_method(m):
- if m.__name__.startswith("read"):
- @functools.wraps(m)
- def wrapped(*args, **kwargs):
- result = m(*args, **kwargs)
- assert isinstance(result, unicode) # pylint:disable=undefined-variable
- return result.encode('latin-1')
- return wrapped
- return m
- self._wrap_method = wrap_method
- mode = mode.replace('U', '')
- else:
- self._translate = False
-
- mode = mode.replace('t', '')
-
- if len(mode) != 1 and mode not in 'rw': # pragma: no cover
- # Python 3 builtin `open` raises a ValueError for invalid modes;
- # Python 2 ignores it. In the past, we raised an AssertionError, if __debug__ was
- # enabled (which it usually was). Match Python 3 because it makes more sense
- # and because __debug__ may not be enabled.
- # NOTE: This is preventing a mode like 'rwb' for binary random access;
- # that code was never tested and was explicitly marked as "not used"
- raise ValueError('mode can only be [rb, rU, wb], not %r' % (orig_mode,))
-
- self._fobj = fobj
-
- # This attribute is documented as available for non-blocking reads.
- self.fileio = GreenFileDescriptorIO(fileno, mode, closefd=close)
-
- self._orig_bufsize = bufsize
- if bufsize < 0 or bufsize == 1:
- bufsize = self.default_bufsize
- elif bufsize == 0:
- bufsize = 1
-
- if mode == 'r':
- IOFamily = BufferedReader
- else:
- assert mode == 'w'
- IOFamily = BufferedWriter
- if self._orig_bufsize == 0:
- # We could also simply pass self.fileio as *io*, but this way
- # we at least consistently expose a BufferedWriter in our *io*
- # attribute.
- IOFamily = FlushingBufferedWriter
-
- super(FileObjectPosix, self).__init__(IOFamily(self.fileio, bufsize), close)
-
- def _do_close(self, fobj, closefd):
- try:
- fobj.close()
- # self.fileio already knows whether or not to close the
- # file descriptor
- self.fileio.close()
- finally:
- self._fobj = None
- self.fileio = None
-
- def __iter__(self):
- return self._io