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
38
from bzrlib.trace import mutter, warning
30
39
import bzrlib.errors as errors
40
from bzrlib.errors import DependencyNotPresent
41
from bzrlib.symbol_versioning import *
43
# {prefix: [transport_classes]}
44
# Transports are inserted onto the list LIFO and tried in order; as a result
45
# transports provided by plugins are tried first, which is usually what we
32
47
_protocol_handlers = {
35
def register_transport(prefix, klass, override=True):
50
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
51
"""Register a transport that can be used to open URLs
53
Normally you should use register_lazy_transport, which defers loading the
54
implementation until it's actually used, and so avoids pulling in possibly
55
large implementation libraries.
57
# Note that this code runs very early in library setup -- trace may not be
36
59
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
60
if deprecated_passed(override):
61
warn("register_transport(override) is deprecated")
62
_protocol_handlers.setdefault(prefix, []).insert(0, klass)
65
def register_lazy_transport(scheme, module, classname):
66
"""Register lazy-loaded transport class.
68
When opening a URL with the given scheme, load the module and then
69
instantiate the particular class.
71
If the module raises DependencyNotPresent when it's imported, it is
72
skipped and another implementation of the protocol is tried. This is
73
intended to be used when the implementation depends on an external
74
implementation that may not be present. If any other error is raised, it
75
propagates up and the attempt to open the url fails.
77
# TODO: If no implementation of a protocol is available because of missing
78
# dependencies, we should perhaps show the message about what dependency
81
mod = __import__(module, globals(), locals(), [classname])
82
klass = getattr(mod, classname)
84
_loader.module = module
85
register_transport(scheme, _loader)
48
88
def _get_protocol_handlers():
49
"""Return a dictionary of prefix:transport-factories."""
89
"""Return a dictionary of {urlprefix: [factory]}"""
50
90
return _protocol_handlers
59
99
_protocol_handlers = new_handlers
102
def _clear_protocol_handlers():
103
global _protocol_handlers
104
_protocol_handlers = {}
62
107
def _get_transport_modules():
63
108
"""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__)
110
for prefix, factory_list in _protocol_handlers.items():
111
for factory in factory_list:
112
if factory.__module__ == "bzrlib.transport":
113
# this is a lazy load transport, because no real ones
114
# are directlry in bzrlib.transport
115
modules.add(factory.module)
117
modules.add(factory.__module__)
72
118
result = list(modules)
123
def register_urlparse_netloc_protocol(protocol):
124
"""Ensure that protocol is setup to be used with urlparse netloc parsing."""
125
if protocol not in urlparse.uses_netloc:
126
urlparse.uses_netloc.append(protocol)
77
129
class Transport(object):
78
130
"""This class encapsulates methods for retrieving or putting a file
79
131
from/to a storage location.
464
518
# This is not implemented, because you need to do special tricks to
465
519
# extract the basename, and add it to rel_to
466
raise NotImplementedError
520
raise NotImplementedError(self.move_multi_to)
468
522
def delete(self, relpath):
469
523
"""Delete the item at relpath"""
470
raise NotImplementedError
524
raise NotImplementedError(self.delete)
472
526
def delete_multi(self, relpaths, pb=None):
473
527
"""Queue up a bunch of deletes to be done.
557
611
:return: A lock object, which should contain an unlock() function.
559
raise NotImplementedError
613
raise NotImplementedError(self.lock_write)
561
615
def is_readonly(self):
562
616
"""Return true if this connection cannot be written to."""
619
def _can_roundtrip_unix_modebits(self):
620
"""Return true if this transport can store and retrieve unix modebits.
622
(For example, 0700 to make a directory owner-private.)
624
Note: most callers will not want to switch on this, but should rather
625
just try and set permissions and let them be either stored or not.
626
This is intended mainly for the use of the test suite.
628
Warning: this is not guaranteed to be accurate as sometimes we can't
629
be sure: for example with vfat mounted on unix, or a windows sftp
631
# TODO: Perhaps return a e.g. TransportCharacteristics that can answer
632
# several questions about the transport.
566
636
def get_transport(base):
567
637
"""Open a transport to access a URL or directory.
569
639
base is either a URL or a directory name.
641
# TODO: give a better error if base looks like a url but there's no
642
# handler for the scheme?
571
643
global _protocol_handlers
575
647
base = unicode(base)
576
for proto, klass in _protocol_handlers.iteritems():
648
for proto, factory_list in _protocol_handlers.iteritems():
577
649
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)
650
t = _try_transport_factories(base, factory_list)
653
# The default handler is the filesystem handler, stored as protocol None
654
return _try_transport_factories(base, _protocol_handlers[None])
657
def _try_transport_factories(base, factory_list):
658
for factory in factory_list:
661
except DependencyNotPresent, e:
662
mutter("failed to instantiate transport %r for %r: %r" %
598
668
def urlescape(relpath):
599
669
"""Escape relpath to be a valid url."""
600
# TODO utf8 it first. utf8relpath = relpath.encode('utf8')
670
if isinstance(relpath, unicode):
671
relpath = relpath.encode('utf-8')
602
672
return urllib.quote(relpath)
605
675
def urlunescape(relpath):
606
676
"""Unescape relpath from url format."""
608
677
return urllib.unquote(relpath)
609
678
# TODO de-utf8 it last. relpath = utf8relpath.decode('utf8')
713
786
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
714
787
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
715
788
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')
789
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
790
'HttpTransport_urllib')
791
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
792
'HttpTransport_urllib')
793
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
795
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
797
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
798
'HttpTransport_urllib')
799
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
800
'HttpTransport_urllib')
801
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
802
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
718
803
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
719
804
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
720
805
register_lazy_transport('memory:/', 'bzrlib.transport.memory', 'MemoryTransport')
721
806
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
807
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
808
register_lazy_transport('vfat+',
809
'bzrlib.transport.fakevfat',
810
'FakeVFATTransportDecorator')