~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

PyCurl range-request integration

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