1
1
# Copyright (C) 2005 by Canonical Ltd
3
3
# Distributed under the GNU General Public Licence v2
5
5
# \subsection{\emph{rio} - simple text metaformat}
7
7
# \emph{r} stands for `restricted', `reproducible', or `rfc822-like'.
18
18
# stream representation of an object and vice versa, and that this relation
19
19
# will continue to hold for future versions of bzr.
21
# In comments, $\min(1,10)$
23
from bzrlib.iterablefile import IterableFile
27
25
# XXX: some redundancy is allowing to write stanzas in isolation as well as
28
26
# through a writer object.
58
def rio_file(stanzas, header=None):
59
"""Produce a rio IterableFile from an iterable of stanzas"""
61
if header is not None:
65
if first_stanza is not True:
67
for line in s.to_lines():
70
return IterableFile(str_iter())
59
73
def read_stanzas(from_file):
61
75
s = read_stanza(from_file)
92
106
"""Append a name and value to the stanza."""
93
107
assert valid_tag(tag), \
94
108
("invalid tag %r" % tag)
95
if isinstance(value, (str, unicode)):
109
if isinstance(value, str):
110
value = unicode(value)
111
elif isinstance(value, unicode):
97
113
## elif isinstance(value, (int, long)):
98
114
## value = str(value) # XXX: python2.4 without L-suffix
100
raise ValueError("invalid value %r" % value)
116
raise TypeError("invalid type for rio value: %r of type %s"
117
% (value, type(value)))
101
118
self.items.append((tag, value))
103
120
def __contains__(self, find_tag):
127
144
return iter(self.items)
129
146
def to_lines(self):
130
"""Generate sequence of lines for external version of this file."""
147
"""Generate sequence of lines for external version of this file.
149
The lines are always utf-8 encoded strings.
131
151
if not self.items:
132
152
# max() complains if sequence is empty
135
155
for tag, value in self.items:
136
assert isinstance(value, (str, unicode))
156
assert isinstance(tag, str), type(tag)
157
assert isinstance(value, unicode)
138
159
result.append(tag + ': \n')
139
160
elif '\n' in value:
140
161
# don't want splitlines behaviour on empty lines
141
162
val_lines = value.split('\n')
142
result.append(tag + ': ' + val_lines[0] + '\n')
163
result.append(tag + ': ' + val_lines[0].encode('utf-8') + '\n')
143
164
for line in val_lines[1:]:
144
result.append('\t' + line + '\n')
165
result.append('\t' + line.encode('utf-8') + '\n')
146
result.append(tag + ': ' + value + '\n')
167
result.append(tag + ': ' + value.encode('utf-8') + '\n')
149
170
def to_string(self):
150
171
"""Return stanza as a single string"""
151
172
return ''.join(self.to_lines())
174
def to_unicode(self):
175
"""Return stanza as a single Unicode string.
177
This is most useful when adding a Stanza to a parent Stanza
183
for tag, value in self.items:
185
result.append(tag + ': \n')
187
# don't want splitlines behaviour on empty lines
188
val_lines = value.split('\n')
189
result.append(tag + ': ' + val_lines[0] + '\n')
190
for line in val_lines[1:]:
191
result.append('\t' + line + '\n')
193
result.append(tag + ': ' + value + '\n')
194
return u''.join(result)
153
196
def write(self, to_file):
154
197
"""Write stanza to a file"""
155
198
to_file.writelines(self.to_lines())
191
243
Only the stanza lines and the trailing blank (if any) are consumed
192
244
from the line_iter.
246
The raw lines must be in utf-8 encoding.
248
unicode_iter = (line.decode('utf-8') for line in line_iter)
249
return read_stanza_unicode(unicode_iter)
252
def read_stanza_unicode(unicode_iter):
253
"""Read a Stanza from a list of lines or a file.
255
The lines should already be in unicode form. This returns a single
256
stanza that was read. If there is a blank line at the end of the Stanza,
257
it is consumed. It is not an error for there to be no blank line at
258
the end of the iterable. If there is a blank line at the beginning,
259
this is treated as an empty Stanza and None is returned.
261
Only the stanza lines and the trailing blank (if any) are consumed
262
from the unicode_iter
264
:param unicode_iter: A iterable, yeilding Unicode strings. See read_stanza
265
if you have a utf-8 encoded string.
266
:return: A Stanza object if there are any lines in the file.
195
269
stanza = Stanza()
197
271
accum_value = None
198
for line in line_iter:
199
if line == None or line == '':
273
# TODO: jam 20060922 This code should raise real errors rather than
274
# using 'assert' to process user input, or raising ValueError
275
# rather than a more specific error.
277
for line in unicode_iter:
278
if line is None or line == '':
200
279
break # end of file
202
281
break # end of stanza
203
assert line[-1] == '\n'
282
assert line.endswith('\n')
205
284
if line[0] == '\t': # continues previous value
213
292
colon_index = line.index(': ')
214
293
except ValueError:
215
raise ValueError('tag/value separator not found in line %r' % real_l)
216
tag = line[:colon_index]
294
raise ValueError('tag/value separator not found in line %r'
296
tag = str(line[:colon_index])
217
297
assert valid_tag(tag), \
218
298
"invalid rio tag %r" % tag
219
299
accum_value = line[colon_index+2:-1]
220
301
if tag is not None: # add last tag-value
221
302
stanza.add(tag, accum_value)