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
25
27
# XXX: some redundancy is allowing to write stanzas in isolation as well as
26
28
# 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())
73
59
def read_stanzas(from_file):
75
61
s = read_stanza(from_file)
106
92
"""Append a name and value to the stanza."""
107
93
assert valid_tag(tag), \
108
94
("invalid tag %r" % tag)
109
if isinstance(value, str):
110
value = unicode(value)
111
elif isinstance(value, unicode):
95
if isinstance(value, (str, unicode)):
113
97
## elif isinstance(value, (int, long)):
114
98
## value = str(value) # XXX: python2.4 without L-suffix
116
raise TypeError("invalid type for rio value: %r of type %s"
117
% (value, type(value)))
100
raise ValueError("invalid value %r" % value)
118
101
self.items.append((tag, value))
120
103
def __contains__(self, find_tag):
144
127
return iter(self.items)
146
129
def to_lines(self):
147
"""Generate sequence of lines for external version of this file.
149
The lines are always utf-8 encoded strings.
130
"""Generate sequence of lines for external version of this file."""
151
131
if not self.items:
152
132
# max() complains if sequence is empty
155
135
for tag, value in self.items:
156
assert isinstance(tag, str), type(tag)
157
assert isinstance(value, unicode)
136
assert isinstance(value, (str, unicode))
159
138
result.append(tag + ': \n')
160
139
elif '\n' in value:
161
140
# don't want splitlines behaviour on empty lines
162
141
val_lines = value.split('\n')
163
result.append(tag + ': ' + val_lines[0].encode('utf-8') + '\n')
142
result.append(tag + ': ' + val_lines[0] + '\n')
164
143
for line in val_lines[1:]:
165
result.append('\t' + line.encode('utf-8') + '\n')
144
result.append('\t' + line + '\n')
167
result.append(tag + ': ' + value.encode('utf-8') + '\n')
146
result.append(tag + ': ' + value + '\n')
170
149
def to_string(self):
171
150
"""Return stanza as a single string"""
172
151
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)
196
153
def write(self, to_file):
197
154
"""Write stanza to a file"""
198
155
to_file.writelines(self.to_lines())
243
191
Only the stanza lines and the trailing blank (if any) are consumed
244
192
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.
269
195
stanza = Stanza()
271
197
accum_value = None
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 == '':
198
for line in line_iter:
199
if line == None or line == '':
279
200
break # end of file
281
202
break # end of stanza
282
assert line.endswith('\n')
203
assert line[-1] == '\n'
284
205
if line[0] == '\t': # continues previous value
292
213
colon_index = line.index(': ')
293
214
except ValueError:
294
raise ValueError('tag/value separator not found in line %r'
296
tag = str(line[:colon_index])
215
raise ValueError('tag/value separator not found in line %r' % real_l)
216
tag = line[:colon_index]
297
217
assert valid_tag(tag), \
298
218
"invalid rio tag %r" % tag
299
219
accum_value = line[colon_index+2:-1]
301
220
if tag is not None: # add last tag-value
302
221
stanza.add(tag, accum_value)