/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

Updated from 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
25
32
from stat import *
26
33
import sys
27
34
from unittest import TestSuite
 
35
import urllib
28
36
 
29
 
from bzrlib.trace import mutter
 
37
from bzrlib.trace import mutter, warning
30
38
import bzrlib.errors as errors
 
39
from bzrlib.errors import DependencyNotPresent
 
40
from bzrlib.symbol_versioning import *
31
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.
32
46
_protocol_handlers = {
33
47
}
34
48
 
35
 
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.
36
58
    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
 
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)
46
85
 
47
86
 
48
87
def _get_protocol_handlers():
49
 
    """Return a dictionary of prefix:transport-factories."""
 
88
    """Return a dictionary of {urlprefix: [factory]}"""
50
89
    return _protocol_handlers
51
90
 
52
91
 
59
98
    _protocol_handlers = new_handlers
60
99
 
61
100
 
 
101
def _clear_protocol_handlers():
 
102
    global _protocol_handlers
 
103
    _protocol_handlers = {}
 
104
 
 
105
 
62
106
def _get_transport_modules():
63
107
    """Return a list of the modules providing transports."""
64
108
    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__)
 
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__)
72
117
    result = list(modules)
73
118
    result.sort()
74
119
    return result
110
155
                raise errors.PermissionDenied(path, extra=e)
111
156
            if e.errno == errno.ENOTEMPTY:
112
157
                raise errors.DirectoryNotEmpty(path, extra=e)
 
158
            if e.errno == errno.EBUSY:
 
159
                raise errors.ResourceBusy(path, extra=e)
113
160
        if raise_generic:
114
161
            raise errors.TransportError(orig_error=e)
115
162
 
118
165
        using a subdirectory or parent directory. This allows connections 
119
166
        to be pooled, rather than a new one needed for each subdir.
120
167
        """
121
 
        raise NotImplementedError
 
168
        raise NotImplementedError(self.clone)
122
169
 
123
170
    def should_cache(self):
124
171
        """Return True if the data pulled across should be cached locally.
184
231
        XXX: Robert Collins 20051016 - is this really needed in the public
185
232
             interface ?
186
233
        """
187
 
        raise NotImplementedError
 
234
        raise NotImplementedError(self.abspath)
188
235
 
189
236
    def relpath(self, abspath):
190
237
        """Return the local path portion from a given absolute path.
208
255
        is not part of the protocol.  In other words, the results of 
209
256
        t.has("a_directory_name") are undefined."
210
257
        """
211
 
        raise NotImplementedError
 
258
        raise NotImplementedError(self.has)
212
259
 
213
260
    def has_multi(self, relpaths, pb=None):
214
261
        """Return True/False for each entry in relpaths"""
244
291
 
245
292
        :param relpath: The relative path to the file
246
293
        """
247
 
        raise NotImplementedError
 
294
        raise NotImplementedError(self.get)
248
295
 
249
296
    def readv(self, relpath, offsets):
250
297
        """Get parts of the file at the given relative path.
315
362
        :param mode: The mode for the newly created file, 
316
363
                     None means just use the default
317
364
        """
318
 
        raise NotImplementedError
 
365
        raise NotImplementedError(self.put)
319
366
 
320
367
    def put_multi(self, files, mode=None, pb=None):
321
368
        """Put a set of files into the location.
331
378
 
332
379
    def mkdir(self, relpath, mode=None):
333
380
        """Create a directory at the given path."""
334
 
        raise NotImplementedError
 
381
        raise NotImplementedError(self.mkdir)
335
382
 
336
383
    def mkdir_multi(self, relpaths, mode=None, pb=None):
337
384
        """Create a group of directories"""
345
392
 
346
393
        returns the length of f before the content was written to it.
347
394
        """
348
 
        raise NotImplementedError
 
395
        raise NotImplementedError(self.append)
349
396
 
350
397
    def append_multi(self, files, pb=None):
351
398
        """Append the text in each file-like or string object to
463
510
        """
464
511
        # This is not implemented, because you need to do special tricks to
465
512
        # extract the basename, and add it to rel_to
466
 
        raise NotImplementedError
 
513
        raise NotImplementedError(self.move_multi_to)
467
514
 
468
515
    def delete(self, relpath):
