~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:
26
26
it.
27
27
"""
28
28
 
 
29
from cStringIO import StringIO
 
30
import re
 
31
import sys
 
32
 
 
33
from bzrlib.lazy_import import lazy_import
 
34
lazy_import(globals(), """
29
35
import errno
30
36
from collections import deque
31
37
from copy import deepcopy
32
 
from cStringIO import StringIO
33
 
import re
34
38
from stat import S_ISDIR
35
 
import sys
36
 
from unittest import TestSuite
 
39
import unittest
37
40
import urllib
38
41
import urlparse
39
42
import warnings
45
48
    symbol_versioning,
46
49
    urlutils,
47
50
    )
48
 
from bzrlib.errors import DependencyNotPresent
49
 
from bzrlib.osutils import pumpfile
 
51
""")
 
52
 
50
53
from bzrlib.symbol_versioning import (
51
54
        deprecated_passed,
52
55
        deprecated_method,
165
168
            port = int(port)
166
169
        except ValueError:
167
170
            # TODO: Should this be ConnectionError?
168
 
            raise errors.TransportError('%s: invalid port number' % port)
 
171
            raise errors.TransportError(
 
172
                'invalid port number %s in url:\n%s' % (port, url))
169
173
    host = urllib.unquote(host)
170
174
 
171
175
    path = urllib.unquote(path)
198
202
    implementations can do pipelining.
199
203
    In general implementations should support having a generator or a list
200
204
    as an argument (ie always iterate, never index)
 
205
 
 
206
    :ivar base: Base URL for the transport; should always end in a slash.
201
207
    """
202
208
 
203
209
    # implementations can override this if it is more efficient
220
226
 
221
227
        This handles things like ENOENT, ENOTDIR, EEXIST, and EACCESS
222
228
        """
223
 
        if hasattr(e, 'errno'):
 
229
        if getattr(e, 'errno', None) is not None:
224
230
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
225
231
                raise errors.NoSuchFile(path, extra=e)
226
232
            # I would rather use errno.EFOO, but there doesn't seem to be
259
265
        """
260
266
        assert not isinstance(from_file, basestring), \
261
267
            '_pump should only be called on files not %s' % (type(from_file,))
262
 
        pumpfile(from_file, to_file)
 
268
        osutils.pumpfile(from_file, to_file)
263
269
 
264
270
    def _get_total(self, multi):
265
271
        """Try to figure out how many entries are in multi,
304
310
 
305
311
    def abspath(self, relpath):
306
312
        """Return the full url to the given relative path.
307
 
        This can be supplied with a string or a list
308
313
 
309
 
        XXX: Robert Collins 20051016 - is this really needed in the public
310
 
             interface ?
 
314
        :param relpath: a string of a relative path
311
315
        """
 
316
 
 
317
        # XXX: Robert Collins 20051016 - is this really needed in the public
 
318
        # interface ?
312
319
        raise NotImplementedError(self.abspath)
313
320
 
 
321
    def _combine_paths(self, base_path, relpath):
 
322
        """Transform a Transport-relative path to a remote absolute path.
 
323
 
 
324
        This does not handle substitution of ~ but does handle '..' and '.'
 
325
        components.
 
326
 
 
327
        Examples::
 
328
 
 
329
            t._combine_paths('/home/sarah', 'project/foo')
 
330
                => '/home/sarah/project/foo'
 
331
            t._combine_paths('/home/sarah', '../../etc')
 
332
                => '/etc'
 
333
 
 
334
        :param base_path: urlencoded path for the transport root; typically a 
 
335
             URL but need not contain scheme/host/etc.
 
336
        :param relpath: relative url string for relative part of remote path.
 
337
        :return: urlencoded string for final path.
 
338
        """
 
339
        # FIXME: share the common code across more transports; variants of
 
340
        # this likely occur in http and sftp too.
 
341
        #
 
342
        # TODO: Also need to consider handling of ~, which might vary between
 
343
        # transports?
 
344
        if not isinstance(relpath, str):
 
345
            raise errors.InvalidURL("not a valid url: %r" % relpath)
 
346
        if relpath.startswith('/'):
 
347
            base_parts = []
 
348
        else:
 
349
            base_parts = base_path.split('/')
 
350
        if len(base_parts) > 0 and base_parts[-1] == '':
 
351
            base_parts = base_parts[:-1]
 
352
        for p in relpath.split('/'):
 
353
            if p == '..':
 
354
                if len(base_parts) == 0:
 
355
                    # In most filesystems, a request for the parent
 
356
                    # of root, just returns root.
 
357
                    continue
 
358
                base_parts.pop()
 
359
            elif p == '.':
 
360
                continue # No-op
 
361
            elif p != '':
 
362
                base_parts.append(p)
 
363
        path = '/'.join(base_parts)
 
364
        return path
 
365
 
314
366
    def relpath(self, abspath):
315
367
        """Return the local path portion from a given absolute path.
316
368
 
342
394
        Note that some transports MAY allow querying on directories, but this
