/brz/remove-bazaar

To get this branch, use:
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