Kaydet (Commit) 1aed624f authored tarafından Alexandre Vassalotti's avatar Alexandre Vassalotti

Backport fast alternate io.BytesIO implementation.

Merged r62778, r62779, r62802, r62806, r62807, r62808, r62809, r62844,
r62846, r62952, r62956.
üst 81673b7b
......@@ -494,6 +494,7 @@ class IOBase(object):
files, the newlines argument to open can be used to select the line
terminator(s) recognized.
"""
self._checkClosed()
if hasattr(self, "peek"):
def nreadahead():
readahead = self.peek(1)
......@@ -508,6 +509,8 @@ class IOBase(object):
return 1
if limit is None:
limit = -1
if not isinstance(limit, (int, long)):
raise TypeError("limit must be an integer")
res = bytearray()
while limit < 0 or len(res) < limit:
b = self.read(nreadahead())
......@@ -536,6 +539,10 @@ class IOBase(object):
lines so far exceeds hint.
"""
if hint is None:
hint = -1
if not isinstance(hint, (int, long)):
raise TypeError("hint must be an integer")
if hint <= 0:
return list(self)
n = 0
lines = []
......@@ -686,7 +693,7 @@ class BufferedIOBase(IOBase):
import array
if not isinstance(b, array.array):
raise err
b[:n] = array.array('b', data)
b[:n] = array.array(b'b', data)
return n
def write(self, b):
......@@ -729,6 +736,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 ###
......@@ -768,7 +777,7 @@ class _BufferedIOMixin(BufferedIOBase):
return self.raw.isatty()
class BytesIO(BufferedIOBase):
class _BytesIO(BufferedIOBase):
"""Buffered I/O implementation using an in-memory bytes buffer."""
......@@ -777,20 +786,28 @@ class BytesIO(BufferedIOBase):
def __init__(self, initial_bytes=None):
buf = bytearray()
if initial_bytes is not None:
buf += initial_bytes
buf += bytearray(initial_bytes)
self._buffer = buf
self._pos = 0
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 not isinstance(n, (int, long)):
raise TypeError("argument must be an integer")
if n < 0:
n = len(self._buffer)
if len(self._buffer) <= self._pos:
return b""
newpos = min(len(self._buffer), self._pos + n)
b = self._buffer[self._pos : newpos]
self._pos = newpos
......@@ -807,6 +824,8 @@ class BytesIO(BufferedIOBase):
if isinstance(b, unicode):
raise TypeError("can't write unicode 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
......@@ -818,28 +837,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,))
self._pos = 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
......@@ -850,6 +879,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):
......@@ -983,6 +1022,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")
......@@ -1102,6 +1147,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
......@@ -1150,11 +1202,7 @@ class TextIOBase(IOBase):
def truncate(self, pos = None):
"""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):
"""Read until newline or EOF.
......@@ -1351,6 +1399,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
......@@ -1542,7 +1596,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
......@@ -1629,8 +1692,12 @@ 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
if not isinstance(limit, (int, long)):
raise TypeError("limit must be an integer")
# Grab all the decoded text (we will rewind any extra bits later).
line = self._get_decoded_chars()
......
......@@ -97,7 +97,7 @@ class IOTest(unittest.TestCase):
self.assertEqual(f.seek(-1, 2), 13)
self.assertEqual(f.tell(), 13)
self.assertEqual(f.truncate(12), 12)
self.assertEqual(f.tell(), 13)
self.assertEqual(f.tell(), 12)
self.assertRaises(TypeError, f.seek, 0.0)
def read_ops(self, f, buffered=False):
......@@ -142,7 +142,7 @@ class IOTest(unittest.TestCase):
self.assertEqual(f.tell(), self.LARGE + 2)
self.assertEqual(f.seek(0, 2), self.LARGE + 2)
self.assertEqual(f.truncate(self.LARGE + 1), self.LARGE + 1)
self.assertEqual(f.tell(), self.LARGE + 2)
self.assertEqual(f.tell(), self.LARGE + 1)
self.assertEqual(f.seek(0, 2), self.LARGE + 1)
self.assertEqual(f.seek(-1, 2), self.LARGE)
self.assertEqual(f.read(2), b"x")
......@@ -726,6 +726,7 @@ class TextIOWrapperTest(unittest.TestCase):
txt.write("BB\nCCC\n")
txt.write("X\rY\r\nZ")
txt.flush()
self.assertEquals(buf.closed, False)
self.assertEquals(buf.getvalue(), expected)
def testNewlines(self):
......@@ -806,7 +807,8 @@ class TextIOWrapperTest(unittest.TestCase):
txt = io.TextIOWrapper(buf, encoding="ascii", newline=newline)
txt.write(data)
txt.close()
self.assertEquals(buf.getvalue(), expected)
self.assertEquals(buf.closed, True)
self.assertRaises(ValueError, buf.getvalue)
finally:
os.linesep = save_linesep
......
This diff is collapsed.
This diff is collapsed.
......@@ -552,11 +552,10 @@ portable_lseek(int fd, PyObject *posobj, int whence)
PyErr_SetString(PyExc_TypeError, "an integer is required");
return NULL;
}
#if !defined(HAVE_LARGEFILE_SUPPORT)
pos = PyLong_AsLong(posobj);
#if defined(HAVE_LARGEFILE_SUPPORT)
pos = PyLong_AsLongLong(posobj);
#else
pos = PyLong_Check(posobj) ?
PyLong_AsLongLong(posobj) : PyLong_AsLong(posobj);
pos = PyLong_AsLong(posobj);
#endif
if (PyErr_Occurred())
return NULL;
......@@ -572,10 +571,10 @@ portable_lseek(int fd, PyObject *posobj, int whence)
if (res < 0)
return PyErr_SetFromErrno(PyExc_IOError);
#if !defined(HAVE_LARGEFILE_SUPPORT)
return PyLong_FromLong(res);
#else
#if defined(HAVE_LARGEFILE_SUPPORT)
return PyLong_FromLongLong(res);
#else
return PyLong_FromLong(res);
#endif
}
......@@ -622,48 +621,29 @@ fileio_truncate(PyFileIOObject *self, PyObject *args)
return NULL;
if (posobj == Py_None || posobj == NULL) {
/* Get the current position. */
posobj = portable_lseek(fd, NULL, 1);
if (posobj == NULL)
return NULL;
return NULL;
}
else {
Py_INCREF(posobj);
/* Move to the position to be truncated. */
posobj = portable_lseek(fd, posobj, 0);
}
#if !defined(HAVE_LARGEFILE_SUPPORT)
pos = PyLong_AsLong(posobj);
#if defined(HAVE_LARGEFILE_SUPPORT)
pos = PyLong_AsLongLong(posobj);
#else
pos = PyLong_Check(posobj) ?
PyLong_AsLongLong(posobj) : PyLong_AsLong(posobj);
pos = PyLong_AsLong(posobj);
#endif
if (PyErr_Occurred()) {
Py_DECREF(posobj);
if (PyErr_Occurred())
return NULL;
}
#ifdef MS_WINDOWS
/* MS _chsize doesn't work if newsize doesn't fit in 32 bits,
so don't even try using it. */
{
HANDLE hFile;
PyObject *pos2, *oldposobj;
/* store the current position */
oldposobj = portable_lseek(self->fd, NULL, 1);
if (oldposobj == NULL) {
Py_DECREF(posobj);
return NULL;
}
/* Have to move current pos to desired endpoint on Windows. */
errno = 0;
pos2 = portable_lseek(fd, posobj, SEEK_SET);
if (pos2 == NULL) {
Py_DECREF(posobj);
Py_DECREF(oldposobj);
return NULL;
}
Py_DECREF(pos2);
/* Truncate. Note that this may grow the file! */
Py_BEGIN_ALLOW_THREADS
......@@ -676,18 +656,6 @@ fileio_truncate(PyFileIOObject *self, PyObject *args)
errno = EACCES;
}
Py_END_ALLOW_THREADS
if (ret == 0) {
/* Move to the previous position in the file */
pos2 = portable_lseek(fd, oldposobj, SEEK_SET);
if (pos2 == NULL) {
Py_DECREF(posobj);
Py_DECREF(oldposobj);
return NULL;
}
}
Py_DECREF(pos2);
Py_DECREF(oldposobj);
}
#else
Py_BEGIN_ALLOW_THREADS
......@@ -697,7 +665,6 @@ fileio_truncate(PyFileIOObject *self, PyObject *args)
#endif /* !MS_WINDOWS */
if (ret != 0) {
Py_DECREF(posobj);
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
......@@ -791,7 +758,8 @@ PyDoc_STRVAR(seek_doc,
PyDoc_STRVAR(truncate_doc,
"truncate([size: int]) -> None. Truncate the file to at most size bytes.\n"
"\n"
"Size defaults to the current file position, as returned by tell().");
"Size defaults to the current file position, as returned by tell()."
"The current file position is changed to the value of size.");
#endif
PyDoc_STRVAR(tell_doc,
......
......@@ -315,8 +315,8 @@ bytes_iconcat(PyBytesObject *self, PyObject *other)
Py_buffer vo;
if (_getbuffer(other, &vo) < 0) {
PyErr_Format(PyExc_TypeError, "can't concat bytes to %.100s",
Py_TYPE(self)->tp_name);
PyErr_Format(PyExc_TypeError, "can't concat %.100s to %.100s",
Py_TYPE(other)->tp_name, Py_TYPE(self)->tp_name);
return NULL;
}
......
......@@ -990,6 +990,10 @@
RelativePath="..\..\Modules\_fileio.c"
>
</File>
<File
RelativePath="..\..\Modules\_bytesio.c"
>
</File>
<File
RelativePath="..\..\Modules\_functoolsmodule.c"
>
......
......@@ -53,6 +53,7 @@ extern void init_winreg(void);
extern void init_struct(void);
extern void initdatetime(void);
extern void init_fileio(void);
extern void init_bytesio(void);
extern void init_functools(void);
extern void init_json(void);
extern void initzlib(void);
......@@ -132,6 +133,7 @@ struct _inittab _PyImport_Inittab[] = {
{"_struct", init_struct},
{"datetime", initdatetime},
{"_fileio", init_fileio},
{"_bytesio", init_bytesio},
{"_functools", init_functools},
{"_json", init_json},
......
......@@ -994,6 +994,10 @@
RelativePath="..\Modules\_fileio.c"
>
</File>
<File
RelativePath="..\Modules\_bytesio.c"
>
</File>
<File
RelativePath="..\Modules\_functoolsmodule.c"
>
......
......@@ -436,6 +436,8 @@ class PyBuildExt(build_ext):
exts.append( Extension('operator', ['operator.c']) )
# Python 3.0 _fileio module
exts.append( Extension("_fileio", ["_fileio.c"]) )
# Python 3.0 _bytesio module
exts.append( Extension("_bytesio", ["_bytesio.c"]) )
# _functools
exts.append( Extension("_functools", ["_functoolsmodule.c"]) )
# _json speedups
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment