diff options
author | Alexandre Vassalotti <alexandre@peadrop.com> | 2008-05-06 19:48:38 (GMT) |
---|---|---|
committer | Alexandre Vassalotti <alexandre@peadrop.com> | 2008-05-06 19:48:38 (GMT) |
commit | 77250f4df7a73a5c87d12d781a562747a855cd95 (patch) | |
tree | 61f83259cd270f6011c75cbe95d2a25d12c29f87 /Lib/io.py | |
parent | 5d8da20dd1034081906dbdffea9c77bf39353dec (diff) | |
download | cpython-77250f4df7a73a5c87d12d781a562747a855cd95.zip cpython-77250f4df7a73a5c87d12d781a562747a855cd95.tar.gz cpython-77250f4df7a73a5c87d12d781a562747a855cd95.tar.bz2 |
Added fast alternate io.BytesIO implementation and its test suite.
Removed old test suite for StringIO.
Modified truncate() to imply a seek to given argument value.
Diffstat (limited to 'Lib/io.py')
-rw-r--r-- | Lib/io.py | 75 |
1 files changed, 66 insertions, 9 deletions
@@ -490,6 +490,7 @@ class IOBase(metaclass=abc.ABCMeta): terminator(s) recognized. """ # For backwards compatibility, a (slowish) readline(). + self._checkClosed() if hasattr(self, "peek"): def nreadahead(): readahead = self.peek(1) @@ -531,7 +532,7 @@ class IOBase(metaclass=abc.ABCMeta): lines will be read if the total size (in bytes/characters) of all lines so far exceeds hint. """ - if hint is None: + if hint is None or hint <= 0: return list(self) n = 0 lines = [] @@ -726,6 +727,8 @@ class _BufferedIOMixin(BufferedIOBase): if pos is None: pos = self.tell() + # XXX: Should seek() be used, instead of passing the position + # XXX directly to truncate? return self.raw.truncate(pos) ### Flush and close ### @@ -765,7 +768,7 @@ class _BufferedIOMixin(BufferedIOBase): return self.raw.isatty() -class BytesIO(BufferedIOBase): +class _BytesIO(BufferedIOBase): """Buffered I/O implementation using an in-memory bytes buffer.""" @@ -779,13 +782,19 @@ class BytesIO(BufferedIOBase): def getvalue(self): """Return the bytes value (contents) of the buffer """ + if self.closed: + raise ValueError("getvalue on closed file") return bytes(self._buffer) def read(self, n=None): + if self.closed: + raise ValueError("read from closed file") if n is None: n = -1 if n < 0: n = len(self._buffer) + if len(self._buffer) <= self._pos: + return self._buffer[:0] newpos = min(len(self._buffer), self._pos + n) b = self._buffer[self._pos : newpos] self._pos = newpos @@ -802,6 +811,8 @@ class BytesIO(BufferedIOBase): if isinstance(b, str): raise TypeError("can't write str to binary stream") n = len(b) + if n == 0: + return 0 newpos = self._pos + n if newpos > len(self._buffer): # Inserts null bytes between the current end of the file @@ -813,28 +824,38 @@ class BytesIO(BufferedIOBase): return n def seek(self, pos, whence=0): + if self.closed: + raise ValueError("seek on closed file") try: pos = pos.__index__() except AttributeError as err: raise TypeError("an integer is required") from err if whence == 0: self._pos = max(0, pos) + if pos < 0: + raise ValueError("negative seek position %r" % (pos,)) elif whence == 1: self._pos = max(0, self._pos + pos) elif whence == 2: self._pos = max(0, len(self._buffer) + pos) else: - raise IOError("invalid whence value") + raise ValueError("invalid whence value") return self._pos def tell(self): + if self.closed: + raise ValueError("tell on closed file") return self._pos def truncate(self, pos=None): + if self.closed: + raise ValueError("truncate on closed file") if pos is None: pos = self._pos + elif pos < 0: + raise ValueError("negative truncate position %r" % (pos,)) del self._buffer[pos:] - return pos + return self.seek(pos) def readable(self): return True @@ -845,6 +866,16 @@ class BytesIO(BufferedIOBase): def seekable(self): return True +# Use the faster implementation of BytesIO if available +try: + import _bytesio + + class BytesIO(_bytesio._BytesIO, BufferedIOBase): + __doc__ = _bytesio._BytesIO.__doc__ + +except ImportError: + BytesIO = _BytesIO + class BufferedReader(_BufferedIOMixin): @@ -978,6 +1009,12 @@ class BufferedWriter(_BufferedIOMixin): raise BlockingIOError(e.errno, e.strerror, overage) return written + def truncate(self, pos=None): + self.flush() + if pos is None: + pos = self.raw.tell() + return self.raw.truncate(pos) + def flush(self): if self.closed: raise ValueError("flush of closed file") @@ -1097,6 +1134,13 @@ class BufferedRandom(BufferedWriter, BufferedReader): else: return self.raw.tell() - len(self._read_buf) + def truncate(self, pos=None): + if pos is None: + pos = self.tell() + # Use seek to flush the read buffer. + self.seek(pos) + return BufferedWriter.truncate(self) + def read(self, n=None): if n is None: n = -1 @@ -1145,11 +1189,7 @@ class TextIOBase(IOBase): def truncate(self, pos: int = None) -> int: """Truncate size to pos.""" - self.flush() - if pos is None: - pos = self.tell() - self.seek(pos) - return self.buffer.truncate() + self._unsupported("truncate") def readline(self) -> str: """Read until newline or EOF. @@ -1346,6 +1386,12 @@ class TextIOWrapper(TextIOBase): def seekable(self): return self._seekable + def readable(self): + return self.buffer.readable() + + def writable(self): + return self.buffer.writable() + def flush(self): self.buffer.flush() self._telling = self._seekable @@ -1539,7 +1585,16 @@ class TextIOWrapper(TextIOBase): finally: decoder.setstate(saved_state) + def truncate(self, pos=None): + self.flush() + if pos is None: + pos = self.tell() + self.seek(pos) + return self.buffer.truncate() + def seek(self, cookie, whence=0): + if self.closed: + raise ValueError("tell on closed file") if not self._seekable: raise IOError("underlying stream is not seekable") if whence == 1: # seek relative to current position @@ -1626,6 +1681,8 @@ class TextIOWrapper(TextIOBase): return line def readline(self, limit=None): + if self.closed: + raise ValueError("read from closed file") if limit is None: limit = -1 |