/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

  • Committer: Martin Pool
  • Date: 2006-01-12 06:37:23 UTC
  • mfrom: (1534.1.6 integration)
  • Revision ID: mbp@sourcefrog.net-20060112063723-4ec91b5ff30f0830
[merge] robertc-integration

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
as remote (such as http or sftp).
20
20
"""
21
21
 
 
22
import errno
 
23
from copy import deepcopy
 
24
import sys
 
25
from unittest import TestSuite
 
26
 
22
27
from bzrlib.trace import mutter
23
28
import bzrlib.errors as errors
24
 
import errno
25
 
import sys
26
29
 
27
30
_protocol_handlers = {
28
31
}
40
43
        _protocol_handlers[prefix] = klass
41
44
 
42
45
 
 
46
def _get_protocol_handlers():
 
47
    """Return a dictionary of prefix:transport-factories."""
 
48
    return _protocol_handlers
 
49
 
 
50
 
 
51
def _set_protocol_handlers(new_handlers):
 
52
    """Replace the current protocol handlers dictionary.
 
53
 
 
54
    WARNING this will remove all build in protocols. Use with care.
 
55
    """
 
56
    global _protocol_handlers
 
57
    _protocol_handlers = new_handlers
 
58
 
 
59
 
 
60
def _get_transport_modules():
 
61
    """Return a list of the modules providing transports."""
 
62
    modules = set()
 
63
    for prefix, factory in _protocol_handlers.items():
 
64
        if factory.__module__ == "bzrlib.transport":
 
65
            # this is a lazy load transport, because no real ones
 
66
            # are directlry in bzrlib.transport
 
67
            modules.add(factory.module)
 
68
        else:
 
69
            modules.add(factory.__module__)
 
70
    result = list(modules)
 
71
    result.sort()
 
72
    return result
 
73
 
 
74
 
43
75
class Transport(object):
44
76
    """This class encapsulates methods for retrieving or putting a file
45
77
    from/to a storage location.
159
191
        """
160
192
        # TODO: This might want to use bzrlib.osutils.relpath
161
193
        #       but we have to watch out because of the prefix issues
162
 
        if not abspath.startswith(self.base):
 
194
        if not (abspath == self.base[:-1] or abspath.startswith(self.base)):
163
195
            raise errors.PathNotChild(abspath, self.base)
164
196
        pl = len(self.base)
165
 
        return abspath[pl:].lstrip('/')
166
 
 
 
197
        return abspath[pl:].strip('/')
167
198
 
168
199
    def has(self, relpath):
169
200
        """Does the file relpath exist?
195
226
        As with other listing functions, only some transports implement this,.
196
227
        you may check via is_listable to determine if it will.
197
228
        """
198
 
        raise NotImplementedError
 
229
        raise errors.TransportNotPossible("This transport has not "
 
230
                                          "implemented iter_files_recursive "
 
231
                                          "(but must claim to be listable "
 
232
                                          "to trigger this error).")
199
233
 
200
234
    def get(self, relpath):
201
235
        """Get the file at the given relative path.
232
266
        raise NotImplementedError
233
267
 
234
268
    def put_multi(self, files, mode=None, pb=None):
235
 
        """Put a set of files or strings into the location.
 
269
        """Put a set of files into the location.
236
270
 
237
271
        :param files: A list of tuples of relpath, file object [(path1, file1), (path2, file2),...]
238
272
        :param pb:  An optional ProgressBar for indicating percent done.
269
303
        return self._iterate_over(files, self.append, pb, 'append', expand=True)
270
304
 
271
305
    def copy(self, rel_from, rel_to):
272
 
        """Copy the item at rel_from to the location at rel_to"""
273
 
        raise NotImplementedError
 
306
        """Copy the item at rel_from to the location at rel_to.
 
307
        
 
308
        Override this for efficiency if a specific transport can do it 
 
309
        faster than this default implementation.
 
310
        """
 
311
        self.put(rel_to, self.get(rel_from))
274
312
 
275
313
    def copy_multi(self, relpaths, pb=None):
276
314
        """Copy a bunch of entries.
297
335
 
298
336
 
299
337
    def move(self, rel_from, rel_to):
300
 
        """Move the item at rel_from to the location at rel_to"""
301
 
        raise NotImplementedError
 
338
        """Move the item at rel_from to the location at rel_to.
 
339
        
 
340
        If a transport can directly implement this it is suggested that
 
