~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Martin Pool
  • Date: 2005-04-15 01:31:21 UTC
  • Revision ID: mbp@sourcefrog.net-20050415013121-b18f1be12a735066
- Doc cleanups from Magnus Therning

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
 
18
# TODO: Maybe store inventory_id in the file?  Not really needed.
 
19
 
 
20
 
18
21
# This should really be an id randomly assigned when the tree is
19
22
# created, but it's not for now.
20
23
ROOT_ID = "TREE_ROOT"
21
24
 
22
25
 
23
26
import sys, os.path, types, re
 
27
from sets import Set
24
28
 
25
29
try:
26
30
    from cElementTree import Element, ElementTree, SubElement
28
32
    from elementtree.ElementTree import Element, ElementTree, SubElement
29
33
 
30
34
from xml import XMLMixin
31
 
from errors import bailout, BzrError, BzrCheckError
 
35
from errors import bailout, BzrError
32
36
 
33
37
import bzrlib
34
38
from bzrlib.osutils import uuid, quotefn, splitpath, joinpath, appendpath
97
101
 
98
102
    # TODO: split InventoryEntry into subclasses for files,
99
103
    # directories, etc etc.
100
 
 
101
 
    text_sha1 = None
102
 
    text_size = None
103
104
    
104
105
    def __init__(self, file_id, name, kind, parent_id, text_id=None):
105
106
        """Create an InventoryEntry
114
115
        '123'
115
116
        >>> e = InventoryEntry('123', 'src/hello.c', 'file', ROOT_ID)
116
117
        Traceback (most recent call last):
117
 
        BzrCheckError: InventoryEntry name 'src/hello.c' is invalid
 
118
        BzrError: ("InventoryEntry name is not a simple filename: 'src/hello.c'", [])
118
119
        """
119
 
        if '/' in name or '\\' in name:
120
 
            raise BzrCheckError('InventoryEntry name %r is invalid' % name)
 
120
        
 
121
        if len(splitpath(name)) != 1:
 
122
            bailout('InventoryEntry name is not a simple filename: %r'
 
123
                    % name)
121
124
        
122
125
        self.file_id = file_id
123
126
        self.name = name
124
127
        self.kind = kind
125
128
        self.text_id = text_id
126
129
        self.parent_id = parent_id
 
130
        self.text_sha1 = None
 
131
        self.text_size = None
127
132
        if kind == 'directory':
128
133
            self.children = {}
129
134
        elif kind == 'file':
144
149
                               self.parent_id, text_id=self.text_id)
145
150
        other.text_sha1 = self.text_sha1
146
151
        other.text_size = self.text_size
147
 
        # note that children are *not* copied; they're pulled across when
148
 
        # others are added
149
152
        return other
150
153
 
151
154
 
210
213
 
211
214
    from_element = classmethod(from_element)
212
215
 
213
 
    def __eq__(self, other):
 
216
    def __cmp__(self, other):
 
217
        if self is other:
 
218
            return 0
214
219
        if not isinstance(other, InventoryEntry):
215
220
            return NotImplemented
216
221
 
217
 
        return (self.file_id == other.file_id) \
218
 
               and (self.name == other.name) \
219
 
               and (self.text_sha1 == other.text_sha1) \
220
 
               and (self.text_size == other.text_size) \
221
 
               and (self.text_id == other.text_id) \
222
 
               and (self.parent_id == other.parent_id) \
223
 
               and (self.kind == other.kind)
224
 
 
225
 
 
226
 
    def __ne__(self, other):
227
 
        return not (self == other)
228
 
 
229
 
    def __hash__(self):
230
 
        raise ValueError('not hashable')
 
222
        return cmp(self.file_id, other.file_id) \
 
223
               or cmp(self.name, other.name) \
 
224
               or cmp(self.text_sha1, other.text_sha1) \
 
225
               or cmp(self.text_size, other.text_size) \
 
226
               or cmp(self.text_id, other.text_id) \
 
227
               or cmp(self.parent_id, other.parent_id) \
 
228
               or cmp(self.kind, other.kind)
231
229
 
232
230
 
233
231
 
239
237
        self.parent_id = None
240
238
        self.name = ''
241
239
 
242
 
    def __eq__(self, other):
 
240
    def __cmp__(self, other):
 
241
        if self is other:
 
242
            return 0
243
243
        if not isinstance(other, RootEntry):
244
244
            return NotImplemented
245
 
        
246
 
        return (self.file_id == other.file_id) \
247
 
               and (self.children == other.children)
 
245
        return cmp(self.file_id, other.file_id) \
 
246
               or cmp(self.children, other.children)
248
247
 
249
248
 
250
249
 
291
290
    </inventory>
292
291
 
293
292
    """
 
293
 
 
294
    ## TODO: Make sure only canonical filenames are stored.
 
295
 
 
296
    ## TODO: Do something sensible about the possible collisions on
 
297
    ## case-losing filesystems.  Perhaps we should just always forbid
 
298
    ## such collisions.
 
299
 
 
300
    ## TODO: No special cases for root, rather just give it a file id
 
301
    ## like everything else.
 
302
 
 
303
    ## TODO: Probably change XML serialization to use nesting rather
 
304
    ## than parent_id pointers.
 
305
 
 
306
    ## TODO: Perhaps hold the ElementTree in memory and work directly
 
307
    ## on that rather than converting into Python objects every time?
 
308
 
294
309
    def __init__(self):
295
310
        """Create or read an inventory.
