~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/store/__init__.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-04-12 08:40:15 UTC
  • mfrom: (1651.1.7 bzr.mbp.escape-stores)
  • Revision ID: pqm@pqm.ubuntu.com-20060412084015-e6472a0717edbca6
(mbp) storage escaping, cleanups

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Development Ltd
 
1
# Copyright (C) 2005, 2006 by Canonical Development Ltd
2
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
34
34
from bzrlib.errors import BzrError, UnlistableStore, TransportNotPossible
35
35
from bzrlib.symbol_versioning import *
36
36
from bzrlib.trace import mutter
37
 
import bzrlib.transport as transport
 
37
from bzrlib.transport import Transport, urlescape
38
38
from bzrlib.transport.local import LocalTransport
39
39
 
40
40
######################################################################
238
238
        raise KeyError(fileid)
239
239
 
240
240
    def __init__(self, a_transport, prefixed=False, compressed=False,
241
 
                 dir_mode=None, file_mode=None):
242
 
        assert isinstance(a_transport, transport.Transport)
 
241
                 dir_mode=None, file_mode=None,
 
242
                 escaped=False):
 
243
        assert isinstance(a_transport, Transport)
243
244
        super(TransportStore, self).__init__()
244
245
        self._transport = a_transport
245
246
        self._prefixed = prefixed
246
247
        # FIXME RBC 20051128 this belongs in TextStore.
247
248
        self._compressed = compressed
248
249
        self._suffixes = set()
 
250
        self._escaped = escaped
249
251
 
250
252
        # It is okay for these to be None, it just means they
251
253
        # will just use the filesystem defaults
252
254
        self._dir_mode = dir_mode
253
255
        self._file_mode = file_mode
254
256
 
 
257
    def _unescape(self, file_id):
 
258
        """If filename escaping is enabled for this store, unescape and return the filename."""
 
259
        if self._escaped:
 
260
            return urllib.unquote(file_id)
 
261
        else:
 
262
            return file_id
 
263
 
255
264
    def _iter_files_recursive(self):
256
265
        """Iterate through the files in the transport."""
257
266
        for quoted_relpath in self._transport.iter_files_recursive():
 
267
            # transport iterator always returns quoted paths, regardless of
 
268
            # escaping
258
269
            yield urllib.unquote(quoted_relpath)
259
270
 
260
271
    def __iter__(self):
269
280
                    if name.endswith('.' + suffix):
270
281
                        skip = True
271
282
            if not skip:
272
 
                yield name
 
283
                yield self._unescape(name)
273
284
 
274
285
    def __len__(self):
275
286
        return len(list(self.__iter__()))
284
295
        else:
285
296
            suffixes = []
286
297
        if self._prefixed:
287
 
            path = [hash_prefix(fileid) + fileid]
 
298
            # hash_prefix adds the '/' separator
 
299
            prefix = self.hash_prefix(fileid)
288
300
        else:
289
 
            path = [fileid]
290
 
        path.extend(suffixes)
291
 
        return transport.urlescape(u'.'.join(path))
 
301
            prefix = ''
 
302
        fileid = self._escape_file_id(fileid)
 
303
        path = prefix + fileid
 
304
        full_path = u'.'.join([path] + suffixes)
 
305
        return urlescape(full_path)
 
306
 
 
307
    def _escape_file_id(self, file_id):
 
308
        """Turn a file id into a filesystem safe string.
 
309
 
 
310
        This is similar to a plain urllib.quote, except
 
311
        it uses specific safe characters, so that it doesn't
 
312
        have to translate a lot of valid file ids.
 
313
        """
 
314
        if not self._escaped:
 
315
            return file_id
 
316
        if isinstance(file_id, unicode):
 
317
            file_id = file_id.encode('utf-8')
 
318
        # @ does not get escaped. This is because it is a valid
 
319
        # filesystem character we use all the time, and it looks
 
320
        # a lot better than seeing %40 all the time.
 
321
        safe = "abcdefghijklmnopqrstuvwxyz0123456789-_@,."
 
322
        r = [((c in safe) and c or ('%%%02x' % ord(c)))
 
323
             for c in file_id]
 
324
        return ''.join(r)
 
325
 
 
326
    def hash_prefix(self, fileid):
 
327
        # fileid should be unescaped
 
328
        if self._escaped:
 
329
            fileid = self._escape_file_id(fileid)
 
330
        return "%02x/" % (adler32(fileid) & 0xff)
292
331
 
293
332
    def __repr__(self):
294
333
        if self._transport is None:
323
362
        return count, total
324
363
 
325
364
 
326
 
def ImmutableMemoryStore():
327
 
    return bzrlib.store.text.TextStore(transport.memory.MemoryTransport())
328
 
        
329
 
 
330
365
@deprecated_function(zero_eight)
331
366
def copy_all(store_from, store_to, pb=None):
332
367
    """Copy all ids from one store to another."""
333
368
    store_to.copy_all_ids(store_from, pb)
334
 
 
335
 
 
336
 
def hash_prefix(fileid):
337
 
    return "%02x/" % (adler32(fileid) & 0xff)
338
 
 
339