343
395
        is not part of the protocol.  In other words, the results of 
344
396
        t.has("a_directory_name") are undefined.
 
397
 
 
398
        :rtype: bool
345
399
        """
346
400
        raise NotImplementedError(self.has)
347
401
 
378
432
        """Get the file at the given relative path.
379
433
 
380
434
        :param relpath: The relative path to the file
 
435
        :rtype: File-like object.
381
436
        """
382
437
        raise NotImplementedError(self.get)
383
438
 
388
443
        """
389
444
        return self.get(relpath).read()
390
445
 
 
446
    def get_smart_client(self):
 
447
        """Return a smart client for this transport if possible.
 
448
 
 
449
        A smart client doesn't imply the presence of a smart server: it implies
 
450
        that the smart protocol can be tunnelled via this transport.
 
451
 
 
452
        :raises NoSmartServer: if no smart server client is available.
 
453
        """
 
454
        raise errors.NoSmartServer(self.base)
 
455
 
 
456
    def get_smart_medium(self):
 
457
        """Return a smart client medium for this transport if possible.
 
458
 
 
459
        A smart medium doesn't imply the presence of a smart server: it implies
 
460
        that the smart protocol can be tunnelled via this transport.
 
461
 
 
462
        :raises NoSmartMedium: if no smart server medium is available.
 
463
        """
 
464
        raise errors.NoSmartMedium(self)
 
465
 
391
466
    def readv(self, relpath, offsets):
392
467
        """Get parts of the file at the given relative path.
393
468
 
398
473
            return
399
474
 
400
475
        fp = self.get(relpath)
401
 
        return self._seek_and_read(fp, offsets)
 
476
        return self._seek_and_read(fp, offsets, relpath)
402
477
 
403
 
    def _seek_and_read(self, fp, offsets):
 
478
    def _seek_and_read(self, fp, offsets, relpath='<unknown>'):
404
479
        """An implementation of readv that uses fp.seek and fp.read.
405
480
 
406
481
        This uses _coalesce_offsets to issue larger reads and fewer seeks.
428
503
            #       benchmarked.
429
504
            fp.seek(c_offset.start)
430
505
            data = fp.read(c_offset.length)
 
506
            if len(data) < c_offset.length:
 
507
                raise errors.ShortReadvError(relpath, c_offset.start,
 
508
                            c_offset.length, actual=len(data))
431
509
            for suboffset, subsize in c_offset.ranges:
432
510
                key = (c_offset.start+suboffset, subsize)
433
511
                data_map[key] = data[suboffset:suboffset+subsize]
526
604
        return self.put_file(relpath, StringIO(bytes), mode=mode)
527
605
 
528
606
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
529
 
                             create_parent_dir=False):
 
607
                             create_parent_dir=False,
 
608
                             dir_mode=None):
530
609
        """Copy the string into the target location.
531
610
 
532
611
        This function is not strictly safe to use. See 
540
619
        :param create_parent_dir: If we cannot create the target file because
541
620
                        the parent directory does not exist, go ahead and
542
621
                        create it, and then try again.
 
622
        :param dir_mode: Possible access permissions for new directories.
543
623
        """
544
624
        assert isinstance(bytes, str), \
545
625
            'bytes must be a plain string, not %s' % type(bytes)
546
626
        self.put_file_non_atomic(relpath, StringIO(bytes), mode=mode,
547
 
                                 create_parent_dir=create_parent_dir)
 
627
                                 create_parent_dir=create_parent_dir,
 
628
                                 dir_mode=dir_mode)
548
629
 
549
630
    def put_file(self, relpath, f, mode=None):
550
631
        """Copy the file-like object into the location.
565
646
        #raise NotImplementedError(self.put_file)
566
647
 
567
648
    def put_file_non_atomic(self, relpath, f, mode=None,
568
 
                            create_parent_dir=False):
 
649
                            create_parent_dir=False,
 
650
                            dir_mode=None):
569
651
        """Copy the file-like object into the target location.
570
652
 
571
653
        This function is not strictly safe to use. It is only meant to
580
662
        :param create_parent_dir: If we cannot create the target file because
581
663
                        the parent directory does not exist, go ahead and
582
664
                        create it, and then try again.
 
665
        :param dir_mode: Possible access permissions for new directories.
583
666
        """
584
667
        # Default implementation just does an atomic put.
585
668
        try:
589
672
                raise
590
673
            parent_dir = osutils.dirname(relpath)
591
674
            if parent_dir:
592
 
                self.mkdir(parent_dir)
 
675
                self.mkdir(parent_dir, mode=dir_mode)
593
676
                return self.put_file(relpath, f, mode=mode)
594
677
 
595
678
    @deprecated_method(zero_eleven)
629
712
        return self.append_file(relpath, f, mode=mode)
630
713
 
631
714
    def append_file(self, relpath, f, mode=None):
632
 
        """Append the text in the file-like object to the supplied location.
633
 
 
634
 
        returns the length of relpath before the content was written to it.
635
 
        
636
 
        If the file does not exist, it is created with the supplied mode.
 
