22
22
_protocol_handlers[prefix] = klass
24
24
class TransportError(BzrError):
25
"""All errors thrown by Transport implementations should derive
28
def __init__(self, msg=None, orig_error=None):
29
if msg is None and orig_error is not None:
31
BzrError.__init__(self, msg)
33
self.orig_error = orig_error
27
35
class AsyncError(TransportError):
30
class TransportNotPossibleError(TransportError):
38
# A set of semi-meaningful errors which can be thrown
39
class TransportNotPossible(TransportError):
31
40
"""This is for transports where a specific function is explicitly not
32
41
possible. Such as pushing files to an HTTP server.
45
class NonRelativePath(TransportError):
46
"""An absolute path was supplied, that could not be decoded into
51
class NoSuchFile(TransportError, IOError):
52
"""A get() was issued for a file that doesn't exist."""
53
def __init__(self, msg=None, orig_error=None):
55
TransportError.__init__(self, msg=msg, orig_error=orig_error)
56
IOError.__init__(self, errno.ENOENT, self.msg)
58
class FileExists(TransportError, OSError):
59
"""An operation was attempted, which would overwrite an entry,
60
but overwritting is not supported.
62
mkdir() can throw this, but put() just overwites existing files.
64
def __init__(self, msg=None, orig_error=None):
66
TransportError.__init__(self, msg=msg, orig_error=orig_error)
67
OSError.__init__(self, errno.EEXIST, self.msg)
69
class PermissionDenied(TransportError):
70
"""An operation cannot succeed because of a lack of permissions."""
73
class ConnectionReset(TransportError):
74
"""The connection has been closed."""
36
77
class AsyncFile(object):
37
78
"""This will be returned from a Transport object,
38
79
whenever an asyncronous get is requested.
195
232
yield self.has(relpath)
198
def get(self, relpath, decode=False):
235
def get(self, relpath):
199
236
"""Get the file at the given relative path.
201
238
:param relpath: The relative path to the file
202
:param decode: If True, assume the file is utf-8 encoded and
203
decode it into Unicode
205
240
raise NotImplementedError
207
def get_multi(self, relpaths, decode=False, pb=None):
242
def get_multi(self, relpaths, pb=None):
208
243
"""Get a list of file-like objects, one for each entry in relpaths.
210
245
:param relpaths: A list of relative paths.
211
:param decode: If True, assume the file is utf-8 encoded and
212
decode it into Unicode
213
246
:param pb: An optional ProgressBar for indicating percent done.
214
247
:return: A list or generator of file-like objects
221
254
for relpath in relpaths:
222
255
self._update_pb(pb, 'get', count, total)
223
yield self.get(relpath, decode=decode)
256
yield self.get(relpath)
226
def put(self, relpath, f, encode=False):
259
def put(self, relpath, f):
227
260
"""Copy the file-like or string object into the location.
229
262
:param relpath: Location to put the contents, relative to base.
230
263
:param f: File-like or string object.
231
:param encode: If True, translate the contents into utf-8 encoded text.
233
265
raise NotImplementedError
235
def put_multi(self, files, encode=False, pb=None):
267
def put_multi(self, files, pb=None):
236
268
"""Put a set of files or strings into the location.
238
270
:param files: A list of tuples of relpath, file object [(path1, file1), (path2, file2),...]
386
418
# which has a lookup of None
387
419
return _protocol_handlers[None](base)
421
def transport_test_ro(tester, t):
422
"""Test a transport object, but only assume that it has Read functionality.
423
The Transport object is connected to the current working directory.
424
So that whatever is done through the transport, should show
425
up in the working directory, and vice-versa.
427
This also tests to make sure that the functions work with both
428
generators and lists (assuming iter(list) is effectively a generator)
431
from local_transport import LocalTransport
434
files = ['a', 'b', 'e', 'g']
435
tester.build_tree(files)
436
tester.assertEqual(t.has('a'), True)
437
tester.assertEqual(t.has('c'), False)
438
tester.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
439
[True, True, False, False, True, False, True, False])
440
tester.assertEqual(list(t.has_multi(iter(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']))),
441
[True, True, False, False, True, False, True, False])
444
tester.assertEqual(t.get('a').read(), open('a').read())
445
content_f = t.get_multi(files)
446
for path,f in zip(files, content_f):
447
tester.assertEqual(open(path).read(), f.read())
449
content_f = t.get_multi(iter(files))
450
for path,f in zip(files, content_f):
451
tester.assertEqual(open(path).read(), f.read())
453
tester.assertRaises(NoSuchFile, t.get, 'c')
455
files = list(t.get_multi(['a', 'b', 'c']))
459
tester.fail('Failed to raise NoSuchFile for missing file in get_multi')
461
files = list(t.get_multi(iter(['a', 'b', 'c', 'e'])))
465
tester.fail('Failed to raise NoSuchFile for missing file in get_multi')
467
open('c', 'wb').write('some text for c\n')
468
tester.assert_(os.path.exists('c'))
469
tester.assertEqual(t.get('c').read(), 'some text for c\n')
470
# Make sure 'has' is updated
471
tester.assertEqual(list(t.has_multi(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])),
472
[True, True, True, False, True, False, True, False])
476
tester.assertEqual(t.has('dir_a'), True)
477
tester.assertEqual(t.has('dir_b'), False)
480
tester.assertEqual(t.has('dir_b'), True)
481
tester.assert_(os.path.isdir('dir_b'))
485
tester.assertEqual(list(t.has_multi(['dir_a', 'dir_b', 'dir_c', 'dir_d', 'dir_e', 'dir_b'])),
486
[True, True, True, True, False, True])
487
for d in ['dir_a', 'dir_b', 'dir_c', 'dir_d']:
488
tester.assert_(os.path.isdir(d))
490
# Test get/put in sub-directories
491
open('dir_a/a', 'wb').write('contents of dir_a/a')
492
open('dir_b/b', 'wb').write('contents of dir_b/b')
493
for f in ('dir_a/a', 'dir_b/b'):
494
tester.assertEqual(t.get(f).read(), open(f).read())
497
dtmp = tempfile.mkdtemp(dir='.', prefix='test-transport-')
498
dtmp_base = os.path.basename(dtmp)
499
local_t = LocalTransport(dtmp)
501
files = ['a', 'b', 'c']
502
t.copy_to(files, local_t)
504
tester.assertEquals(open(f).read(), open(os.path.join(dtmp_base, f)).read())
389
506
def transport_test(tester, t):
390
507
"""Test a transport object. Basically, it assumes that the
391
508
Transport object is connected to the current working directory.
471
588
for d in ['dir_a', 'dir_b', 'dir_c', 'dir_d']:
472
589
tester.assert_(os.path.isdir(d))
474
tester.assertRaises(TransportError, t.mkdir, 'path/doesnt/exist')
475
tester.assertRaises(TransportError, t.mkdir, 'dir_a') # Creating a directory again should fail
591
tester.assertRaises(NoSuchFile, t.mkdir, 'path/doesnt/exist')
592
tester.assertRaises(FileExists, t.mkdir, 'dir_a') # Creating a directory again should fail
477
# This one may fail for some transports.
478
# Specifically, I know RsyncTransport doesn't check for the directory
479
# existing, before it creates it. The reason is that it seems to
480
# expensive, it does check to see if the local directory already exists,
481
# and will throw an exception for that
482
# FIXME: Make this work everywhere
484
#tester.assertRaises(TransportError, t.mkdir, 'dir_e')
595
tester.assertRaises(FileExists, t.mkdir, 'dir_e')
486
597
# Test get/put in sub-directories
487
598
tester.assertEqual(t.put_multi([('dir_a/a', 'contents of dir_a/a'),