58
from bzrlib.trace import mutter, warning
60
# {prefix: [transport_classes]}
61
# Transports are inserted onto the list LIFO and tried in order; as a result
62
# transports provided by plugins are tried first, which is usually what we
64
_protocol_handlers = {
67
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
68
"""Register a transport that can be used to open URLs
70
Normally you should use register_lazy_transport, which defers loading the
71
implementation until it's actually used, and so avoids pulling in possibly
72
large implementation libraries.
74
# Note that this code runs very early in library setup -- trace may not be
76
global _protocol_handlers
77
if deprecated_passed(override):
78
warnings.warn("register_transport(override) is deprecated")
79
_protocol_handlers.setdefault(prefix, []).insert(0, klass)
82
def register_lazy_transport(scheme, module, classname):
83
"""Register lazy-loaded transport class.
85
When opening a URL with the given scheme, load the module and then
86
instantiate the particular class.
88
If the module raises DependencyNotPresent when it's imported, it is
89
skipped and another implementation of the protocol is tried. This is
90
intended to be used when the implementation depends on an external
91
implementation that may not be present. If any other error is raised, it
92
propagates up and the attempt to open the url fails.
94
# TODO: If no implementation of a protocol is available because of missing
95
# dependencies, we should perhaps show the message about what dependency
98
mod = __import__(module, globals(), locals(), [classname])
99
klass = getattr(mod, classname)
101
_loader.module = module
102
register_transport(scheme, _loader)
60
from bzrlib.trace import (
65
from bzrlib import registry
105
68
def _get_protocol_handlers():
106
69
"""Return a dictionary of {urlprefix: [factory]}"""
107
return _protocol_handlers
70
return transport_list_registry
110
73
def _set_protocol_handlers(new_handlers):
113
76
WARNING this will remove all build in protocols. Use with care.
115
global _protocol_handlers
116
_protocol_handlers = new_handlers
78
global transport_list_registry
79
transport_list_registry = new_handlers
119
82
def _clear_protocol_handlers():
120
global _protocol_handlers
121
_protocol_handlers = {}
83
global transport_list_registry
84
transport_list_registry = TransportListRegistry()
124
87
def _get_transport_modules():
125
88
"""Return a list of the modules providing transports."""
127
for prefix, factory_list in _protocol_handlers.items():
90
for prefix, factory_list in transport_list_registry.iteritems():
128
91
for factory in factory_list:
129
if factory.__module__ == "bzrlib.transport":
130
# this is a lazy load transport, because no real ones
131
# are directlry in bzrlib.transport
132
modules.add(factory.module)
92
if hasattr(factory, "_module_name"):
93
modules.add(factory._module_name)
134
modules.add(factory.__module__)
95
modules.add(factory._obj.__module__)
96
# Add chroot directly, because there is not handler registered for it.
97
modules.add('bzrlib.transport.chroot')
135
98
result = list(modules)
103
class TransportListRegistry(registry.Registry):
104
"""A registry which simplifies tracking available Transports.
106
A registration of a new protocol requires two step:
107
1) register the prefix with the function register_transport( )
108
2) register the protocol provider with the function
109
register_transport_provider( ) ( and the "lazy" variant )
111
This in needed because:
112
a) a single provider can support multple protcol ( like the ftp
113
privider which supports both the ftp:// and the aftp:// protocols )
114
b) a single protocol can have multiple providers ( like the http://
115
protocol which is supported by both the urllib and pycurl privider )
118
def register_transport_provider(self, key, obj):
119
self.get(key).insert(0, registry._ObjectGetter(obj))
121
def register_lazy_transport_provider(self, key, module_name, member_name):
122
self.get(key).insert(0,
123
registry._LazyObjectGetter(module_name, member_name))
125
def register_transport(self, key, help=None, info=None):
126
self.register(key, [], help, info)
128
def set_default_transport(self, key=None):
129
"""Return either 'key' or the default key if key is None"""
130
self._default_key = key
133
transport_list_registry = TransportListRegistry( )
136
def register_transport_proto(prefix, help=None, info=None):
137
transport_list_registry.register_transport(prefix, help, info)
140
def register_lazy_transport(prefix, module, classname):
141
if not prefix in transport_list_registry:
142
register_transport_proto(prefix)
143
transport_list_registry.register_lazy_transport_provider(prefix, module, classname)
146
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
147
if not prefix in transport_list_registry:
148
register_transport_proto(prefix)
149
transport_list_registry.register_transport_provider(prefix, klass)
140
152
def register_urlparse_netloc_protocol(protocol):
141
153
"""Ensure that protocol is setup to be used with urlparse netloc parsing."""
142
154
if protocol not in urlparse.uses_netloc:
143
155
urlparse.uses_netloc.append(protocol)
158
def unregister_transport(scheme, factory):
159
"""Unregister a transport."""
160
l = transport_list_registry.get(scheme)
164
transport_list_registry.get(scheme).remove(i)
167
transport_list_registry.remove(scheme)
146
171
def split_url(url):
147
172
# TODO: jam 20060606 urls should only be ascii, or they should raise InvalidURL
148
173
if isinstance(url, unicode):
1051
1115
base = convert_path_to_url(base, 'Unsupported protocol: %s')
1053
1117
# The default handler is the filesystem handler, stored as protocol None
1054
return _try_transport_factories(base, _protocol_handlers[None])[0]
1118
return _try_transport_factories(base,
1119
transport_list_registry.get(None))[0]
1121
def do_catching_redirections(action, transport, redirected):
1122
"""Execute an action with given transport catching redirections.
1124
This is a facility provided for callers needing to follow redirections
1125
silently. The silence is relative: it is the caller responsability to
1126
inform the user about each redirection or only inform the user of a user
1127
via the exception parameter.
1129
:param action: A callable, what the caller want to do while catching
1131
:param transport: The initial transport used.
1132
:param redirected: A callable receiving the redirected transport and the
1133
RedirectRequested exception.
1135
:return: Whatever 'action' returns
1137
MAX_REDIRECTIONS = 8
1139
# If a loop occurs, there is little we can do. So we don't try to detect
1140
# them, just getting out if too much redirections occurs. The solution
1141
# is outside: where the loop is defined.
1142
for redirections in range(MAX_REDIRECTIONS):
1144
return action(transport)
1145
except errors.RedirectRequested, e:
1146
redirection_notice = '%s is%s redirected to %s' % (
1147
e.source, e.permanently, e.target)
1148
transport = redirected(transport, e, redirection_notice)
1150
# Loop exited without resolving redirect ? Either the
1151
# user has kept a very very very old reference or a loop
1152
# occurred in the redirections. Nothing we can cure here:
1153
# tell the user. Note that as the user has been informed
1154
# about each redirection (it is the caller responsibility
1155
# to do that in redirected via the provided
1156
# redirection_notice). The caller may provide more
1157
# information if needed (like what file or directory we
1158
# were trying to act upon when the redirection loop
1160
raise errors.TooManyRedirections
1057
1163
def _try_transport_factories(base, factory_list):
1058
1164
last_err = None
1059
1165
for factory in factory_list:
1061
return factory(base), None
1062
except DependencyNotPresent, e:
1167
return factory.get_obj()(base), None
1168
except errors.DependencyNotPresent, e:
1063
1169
mutter("failed to instantiate transport %r for %r: %r" %
1064
1170
(factory, base, e))
1099
1205
raise NotImplementedError
1101
1207
def get_bogus_url(self):
1102
"""Return a url for this protocol, that will fail to connect."""
1208
"""Return a url for this protocol, that will fail to connect.
1210
This may raise NotImplementedError to indicate that this server cannot
1103
1213
raise NotImplementedError
1106
class TransportTestProviderAdapter(object):
1107
"""A tool to generate a suite testing all transports for a single test.
1109
This is done by copying the test once for each transport and injecting
1110
the transport_class and transport_server classes into each copy. Each copy
1111
is also given a new id() to make it easy to identify.
1114
def adapt(self, test):
1115
result = TestSuite()
1116
for klass, server_factory in self._test_permutations():
1117
new_test = deepcopy(test)
1118
new_test.transport_class = klass
1119
new_test.transport_server = server_factory
1120
def make_new_test_id():
1121
new_id = "%s(%s)" % (new_test.id(), server_factory.__name__)
1122
return lambda: new_id
1123
new_test.id = make_new_test_id()
1124
result.addTest(new_test)
1127
def get_transport_test_permutations(self, module):
1128
"""Get the permutations module wants to have tested."""
1129
if getattr(module, 'get_test_permutations', None) is None:
1130
warning("transport module %s doesn't provide get_test_permutations()"
1133
return module.get_test_permutations()
1135
def _test_permutations(self):
1136
"""Return a list of the klass, server_factory pairs to test."""
1138
for module in _get_transport_modules():
1140
result.extend(self.get_transport_test_permutations(reduce(getattr,
1141
(module).split('.')[1:],
1142
__import__(module))))
1143
except errors.DependencyNotPresent, e:
1144
# Continue even if a dependency prevents us
1145
# from running this test
1150
1216
class TransportLogger(object):
1151
1217
"""Adapt a transport to get clear logging data on api calls.
1169
1235
def readv(self, name, offsets):
1170
1236
self._calls.append((name, offsets))
1171
1237
return self._adapted.readv(name, offsets)
1174
1240
# None is the default transport, for things with no url scheme
1175
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
1241
register_transport_proto('file://',
1242
help="Access using the standard filesystem (default)")
1176
1243
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
1244
transport_list_registry.set_default_transport("file://")
1246
register_transport_proto('sftp://',
1247
help="Access using SFTP (most SSH servers provide SFTP).")
1177
1248
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
1249
# Decorated http transport
1250
register_transport_proto('http+urllib://',
1251
# help="Read-only access of branches exported on the web."
1178
1253
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
1179
1254
'HttpTransport_urllib')
1255
register_transport_proto('https+urllib://',
1256
# help="Read-only access of branches exported on the web using SSL."
1180
1258
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
1181
1259
'HttpTransport_urllib')
1260
register_transport_proto('http+pycurl://',
1261
# help="Read-only access of branches exported on the web."
1182
1263
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
1183
1264
'PyCurlTransport')
1265
register_transport_proto('https+pycurl://',
1266
# help="Read-only access of branches exported on the web using SSL."
1184
1268
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
1185
1269
'PyCurlTransport')
1270
# Default http transports (last declared wins (if it can be imported))
1271
register_transport_proto('http://',
1272
help="Read-only access of branches exported on the web.")
1273
register_transport_proto('https://',
1274
help="Read-only access of branches exported on the web using SSL.")
1186
1275
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
1187
1276
'HttpTransport_urllib')
1188
1277
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1189
1278
'HttpTransport_urllib')
1190
1279
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
1191
1280
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
1282
register_transport_proto('ftp://',
1283
help="Access using passive FTP.")
1192
1284
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1285
register_transport_proto('aftp://',
1286
help="Access using active FTP.")
1193
1287
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1289
register_transport_proto('memory://')
1194
1290
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
1291
register_transport_proto('chroot+')
1293
register_transport_proto('readonly+',
1294
# help="This modifier converts any transport to be readonly."
1195
1296
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
1297
register_transport_proto('fakenfs+')
1196
1298
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
1299
register_transport_proto('vfat+')
1197
1300
register_lazy_transport('vfat+',
1198
1301
'bzrlib.transport.fakevfat',
1199
1302
'FakeVFATTransportDecorator')
1303
register_transport_proto('bzr://',
1304
help="Fast access using the Bazaar smart server.")
1200
1306
register_lazy_transport('bzr://',
1201
'bzrlib.transport.smart',
1202
'SmartTCPTransport')
1307
'bzrlib.transport.remote',
1308
'RemoteTCPTransport')
1309
register_transport_proto('bzr+http://',
1310
# help="Fast access using the Bazaar smart server over HTTP."
1312
register_lazy_transport('bzr+http://',
1313
'bzrlib.transport.remote',
1314
'RemoteHTTPTransport')
1315
register_transport_proto('bzr+ssh://',
1316
help="Fast access using the Bazaar smart server over SSH.")
1203
1317
register_lazy_transport('bzr+ssh://',
1204
'bzrlib.transport.smart',
1205
'SmartSSHTransport')
1318
'bzrlib.transport.remote',
1319
'RemoteSSHTransport')