296
311
 
328
343
            yield name, ie
329
344
            if ie.kind == 'directory':
330
345
                for cn, cie in self.iter_entries(from_dir=ie.file_id):
331
 
                    yield os.path.join(name, cn), cie
332
 
 
333
 
 
334
 
    def entries(self):
335
 
        """Return list of (path, ie) for all entries except the root.
336
 
 
337
 
        This may be faster than iter_entries.
 
346
                    yield '/'.join((name, cn)), cie
 
347
                    
 
348
 
 
349
 
 
350
    def directories(self):
 
351
        """Return (path, entry) pairs for all directories.
338
352
        """
339
 
        accum = []
340
 
        def descend(dir_ie, dir_path):
341
 
            kids = dir_ie.children.items()
342
 
            kids.sort()
343
 
            for name, ie in kids:
344
 
                child_path = os.path.join(dir_path, name)
345
 
                accum.append((child_path, ie))
 
353
        def descend(parent_ie):
 
354
            parent_name = parent_ie.name
 
355
            yield parent_name, parent_ie
 
356
 
 
357
            # directory children in sorted order
 
358
            dn = []
 
359
            for ie in parent_ie.children.itervalues():
346
360
                if ie.kind == 'directory':
347
 
                    descend(ie, child_path)
348
 
 
349
 
        descend(self.root, '')
350
 
        return accum
351
 
 
352
 
 
353
 
    def directories(self):
354
 
        """Return (path, entry) pairs for all directories, including the root.
355
 
        """
356
 
        accum = []
357
 
        def descend(parent_ie, parent_path):
358
 
            accum.append((parent_path, parent_ie))
 
361
                    dn.append((ie.name, ie))
 
362
            dn.sort()
359
363
            
360
 
            kids = [(ie.name, ie) for ie in parent_ie.children.itervalues() if ie.kind == 'directory']
361
 
            kids.sort()
 
364
            for name, child_ie in dn:
 
365
                for sub_name, sub_ie in descend(child_ie):
 
366
                    yield appendpath(parent_name, sub_name), sub_ie
362
367
 
363
 
            for name, child_ie in kids:
364
 
                child_path = os.path.join(parent_path, name)
365
 
                descend(child_ie, child_path)
366
 
        descend(self.root, '')
367
 
        return accum
 
368
        for name, ie in descend(self.root):
 
369
            yield name, ie
368
370
        
369
371
 
370
372
 
389
391
        >>> inv['123123'].name
390
392
        'hello.c'
391
393
        """
 
394
        if file_id == None:
 
395
            raise BzrError("can't look up file_id None")
 
396
            
392
397
        try:
393
398
            return self._byid[file_id]
394
399
        except KeyError:
395
 
            if file_id == None:
396
 
                raise BzrError("can't look up file_id None")
397
 
            else:
398
 
                raise BzrError("file_id {%s} not in inventory" % file_id)
399
 
 
400
 
 
401
 
    def get_file_kind(self, file_id):
402
 
        return self._byid[file_id].kind
 
400
            raise BzrError("file_id {%s} not in inventory" % file_id)
 
401
 
403
402
 
404
403
    def get_child(self, parent_id, filename):
405
404
        return self[parent_id].children.get(filename)
470
469
        del self[ie.parent_id].children[ie.name]
471
470
 
472
471
 
 
472
    def id_set(self):
 
473
        return Set(self._byid)
 
474
 
 
475
 
473
476
    def to_element(self):
474
477
        """Convert to XML Element"""
475
478
        e = Element('inventory')
498
501
    from_element = classmethod(from_element)
499
502
 
500
503
 
501
 
    def __eq__(self, other):
 
504
    def __cmp__(self, other):
502
505
        """Compare two sets by comparing their contents.
503
506
 
504
507
        >>> i1 = Inventory()
512
515
        >>> i1 == i2
513
516
        True
514
517
        """
 
518
        if self is other:
 
519
            return 0
 
520
        
515
521
        if not isinstance(other, Inventory):
516
522
            return NotImplemented
517
523
 
518
 
        if len(self._byid) != len(other._byid):
519
 
            # shortcut: obviously not the same
520
 
            return False
521
 
 
522
 
        return self._byid == other._byid
523
 
 
524
 
 
525
 
    def __ne__(self, other):
526
 
        return not (self == other)
527
 
 
528
 
 
529
 
    def __hash__(self):
530
 
        raise ValueError('not hashable')
531
 
 
 
524
        if self.id_set() ^ other.id_set():
 
525
            return 1
 
526
 
 
527
        for file_id in self._byid:
 
528
            c = cmp(self[file_id], other[file_id])
 
529
            if c: return c
 
530
 
 
531
        return 0
532
532
 
533
533
 
534
534
    def get_idpath(self, file_id):
555
555
 
556
556
        # get all names, skipping root
557
557
        p = [self[fid].name for fid in self.get_idpath(file_id)[1:]]
558
 
        return os.sep.join(p)
 
558
        return '/'.join(p)
559
559
            
560
560
 
561
561