~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/xml.py

and the tutorial patch came back, the very next day

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/env python
1
2
# -*- coding: UTF-8 -*-
2
3
 
3
4
# This program is free software; you can redistribute it and/or modify
22
23
# importing this module is fairly slow because it has to load several
23
24
# ElementTree bits
24
25
 
25
 
from bzrlib.trace import mutter, warning
26
 
 
27
26
try:
28
 
    from cElementTree import (ElementTree, SubElement, Element,
29
 
                              XMLTreeBuilder, fromstring, tostring)
 
27
    from util.cElementTree import ElementTree, SubElement, Element
30
28
except ImportError:
31
 
    mutter('WARNING: using slower ElementTree; consider installing cElementTree'
32
 
           " and make sure it's on your PYTHONPATH")
33
 
    from util.elementtree.ElementTree import (ElementTree, SubElement,
34
 
                                              Element, XMLTreeBuilder,
35
 
                                              fromstring, tostring)
 
29
    from util.elementtree.ElementTree import ElementTree, SubElement, Element
36
30
 
 
31
from bzrlib.inventory import ROOT_ID, Inventory, InventoryEntry
 
32
from bzrlib.revision import Revision, RevisionReference        
37
33
from bzrlib.errors import BzrError
38
34
 
39
35
 
44
40
        elt = self._pack_inventory(inv)
45
41
        self._write_element(elt, f)
46
42
 
47
 
    def write_inventory_to_string(self, inv):
48
 
        return tostring(self._pack_inventory(inv)) + '\n'
49
 
 
50
 
    def read_inventory_from_string(self, xml_string):
51
 
        return self._unpack_inventory(fromstring(xml_string))
52
 
 
53
43
    def read_inventory(self, f):
54
44
        return self._unpack_inventory(self._read_element(f))
55
45
 
56
46
    def write_revision(self, rev, f):
57
47
        self._write_element(self._pack_revision(rev), f)
58
48
 
59
 
    def write_revision_to_string(self, rev):
60
 
        return tostring(self._pack_revision(rev)) + '\n'
61
 
 
62
49
    def read_revision(self, f):
63
50
        return self._unpack_revision(self._read_element(f))
64
51
 
65
 
    def read_revision_from_string(self, xml_string):
66
 
        return self._unpack_revision(fromstring(xml_string))
67
 
 
68
52
    def _write_element(self, elt, f):
69
53
        ElementTree(elt).write(f, 'utf-8')
70
54
        f.write('\n')
71
55
 
72
56
    def _read_element(self, f):
73
57
        return ElementTree().parse(f)
 
58
 
 
59
 
 
60
 
 
61
class _Serializer_v4(Serializer):
 
62
    """Version 0.0.4 serializer
 
63
 
 
64
    You should use the serialzer_v4 singleton."""
 
65
    
 
66
    __slots__ = []
 
67
    
 
68
    def _pack_inventory(self, inv):
 
69
        """Convert to XML Element"""
 
70
        e = Element('inventory')
 
71
        e.text = '\n'
 
72
        if inv.root.file_id not in (None, ROOT_ID):
 
73
            e.set('file_id', inv.root.file_id)
 
74
        for path, ie in inv.iter_entries():
 
75
            e.append(self._pack_entry(ie))
 
76
        return e
 
77
 
 
78
 
 
79
    def _pack_entry(self, ie):
 
80
        """Convert InventoryEntry to XML element"""
 
81
        e = Element('entry')
 
82
        e.set('name', ie.name)
 
83
        e.set('file_id', ie.file_id)
 
84
        e.set('kind', ie.kind)
 
85
 
 
86
        if ie.text_size != None:
 
87
            e.set('text_size', '%d' % ie.text_size)
 
88
 
 
89
        for f in ['text_id', 'text_sha1']:
 
90
            v = getattr(ie, f)
 
91
            if v != None:
 
92
                e.set(f, v)
 
93
 
 
94
        # to be conservative, we don't externalize the root pointers
 
95
        # for now, leaving them as null in the xml form.  in a future
 
96
        # version it will be implied by nested elements.
 
97
        if ie.parent_id != ROOT_ID:
 
98
            assert isinstance(ie.parent_id, basestring)
 
99
            e.set('parent_id', ie.parent_id)
 
100
 
 
101
        e.tail = '\n'
 
102
 
 
103
        return e
 
104
 
 
105
 
 
106
    def _unpack_inventory(self, elt):
 
107
        """Construct from XML Element
 
108
        """
 
109
        assert elt.tag == 'inventory'
 
110
        root_id = elt.get('file_id') or ROOT_ID
 
111
        inv = Inventory(root_id)
 
112
        for e in elt:
 
113
            ie = self._unpack_entry(e)
 
114
            if ie.parent_id == ROOT_ID:
 
115
                ie.parent_id = root_id
 
116
            inv.add(ie)
 
117
        return inv
 
118
 
 
119
 
 
120
    def _unpack_entry(self, elt):
 