469
516
        """Delete the item at relpath"""
470
 
        raise NotImplementedError
 
517
        raise NotImplementedError(self.delete)
471
518
 
472
519
    def delete_multi(self, relpaths, pb=None):
473
520
        """Queue up a bunch of deletes to be done.
510
557
        ALSO NOTE: Stats of directories may not be supported on some 
511
558
        transports.
512
559
        """
513
 
        raise NotImplementedError
 
560
        raise NotImplementedError(self.stat)
514
561
 
515
562
    def rmdir(self, relpath):
516
563
        """Remove a directory at the given path."""
530
577
 
531
578
    def listable(self):
532
579
        """Return True if this store supports listing."""
533
 
        raise NotImplementedError
 
580
        raise NotImplementedError(self.listable)
534
581
 
535
582
    def list_dir(self, relpath):
536
583
        """Return a list of all files at the given location.
548
595
 
549
596
        :return: A lock object, which should contain an unlock() function.
550
597
        """
551
 
        raise NotImplementedError
 
598
        raise NotImplementedError(self.lock_read)
552
599
 
553
600
    def lock_write(self, relpath):
554
601
        """Lock the given file for exclusive (write) access.
556
603
 
557
604
        :return: A lock object, which should contain an unlock() function.
558
605
        """
559
 
        raise NotImplementedError
 
606
        raise NotImplementedError(self.lock_write)
560
607
 
561
608
    def is_readonly(self):
562
609
        """Return true if this connection cannot be written to."""
568
615
 
569
616
    base is either a URL or a directory name.  
570
617
    """
 
618
    # TODO: give a better error if base looks like a url but there's no
 
619
    # handler for the scheme?
571
620
    global _protocol_handlers
572
621
    if base is None:
573
622
        base = u'.'
574
623
    else:
575
624
        base = unicode(base)
576
 
    for proto, klass in _protocol_handlers.iteritems():
 
625
    for proto, factory_list in _protocol_handlers.iteritems():
577
626
        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)
 
627
            t = _try_transport_factories(base, factory_list)
 
628
            if t:
 
629
                return t
 
630
    # The default handler is the filesystem handler, stored as protocol None
 
631
    return _try_transport_factories(base, _protocol_handlers[None])
 
632
 
 
633
 
 
634
def _try_transport_factories(base, factory_list):
 
635
    for factory in factory_list:
 
636
        try:
 
637
            return factory(base)
 
638
        except DependencyNotPresent, e:
 
639
            mutter("failed to instantiate transport %r for %r: %r" %
 
640
                    (factory, base, e))
 
641
            continue
 
642
    return None
596
643
 
597
644
 
598
645
def urlescape(relpath):
599
646
    """Escape relpath to be a valid url."""
600
647
    # TODO utf8 it first. utf8relpath = relpath.encode('utf8')
601
 
    import urllib
602
648
    return urllib.quote(relpath)
603
649
 
604
650
 
605
651
def urlunescape(relpath):
606
652
    """Unescape relpath from url format."""
607
 
    import urllib
608
653
    return urllib.unquote(relpath)
609
654
    # TODO de-utf8 it last. relpath = utf8relpath.decode('utf8')
610
655
 
668
713
 
669
714
    def get_transport_test_permutations(self, module):
670
715
        """Get the permutations module wants to have tested."""
 
716
        if not hasattr(module, 'get_test_permutations'):
 
717
            warning("transport module %s doesn't provide get_test_permutations()"
 
718
                    % module.__name__)
 
719
            return []
671
720
        return module.get_test_permutations()
672
721
 
673
722
    def _test_permutations(self):
713
762
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
714
763
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
715
764
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')
 
765
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
 
766
                        'HttpTransport_urllib')
 
767
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
 
768
                        'HttpTransport_urllib')
 
769
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
 
770
                        'PyCurlTransport')
 
771
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
 
772
                        'PyCurlTransport')
 
773
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
 
774
                        'HttpTransport_urllib')
 
775
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
 
776
                        'HttpTransport_urllib')
 
777
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
 
778
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
718
779
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
719
780
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
720
781
register_lazy_transport('memory:/', 'bzrlib.transport.memory', 'MemoryTransport')
721
782
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
 
783
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')