~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
lazy_import(globals(), """
35
35
import errno
36
36
from collections import deque
37
 
from copy import deepcopy
38
37
from stat import S_ISDIR
39
38
import unittest
40
39
import urllib
215
214
                   (other.start, other.length, other.ranges))
216
215
 
217
216
 
 
217
class LateReadError(object):
 
218
    """A helper for transports which pretends to be a readable file.
 
219
 
 
220
    When read() is called, errors.ReadError is raised.
 
221
    """
 
222
 
 
223
    def __init__(self, path):
 
224
        self._path = path
 
225
 
 
226
    def close(self):
 
227
        """a no-op - do nothing."""
 
228
 
 
229
    def _fail(self):
 
230
        """Raise ReadError."""
 
231
        raise errors.ReadError(self._path)
 
232
 
 
233
    def __iter__(self):
 
234
        self._fail()
 
235
 
 
236
    def read(self, count=-1):
 
237
        self._fail()
 
238
 
 
239
    def readlines(self):
 
240
        self._fail()
 
241
 
 
242
 
218
243
class Transport(object):
219
244
    """This class encapsulates methods for retrieving or putting a file
220
245
    from/to a storage location.
473
498
    def get(self, relpath):
474
499
        """Get the file at the given relative path.
475
500
 
 
501
        This may fail in a number of ways:
 
502
         - HTTP servers may return content for a directory. (unexpected
 
503
           content failure)
 
504
         - FTP servers may indicate NoSuchFile for a directory.
 
505
         - SFTP servers may give a file handle for a directory that will
 
506
           fail on read().
 
507
 
 
508
        For correct use of the interface, be sure to catch errors.PathError
 
509
        when calling it and catch errors.ReadError when reading from the
 
510
        returned object.
 
511
 
476
512
        :param relpath: The relative path to the file
477
513
        :rtype: File-like object.
478
514
        """
575
611
        :param fudge_factor: All transports have some level of 'it is
576
612
                better to read some more data and throw it away rather 
577
613
                than seek', so collapse if we are 'close enough'
578
 
        :return: yield _CoalescedOffset objects, which have members for wher
 
614
        :return: yield _CoalescedOffset objects, which have members for where
579
615
                to start, how much to read, and how to split those 
580
616
                chunks back up
581
617
        """
1177
1213
        raise NotImplementedError
1178
1214
 
1179
1215
 
1180
 
class TransportTestProviderAdapter(object):
1181
 
    """A tool to generate a suite testing all transports for a single test.
1182
 
 
1183
 
    This is done by copying the test once for each transport and injecting
1184
 
    the transport_class and transport_server classes into each copy. Each copy
1185
 
    is also given a new id() to make it easy to identify.
1186
 
    """
1187
 
 
1188
 
    def adapt(self, test):
1189
 
        result = unittest.TestSuite()
1190
 
        for klass, server_factory in self._test_permutations():
1191
 
            new_test = deepcopy(test)
1192
 
            new_test.transport_class = klass
1193
 
            new_test.transport_server = server_factory
1194
 
            def make_new_test_id():
1195
 
                new_id = "%s(%s)" % (new_test.id(), server_factory.__name__)
1196
 
                return lambda: new_id
1197
 
            new_test.id = make_new_test_id()
1198
 
            result.addTest(new_test)
1199
 
        return result
1200
 
 
1201
 
    def get_transport_test_permutations(self, module):
1202
 
        """Get the permutations module wants to have tested."""
1203
 
        if getattr(module, 'get_test_permutations', None) is None:
1204
 
            raise AssertionError("transport module %s doesn't provide get_test_permutations()"
1205
 
                    % module.__name__)
1206
 
            ##warning("transport module %s doesn't provide get_test_permutations()"
1207
 
            ##       % module.__name__)
1208
 
            return []
1209
 
        return module.get_test_permutations()
1210
 
 
1211
 
    def _test_permutations(self):
1212
 
        """Return a list of the klass, server_factory pairs to test."""
1213
 
        result = []
1214
 
        for module in _get_transport_modules():
1215
 
            try:
1216
 
                result.extend(self.get_transport_test_permutations(reduce(getattr, 
1217
 
                    (module).split('.')[1:],
1218
 
                     __import__(module))))
1219
 
            except errors.DependencyNotPresent, e:
1220
 
                # Continue even if a dependency prevents us 
1221
 
                # from running this test
1222
 
                pass
1223
 
        return result
1224
 
 
1225
 
 
1226
1216
class TransportLogger(object):
1227
1217
    """Adapt a transport to get clear logging data on api calls.
1228
1218
    
1304
1294
#              help="This modifier converts any transport to be readonly."
1305
1295
            )
1306
1296
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
 
1297
 
1307
1298
register_transport_proto('fakenfs+')
1308
1299
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
 
1300
 
 
1301
register_transport_proto('brokenrename+')
 
1302
register_lazy_transport('brokenrename+', 'bzrlib.transport.brokenrename',
 
1303
        'BrokenRenameTransportDecorator')
 
1304
 
1309
1305
register_transport_proto('vfat+')
1310
1306
register_lazy_transport('vfat+',
1311
1307
                        'bzrlib.transport.fakevfat',