|
|
|
|
| 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']
|
|
|
| >>> try: from cStringIO import StringIO
|
| ... except ImportError: from io import StringIO
|
|
|
| >>> 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 empty(self):
|
| if self.stream.tell():
|
| return False
|
| return all([child.empty() for child in self.prepended_children]) if self.prepended_children else True
|
|
|
| def getvalue(self):
|
| content = []
|
| self._collect_in(content)
|
| return "".join(content)
|
|
|
| def _collect_in(self, target_list):
|
| for x in self.prepended_children:
|
| x._collect_in(target_list)
|
| stream_content = self.stream.getvalue()
|
| if stream_content:
|
| target_list.append(stream_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 reset(self):
|
| self.prepended_children = []
|
| 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
|
|
|
| """
|
| # Print the result of allmarkers in a nice human-readable form. Use it only for debugging.
|
| # Prints e.g.
|
| # /path/to/source.pyx:
|
| # cython line 2 maps to 3299-3343
|
| # cython line 4 maps to 2236-2245 2306 3188-3201
|
| # /path/to/othersource.pyx:
|
| # cython line 3 maps to 1234-1270
|
| # ...
|
| # Note: In the example above, 3343 maps to line 2, 3344 does not.
|
| def print_hr_allmarkers(self):
|
| from collections import defaultdict
|
| markers = self.allmarkers()
|
| totmap = defaultdict(lambda: defaultdict(list))
|
| for c_lineno, (cython_desc, cython_lineno) in enumerate(markers):
|
| if cython_lineno > 0 and cython_desc.filename is not None:
|
| totmap[cython_desc.filename][cython_lineno].append(c_lineno + 1)
|
| reprstr = ""
|
| if totmap == 0:
|
| reprstr += "allmarkers is empty\n"
|
| try:
|
| sorted(totmap.items())
|
| except:
|
| print(totmap)
|
| print(totmap.items())
|
| for cython_path, filemap in sorted(totmap.items()):
|
| reprstr += cython_path + ":\n"
|
| for cython_lineno, c_linenos in sorted(filemap.items()):
|
| reprstr += "\tcython line " + str(cython_lineno) + " maps to "
|
| i = 0
|
| while i < len(c_linenos):
|
| reprstr += str(c_linenos[i])
|
| flag = False
|
| while i+1 < len(c_linenos) and c_linenos[i+1] == c_linenos[i]+1:
|
| i += 1
|
| flag = True
|
| if flag:
|
| reprstr += "-" + str(c_linenos[i]) + " "
|
| i += 1
|
| reprstr += "\n"
|
|
|
| import sys
|
| sys.stdout.write(reprstr)
|
| """
|
|
|