bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
1 |
# Copyright (C) 2005 by Canonical Ltd
|
2 |
#
|
|
3 |
# This program is free software; you can redistribute it and/or modify
|
|
4 |
# it under the terms of the GNU General Public License as published by
|
|
5 |
# the Free Software Foundation; either version 2 of the License, or
|
|
6 |
# (at your option) any later version.
|
|
7 |
#
|
|
8 |
# This program is distributed in the hope that it will be useful,
|
|
9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11 |
# GNU General Public License for more details.
|
|
12 |
#
|
|
13 |
# You should have received a copy of the GNU General Public License
|
|
14 |
# along with this program; if not, write to the Free Software
|
|
15 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
16 |
||
17 |
"""basic_io - simple text metaformat
|
|
18 |
||
19 |
The stored data consists of a series of *stanzas*, each of which contains
|
|
20 |
*fields* identified by an ascii name. The contents of each field can be
|
|
21 |
either an integer (scored in decimal) or a Unicode string.
|
|
22 |
"""
|
|
23 |
||
24 |
import re |
|
25 |
||
|
1185.44.19
by Martin Pool
New BasicReader to pull a sequence of stanzas from a file, and test. |
26 |
# XXX: basic_io is kind of a dumb name; it seems to imply an io layer not a
|
27 |
# format
|
|
28 |
#
|
|
29 |
# XXX: some redundancy is allowing to write stanzas in isolation as well as
|
|
|
1185.44.22
by Martin Pool
More basic_io tweaking |
30 |
# through a writer object.
|
|
1185.44.19
by Martin Pool
New BasicReader to pull a sequence of stanzas from a file, and test. |
31 |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
32 |
class BasicWriter(object): |
|
1185.44.16
by Martin Pool
More basic_io development: |
33 |
def __init__(self, to_file): |
34 |
self._soft_nl = False |
|
35 |
self._to_file = to_file |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
36 |
|
37 |
def write_stanza(self, stanza): |
|
|
1185.44.16
by Martin Pool
More basic_io development: |
38 |
if self._soft_nl: |
39 |
print >>self._to_file |
|
40 |
stanza.write(self._to_file) |
|
41 |
self._soft_nl = True |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
42 |
|
43 |
||
|
1185.44.19
by Martin Pool
New BasicReader to pull a sequence of stanzas from a file, and test. |
44 |
class BasicReader(object): |
45 |
"""Read stanzas from a file as a sequence |
|
46 |
|
|
47 |
to_file can be anything that can be enumerated as a sequence of
|
|
48 |
lines (with newlines.)
|
|
49 |
"""
|
|
50 |
def __init__(self, from_file): |
|
51 |
self._from_file = from_file |
|
52 |
||
53 |
def __iter__(self): |
|
54 |
while True: |
|
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
55 |
s = read_stanza(self._from_file) |
|
1185.44.19
by Martin Pool
New BasicReader to pull a sequence of stanzas from a file, and test. |
56 |
if s is None: |
57 |
break
|
|
58 |
else: |
|
59 |
yield s |
|
60 |
||
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
61 |
def read_stanzas(from_file): |
62 |
while True: |
|
63 |
s = read_stanza(from_file) |
|
64 |
if s is None: |
|
65 |
break
|
|
66 |
else: |
|
67 |
yield s |
|
|
1185.44.19
by Martin Pool
New BasicReader to pull a sequence of stanzas from a file, and test. |
68 |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
69 |
class Stanza(object): |
70 |
"""One stanza for basic_io. |
|
71 |
||
72 |
Each stanza contains a set of named fields.
|
|
73 |
|
|
74 |
Names must be non-empty ascii alphanumeric plus _. Names can be repeated
|
|
75 |
within a stanza. Names are case-sensitive. The ordering of fields is
|
|
76 |
preserved.
|
|
77 |
||
78 |
Each field value must be either an int or a string.
|
|
79 |
"""
|
|
80 |
||
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
81 |
__slots__ = ['items'] |
82 |
||
83 |
def __init__(self, **kwargs): |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
84 |
"""Construct a new Stanza. |
85 |
||
86 |
The keyword arguments, if any, are added in sorted order to the stanza.
|
|
87 |
"""
|
|
|
1185.44.21
by Martin Pool
Experiments with basic_io for inventory |
88 |
if kwargs: |
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
89 |
self.items = sorted(kwargs.items()) |
90 |
else: |
|
91 |
self.items = [] |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
92 |
|
93 |
def add(self, tag, value): |
|
94 |
"""Append a name and value to the stanza.""" |
|
|
1185.44.22
by Martin Pool
More basic_io tweaking |
95 |
## if not valid_tag(tag):
|
96 |
## raise ValueError("invalid tag %r" % tag)
|
|
97 |
## if not isinstance(value, (int, long, str, unicode)):
|
|
98 |
## raise ValueError("invalid value %r" % value)
|
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
99 |
self.items.append((tag, value)) |
100 |
||
101 |
def __contains__(self, find_tag): |
|
102 |
"""True if there is any field in this stanza with the given tag.""" |
|
103 |
for tag, value in self.items: |
|
104 |
if tag == find_tag: |
|
105 |
return True |
|
106 |
return False |
|
107 |
||
|
1185.44.9
by Martin Pool
Add len(Stanza) |
108 |
def __len__(self): |
109 |
"""Return number of pairs in the stanza.""" |
|
110 |
return len(self.items) |
|
111 |
||
|
1185.44.11
by Martin Pool
Add Stanza eq and ne methods and test. |
112 |
def __eq__(self, other): |
113 |
if not isinstance(other, Stanza): |
|
114 |
return False |
|
115 |
return self.items == other.items |
|
116 |
||
117 |
def __ne__(self, other): |
|
118 |
return not self.__eq__(other) |
|
119 |
||
|
1185.44.12
by Martin Pool
Fix up reading basic_io multiline strings |
120 |
def __repr__(self): |
121 |
return "Stanza(%r)" % self.items |
|
122 |
||
|
1185.44.6
by Martin Pool
Simple basic_io from_lines and .get() |
123 |
def iter_pairs(self): |
|
1185.44.2
by Martin Pool
Iteration of Stanzas() |
124 |
"""Return iterator of tag, value pairs.""" |
125 |
return iter(self.items) |
|
126 |
||
|
1185.44.3
by Martin Pool
More basic_io development |
127 |
def to_lines(self): |
128 |
"""Generate sequence of lines for external version of this file.""" |
|
|
1185.44.29
by Martin Pool
Packing of empty basic_io stanzas |
129 |
if not self.items: |
130 |
# max() complains if sequence is empty
|
|
131 |
return
|
|
|
1185.44.27
by Martin Pool
Go back to multiline quoted string values. |
132 |
indent = max(len(kv[0]) for kv in self.items) |
|
1185.44.22
by Martin Pool
More basic_io tweaking |
133 |
for tag, value in self.items: |
134 |
if isinstance(value, (int, long)): |
|
135 |
# must use %d so bools are written as ints
|
|
|
1185.44.27
by Martin Pool
Go back to multiline quoted string values. |
136 |
yield '%*s %d\n' % (indent, tag, value) |
|
1185.44.22
by Martin Pool
More basic_io tweaking |
137 |
else: |
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
138 |
assert isinstance(value, (str, unicode)), ("invalid value %r" % value) |
139 |
qv = value.replace('\\', r'\\') \ |
|
|
1185.44.27
by Martin Pool
Go back to multiline quoted string values. |
140 |
.replace('"', r'\"') |
141 |
yield '%*s "%s"\n' % (indent, tag, qv) |
|
|
1185.44.3
by Martin Pool
More basic_io development |
142 |
|
143 |
def to_string(self): |
|
|
1185.44.6
by Martin Pool
Simple basic_io from_lines and .get() |
144 |
"""Return stanza as a single string""" |
|
1185.44.3
by Martin Pool
More basic_io development |
145 |
return ''.join(self.to_lines()) |
146 |
||
147 |
def write(self, to_file): |
|
|
1185.44.6
by Martin Pool
Simple basic_io from_lines and .get() |
148 |
"""Write stanza to a file""" |
|
1185.44.3
by Martin Pool
More basic_io development |
149 |
to_file.writelines(self.to_lines()) |
150 |
||
|
1185.44.6
by Martin Pool
Simple basic_io from_lines and .get() |
151 |
def get(self, tag): |
152 |
"""Return the value for a field wih given tag. |
|
153 |
||
154 |
If there is more than one value, only the first is returned. If the
|
|
155 |
tag is not present, KeyError is raised.
|
|
156 |
"""
|
|
157 |
for t, v in self.items: |
|
158 |
if t == tag: |
|
159 |
return v |
|
160 |
else: |
|
161 |
raise KeyError(tag) |
|
|
1185.44.15
by Martin Pool
Add Stanza.get_all and test repeated fields |
162 |
|
|
1185.44.21
by Martin Pool
Experiments with basic_io for inventory |
163 |
__getitem__ = get |
164 |
||
|
1185.44.15
by Martin Pool
Add Stanza.get_all and test repeated fields |
165 |
def get_all(self, tag): |
166 |
r = [] |
|
167 |
for t, v in self.items: |
|
168 |
if t == tag: |
|
169 |
r.append(v) |
|
170 |
return r |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
171 |
|
172 |
TAG_RE = re.compile(r'^[-a-zA-Z0-9_]+$') |
|
173 |
def valid_tag(tag): |
|
174 |
return bool(TAG_RE.match(tag)) |
|
175 |
||
176 |
||
|
1185.44.27
by Martin Pool
Go back to multiline quoted string values. |
177 |
def read_stanza(line_iter): |
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
178 |
"""Return new Stanza read from list of lines or a file""" |
179 |
items = [] |
|
|
1185.44.29
by Martin Pool
Packing of empty basic_io stanzas |
180 |
got_lines = False |
|
1185.44.27
by Martin Pool
Go back to multiline quoted string values. |
181 |
for l in line_iter: |
|
1185.44.29
by Martin Pool
Packing of empty basic_io stanzas |
182 |
if l == None or l == '': |
183 |
break # eof |
|
184 |
got_lines = True |
|
185 |
if l == '\n': |
|
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
186 |
break
|
187 |
assert l[-1] == '\n' |
|
|
1185.44.33
by Martin Pool
Fix up handling of escaped doublequotes at end of line. |
188 |
real_l = l |
|
1185.44.27
by Martin Pool
Go back to multiline quoted string values. |
189 |
l = l.lstrip() |
|
1185.44.33
by Martin Pool
Fix up handling of escaped doublequotes at end of line. |
190 |
try: |
191 |
space = l.index(' ') |
|
192 |
except ValueError: |
|
193 |
raise ValueError('tag/value separator not found in line %r' % real_l) |
|
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
194 |
tag = l[:space] |
195 |
assert valid_tag(tag), \ |
|
196 |
"invalid basic_io tag %r" % tag |
|
197 |
rest = l[space+1:] |
|
198 |
if l[space+1] == '"': |
|
|
1185.44.27
by Martin Pool
Go back to multiline quoted string values. |
199 |
value = '' |
200 |
valpart = l[space+2:] |
|
201 |
while True: |
|
202 |
assert valpart[-1] == '\n' |
|
|
1185.44.33
by Martin Pool
Fix up handling of escaped doublequotes at end of line. |
203 |
len_valpart = len(valpart) |
|
1185.44.34
by Martin Pool
Addition test and bugfix for basic_io escaped strings. |
204 |
if len_valpart >= 2 and valpart[-2] == '"': |
|
1185.44.33
by Martin Pool
Fix up handling of escaped doublequotes at end of line. |
205 |
# is this a real terminating doublequote, or is it escaped
|
206 |
# by a preceding backslash that is not itself escaped?
|
|
207 |
i = 3 |
|
208 |
while i <= len_valpart and valpart[-i] == '\\': |
|
209 |
i += 1 |
|
210 |
num_slashes = i - 3 |
|
211 |
if num_slashes & 1: |
|
212 |
# it's escaped, so the escaped backslash and newline
|
|
213 |
# are passed through
|
|
214 |
value += valpart |
|
215 |
else: |
|
216 |
value += valpart[:-2] |
|
217 |
break
|
|
|
1185.44.27
by Martin Pool
Go back to multiline quoted string values. |
218 |
else: |
219 |
value += valpart |
|
220 |
try: |
|
221 |
valpart = line_iter.next() |
|
222 |
except StopIteration: |
|
223 |
raise ValueError('end of file in quoted string after %r' % value) |
|
224 |
value = value.replace('\\"', '"').replace('\\\\', '\\') |
|
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
225 |
else: |
|
1185.44.33
by Martin Pool
Fix up handling of escaped doublequotes at end of line. |
226 |
value_str = l[space+1:] |
227 |
try: |
|
228 |
value = int(value_str) |
|
229 |
except ValueError: |
|
230 |
raise ValueError('invalid integer %r for tag %r in line %r' |
|
231 |
% (value_str, tag, real_l)) |
|
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
232 |
items.append((tag, value)) |
|
1185.44.29
by Martin Pool
Packing of empty basic_io stanzas |
233 |
if not got_lines: |
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
234 |
return None # didn't see any content |
235 |
s = Stanza() |
|
236 |
s.items = items |
|
237 |
return s |
|
|
1185.44.22
by Martin Pool
More basic_io tweaking |
238 |
|
239 |
||
|
1185.44.6
by Martin Pool
Simple basic_io from_lines and .get() |
240 |
############################################################
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
241 |
|
242 |
# XXX: Move these to object serialization code.
|
|
243 |
||
244 |
def write_revision(writer, revision): |
|
245 |
s = Stanza(revision=revision.revision_id, |
|
246 |
committer=revision.committer, |
|
247 |
timezone=long(revision.timezone), |
|
248 |
timestamp=long(revision.timestamp), |
|
|
1185.44.31
by Martin Pool
Update basic_io revision output |
249 |
inventory_sha1=revision.inventory_sha1, |
250 |
message=revision.message) |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
251 |
for parent_id in revision.parent_ids: |
252 |
s.add('parent', parent_id) |
|
253 |
for prop_name, prop_value in revision.properties.items(): |
|
254 |
s.add(prop_name, prop_value) |
|
|
1185.44.31
by Martin Pool
Update basic_io revision output |
255 |
writer.write_stanza(s) |
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
256 |
|
257 |
def write_inventory(writer, inventory): |
|
258 |
s = Stanza(inventory_version=7) |
|
259 |
writer.write_stanza(s) |
|
260 |
||
261 |
for path, ie in inventory.iter_entries(): |
|
262 |
s = Stanza() |
|
|
1185.44.16
by Martin Pool
More basic_io development: |
263 |
s.add(ie.kind, ie.file_id) |
264 |
for attr in ['name', 'parent_id', 'revision', |
|
|
1185.44.1
by Martin Pool
Start bringing in basicio code |
265 |
'text_sha1', 'text_size', 'executable', 'symlink_target', |
266 |
]:
|
|
267 |
attr_val = getattr(ie, attr, None) |
|
268 |
if attr == 'executable' and attr_val == 0: |
|
269 |
continue
|
|
270 |
if attr == 'parent_id' and attr_val == 'TREE_ROOT': |
|
271 |
continue
|
|
272 |
if attr_val is not None: |
|
273 |
s.add(attr, attr_val) |
|
274 |
writer.write_stanza(s) |
|
|
1185.44.21
by Martin Pool
Experiments with basic_io for inventory |
275 |
|
276 |
||
277 |
def read_inventory(inv_file): |
|
278 |
"""Read inventory object from basic_io formatted inventory file""" |
|
279 |
from bzrlib.inventory import Inventory, InventoryFile |
|
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
280 |
s = read_stanza(inv_file) |
|
1185.44.21
by Martin Pool
Experiments with basic_io for inventory |
281 |
assert s['inventory_version'] == 7 |
282 |
inv = Inventory() |
|
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
283 |
for s in read_stanzas(inv_file): |
|
1185.44.21
by Martin Pool
Experiments with basic_io for inventory |
284 |
kind, file_id = s.items[0] |
285 |
parent_id = None |
|
286 |
if 'parent_id' in s: |
|
287 |
parent_id = s['parent_id'] |
|
288 |
if kind == 'file': |
|
289 |
ie = InventoryFile(file_id, s['name'], parent_id) |
|
|
1185.44.23
by Martin Pool
More basic_io tweaking; break format |
290 |
ie.text_sha1 = s['text_sha1'] |
291 |
ie.text_size = s['text_size'] |
|
|
1185.44.21
by Martin Pool
Experiments with basic_io for inventory |
292 |
else: |
293 |
raise NotImplementedError() |
|
294 |
inv.add(ie) |
|
295 |
return inv |