121
        assert elt.tag == 'entry'
 
122
 
 
123
        ## original format inventories don't have a parent_id for
 
124
        ## nodes in the root directory, but it's cleaner to use one
 
125
        ## internally.
 
126
        parent_id = elt.get('parent_id')
 
127
        if parent_id == None:
 
128
            parent_id = ROOT_ID
 
129
 
 
130
        ie = InventoryEntry(elt.get('file_id'),
 
131
                              elt.get('name'),
 
132
                              elt.get('kind'),
 
133
                              parent_id)
 
134
        ie.text_id = elt.get('text_id')
 
135
        ie.text_sha1 = elt.get('text_sha1')
 
136
 
 
137
        ## mutter("read inventoryentry: %r" % (elt.attrib))
 
138
 
 
139
        v = elt.get('text_size')
 
140
        ie.text_size = v and int(v)
 
141
 
 
142
        return ie
 
143
 
 
144
 
 
145
    def _pack_revision(self, rev):
 
146
        """Revision object -> xml tree"""
 
147
        root = Element('revision',
 
148
                       committer = rev.committer,
 
149
                       timestamp = '%.9f' % rev.timestamp,
 
150
                       revision_id = rev.revision_id,
 
151
                       inventory_id = rev.inventory_id,
 
152
                       inventory_sha1 = rev.inventory_sha1,
 
153
                       )
 
154
        if rev.timezone:
 
155
            root.set('timezone', str(rev.timezone))
 
156
        root.text = '\n'
 
157
 
 
158
        msg = SubElement(root, 'message')
 
159
        msg.text = rev.message
 
160
        msg.tail = '\n'
 
161
 
 
162
        if rev.parents:
 
163
            pelts = SubElement(root, 'parents')
 
164
            pelts.tail = pelts.text = '\n'
 
165
            for rr in rev.parents:
 
166
                assert isinstance(rr, RevisionReference)
 
167
                p = SubElement(pelts, 'revision_ref')
 
168
                p.tail = '\n'
 
169
                assert rr.revision_id
 
170
                p.set('revision_id', rr.revision_id)
 
171
                if rr.revision_sha1:
 
172
                    p.set('revision_sha1', rr.revision_sha1)
 
173
 
 
174
        return root
 
175
 
 
176
    
 
177
    def _unpack_revision(self, elt):
 
178
        """XML Element -> Revision object"""
 
179
        
 
180
        # <changeset> is deprecated...
 
181
        if elt.tag not in ('revision', 'changeset'):
 
182
            raise BzrError("unexpected tag in revision file: %r" % elt)
 
183
 
 
184
        rev = Revision(committer = elt.get('committer'),
 
185
                       timestamp = float(elt.get('timestamp')),
 
186
                       revision_id = elt.get('revision_id'),
 
187
                       inventory_id = elt.get('inventory_id'),
 
188
                       inventory_sha1 = elt.get('inventory_sha1')
 
189
                       )
 
190
 
 
191
        precursor = elt.get('precursor')
 
192
        precursor_sha1 = elt.get('precursor_sha1')
 
193
 
 
194
        pelts = elt.find('parents')
 
195
 
 
196
        if pelts:
 
197
            for p in pelts:
 
198
                assert p.tag == 'revision_ref', \
 
199
                       "bad parent node tag %r" % p.tag
 
200
                rev_ref = RevisionReference(p.get('revision_id'),
 
201
                                            p.get('revision_sha1'))
 
202
                rev.parents.append(rev_ref)
 
203
 
 
204
            if precursor:
 
205
                # must be consistent
 
206
                prec_parent = rev.parents[0].revision_id
 
207
                assert prec_parent == precursor
 
208
        elif precursor:
 
209
            # revisions written prior to 0.0.5 have a single precursor
 
210
            # give as an attribute
 
211
            rev_ref = RevisionReference(precursor, precursor_sha1)
 
212
            rev.parents.append(rev_ref)
 
213
 
 
214
        v = elt.get('timezone')
 
215
        rev.timezone = v and int(v)
 
216
 
 
217
        rev.message = elt.findtext('message') # text of <message>
 
218
        return rev
 
219
 
 
220
 
 
221
 
 
222
class _Serializer_v5(Serializer):
 
223
    """Version 5 serializer
 
224
 
 
225
    Packs objects into XML and vice versa.
 
226
 
 
227
    You should use the serialzer_v5 singleton."""
 
228
    
 
229
    __slots__ = []
 
230
    
 
231
    def _pack_inventory(self, inv):
 
232
        """Convert to XML Element"""
 
233
        e = Element('inventory')
 
234
        e.text = '\n'
 
235
        if inv.root.file_id not in (None, ROOT_ID):
 
236
            e.set('file_id', inv.root.file_id)
 
237
        for path, ie in inv.iter_entries():
 
238
            e.append(self._pack_entry(ie))
 
239
        return e
 
