14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmr"""File-like objects that read from or write to a string buffer.
24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmThis implements (nearly) all stdio methods.
44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf = StringIO()      # ready for writing
64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf = StringIO(buf)   # ready for reading
74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf.close()           # explicitly release resources held
84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmflag = f.isatty()   # always false
94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmpos = f.tell()      # get current position
104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf.seek(pos)         # set current position
114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf.seek(pos, mode)   # mode 0: absolute; 1: relative; 2: relative to EOF
124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmbuf = f.read()      # read until EOF
134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmbuf = f.read(n)     # read up to n bytes
144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmbuf = f.readline()  # read until end of line ('\n') or EOF
154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmlist = f.readlines()# list of f.readline() results until EOF
164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf.truncate([size])  # truncate file at to at most size (default: current pos)
174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf.write(buf)        # write at current position
184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf.writelines(list)  # for line in list: f.write(line)
194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmf.getvalue()        # return whole file's contents as a string
204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmNotes:
224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- Using a real file is often faster (but less convenient).
234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- There's also a much faster implementation in C, called cStringIO, but
244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm  it's not subclassable.
254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- fileno() is left unimplemented so that code which uses it triggers
264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm  an exception early.
274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- Seeking far beyond EOF and then writing will insert real null
284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm  bytes that occupy space in the buffer.
294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm- There's a simple test set (see end of this file).
304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""
314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtry:
324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    from errno import EINVAL
334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmexcept ImportError:
344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    EINVAL = 22
354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm__all__ = ["StringIO"]
374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef _complain_ifclosed(closed):
394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if closed:
404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise ValueError, "I/O operation on closed file"
414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass StringIO:
434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """class StringIO([buffer])
444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    When a StringIO object is created, it can be initialized to an existing
464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    string by passing the string to the constructor. If no string is given,
474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    the StringIO will start empty.
484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    The StringIO object can accept either Unicode or 8-bit strings, but
504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    mixing the two may take some care. If both are used, 8-bit strings that
514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    cannot be interpreted as 7-bit ASCII (that use the 8th bit) will cause
524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    a UnicodeError to be raised when getvalue() is called.
534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """
544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, buf = ''):
554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # Force self.buf to be a string or unicode
564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not isinstance(buf, basestring):
574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            buf = str(buf)
584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.buf = buf
594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.len = len(buf)
604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.buflist = []
614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.pos = 0
624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.closed = False
634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.softspace = 0
644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __iter__(self):
664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self
674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def next(self):
694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """A file object is its own iterator, for example iter(f) returns f
704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        (unless f is closed). When a file is used as an iterator, typically
714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        in a for loop (for example, for line in f: print line), the next()
724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        method is called repeatedly. This method returns the next input line,
734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        or raises StopIteration when EOF is hit.
744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        r = self.readline()
774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not r:
784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise StopIteration
794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return r
804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def close(self):
824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Free the memory buffer.
834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not self.closed:
854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.closed = True
864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            del self.buf, self.pos
874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def isatty(self):
894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Returns False because StringIO objects are not connected to a
904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        tty-like device.
914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return False
944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def seek(self, pos, mode = 0):
964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Set the file's current position.
974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        The mode argument is optional and defaults to 0 (absolute file
994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        positioning); other values are 1 (seek relative to the current
1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        position) and 2 (seek relative to the file's end).
1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        There is no return value.
1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.buflist:
1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buf += ''.join(self.buflist)
1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buflist = []
1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if mode == 1:
1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            pos += self.pos
1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif mode == 2:
1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            pos += self.len
1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.pos = max(0, pos)
1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def tell(self):
1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Return the file's current position."""
1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.pos
1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def read(self, n = -1):
1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Read at most size bytes from the file
1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        (less if the read hits EOF before obtaining size bytes).
1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        If the size argument is negative or omitted, read all data until EOF
1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        is reached. The bytes are returned as a string object. An empty
1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        string is returned when EOF is encountered immediately.
1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.buflist:
1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buf += ''.join(self.buflist)
1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buflist = []
1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if n is None or n < 0:
1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            newpos = self.len
1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            newpos = min(self.pos+n, self.len)
1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        r = self.buf[self.pos:newpos]
1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.pos = newpos
1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return r
1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def readline(self, length=None):
1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        r"""Read one entire line from the file.
1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        A trailing newline character is kept in the string (but may be absent
1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        when a file ends with an incomplete line). If the size argument is
1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        present and non-negative, it is a maximum byte count (including the
1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        trailing newline) and an incomplete line may be returned.
1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        An empty string is returned only when EOF is encountered immediately.
1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Note: Unlike stdio's fgets(), the returned string contains null
1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        characters ('\0') if they occurred in the input.
1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.buflist:
1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buf += ''.join(self.buflist)
1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buflist = []
1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        i = self.buf.find('\n', self.pos)
1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if i < 0:
1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            newpos = self.len
1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            newpos = i+1
1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if length is not None and length > 0:
1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self.pos + length < newpos:
1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                newpos = self.pos + length
1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        r = self.buf[self.pos:newpos]
1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.pos = newpos
1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return r
1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def readlines(self, sizehint = 0):
1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Read until EOF using readline() and return a list containing the
1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        lines thus read.
1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        If the optional sizehint argument is present, instead of reading up
1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        to EOF, whole lines totalling approximately sizehint bytes (or more
1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        to accommodate a final whole line).
1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        total = 0
1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        lines = []
1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        line = self.readline()
1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while line:
1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            lines.append(line)
1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            total += len(line)
1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if 0 < sizehint <= total:
1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                break
1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            line = self.readline()
1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return lines
1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def truncate(self, size=None):
1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Truncate the file's size.
1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        If the optional size argument is present, the file is truncated to
1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        (at most) that size. The size defaults to the current position.
1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        The current file position is not changed unless the position
1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        is beyond the new file size.
1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        If the specified size exceeds the file's current size, the
1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        file remains unchanged.
1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if size is None:
2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            size = self.pos
2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif size < 0:
2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise IOError(EINVAL, "Negative size not allowed")
2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        elif size < self.pos:
2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.pos = size
2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.buf = self.getvalue()[:size]
2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.len = size
2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def write(self, s):
2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Write a string to the file.
2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        There is no return value.
2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not s: return
2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        # Force s to be a string or unicode
2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if not isinstance(s, basestring):
2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            s = str(s)
2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        spos = self.pos
2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        slen = self.len
2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if spos == slen:
2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buflist.append(s)
2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.len = self.pos = spos + len(s)
2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return
2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if spos > slen:
2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buflist.append('\0'*(spos - slen))
2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            slen = spos
2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        newpos = spos + len(s)
2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if spos < slen:
2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if self.buflist:
2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                self.buf += ''.join(self.buflist)
2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buflist = [self.buf[:spos], s, self.buf[newpos:]]
2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buf = ''
2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if newpos > slen:
2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                slen = newpos
2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buflist.append(s)
2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            slen = newpos
2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.len = slen
2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.pos = newpos
2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def writelines(self, iterable):
2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Write a sequence of strings to the file. The sequence can be any
2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        iterable object producing strings, typically a list of strings. There
2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        is no return value.
2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        (The name is intended to match readlines(); writelines() does not add
2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        line separators.)
2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        write = self.write
2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for line in iterable:
2514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            write(line)
2524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def flush(self):
2544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """Flush the internal buffer
2554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
2564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
2574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def getvalue(self):
2594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
2604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Retrieve the entire contents of the "file" at any time before
2614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        the StringIO object's close() method is called.
2624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        The StringIO object can accept either Unicode or 8-bit strings,
2644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        but mixing the two may take some care. If both are used, 8-bit
2654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        strings that cannot be interpreted as 7-bit ASCII (that use the
2664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        8th bit) will cause a UnicodeError to be raised when getvalue()
2674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        is called.
2684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        """
2694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        _complain_ifclosed(self.closed)
2704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if self.buflist:
2714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buf += ''.join(self.buflist)
2724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            self.buflist = []
2734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.buf
2744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# A little test suite
2774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
2784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef test():
2794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    import sys
2804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if sys.argv[1:]:
2814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        file = sys.argv[1]
2824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    else:
2834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        file = '/etc/passwd'
2844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    lines = open(file, 'r').readlines()
2854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    text = open(file, 'r').read()
2864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f = StringIO()
2874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    for line in lines[:-2]:
2884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        f.write(line)
2894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.writelines(lines[-2:])
2904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if f.getvalue() != text:
2914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise RuntimeError, 'write failed'
2924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    length = f.tell()
2934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print 'File length =', length
2944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.seek(len(lines[0]))
2954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.write(lines[1])
2964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.seek(0)
2974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print 'First line =', repr(f.readline())
2984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print 'Position =', f.tell()
2994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    line = f.readline()
3004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print 'Second line =', repr(line)
3014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.seek(-len(line), 1)
3024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    line2 = f.read(len(line))
3034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if line != line2:
3044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise RuntimeError, 'bad result after seek back'
3054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.seek(len(line2), 1)
3064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    list = f.readlines()
3074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    line = list[-1]
3084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.seek(f.tell() - len(line))
3094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    line2 = f.read()
3104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if line != line2:
3114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise RuntimeError, 'bad result after seek back from EOF'
3124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print 'Read', len(list), 'more lines'
3134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print 'File length =', f.tell()
3144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if f.tell() != length:
3154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise RuntimeError, 'bad length'
3164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.truncate(length/2)
3174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.seek(0, 2)
3184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    print 'Truncated length =', f.tell()
3194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if f.tell() != length/2:
3204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise RuntimeError, 'truncate did not adjust length'
3214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.close()
3224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
3234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmif __name__ == '__main__':
3244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    test()
325