13257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielr"""File-like objects that read from or write to a string buffer.
23257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
33257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielThis implements (nearly) all stdio methods.
43257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
53257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf = StringIO()      # ready for writing
63257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf = StringIO(buf)   # ready for reading
73257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf.close()           # explicitly release resources held
83257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielflag = f.isatty()   # always false
93257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielpos = f.tell()      # get current position
103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf.seek(pos)         # set current position
113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf.seek(pos, mode)   # mode 0: absolute; 1: relative; 2: relative to EOF
123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielbuf = f.read()      # read until EOF
133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielbuf = f.read(n)     # read up to n bytes
143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielbuf = f.readline()  # read until end of line ('\n') or EOF
153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniellist = f.readlines()# list of f.readline() results until EOF
163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf.truncate([size])  # truncate file at to at most size (default: current pos)
173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf.write(buf)        # write at current position
183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf.writelines(list)  # for line in list: f.write(line)
193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielf.getvalue()        # return whole file's contents as a string
203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielNotes:
223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel- Using a real file is often faster (but less convenient).
233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel- There's also a much faster implementation in C, called cStringIO, but
243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel  it's not subclassable.
253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel- fileno() is left unimplemented so that code which uses it triggers
263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel  an exception early.
273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel- Seeking far beyond EOF and then writing will insert real null
283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel  bytes that occupy space in the buffer.
293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel- There's a simple test set (see end of this file).
303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel"""
313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieltry:
323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    from errno import EINVAL
333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielexcept ImportError:
343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    EINVAL = 22
353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel__all__ = ["StringIO"]
373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef _complain_ifclosed(closed):
393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if closed:
403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        raise ValueError, "I/O operation on closed file"
413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielclass StringIO:
433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """class StringIO([buffer])
443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    When a StringIO object is created, it can be initialized to an existing
463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    string by passing the string to the constructor. If no string is given,
473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    the StringIO will start empty.
483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    The StringIO object can accept either Unicode or 8-bit strings, but
503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    mixing the two may take some care. If both are used, 8-bit strings that
513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    cannot be interpreted as 7-bit ASCII (that use the 8th bit) will cause
523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    a UnicodeError to be raised when getvalue() is called.
533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    """
543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def __init__(self, buf = ''):
553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # Force self.buf to be a string or unicode
563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not isinstance(buf, basestring):
573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            buf = str(buf)
583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.buf = buf
593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.len = len(buf)
603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.buflist = []
613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.pos = 0
623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.closed = False
633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.softspace = 0
643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def __iter__(self):
663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return self
673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def next(self):
693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """A file object is its own iterator, for example iter(f) returns f
703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        (unless f is closed). When a file is used as an iterator, typically
713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        in a for loop (for example, for line in f: print line), the next()
723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        method is called repeatedly. This method returns the next input line,
733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        or raises StopIteration when EOF is hit.
743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        r = self.readline()
773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not r:
783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            raise StopIteration
793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return r
803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def close(self):
823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Free the memory buffer.
833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not self.closed:
853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.closed = True
863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            del self.buf, self.pos
873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def isatty(self):
893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Returns False because StringIO objects are not connected to a
903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        tty-like device.
913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return False
943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def seek(self, pos, mode = 0):
963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Set the file's current position.
973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        The mode argument is optional and defaults to 0 (absolute file
993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        positioning); other values are 1 (seek relative to the current
1003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        position) and 2 (seek relative to the file's end).
1013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        There is no return value.
1033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
1043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
1053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.buflist:
1063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buf += ''.join(self.buflist)
1073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buflist = []
1083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if mode == 1:
1093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            pos += self.pos
1103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        elif mode == 2:
1113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            pos += self.len
1123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.pos = max(0, pos)
1133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def tell(self):
1153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Return the file's current position."""
1163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
1173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return self.pos
1183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def read(self, n = -1):
1203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Read at most size bytes from the file
1213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        (less if the read hits EOF before obtaining size bytes).
1223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        If the size argument is negative or omitted, read all data until EOF
1243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        is reached. The bytes are returned as a string object. An empty
1253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        string is returned when EOF is encountered immediately.
1263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
1273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
1283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.buflist:
1293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buf += ''.join(self.buflist)
1303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buflist = []
1313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if n is None or n < 0:
1323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            newpos = self.len
1333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
1343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            newpos = min(self.pos+n, self.len)
1353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        r = self.buf[self.pos:newpos]
1363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.pos = newpos
1373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return r
1383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def readline(self, length=None):
1403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        r"""Read one entire line from the file.
1413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        A trailing newline character is kept in the string (but may be absent
1433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        when a file ends with an incomplete line). If the size argument is
1443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        present and non-negative, it is a maximum byte count (including the
1453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        trailing newline) and an incomplete line may be returned.
1463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        An empty string is returned only when EOF is encountered immediately.
1483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        Note: Unlike stdio's fgets(), the returned string contains null
1503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        characters ('\0') if they occurred in the input.
1513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
1523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
1533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.buflist:
1543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buf += ''.join(self.buflist)
1553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buflist = []
1563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        i = self.buf.find('\n', self.pos)
1573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if i < 0:
1583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            newpos = self.len
1593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
1603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            newpos = i+1
1613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if length is not None and length >= 0:
1623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self.pos + length < newpos:
1633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                newpos = self.pos + length
1643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        r = self.buf[self.pos:newpos]
1653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.pos = newpos
1663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return r
1673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def readlines(self, sizehint = 0):
1693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Read until EOF using readline() and return a list containing the
1703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        lines thus read.
1713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        If the optional sizehint argument is present, instead of reading up
1733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        to EOF, whole lines totalling approximately sizehint bytes (or more
1743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        to accommodate a final whole line).
1753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
1763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        total = 0
1773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        lines = []
1783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        line = self.readline()
1793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        while line:
1803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            lines.append(line)
1813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            total += len(line)
1823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if 0 < sizehint <= total:
1833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                break
1843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            line = self.readline()
1853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return lines
1863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def truncate(self, size=None):
1883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Truncate the file's size.
1893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        If the optional size argument is present, the file is truncated to
1913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        (at most) that size. The size defaults to the current position.
1923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        The current file position is not changed unless the position
1933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        is beyond the new file size.
1943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
1953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        If the specified size exceeds the file's current size, the
1963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        file remains unchanged.
1973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
1983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
1993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if size is None:
2003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            size = self.pos
2013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        elif size < 0:
2023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            raise IOError(EINVAL, "Negative size not allowed")
2033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        elif size < self.pos:
2043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.pos = size
2053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.buf = self.getvalue()[:size]
2063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.len = size
2073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def write(self, s):
2093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Write a string to the file.
2103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        There is no return value.
2123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
2133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
2143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not s: return
2153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        # Force s to be a string or unicode
2163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if not isinstance(s, basestring):
2173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            s = str(s)
2183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        spos = self.pos
2193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        slen = self.len
2203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if spos == slen:
2213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buflist.append(s)
2223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.len = self.pos = spos + len(s)
2233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            return
2243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if spos > slen:
2253257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buflist.append('\0'*(spos - slen))
2263257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            slen = spos
2273257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        newpos = spos + len(s)
2283257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if spos < slen:
2293257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if self.buflist:
2303257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                self.buf += ''.join(self.buflist)
2313257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buflist = [self.buf[:spos], s, self.buf[newpos:]]
2323257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buf = ''
2333257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            if newpos > slen:
2343257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel                slen = newpos
2353257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        else:
2363257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buflist.append(s)
2373257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            slen = newpos
2383257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.len = slen
2393257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        self.pos = newpos
2403257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2413257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def writelines(self, iterable):
2423257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Write a sequence of strings to the file. The sequence can be any
2433257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        iterable object producing strings, typically a list of strings. There
2443257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        is no return value.
2453257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2463257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        (The name is intended to match readlines(); writelines() does not add
2473257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        line separators.)
2483257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
2493257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        write = self.write
2503257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        for line in iterable:
2513257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            write(line)
2523257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2533257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def flush(self):
2543257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """Flush the internal buffer
2553257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
2563257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
2573257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2583257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    def getvalue(self):
2593257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
2603257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        Retrieve the entire contents of the "file" at any time before
2613257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        the StringIO object's close() method is called.
2623257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2633257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        The StringIO object can accept either Unicode or 8-bit strings,
2643257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        but mixing the two may take some care. If both are used, 8-bit
2653257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        strings that cannot be interpreted as 7-bit ASCII (that use the
2663257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        8th bit) will cause a UnicodeError to be raised when getvalue()
2673257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        is called.
2683257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        """
2693257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        _complain_ifclosed(self.closed)
2703257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        if self.buflist:
2713257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buf += ''.join(self.buflist)
2723257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel            self.buflist = []
2733257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        return self.buf
2743257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2753257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2763257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel# A little test suite
2773257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
2783257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanieldef test():
2793257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    import sys
2803257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if sys.argv[1:]:
2813257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        file = sys.argv[1]
2823257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    else:
2833257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        file = '/etc/passwd'
2843257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    lines = open(file, 'r').readlines()
2853257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    text = open(file, 'r').read()
2863257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f = StringIO()
2873257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    for line in lines[:-2]:
2883257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        f.write(line)
2893257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.writelines(lines[-2:])
2903257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if f.getvalue() != text:
2913257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        raise RuntimeError, 'write failed'
2923257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    length = f.tell()
2933257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    print 'File length =', length
2943257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.seek(len(lines[0]))
2953257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.write(lines[1])
2963257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.seek(0)
2973257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    print 'First line =', repr(f.readline())
2983257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    print 'Position =', f.tell()
2993257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    line = f.readline()
3003257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    print 'Second line =', repr(line)
3013257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.seek(-len(line), 1)
3023257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    line2 = f.read(len(line))
3033257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if line != line2:
3043257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        raise RuntimeError, 'bad result after seek back'
3053257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.seek(len(line2), 1)
3063257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    list = f.readlines()
3073257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    line = list[-1]
3083257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.seek(f.tell() - len(line))
3093257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    line2 = f.read()
3103257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if line != line2:
3113257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        raise RuntimeError, 'bad result after seek back from EOF'
3123257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    print 'Read', len(list), 'more lines'
3133257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    print 'File length =', f.tell()
3143257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if f.tell() != length:
3153257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        raise RuntimeError, 'bad length'
3163257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.truncate(length/2)
3173257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.seek(0, 2)
3183257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    print 'Truncated length =', f.tell()
3193257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    if f.tell() != length/2:
3203257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel        raise RuntimeError, 'truncate did not adjust length'
3213257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    f.close()
3223257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel
3233257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDanielif __name__ == '__main__':
3243257aa99321d745773a6bd1bd4ce7f6fafe74411Daryl McDaniel    test()
325