~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
16
17
"""Transport is an abstraction layer to handle file access.
17
18
 
18
19
The abstraction is to allow access from the local filesystem, as well
19
20
as remote (such as http or sftp).
 
21
 
 
22
Transports are constructed from a string, being a URL or (as a degenerate
 
23
case) a local filesystem path.  This is typically the top directory of
 
24
a bzrdir, repository, or similar object we are interested in working with.
 
25
The Transport returned has methods to read, write and manipulate files within
 
26
it.
20
27
"""
21
28
 
22
29
import errno
27
34
from unittest import TestSuite
28
35
import urllib
29
36
 
30
 
from bzrlib.trace import mutter
 
37
from bzrlib.trace import mutter, warning
31
38
import bzrlib.errors as errors
 
39
from bzrlib.errors import DependencyNotPresent
 
40
from bzrlib.symbol_versioning import *
32
41
 
 
42
# {prefix: [transport_classes]}
 
43
# Transports are inserted onto the list LIFO and tried in order; as a result
 
44
# transports provided by plugins are tried first, which is usually what we
 
45
# want.
33
46
_protocol_handlers = {
34
47
}
35
48
 
36
 
def register_transport(prefix, klass, override=True):
 
49
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
 
50
    """Register a transport that can be used to open URLs
 
51
 
 
52
    Normally you should use register_lazy_transport, which defers loading the
 
53
    implementation until it's actually used, and so avoids pulling in possibly
 
54
    large implementation libraries.
 
55
    """
 
56
    # Note that this code runs very early in library setup -- trace may not be
 
57
    # working, etc.
37
58
    global _protocol_handlers
38
 
    # trace messages commented out because they're typically 
39
 
    # run during import before trace is set up
40
 
    if _protocol_handlers.has_key(prefix):
41
 
        if override:
42
 
            ## mutter('overriding transport: %s => %s' % (prefix, klass.__name__))
43
 
            _protocol_handlers[prefix] = klass
44
 
    else:
45
 
        ## mutter('registering transport: %s => %s' % (prefix, klass.__name__))
46
 
        _protocol_handlers[prefix] = klass
 
59
    if deprecated_passed(override):
 
60
        warn("register_transport(override) is deprecated")
 
61
    _protocol_handlers.setdefault(prefix, []).insert(0, klass)
 
62
 
 
63
 
 
64
def register_lazy_transport(scheme, module, classname):
 
65
    """Register lazy-loaded transport class.
 
66
 
 
67
    When opening a URL with the given scheme, load the module and then
 
68
    instantiate the particular class.  
 
69
 
 
70
    If the module raises DependencyNotPresent when it's imported, it is
 
71
    skipped and another implementation of the protocol is tried.  This is
 
72
    intended to be used when the implementation depends on an external
 
73
    implementation that may not be present.  If any other error is raised, it
 
74
    propagates up and the attempt to open the url fails.
 
75
    """
 
76
    # TODO: If no implementation of a protocol is available because of missing
 
77
    # dependencies, we should perhaps show the message about what dependency
 
78
    # was missing.
 
79
    def _loader(base):
 
80
        mod = __import__(module, globals(), locals(), [classname])
 
81
        klass = getattr(mod, classname)
 
82
        return klass(base)
 
83
    _loader.module = module
 
84
    register_transport(scheme, _loader)
47
85
 
48
86
 
49
87
def _get_protocol_handlers():
50
 
    """Return a dictionary of prefix:transport-factories."""
 
88
    """Return a dictionary of {urlprefix: [factory]}"""
51
89
    return _protocol_handlers
52
90
 
53
91
 
60
98
    _protocol_handlers = new_handlers
61
99
 
62
100
 
 
101
def _clear_protocol_handlers():
 
102
    global _protocol_handlers
 
103
    _protocol_handlers = {}
 
104
 
 
105
 
63
106
def _get_transport_modules():
64
107
    """Return a list of the modules providing transports."""
65
108
    modules = set()