715
        """Append bytes from a file-like object to a file at relpath.
 
716
 
 
717
        The file is created if it does not already exist.
 
718
 
 
719
        :param f: a file-like object of the bytes to append.
 
720
        :param mode: Unix mode for newly created files.  This is not used for
 
721
            existing files.
 
722
 
 
723
        :returns: the length of relpath before the content was written to it.
637
724
        """
638
725
        symbol_versioning.warn('Transport %s should implement append_file,'
639
726
                               ' rather than implementing append() as of'
643
730
        return self.append(relpath, f, mode=mode)
644
731
 
645
732
    def append_bytes(self, relpath, bytes, mode=None):
646
 
        """Append the text in the string object to the supplied location.
647
 
 
648
 
        returns the length of relpath before the content was written to it.
649
 
        
650
 
        If the file does not exist, it is created with the supplied mode.
 
733
        """Append bytes to a file at relpath.
 
734
 
 
735
        The file is created if it does not already exist.
 
736
 
 
737
        :type f: str
 
738
        :param f: a string of the bytes to append.
 
739
        :param mode: Unix mode for newly created files.  This is not used for
 
740
            existing files.
 
741
 
 
742
        :returns: the length of relpath before the content was written to it.
651
743
        """
652
744
        assert isinstance(bytes, str), \
653
745
            'bytes must be a plain string, not %s' % type(bytes)
843
935
        WARNING: many transports do not support this, so trying avoid using
844
936
        it if at all possible.
845
937
        """
846
 
        raise errors.TransportNotPossible("This transport has not "
 
938
        raise errors.TransportNotPossible("Transport %r has not "
847
939
                                          "implemented list_dir "
848
940
                                          "(but must claim to be listable "
849
 
                                          "to trigger this error).")
 
941
                                          "to trigger this error)."
 
942
                                          % (self))
850
943
 
851
944
    def lock_read(self, relpath):
852
945
        """Lock the given file for shared (read) access.
853
 
        WARNING: many transports do not support this, so trying avoid using it
 
946
 
 
947
        WARNING: many transports do not support this, so trying avoid using it.
 
948
        These methods may be removed in the future.
 
949
 
 
950
        Transports may raise TransportNotPossible if OS-level locks cannot be
 
951
        taken over this transport.  
854
952
 
855
953
        :return: A lock object, which should contain an unlock() function.
856
954
        """
857
 
        raise NotImplementedError(self.lock_read)
 
955
        raise errors.TransportNotPossible("transport locks not supported on %s" % self)
858
956
 
859
957
    def lock_write(self, relpath):
860
958
        """Lock the given file for exclusive (write) access.
861
 
        WARNING: many transports do not support this, so trying avoid using it
 
959
 
 
960
        WARNING: many transports do not support this, so trying avoid using it.
 
961
        These methods may be removed in the future.
 
962
 
 
963
        Transports may raise TransportNotPossible if OS-level locks cannot be
 
964
        taken over this transport.
862
965
 
863
966
        :return: A lock object, which should contain an unlock() function.
864
967
        """
865
 
        raise NotImplementedError(self.lock_write)
 
968
        raise errors.TransportNotPossible("transport locks not supported on %s" % self)
866
969
 
867
970
    def is_readonly(self):
868
971
        """Return true if this connection cannot be written to."""
943
1046
    for factory in factory_list:
944
1047
        try:
945
1048
            return factory(base), None
946
 
        except DependencyNotPresent, e:
 
1049
        except errors.DependencyNotPresent, e:
947
1050
            mutter("failed to instantiate transport %r for %r: %r" %
948
1051
                    (factory, base, e))
949
1052
            last_err = e
996
1099
    """
997
1100
 
998
1101
    def adapt(self, test):
999
 
        result = TestSuite()
 
1102
        result = unittest.TestSuite()
1000
1103
        for klass, server_factory in self._test_permutations():
1001
1104
            new_test = deepcopy(test)
1002
1105
            new_test.transport_class = klass
1010
1113
 
1011
1114
    def get_transport_test_permutations(self, module):
1012
1115
        """Get the permutations module wants to have tested."""
1013
 
        if not hasattr(module, 'get_test_permutations'):
 
1116
        if getattr(module, 'get_test_permutations', None) is None:
1014
1117
            warning("transport module %s doesn't provide get_test_permutations()"
1015
1118
                    % module.__name__)
1016
1119
            return []
1078
1181
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
1079
1182
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
1080
1183
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
1081
 
register_lazy_transport('vfat+', 
 
1184
register_lazy_transport('vfat+',
1082
1185
                        'bzrlib.transport.fakevfat',
1083
1186
                        'FakeVFATTransportDecorator')
 
1187
register_lazy_transport('bzr://',
 
1188
                        'bzrlib.transport.smart',
 
1189
                        'SmartTCPTransport')
 
1190
register_lazy_transport('bzr+ssh://',
 
1191
                        'bzrlib.transport.smart',
 
1192
                        'SmartSSHTransport')