2
Implements a buffer with insertion points. When you know you need to
3
"get back" to a place and write more later, simply call insertion_point()
4
at that spot and get a new StringIOTree object that is "left behind".
9
>>> _= a.write('first\n')
10
>>> b = a.insertion_point()
11
>>> _= a.write('third\n')
12
>>> _= b.write('second\n')
13
>>> a.getvalue().split()
14
['first', 'second', 'third']
16
>>> c = b.insertion_point()
17
>>> d = c.insertion_point()
18
>>> _= d.write('alpha\n')
19
>>> _= b.write('gamma\n')
20
>>> _= c.write('beta\n')
21
>>> b.getvalue().split()
22
['second', 'alpha', 'beta', 'gamma']
24
>>> try: from cStringIO import StringIO
25
... except ImportError: from io import StringIO
29
>>> _= i.write('inserted\n')
32
>>> out.getvalue().split()
33
['first', 'second', 'alpha', 'inserted', 'beta', 'gamma', 'third']
37
from io import StringIO
45
def __init__(self, stream=None):
46
self.prepended_children = []
50
self.write = stream.write
54
if self.stream.tell():
56
return all([child.empty() for child in self.prepended_children]) if self.prepended_children else True
60
self._collect_in(content)
61
return "".join(content)
63
def _collect_in(self, target_list):
65
for x in self.prepended_children:
66
x._collect_in(target_list)
67
stream_content = self.stream.getvalue()
69
target_list.append(stream_content)
71
def copyto(self, target):
72
"""Potentially cheaper than getvalue as no string concatenation
75
for child in self.prepended_children:
77
stream_content = self.stream.getvalue()
79
target.write(stream_content)
82
# Save what we have written until now so that the buffer
83
# itself is empty -- this makes it ready for insertion
84
if self.stream.tell():
85
self.prepended_children.append(StringIOTree(self.stream))
86
self.prepended_children[-1].markers = self.markers
88
self.stream = StringIO()
89
self.write = self.stream.write
92
self.prepended_children = []
94
self.stream = StringIO()
95
self.write = self.stream.write
97
def insert(self, iotree):
99
Insert a StringIOTree (and all of its contents) at this location.
100
Further writing to self appears after what is inserted.
103
self.prepended_children.append(iotree)
105
def insertion_point(self):
107
Returns a new StringIOTree, which is left behind at the current position
108
(it what is written to the result will appear right before whatever is
109
next written to self).
111
Calling getvalue() or copyto() on the result will only return the
112
contents written to it.
114
# Save what we have written until now
115
# This is so that getvalue on the result doesn't include it.
117
# Construct the new forked object to return
118
other = StringIOTree()
119
self.prepended_children.append(other)
122
def allmarkers(self):
124
children = self.prepended_children
125
return [m for c in children for m in c.allmarkers()] + self.markers
128
# Print the result of allmarkers in a nice human-readable form. Use it only for debugging.
130
# /path/to/source.pyx:
131
# cython line 2 maps to 3299-3343
132
# cython line 4 maps to 2236-2245 2306 3188-3201
133
# /path/to/othersource.pyx:
134
# cython line 3 maps to 1234-1270
136
# Note: In the example above, 3343 maps to line 2, 3344 does not.
137
def print_hr_allmarkers(self):
138
from collections import defaultdict
139
markers = self.allmarkers()
140
totmap = defaultdict(lambda: defaultdict(list))
141
for c_lineno, (cython_desc, cython_lineno) in enumerate(markers):
142
if cython_lineno > 0 and cython_desc.filename is not None:
143
totmap[cython_desc.filename][cython_lineno].append(c_lineno + 1)
146
reprstr += "allmarkers is empty\n"
148
sorted(totmap.items())
151
print(totmap.items())
152
for cython_path, filemap in sorted(totmap.items()):
153
reprstr += cython_path + ":\n"
154
for cython_lineno, c_linenos in sorted(filemap.items()):
155
reprstr += "\tcython line " + str(cython_lineno) + " maps to "
157
while i < len(c_linenos):
158
reprstr += str(c_linenos[i])
160
while i+1 < len(c_linenos) and c_linenos[i+1] == c_linenos[i]+1:
164
reprstr += "-" + str(c_linenos[i]) + " "
169
sys.stdout.write(reprstr)