66
 
    for prefix, factory in _protocol_handlers.items():
67
 
        if factory.__module__ == "bzrlib.transport":
68
 
            # this is a lazy load transport, because no real ones
69
 
            # are directlry in bzrlib.transport
70
 
            modules.add(factory.module)
71
 
        else:
72
 
            modules.add(factory.__module__)
 
109
    for prefix, factory_list in _protocol_handlers.items():
 
110
        for factory in factory_list:
 
111
            if factory.__module__ == "bzrlib.transport":
 
112
                # this is a lazy load transport, because no real ones
 
113
                # are directlry in bzrlib.transport
 
114
                modules.add(factory.module)
 
115
            else:
 
116
                modules.add(factory.__module__)
73
117
    result = list(modules)
74
118
    result.sort()
75
119
    return result
119
163
        using a subdirectory or parent directory. This allows connections 
120
164
        to be pooled, rather than a new one needed for each subdir.
121
165
        """
122
 
        raise NotImplementedError
 
166
        raise NotImplementedError(self.clone)
123
167
 
124
168
    def should_cache(self):
125
169
        """Return True if the data pulled across should be cached locally.
185
229
        XXX: Robert Collins 20051016 - is this really needed in the public
186
230
             interface ?
187
231
        """
188
 
        raise NotImplementedError
 
232
        raise NotImplementedError(self.abspath)
189
233
 
190
234
    def relpath(self, abspath):
191
235
        """Return the local path portion from a given absolute path.
209
253
        is not part of the protocol.  In other words, the results of 
210
254
        t.has("a_directory_name") are undefined."
211
255
        """
212
 
        raise NotImplementedError
 
256
        raise NotImplementedError(self.has)
213
257
 
214
258
    def has_multi(self, relpaths, pb=None):
215
259
        """Return True/False for each entry in relpaths"""
245
289
 
246
290
        :param relpath: The relative path to the file
247
291
        """
248
 
        raise NotImplementedError
 
292
        raise NotImplementedError(self.get)
249
293
 
250
294
    def readv(self, relpath, offsets):
251
295
        """Get parts of the file at the given relative path.
316
360
        :param mode: The mode for the newly created file, 
317
361
                     None means just use the default
318
362
        """
319
 
        raise NotImplementedError
 
363
        raise NotImplementedError(self.put)
320
364
 
321
365
    def put_multi(self, files, mode=None, pb=None):
322
366
        """Put a set of files into the location.
332
376
 
333
377
    def mkdir(self, relpath, mode=None):
334
378
        """Create a directory at the given path."""
335
 
        raise NotImplementedError
 
379
        raise NotImplementedError(self.mkdir)
336
380
 
337
381
    def mkdir_multi(self, relpaths, mode=None, pb=None):
338
382
        """Create a group of directories"""
346
390
 
347
391
        returns the length of f before the content was written to it.
348
392
        """
349
 
        raise NotImplementedError
 
393
        raise NotImplementedError(self.append)
350
394
 
351
395
    def append_multi(self, files, pb=None):
352
396
        """Append the text in each file-like or string object to
464
508
        """
465
509
        # This is not implemented, because you need to do special tricks to
466
510
        # extract the basename, and add it to rel_to
467
 
        raise NotImplementedError
 
511
        raise NotImplementedError(self.move_multi_to)
468
512
 
469
513
    def delete(self, relpath):
470
514
        """Delete the item at relpath"""
471
 
        raise NotImplementedError
 
515
        raise NotImplementedError(self.delete)
472
516
 
473
517
    def delete_multi(self, relpaths, pb=None):
474
518
        """Queue up a bunch of deletes to be done.
511
555
        ALSO NOTE: Stats of directories may not be supported on some 
512
556
        transports.
513
557
        """
514
 
        raise NotImplementedError
 
558
        raise NotImplementedError(self.stat)
515
559
 
516
560
    def rmdir(self, relpath):
517
561
        """Remove a directory at the given path."""
531
575
 
532
576
    def listable(self):
