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
17
"""Transport is an abstraction layer to handle file access.
18
19
The abstraction is to allow access from the local filesystem, as well
19
20
as remote (such as http or sftp).
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
27
34
from unittest import TestSuite
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 *
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
32
46
_protocol_handlers = {
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
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.
56
# Note that this code runs very early in library setup -- trace may not be
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):
41
## mutter('overriding transport: %s => %s' % (prefix, klass.__name__))
42
_protocol_handlers[prefix] = klass
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)
64
def register_lazy_transport(scheme, module, classname):
65
"""Register lazy-loaded transport class.
67
When opening a URL with the given scheme, load the module and then
68
instantiate the particular class.
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.
76
# TODO: If no implementation of a protocol is available because of missing
77
# dependencies, we should perhaps show the message about what dependency
80
mod = __import__(module, globals(), locals(), [classname])
81
klass = getattr(mod, classname)
83
_loader.module = module
84
register_transport(scheme, _loader)
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
59
98
_protocol_handlers = new_handlers
101
def _clear_protocol_handlers():
102
global _protocol_handlers
103
_protocol_handlers = {}
62
106
def _get_transport_modules():
63
107
"""Return a list of the modules providing transports."""
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)
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)
116
modules.add(factory.__module__)
72
117
result = list(modules)
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)
336
383
def mkdir_multi(self, relpaths, mode=None, pb=None):
337
384
"""Create a group of directories"""
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)
468
515
def delete(self, relpath):
469
516
"""Delete the item at relpath"""
470
raise NotImplementedError
517
raise NotImplementedError(self.delete)
472
519
def delete_multi(self, relpaths, pb=None):
473
520
"""Queue up a bunch of deletes to be done.
569
616
base is either a URL or a directory name.
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
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):
579
# The default handler is the filesystem handler
580
# which has a lookup of None
581
return _protocol_handlers[None](base)
584
def register_lazy_transport(scheme, module, classname):
585
"""Register lazy-loaded transport class.
587
When opening a URL with the given scheme, load the module and then
588
instantiate the particular class.
591
mod = __import__(module, globals(), locals(), [classname])
592
klass = getattr(mod, classname)
594
_loader.module = module
595
register_transport(scheme, _loader)
627
t = _try_transport_factories(base, factory_list)
630
# The default handler is the filesystem handler, stored as protocol None
631
return _try_transport_factories(base, _protocol_handlers[None])
634
def _try_transport_factories(base, factory_list):
635
for factory in factory_list:
638
except DependencyNotPresent, e:
639
mutter("failed to instantiate transport %r for %r: %r" %
598
645
def urlescape(relpath):
599
646
"""Escape relpath to be a valid url."""
600
647
# TODO utf8 it first. utf8relpath = relpath.encode('utf8')
602
648
return urllib.quote(relpath)
605
651
def urlunescape(relpath):
606
652
"""Unescape relpath from url format."""
608
653
return urllib.unquote(relpath)
609
654
# TODO de-utf8 it last. relpath = utf8relpath.decode('utf8')
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',
771
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
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')