diff options
Diffstat (limited to 'Lib/_pyio.py')
-rw-r--r-- | Lib/_pyio.py | 105 |
1 files changed, 92 insertions, 13 deletions
diff --git a/Lib/_pyio.py b/Lib/_pyio.py index d4cfb6e..d23b032 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -6,6 +6,7 @@ import os import abc import codecs import errno +import array # Import _thread instead of threading to reduce startup cost try: from _thread import allocate_lock as Lock @@ -662,16 +663,33 @@ class BufferedIOBase(IOBase): Raises BlockingIOError if the underlying raw stream has no data at the moment. """ - # XXX This ought to work with anything that supports the buffer API - data = self.read(len(b)) + + return self._readinto(b, read1=False) + + def readinto1(self, b): + """Read up to len(b) bytes into *b*, using at most one system call + + Returns an int representing the number of bytes read (0 for EOF). + + Raises BlockingIOError if the underlying raw stream has no + data at the moment. + """ + + return self._readinto(b, read1=True) + + def _readinto(self, b, read1): + if not isinstance(b, memoryview): + b = memoryview(b) + b = b.cast('B') + + if read1: + data = self.read1(len(b)) + else: + 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', data) + + b[:n] = data + return n def write(self, b): @@ -790,13 +808,14 @@ class _BufferedIOMixin(BufferedIOBase): .format(self.__class__.__name__)) def __repr__(self): - clsname = self.__class__.__name__ + modname = self.__class__.__module__ + clsname = self.__class__.__qualname__ try: name = self.name except AttributeError: - return "<_pyio.{0}>".format(clsname) + return "<{}.{}>".format(modname, clsname) else: - return "<_pyio.{0} name={1!r}>".format(clsname, name) + return "<{}.{} name={!r}>".format(modname, clsname, name) ### Lower-level APIs ### @@ -1065,6 +1084,58 @@ class BufferedReader(_BufferedIOMixin): return self._read_unlocked( min(size, len(self._read_buf) - self._read_pos)) + # Implementing readinto() and readinto1() is not strictly necessary (we + # could rely on the base class that provides an implementation in terms of + # read() and read1()). We do it anyway to keep the _pyio implementation + # similar to the io implementation (which implements the methods for + # performance reasons). + def _readinto(self, buf, read1): + """Read data into *buf* with at most one system call.""" + + if len(buf) == 0: + return 0 + + # Need to create a memoryview object of type 'b', otherwise + # we may not be able to assign bytes to it, and slicing it + # would create a new object. + if not isinstance(buf, memoryview): + buf = memoryview(buf) + buf = buf.cast('B') + + written = 0 + with self._read_lock: + while written < len(buf): + + # First try to read from internal buffer + avail = min(len(self._read_buf) - self._read_pos, len(buf)) + if avail: + buf[written:written+avail] = \ + self._read_buf[self._read_pos:self._read_pos+avail] + self._read_pos += avail + written += avail + if written == len(buf): + break + + # If remaining space in callers buffer is larger than + # internal buffer, read directly into callers buffer + if len(buf) - written > self.buffer_size: + n = self.raw.readinto(buf[written:]) + if not n: + break # eof + written += n + + # Otherwise refill internal buffer - unless we're + # in read1 mode and already got some data + elif not (read1 and written): + if not self._peek_unlocked(1): + break # eof + + # In readinto1 mode, return as soon as we have some data + if read1 and written: + break + + return written + def tell(self): return _BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos @@ -1214,6 +1285,9 @@ class BufferedRWPair(BufferedIOBase): def read1(self, size): return self.reader.read1(size) + def readinto1(self, b): + return self.reader.readinto1(b) + def readable(self): return self.reader.readable() @@ -1296,6 +1370,10 @@ class BufferedRandom(BufferedWriter, BufferedReader): self.flush() return BufferedReader.read1(self, size) + def readinto1(self, b): + self.flush() + return BufferedReader.readinto1(self, b) + def write(self, b): if self._read_buf: # Undo readahead @@ -1558,7 +1636,8 @@ class TextIOWrapper(TextIOBase): # - "chars_..." for integer variables that count decoded characters def __repr__(self): - result = "<_pyio.TextIOWrapper" + result = "<{}.{}".format(self.__class__.__module__, + self.__class__.__qualname__) try: name = self.name except AttributeError: |