533
577
        """Return True if this store supports listing."""
534
 
        raise NotImplementedError
 
578
        raise NotImplementedError(self.listable)
535
579
 
536
580
    def list_dir(self, relpath):
537
581
        """Return a list of all files at the given location.
549
593
 
550
594
        :return: A lock object, which should contain an unlock() function.
551
595
        """
552
 
        raise NotImplementedError
 
596
        raise NotImplementedError(self.lock_read)
553
597
 
554
598
    def lock_write(self, relpath):
555
599
        """Lock the given file for exclusive (write) access.
557
601
 
558
602
        :return: A lock object, which should contain an unlock() function.
559
603
        """
560
 
        raise NotImplementedError
 
604
        raise NotImplementedError(self.lock_write)
561
605
 
562
606
    def is_readonly(self):
563
607
        """Return true if this connection cannot be written to."""
569
613
 
570
614
    base is either a URL or a directory name.  
571
615
    """
 
616
    # TODO: give a better error if base looks like a url but there's no
 
617
    # handler for the scheme?
572
618
    global _protocol_handlers
573
619
    if base is None:
574
620
        base = u'.'
575
621
    else:
576
622
        base = unicode(base)
577
 
    for proto, klass in _protocol_handlers.iteritems():
 
623
    for proto, factory_list in _protocol_handlers.iteritems():
578
624
        if proto is not None and base.startswith(proto):
579
 
            return klass(base)
580
 
    # The default handler is the filesystem handler
581
 
    # which has a lookup of None
582
 
    return _protocol_handlers[None](base)
583
 
 
584
 
 
585
 
def register_lazy_transport(scheme, module, classname):
586
 
    """Register lazy-loaded transport class.
587
 
 
588
 
    When opening a URL with the given scheme, load the module and then
589
 
    instantiate the particular class.  
590
 
    """
591
 
    def _loader(base):
592
 
        mod = __import__(module, globals(), locals(), [classname])
593
 
        klass = getattr(mod, classname)
594
 
        return klass(base)
595
 
    _loader.module = module
596
 
    register_transport(scheme, _loader)
 
625
            t = _try_transport_factories(base, factory_list)
 
626
            if t:
 
627
                return t
 
628
    # The default handler is the filesystem handler, stored as protocol None
 
629
    return _try_transport_factories(base, _protocol_handlers[None])
 
630
 
 
631
 
 
632
def _try_transport_factories(base, factory_list):
 
633
    for factory in factory_list:
 
634
        try:
 
635
            return factory(base)
 
636
        except DependencyNotPresent, e:
 
637
            mutter("failed to instantiate transport %r for %r: %r" %
 
638
                    (factory, base, e))
 
639
            continue
 
640
    return None
597
641
 
598
642
 
599
643
def urlescape(relpath):
667
711
 
668
712
    def get_transport_test_permutations(self, module):
669
713
        """Get the permutations module wants to have tested."""
 
714
        if not hasattr(module, 'get_test_permutations'):
 
715
            warning("transport module %s doesn't provide get_test_permutations()"
 
716
                    % module.__name__)
 
717
            return []
670
718
        return module.get_test_permutations()
671
719
 
672
720
    def _test_permutations(self):
712
760
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
713
761
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
714
762
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
715
 
register_lazy_transport('http://', 'bzrlib.transport.http', 'HttpTransport')
716
 
register_lazy_transport('https://', 'bzrlib.transport.http', 'HttpTransport')
 
763
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
 
764
                        'HttpTransport_urllib')
 
765
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
 
766
                        'HttpTransport_urllib')
 
767
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
 
768
                        'PyCurlTransport')
 
769
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
 
770
                        'PyCurlTransport')
 
771
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
 
772
                        'HttpTransport_urllib')
 
773
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
 
774
                        'HttpTransport_urllib')
 
775
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
 
776
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
717
777
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
718
778
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
719
779
register_lazy_transport('memory:/', 'bzrlib.transport.memory', 'MemoryTransport')