341
        it do so for efficiency.
 
342
        """
 
343
        self.copy(rel_from, rel_to)
 
344
        self.delete(rel_from)
302
345
 
303
346
    def move_multi(self, relpaths, pb=None):
304
347
        """Move a bunch of entries.
362
405
        it if at all possible.
363
406
        """
364
407
        raise errors.TransportNotPossible("This transport has not "
365
 
                                          "implemented list_dir.")
 
408
                                          "implemented list_dir "
 
409
                                          "(but must claim to be listable "
 
410
                                          "to trigger this error).")
366
411
 
367
412
    def lock_read(self, relpath):
368
413
        """Lock the given file for shared (read) access.
380
425
        """
381
426
        raise NotImplementedError
382
427
 
 
428
    def is_readonly(self):
 
429
        """Return true if this connection cannot be written to."""
 
430
        return False
 
431
 
383
432
 
384
433
def get_transport(base):
385
434
    global _protocol_handlers
405
454
        mod = __import__(module, globals(), locals(), [classname])
406
455
        klass = getattr(mod, classname)
407
456
        return klass(base)
 
457
    _loader.module = module
408
458
    register_transport(scheme, _loader)
409
459
 
410
460
 
415
465
    return urllib.quote(relpath)
416
466
 
417
467
 
 
468
class Server(object):
 
469
    """A Transport Server.
 
470
    
 
471
    The Server interface provides a server for a given transport. We use
 
472
    these servers as loopback testing tools. For any given transport the
 
473
    Servers it provides must either allow writing, or serve the contents
 
474
    of os.getcwdu() at the time setUp is called.
 
475
    
 
476
    Note that these are real servers - they must implement all the things
 
477
    that we want bzr transports to take advantage of.
 
478
    """
 
479
 
 
480
    def setUp(self):
 
481
        """Setup the server to service requests."""
 
482
 
 
483
    def tearDown(self):
 
484
        """Remove the server and cleanup any resources it owns."""
 
485
 
 
486
    def get_url(self):
 
487
        """Return a url for this server.
 
488
        
 
489
        If the transport does not represent a disk directory (i.e. it is 
 
490
        a database like svn, or a memory only transport, it should return
 
491
        a connection to a newly established resource for this Server.
 
492
        Otherwise it should return a url that will provide access to the path
 
493
        that was os.getcwdu() when setUp() was called.
 
494
        
 
495
        Subsequent calls will return the same resource.
 
496
        """
 
497
        raise NotImplementedError
 
498
 
 
499
    def get_bogus_url(self):
 
500
        """Return a url for this protocol, that will fail to connect."""
 
501
        raise NotImplementedError
 
502
 
 
503
 
 
504
class TransportTestProviderAdapter(object):
 
505
    """A tool to generate a suite testing all transports for a single test.
 
506
 
 
507
    This is done by copying the test once for each transport and injecting
 
508
    the transport_class and transport_server classes into each copy. Each copy
 
509
    is also given a new id() to make it easy to identify.
 
510
    """
 
511
 
 
512
    def adapt(self, test):
 
513
        result = TestSuite()
 
514
        for klass, server_factory in self._test_permutations():
 
515
            new_test = deepcopy(test)
 
516
            new_test.transport_class = klass
 
517
            new_test.transport_server = server_factory
 
518
            def make_new_test_id():
 
519
                new_id = "%s(%s)" % (new_test.id(), server_factory.__name__)
 
520
                return lambda: new_id
 
521
            new_test.id = make_new_test_id()
 
522
            result.addTest(new_test)
 
523
        return result
 
524
 
 
525
    def get_transport_test_permutations(self, module):
 
526
        """Get the permutations module wants to have tested."""
 
527
        return module.get_test_permutations()
 
528
 
 
529
    def _test_permutations(self):
 
530
        """Return a list of the klass, server_factory pairs to test."""
 
531
        result = []
 
532
        for module in _get_transport_modules():
 
533
            result.extend(self.get_transport_test_permutations(reduce(getattr, 
 
534
                (module).split('.')[1:],
 
535
                 __import__(module))))
 
536
        return result
 
537
        
 
538
 
418
539
# None is the default transport, for things with no url scheme
419
540
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
420
541
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
423
544
register_lazy_transport('https://', 'bzrlib.transport.http', 'HttpTransport')
424
545
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
425
546
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
 
547
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')