40
43
_protocol_handlers[prefix] = klass
46
def _get_protocol_handlers():
47
"""Return a dictionary of prefix:transport-factories."""
48
return _protocol_handlers
51
def _set_protocol_handlers(new_handlers):
52
"""Replace the current protocol handlers dictionary.
54
WARNING this will remove all build in protocols. Use with care.
56
global _protocol_handlers
57
_protocol_handlers = new_handlers
60
def _get_transport_modules():
61
"""Return a list of the modules providing transports."""
63
for prefix, factory in _protocol_handlers.items():
64
if factory.__module__ == "bzrlib.transport":
65
# this is a lazy load transport, because no real ones
66
# are directlry in bzrlib.transport
67
modules.add(factory.module)
69
modules.add(factory.__module__)
70
result = list(modules)
43
75
class Transport(object):
44
76
"""This class encapsulates methods for retrieving or putting a file
45
77
from/to a storage location.
160
192
# TODO: This might want to use bzrlib.osutils.relpath
161
193
# but we have to watch out because of the prefix issues
162
if not abspath.startswith(self.base):
194
if not (abspath == self.base[:-1] or abspath.startswith(self.base)):
163
195
raise errors.PathNotChild(abspath, self.base)
164
196
pl = len(self.base)
165
return abspath[pl:].lstrip('/')
197
return abspath[pl:].strip('/')
168
199
def has(self, relpath):
169
200
"""Does the file relpath exist?
195
226
As with other listing functions, only some transports implement this,.
196
227
you may check via is_listable to determine if it will.
198
raise NotImplementedError
229
raise errors.TransportNotPossible("This transport has not "
230
"implemented iter_files_recursive "
231
"(but must claim to be listable "
232
"to trigger this error).")
200
234
def get(self, relpath):
201
235
"""Get the file at the given relative path.
232
266
raise NotImplementedError
234
268
def put_multi(self, files, mode=None, pb=None):
235
"""Put a set of files or strings into the location.
269
"""Put a set of files into the location.
237
271
:param files: A list of tuples of relpath, file object [(path1, file1), (path2, file2),...]
238
272
:param pb: An optional ProgressBar for indicating percent done.
269
303
return self._iterate_over(files, self.append, pb, 'append', expand=True)
271
305
def copy(self, rel_from, rel_to):
272
"""Copy the item at rel_from to the location at rel_to"""
273
raise NotImplementedError
306
"""Copy the item at rel_from to the location at rel_to.
308
Override this for efficiency if a specific transport can do it
309
faster than this default implementation.
311
self.put(rel_to, self.get(rel_from))
275
313
def copy_multi(self, relpaths, pb=None):
276
314
"""Copy a bunch of entries.
299
337
def move(self, rel_from, rel_to):
300
"""Move the item at rel_from to the location at rel_to"""
301
raise NotImplementedError
338
"""Move the item at rel_from to the location at rel_to.
340
If a transport can directly implement this it is suggested that
341
it do so for efficiency.
343
self.copy(rel_from, rel_to)
344
self.delete(rel_from)
303
346
def move_multi(self, relpaths, pb=None):
304
347
"""Move a bunch of entries.
415
465
return urllib.quote(relpath)
468
class Server(object):
469
"""A Transport Server.
471
The Server interface provides a server for a given transport. We use
472
these servers as loopback testing tools. For any given transport the
473
Servers it provides must either allow writing, or serve the contents
474
of os.getcwdu() at the time setUp is called.
476
Note that these are real servers - they must implement all the things
477
that we want bzr transports to take advantage of.
481
"""Setup the server to service requests."""
484
"""Remove the server and cleanup any resources it owns."""
487
"""Return a url for this server.
489
If the transport does not represent a disk directory (i.e. it is
490
a database like svn, or a memory only transport, it should return
491
a connection to a newly established resource for this Server.
492
Otherwise it should return a url that will provide access to the path
493
that was os.getcwdu() when setUp() was called.
495
Subsequent calls will return the same resource.
497
raise NotImplementedError
499
def get_bogus_url(self):
500
"""Return a url for this protocol, that will fail to connect."""
501
raise NotImplementedError
504
class TransportTestProviderAdapter(object):
505
"""A tool to generate a suite testing all transports for a single test.
507
This is done by copying the test once for each transport and injecting
508
the transport_class and transport_server classes into each copy. Each copy
509
is also given a new id() to make it easy to identify.
512
def adapt(self, test):
514
for klass, server_factory in self._test_permutations():
515
new_test = deepcopy(test)
516
new_test.transport_class = klass
517
new_test.transport_server = server_factory
518
def make_new_test_id():
519
new_id = "%s(%s)" % (new_test.id(), server_factory.__name__)
520
return lambda: new_id
521
new_test.id = make_new_test_id()
522
result.addTest(new_test)
525
def get_transport_test_permutations(self, module):
526
"""Get the permutations module wants to have tested."""
527
return module.get_test_permutations()
529
def _test_permutations(self):
530
"""Return a list of the klass, server_factory pairs to test."""
532
for module in _get_transport_modules():
533
result.extend(self.get_transport_test_permutations(reduce(getattr,
534
(module).split('.')[1:],
535
__import__(module))))
418
539
# None is the default transport, for things with no url scheme
419
540
register_lazy_transport(None, 'bzrlib.transport.local', 'LocalTransport')
420
541
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
423
544
register_lazy_transport('https://', 'bzrlib.transport.http', 'HttpTransport')
424
545
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
425
546
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
547
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')