61
from bzrlib.trace import mutter, warning
63
# {prefix: [transport_classes]}
64
# Transports are inserted onto the list LIFO and tried in order; as a result
65
# transports provided by plugins are tried first, which is usually what we
67
_protocol_handlers = {
70
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
71
"""Register a transport that can be used to open URLs
73
Normally you should use register_lazy_transport, which defers loading the
74
implementation until it's actually used, and so avoids pulling in possibly
75
large implementation libraries.
77
# Note that this code runs very early in library setup -- trace may not be
79
global _protocol_handlers
80
if deprecated_passed(override):
81
warnings.warn("register_transport(override) is deprecated")
82
_protocol_handlers.setdefault(prefix, []).insert(0, klass)
85
def register_lazy_transport(scheme, module, classname):
86
"""Register lazy-loaded transport class.
88
When opening a URL with the given scheme, load the module and then
89
instantiate the particular class.
91
If the module raises DependencyNotPresent when it's imported, it is
92
skipped and another implementation of the protocol is tried. This is
93
intended to be used when the implementation depends on an external
94
implementation that may not be present. If any other error is raised, it
95
propagates up and the attempt to open the url fails.
97
# TODO: If no implementation of a protocol is available because of missing
98
# dependencies, we should perhaps show the message about what dependency
101
mod = __import__(module, globals(), locals(), [classname])
102
klass = getattr(mod, classname)
104
_loader.module = module
105
register_transport(scheme, _loader)
61
from bzrlib.trace import (
66
from bzrlib import registry
108
69
def _get_protocol_handlers():
109
70
"""Return a dictionary of {urlprefix: [factory]}"""
110
return _protocol_handlers
71
return transport_list_registry
113
74
def _set_protocol_handlers(new_handlers):
116
77
WARNING this will remove all build in protocols. Use with care.
118
global _protocol_handlers
119
_protocol_handlers = new_handlers
79
global transport_list_registry
80
transport_list_registry = new_handlers
122
83
def _clear_protocol_handlers():
123
global _protocol_handlers
124
_protocol_handlers = {}
84
global transport_list_registry
85
transport_list_registry = TransportListRegistry()
127
88
def _get_transport_modules():
128
89
"""Return a list of the modules providing transports."""
130
for prefix, factory_list in _protocol_handlers.items():
91
for prefix, factory_list in transport_list_registry.iteritems():
131
92
for factory in factory_list:
132
if factory.__module__ == "bzrlib.transport":
133
# this is a lazy load transport, because no real ones
134
# are directlry in bzrlib.transport
135
modules.add(factory.module)
93
if hasattr(factory, "_module_name"):
94
modules.add(factory._module_name)
137
modules.add(factory.__module__)
96
modules.add(factory._obj.__module__)
97
# Add chroot directly, because there is not handler registered for it.
98
modules.add('bzrlib.transport.chroot')
138
99
result = list(modules)
104
class TransportListRegistry(registry.Registry):
105
"""A registry which simplifies tracking available Transports.
107
A registration of a new protocol requires two step:
108
1) register the prefix with the function register_transport( )
109
2) register the protocol provider with the function
110
register_transport_provider( ) ( and the "lazy" variant )
112
This in needed because:
113
a) a single provider can support multple protcol ( like the ftp
114
privider which supports both the ftp:// and the aftp:// protocols )
115
b) a single protocol can have multiple providers ( like the http://
116
protocol which is supported by both the urllib and pycurl privider )
119
def register_transport_provider(self, key, obj):
120
self.get(key).insert(0, registry._ObjectGetter(obj))
122
def register_lazy_transport_provider(self, key, module_name, member_name):
123
self.get(key).insert(0,
124
registry._LazyObjectGetter(module_name, member_name))
126
def register_transport(self, key, help=None, info=None):
127
self.register(key, [], help, info)
129
def set_default_transport(self, key=None):
130
"""Return either 'key' or the default key if key is None"""
131
self._default_key = key
134
transport_list_registry = TransportListRegistry( )
137
def register_transport_proto(prefix, help=None, info=None):
138
transport_list_registry.register_transport(prefix, help, info)
141
def register_lazy_transport(prefix, module, classname):
142
if not prefix in transport_list_registry:
143
register_transport_proto(prefix)
144
transport_list_registry.register_lazy_transport_provider(prefix, module, classname)
147
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
148
if not prefix in transport_list_registry:
149
register_transport_proto(prefix)
150
transport_list_registry.register_transport_provider(prefix, klass)
143
153
def register_urlparse_netloc_protocol(protocol):
144
154
"""Ensure that protocol is setup to be used with urlparse netloc parsing."""
145
155
if protocol not in urlparse.uses_netloc:
146
156
urlparse.uses_netloc.append(protocol)
159
def unregister_transport(scheme, factory):
160
"""Unregister a transport."""
161
l = transport_list_registry.get(scheme)
165
transport_list_registry.get(scheme).remove(i)
168
transport_list_registry.remove(scheme)
149
172
def split_url(url):
150
173
# TODO: jam 20060606 urls should only be ascii, or they should raise InvalidURL
151
174
if isinstance(url, unicode):
1042
1063
base = convert_path_to_url(base, 'Unsupported protocol: %s')
1044
1065
# The default handler is the filesystem handler, stored as protocol None
1045
return _try_transport_factories(base, _protocol_handlers[None])[0]
1066
return _try_transport_factories(base,
1067
transport_list_registry.get(None))[0]
1069
def do_catching_redirections(action, transport, redirected):
1070
"""Execute an action with given transport catching redirections.
1072
This is a facility provided for callers needing to follow redirections
1073
silently. The silence is relative: it is the caller responsability to
1074
inform the user about each redirection or only inform the user of a user
1075
via the exception parameter.
1077
:param action: A callable, what the caller want to do while catching
1079
:param transport: The initial transport used.
1080
:param redirected: A callable receiving the redirected transport and the
1081
RedirectRequested exception.
1083
:return: Whatever 'action' returns
1085
MAX_REDIRECTIONS = 8
1087
# If a loop occurs, there is little we can do. So we don't try to detect
1088
# them, just getting out if too much redirections occurs. The solution
1089
# is outside: where the loop is defined.
1090
for redirections in range(MAX_REDIRECTIONS):
1092
return action(transport)
1093
except errors.RedirectRequested, e:
1094
redirection_notice = '%s is%s redirected to %s' % (
1095
e.source, e.permanently, e.target)
1096
transport = redirected(transport, e, redirection_notice)
1098
# Loop exited without resolving redirect ? Either the
1099
# user has kept a very very very old reference or a loop
1100
# occurred in the redirections. Nothing we can cure here:
1101
# tell the user. Note that as the user has been informed
1102
# about each redirection (it is the caller responsibility
1103
# to do that in redirected via the provided
1104
# redirection_notice). The caller may provide more
1105
# information if needed (like what file or directory we
1106
# were trying to act upon when the redirection loop
1108
raise errors.TooManyRedirections
1048
1111
def _try_transport_factories(base, factory_list):
1049
1112
last_err = None
1050
1113
for factory in factory_list:
1052
return factory(base), None
1115
return factory.get_obj()(base), None
1053
1116
except errors.DependencyNotPresent, e:
1054
1117
mutter("failed to instantiate transport %r for %r: %r" %
1055
1118
(factory, base, e))
1160
1227
def readv(self, name, offsets):
1161
1228
self._calls.append((name, offsets))
1162
1229
return self._adapted.readv(name, offsets)
1165
1232
# None is the default transport, for things with no url scheme
1166
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
1233
register_transport_proto('file://',
1234
help="Access using the standard filesystem (default)")
1167
1235
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
1236
transport_list_registry.set_default_transport("file://")
1238
register_transport_proto('sftp://',
1239
help="Access using SFTP (most SSH servers provide SFTP).")
1168
1240
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
1241
# Decorated http transport
1242
register_transport_proto('http+urllib://',
1243
# help="Read-only access of branches exported on the web."
1169
1245
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
1170
1246
'HttpTransport_urllib')
1247
register_transport_proto('https+urllib://',
1248
# help="Read-only access of branches exported on the web using SSL."
1171
1250
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
1172
1251
'HttpTransport_urllib')
1252
register_transport_proto('http+pycurl://',
1253
# help="Read-only access of branches exported on the web."
1173
1255
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
1174
1256
'PyCurlTransport')
1257
register_transport_proto('https+pycurl://',
1258
# help="Read-only access of branches exported on the web using SSL."
1175
1260
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
1176
1261
'PyCurlTransport')
1262
# Default http transports (last declared wins (if it can be imported))
1263
register_transport_proto('http://',
1264
help="Read-only access of branches exported on the web.")
1265
register_transport_proto('https://',
1266
help="Read-only access of branches exported on the web using SSL.")
1177
1267
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
1178
1268
'HttpTransport_urllib')
1179
1269
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1180
1270
'HttpTransport_urllib')
1181
1271
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
1182
1272
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
1274
register_transport_proto('ftp://',
1275
help="Access using passive FTP.")
1183
1276
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1277
register_transport_proto('aftp://',
1278
help="Access using active FTP.")
1184
1279
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1281
register_transport_proto('memory://')
1185
1282
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
1186
register_lazy_transport('chroot+', 'bzrlib.transport.chroot',
1187
'ChrootTransportDecorator')
1283
register_transport_proto('chroot+')
1285
register_transport_proto('readonly+',
1286
# help="This modifier converts any transport to be readonly."
1188
1288
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
1289
register_transport_proto('fakenfs+')
1189
1290
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
1291
register_transport_proto('vfat+')
1190
1292
register_lazy_transport('vfat+',
1191
1293
'bzrlib.transport.fakevfat',
1192
1294
'FakeVFATTransportDecorator')
1295
register_transport_proto('bzr://',
1296
help="Fast access using the Bazaar smart server.")
1193
1298
register_lazy_transport('bzr://',
1194
'bzrlib.transport.smart',
1195
'SmartTCPTransport')
1299
'bzrlib.transport.remote',
1300
'RemoteTCPTransport')
1301
register_transport_proto('bzr+http://',
1302
# help="Fast access using the Bazaar smart server over HTTP."
1196
1304
register_lazy_transport('bzr+http://',
1197
'bzrlib.transport.smart',
1198
'SmartHTTPTransport')
1305
'bzrlib.transport.remote',
1306
'RemoteHTTPTransport')
1307
register_transport_proto('bzr+ssh://',
1308
help="Fast access using the Bazaar smart server over SSH.")
1199
1309
register_lazy_transport('bzr+ssh://',
1200
'bzrlib.transport.smart',
1201
'SmartSSHTransport')
1310
'bzrlib.transport.remote',
1311
'RemoteSSHTransport')