| | |
| |
|
| | 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 |
| |
|