|
|
|
|
|
|
|
|
r""" |
|
|
Implements a buffer with insertion points. When you know you need to |
|
|
"get back" to a place and write more later, simply call insertion_point() |
|
|
at that spot and get a new StringIOTree object that is "left behind". |
|
|
|
|
|
EXAMPLE: |
|
|
|
|
|
>>> a = StringIOTree() |
|
|
>>> _= a.write('first\n') |
|
|
>>> b = a.insertion_point() |
|
|
>>> _= a.write('third\n') |
|
|
>>> _= b.write('second\n') |
|
|
>>> a.getvalue().split() |
|
|
['first', 'second', 'third'] |
|
|
|
|
|
>>> c = b.insertion_point() |
|
|
>>> d = c.insertion_point() |
|
|
>>> _= d.write('alpha\n') |
|
|
>>> _= b.write('gamma\n') |
|
|
>>> _= c.write('beta\n') |
|
|
>>> b.getvalue().split() |
|
|
['second', 'alpha', 'beta', 'gamma'] |
|
|
|
|
|
>>> i = StringIOTree() |
|
|
>>> d.insert(i) |
|
|
>>> _= i.write('inserted\n') |
|
|
>>> out = StringIO() |
|
|
>>> a.copyto(out) |
|
|
>>> out.getvalue().split() |
|
|
['first', 'second', 'alpha', 'inserted', 'beta', 'gamma', 'third'] |
|
|
""" |
|
|
|
|
|
from __future__ import absolute_import |
|
|
|
|
|
try: |
|
|
|
|
|
from cStringIO import StringIO |
|
|
except ImportError: |
|
|
from io import StringIO |
|
|
|
|
|
|
|
|
class StringIOTree(object): |
|
|
""" |
|
|
See module docs. |
|
|
""" |
|
|
|
|
|
def __init__(self, stream=None): |
|
|
self.prepended_children = [] |
|
|
if stream is None: |
|
|
stream = StringIO() |
|
|
self.stream = stream |
|
|
self.write = stream.write |
|
|
self.markers = [] |
|
|
|
|
|
def getvalue(self): |
|
|
content = [x.getvalue() for x in self.prepended_children] |
|
|
content.append(self.stream.getvalue()) |
|
|
return "".join(content) |
|
|
|
|
|
def copyto(self, target): |
|
|
"""Potentially cheaper than getvalue as no string concatenation |
|
|
needs to happen.""" |
|
|
for child in self.prepended_children: |
|
|
child.copyto(target) |
|
|
stream_content = self.stream.getvalue() |
|
|
if stream_content: |
|
|
target.write(stream_content) |
|
|
|
|
|
def commit(self): |
|
|
|
|
|
|
|
|
if self.stream.tell(): |
|
|
self.prepended_children.append(StringIOTree(self.stream)) |
|
|
self.prepended_children[-1].markers = self.markers |
|
|
self.markers = [] |
|
|
self.stream = StringIO() |
|
|
self.write = self.stream.write |
|
|
|
|
|
def insert(self, iotree): |
|
|
""" |
|
|
Insert a StringIOTree (and all of its contents) at this location. |
|
|
Further writing to self appears after what is inserted. |
|
|
""" |
|
|
self.commit() |
|
|
self.prepended_children.append(iotree) |
|
|
|
|
|
def insertion_point(self): |
|
|
""" |
|
|
Returns a new StringIOTree, which is left behind at the current position |
|
|
(it what is written to the result will appear right before whatever is |
|
|
next written to self). |
|
|
|
|
|
Calling getvalue() or copyto() on the result will only return the |
|
|
contents written to it. |
|
|
""" |
|
|
|
|
|
|
|
|
self.commit() |
|
|
|
|
|
other = StringIOTree() |
|
|
self.prepended_children.append(other) |
|
|
return other |
|
|
|
|
|
def allmarkers(self): |
|
|
children = self.prepended_children |
|
|
return [m for c in children for m in c.allmarkers()] + self.markers |
|
|
|