15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from cStringIO import StringIO 25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)class StringIOTree(object): 45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) See module docs. 65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def __init__(self, stream=None): 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.prepended_children = [] 105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if stream is None: 115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) stream = StringIO() 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.stream = stream 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.write = stream.write 145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.markers = [] 155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def getvalue(self): 175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content = [x.getvalue() for x in self.prepended_children] 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) content.append(self.stream.getvalue()) 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return "".join(content) 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def copyto(self, target): 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Potentially cheaper than getvalue as no string concatenation 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) needs to happen.""" 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for child in self.prepended_children: 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) child.copyto(target) 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) stream_content = self.stream.getvalue() 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if stream_content: 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) target.write(stream_content) 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def commit(self): 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Save what we have written until now so that the buffer 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # itself is empty -- this makes it ready for insertion 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if self.stream.tell(): 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.prepended_children.append(StringIOTree(self.stream)) 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.prepended_children[-1].markers = self.markers 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.markers = [] 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.stream = StringIO() 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.write = self.stream.write 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def insert(self, iotree): 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Insert a StringIOTree (and all of its contents) at this location. 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Further writing to self appears after what is inserted. 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.commit() 465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.prepended_children.append(iotree) 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def insertion_point(self): 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns a new StringIOTree, which is left behind at the current position 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) (it what is written to the result will appear right before whatever is 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) next written to self). 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Calling getvalue() or copyto() on the result will only return the 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) contents written to it. 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Save what we have written until now 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # This is so that getvalue on the result doesn't include it. 595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.commit() 605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Construct the new forked object to return 615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) other = StringIOTree() 625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self.prepended_children.append(other) 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return other 645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def allmarkers(self): 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) children = self.prepended_children 675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return [m for c in children for m in c.allmarkers()] + self.markers 685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)__doc__ = r""" 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)Implements a buffer with insertion points. When you know you need to 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)"get back" to a place and write more later, simply call insertion_point() 735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)at that spot and get a new StringIOTree object that is "left behind". 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)EXAMPLE: 765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> a = StringIOTree() 785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> a.write('first\n') 795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> b = a.insertion_point() 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> a.write('third\n') 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> b.write('second\n') 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> a.getvalue().split() 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)['first', 'second', 'third'] 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> c = b.insertion_point() 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> d = c.insertion_point() 875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> d.write('alpha\n') 885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> b.write('gamma\n') 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> c.write('beta\n') 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> b.getvalue().split() 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)['second', 'alpha', 'beta', 'gamma'] 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> i = StringIOTree() 935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> d.insert(i) 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> i.write('inserted\n') 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> out = StringIO() 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> a.copyto(out) 975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)>>> out.getvalue().split() 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)['first', 'second', 'alpha', 'inserted', 'beta', 'gamma', 'third'] 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)""" 100