240
 
 
241
 
 
242
    def _pack_entry(self, ie):
 
243
        """Convert InventoryEntry to XML element"""
 
244
        assert ie.kind == 'directory' or ie.kind == 'file'
 
245
        e = Element(ie.kind)
 
246
        e.set('name', ie.name)
 
247
        e.set('file_id', ie.file_id)
 
248
 
 
249
        if ie.text_size != None:
 
250
            e.set('text_size', '%d' % ie.text_size)
 
251
 
 
252
        for f in ['text_version', 'text_sha1', 'entry_version']:
 
253
            v = getattr(ie, f)
 
254
            if v != None:
 
255
                e.set(f, v)
 
256
 
 
257
        # to be conservative, we don't externalize the root pointers
 
258
        # for now, leaving them as null in the xml form.  in a future
 
259
        # version it will be implied by nested elements.
 
260
        if ie.parent_id != ROOT_ID:
 
261
            assert isinstance(ie.parent_id, basestring)
 
262
            e.set('parent_id', ie.parent_id)
 
263
 
 
264
        e.tail = '\n'
 
265
 
 
266
        return e
 
267
 
 
268
 
 
269
    def _pack_revision(self, rev):
 
270
        """Revision object -> xml tree"""
 
271
        root = Element('revision',
 
272
                       committer = rev.committer,
 
273
                       timestamp = '%.9f' % rev.timestamp,
 
274
                       revision_id = rev.revision_id,
 
275
                       inventory_id = rev.inventory_id,
 
276
                       inventory_sha1 = rev.inventory_sha1,
 
277
                       )
 
278
        if rev.timezone:
 
279
            root.set('timezone', str(rev.timezone))
 
280
        root.text = '\n'
 
281
 
 
282
        msg = SubElement(root, 'message')
 
283
        msg.text = rev.message
 
284
        msg.tail = '\n'
 
285
 
 
286
        if rev.parents:
 
287
            pelts = SubElement(root, 'parents')
 
288
            pelts.tail = pelts.text = '\n'
 
289
            for rr in rev.parents:
 
290
                assert isinstance(rr, RevisionReference)
 
291
                p = SubElement(pelts, 'revision_ref')
 
292
                p.tail = '\n'
 
293
                assert rr.revision_id
 
294
                p.set('revision_id', rr.revision_id)
 
295
 
 
296
        return root
 
297
 
 
298
    
 
299
 
 
300
    def _unpack_inventory(self, elt):
 
301
        """Construct from XML Element
 
302
        """
 
303
        assert elt.tag == 'inventory'
 
304
        root_id = elt.get('file_id') or ROOT_ID
 
305
        inv = Inventory(root_id)
 
306
        for e in elt:
 
307
            ie = self._unpack_entry(e)
 
308
            if ie.parent_id == ROOT_ID:
 
309
                ie.parent_id = root_id
 
310
            inv.add(ie)
 
311
        return inv
 
312
 
 
313
 
 
314
    def _unpack_entry(self, elt):
 
315
        kind = elt.tag
 
316
        assert kind == 'directory' or kind == 'file'
 
317
 
 
318
        parent_id = elt.get('parent_id')
 
319
        if parent_id == None:
 
320
            parent_id = ROOT_ID
 
321
 
 
322
        ie = InventoryEntry(elt.get('file_id'),
 
323
                            elt.get('name'),
 
324
                            kind,
 
325
                            parent_id)
 
326
        ie.text_version = elt.get('text_version')
 
327
        ie.entry_version = elt.get('entry_version')
 
328
        ie.text_sha1 = elt.get('text_sha1')
 
329
        v = elt.get('text_size')
 
330
        ie.text_size = v and int(v)
 
331
 
 
332
        return ie
 
333
 
 
334
 
 
335
    def _unpack_revision(self, elt):
 
336
        """XML Element -> Revision object"""
 
337
        assert elt.tag == 'revision'
 
338
        
 
339
        rev = Revision(committer = elt.get('committer'),
 
340
                       timestamp = float(elt.get('timestamp')),
 
341
                       revision_id = elt.get('revision_id'),
 
342
                       inventory_id = elt.get('inventory_id'),
 
343
                       inventory_sha1 = elt.get('inventory_sha1')
 
344
                       )
 
345
 
 
346
        for p in elt.find('parents'):
 
347
            assert p.tag == 'revision_ref', \
 
348
                   "bad parent node tag %r" % p.tag
 
349
            rev_ref = RevisionReference(p.get('revision_id'))
 
350
            rev.parents.append(rev_ref)
 
351
 
 
352
        v = elt.get('timezone')
 
353
        rev.timezone = v and int(v)
 
354
 
 
355
        rev.message = elt.findtext('message') # text of <message>
 
356
        return rev
 
357
 
 
358
 
 
359
 
 
360
"""singleton instance"""
 
361
serializer_v4 = _Serializer_v4()
 
362
 
 
363
serializer_v5 = _Serializer_v5()