~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:
29
29
import errno
30
30
from collections import deque
31
31
from copy import deepcopy
 
32
from cStringIO import StringIO
32
33
import re
33
34
from stat import S_ISDIR
34
35
import sys
38
39
import warnings
39
40
 
40
41
import bzrlib
41
 
import bzrlib.errors as errors
 
42
from bzrlib import (
 
43
    errors,
 
44
    osutils,
 
45
    symbol_versioning,
 
46
    urlutils,
 
47
    )
42
48
from bzrlib.errors import DependencyNotPresent
43
 
import bzrlib.osutils as osutils
44
49
from bzrlib.osutils import pumpfile
45
 
from bzrlib.symbol_versioning import (deprecated_passed, deprecated_method, deprecated_function, 
 
50
from bzrlib.symbol_versioning import (
 
51
        deprecated_passed,
 
52
        deprecated_method,
 
53
        deprecated_function,
46
54
        DEPRECATED_PARAMETER,
47
 
        zero_eight)
 
55
        zero_eight,
 
56
        zero_eleven,
 
57
        )
48
58
from bzrlib.trace import mutter, warning
49
 
import bzrlib.urlutils as urlutils
50
59
 
51
60
# {prefix: [transport_classes]}
52
61
# Transports are inserted onto the list LIFO and tried in order; as a result
156
165
            port = int(port)
157
166
        except ValueError:
158
167
            # TODO: Should this be ConnectionError?
159
 
            raise errors.TransportError('%s: invalid port number' % port)
 
168
            raise errors.TransportError(
 
169
                'invalid port number %s in url:\n%s' % (port, url))
160
170
    host = urllib.unquote(host)
161
171
 
162
172
    path = urllib.unquote(path)
189
199
    implementations can do pipelining.
190
200
    In general implementations should support having a generator or a list
191
201
    as an argument (ie always iterate, never index)
 
202
 
 
203
    :ivar base: Base URL for the transport; should always end in a slash.
192
204
    """
193
205
 
194
206
    # implementations can override this if it is more efficient
211
223
 
212
224
        This handles things like ENOENT, ENOTDIR, EEXIST, and EACCESS
213
225
        """
214
 
        if hasattr(e, 'errno'):
 
226
        if getattr(e, 'errno', None) is not None:
215
227
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
216
228
                raise errors.NoSuchFile(path, extra=e)
217
229
            # I would rather use errno.EFOO, but there doesn't seem to be
248
260
        object or string to another one.
249
261
        This just gives them something easy to call.
250
262
        """
251
 
        if isinstance(from_file, basestring):
252
 
            to_file.write(from_file)
253
 
        else:
254
 
            pumpfile(from_file, to_file)
 
263
        assert not isinstance(from_file, basestring), \
 
264
            '_pump should only be called on files not %s' % (type(from_file,))
 
265
        pumpfile(from_file, to_file)
255
266
 
256
267
    def _get_total(self, multi):
257
268
        """Try to figure out how many entries are in multi,
296
307
 
297
308
    def abspath(self, relpath):
298
309
        """Return the full url to the given relative path.
299
 
        This can be supplied with a string or a list
300
310
 
301
 
        XXX: Robert Collins 20051016 - is this really needed in the public
302
 
             interface ?
 
311
        :param relpath: a string of a relative path
303
312
        """
 
313
 
 
314
        # XXX: Robert Collins 20051016 - is this really needed in the public
 
315
        # interface ?
304
316
        raise NotImplementedError(self.abspath)
305
317
 
 
318
    def _combine_paths(self, base_path, relpath):
 
319
        """Transform a Transport-relative path to a remote absolute path.
 
320
 
 
321
        This does not handle substitution of ~ but does handle '..' and '.'
 
322
        components.
 
323
 
 
324
        Examples::
 
325
 
 
326
            >>> t = Transport('/')
 
327
            >>> t._combine_paths('/home/sarah', 'project/foo')
 
328
            '/home/sarah/project/foo'
 
329
            >>> t._combine_paths('/home/sarah', '../../etc')
 
330
            '/etc'
 
331
 
 
332
        :param base_path: urlencoded path for the transport root; typically a 
 
333
             URL but need not contain scheme/host/etc.
 
334
        :param relpath: relative url string for relative part of remote path.
 
335
        :return: urlencoded string for final path.
 
336
        """
 
337
        # FIXME: share the common code across more transports; variants of
 
338
        # this likely occur in http and sftp too.
 
339
        #
 
340
        # TODO: Also need to consider handling of ~, which might vary between
 
341
        # transports?
 
342
        if not isinstance(relpath, str):
 
343
            raise errors.InvalidURL("not a valid url: %r" % relpath)
 
344
        if relpath.startswith('/'):
 
345
            base_parts = []
 
346
        else:
 
347
            base_parts = base_path.split('/')
 
348
        if len(base_parts) > 0 and base_parts[-1] == '':
 
349
            base_parts = base_parts[:-1]
 
350
        for p in relpath.split('/'):
 
351
            if p == '..':
 
352
                if len(base_parts) == 0:
 
353
                    # In most filesystems, a request for the parent
 
354
                    # of root, just returns root.
 
355
                    continue
 
356
                base_parts.pop()
 
357
            elif p == '.':
 
358
                continue # No-op
 
359
            elif p != '':
 
360
                base_parts.append(p)
 
361
        path = '/'.join(base_parts)
 
362
        return path
 
363
 
306
364
    def relpath(self, abspath):
307
365
        """Return the local path portion from a given absolute path.
308
366
 
334
392
        Note that some transports MAY allow querying on directories, but this
335
393
        is not part of the protocol.  In other words, the results of 
336
394
        t.has("a_directory_name") are undefined.
 
395
 
 
396
        :rtype: bool
337
397
        """
338
398
        raise NotImplementedError(self.has)
339
399
 
370
430
        """Get the file at the given relative path.
371
431
 
372
432
        :param relpath: The relative path to the file
 
433
        :rtype: File-like object.
373
434
        """
374
435
        raise NotImplementedError(self.get)
375
436
 
 
437
    def get_bytes(self, relpath):
 
438
        """Get a raw string of the bytes for a file at the given location.
 
439
 
 
440
        :param relpath: The relative path to the file
 
441
        """
 
442
        return self.get(relpath).read()
 
443
 
 
444
    def get_smart_client(self):
 
445
        """Return a smart client for this transport if possible.
 
446
 
 
447
        :raises NoSmartServer: if no smart server client is available.
 
448
        """
 
449
        raise errors.NoSmartServer(self.base)
 
450
 
376
451
    def readv(self, relpath, offsets):
377
452
        """Get parts of the file at the given relative path.
378
453
 
383
458
            return
384
459
 
385
460
        fp = self.get(relpath)
386
 
        return self._seek_and_read(fp, offsets)
 
461
        return self._seek_and_read(fp, offsets, relpath)
387
462
 
388
 
    def _seek_and_read(self, fp, offsets):
 
463
    def _seek_and_read(self, fp, offsets, relpath='<unknown>'):
389
464
        """An implementation of readv that uses fp.seek and fp.read.
390
465
 
391
466
        This uses _coalesce_offsets to issue larger reads and fewer seeks.
413
488
            #       benchmarked.
414
489
            fp.seek(c_offset.start)
415
490
            data = fp.read(c_offset.length)
 
491
            if len(data) < c_offset.length:
 
492
                raise errors.ShortReadvError(relpath, c_offset.start,
 
493
                            c_offset.length, actual=len(data))
416
494
            for suboffset, subsize in c_offset.ranges:
417
495
                key = (c_offset.start+suboffset, subsize)
418
496
                data_map[key] = data[suboffset:suboffset+subsize]
483
561
            yield self.get(relpath)
484
562
            count += 1
485
563
 
 
564
    @deprecated_method(zero_eleven)
486
565
    def put(self, relpath, f, mode=None):
487
 
        """Copy the file-like or string object into the location.
 
566
        """Copy the file-like object into the location.
488
567
 
489
568
        :param relpath: Location to put the contents, relative to base.
490
 
        :param f:       File-like or string object.
 
569
        :param f:       File-like object.
491
570
        :param mode: The mode for the newly created file, 
492
571
                     None means just use the default
493
572
        """
494
 
        raise NotImplementedError(self.put)
495
 
 
 
573
        if isinstance(f, str):
 
574
            return self.put_bytes(relpath, f, mode=mode)
 
575
        else:
 
576
            return self.put_file(relpath, f, mode=mode)
 
577
 
 
578
    def put_bytes(self, relpath, bytes, mode=None):
 
579
        """Atomically put the supplied bytes into the given location.
 
580
 
 
581
        :param relpath: The location to put the contents, relative to the
 
582
            transport base.
 
583
        :param bytes: A bytestring of data.
 
584
        :param mode: Create the file with the given mode.
 
585
        :return: None
 
586
        """
 
587
        assert isinstance(bytes, str), \
 
588
            'bytes must be a plain string, not %s' % type(bytes)
 
589
        return self.put_file(relpath, StringIO(bytes), mode=mode)
 
590
 
 
591
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
 
592
                             create_parent_dir=False,
 
593
                             dir_mode=None):
 
594
        """Copy the string into the target location.
 
595
 
 
596
        This function is not strictly safe to use. See 
 
597
        Transport.put_bytes_non_atomic for more information.
 
598
 
 
599
        :param relpath: The remote location to put the contents.
 
600
        :param bytes:   A string object containing the raw bytes to write into
 
601
                        the target file.
 
602
        :param mode:    Possible access permissions for new file.
 
603
                        None means do not set remote permissions.
 
604
        :param create_parent_dir: If we cannot create the target file because
 
605
                        the parent directory does not exist, go ahead and
 
606
                        create it, and then try again.
 
607
        :param dir_mode: Possible access permissions for new directories.
 
608
        """
 
609
        assert isinstance(bytes, str), \
 
610
            'bytes must be a plain string, not %s' % type(bytes)
 
611
        self.put_file_non_atomic(relpath, StringIO(bytes), mode=mode,
 
612
                                 create_parent_dir=create_parent_dir,
 
613
                                 dir_mode=dir_mode)
 
614
 
 
615
    def put_file(self, relpath, f, mode=None):
 
616
        """Copy the file-like object into the location.
 
617
 
 
618
        :param relpath: Location to put the contents, relative to base.
 
619
        :param f:       File-like object.
 
620
        :param mode: The mode for the newly created file,
 
621
                     None means just use the default.
 
622
        """
 
623
        # We would like to mark this as NotImplemented, but most likely
 
624
        # transports have defined it in terms of the old api.
 
625
        symbol_versioning.warn('Transport %s should implement put_file,'
 
626
                               ' rather than implementing put() as of'
 
627
                               ' version 0.11.'
 
628
                               % (self.__class__.__name__,),
 
629
                               DeprecationWarning)
 
630
        return self.put(relpath, f, mode=mode)
 
631
        #raise NotImplementedError(self.put_file)
 
632
 
 
633
    def put_file_non_atomic(self, relpath, f, mode=None,
 
634
                            create_parent_dir=False,
 
635
                            dir_mode=None):
 
636
        """Copy the file-like object into the target location.
 
637
 
 
638
        This function is not strictly safe to use. It is only meant to
 
639
        be used when you already know that the target does not exist.
 
640
        It is not safe, because it will open and truncate the remote
 
641
        file. So there may be a time when the file has invalid contents.
 
642
 
 
643
        :param relpath: The remote location to put the contents.
 
644
        :param f:       File-like object.
 
645
        :param mode:    Possible access permissions for new file.
 
646
                        None means do not set remote permissions.
 
647
        :param create_parent_dir: If we cannot create the target file because
 
648
                        the parent directory does not exist, go ahead and
 
649
                        create it, and then try again.
 
650
        :param dir_mode: Possible access permissions for new directories.
 
651
        """
 
652
        # Default implementation just does an atomic put.
 
653
        try:
 
654
            return self.put_file(relpath, f, mode=mode)
 
655
        except errors.NoSuchFile:
 
656
            if not create_parent_dir:
 
657
                raise
 
658
            parent_dir = osutils.dirname(relpath)
 
659
            if parent_dir:
 
660
                self.mkdir(parent_dir, mode=dir_mode)
 
661
                return self.put_file(relpath, f, mode=mode)
 
662
 
 
663
    @deprecated_method(zero_eleven)
496
664
    def put_multi(self, files, mode=None, pb=None):
497
665
        """Put a set of files into the location.
498
666
 
501
669
        :param mode: The mode for the newly created files
502
670
        :return: The number of files copied.
503
671
        """
504
 
        def put(path, f):
505
 
            self.put(path, f, mode=mode)
506
 
        return len(self._iterate_over(files, put, pb, 'put', expand=True))
 
672
        def _put(path, f):
 
673
            if isinstance(f, str):
 
674
                self.put_bytes(path, f, mode=mode)
 
675
            else:
 
676
                self.put_file(path, f, mode=mode)
 
677
        return len(self._iterate_over(files, _put, pb, 'put', expand=True))
507
678
 
508
679
    def mkdir(self, relpath, mode=None):
509
680
        """Create a directory at the given path."""
515
686
            self.mkdir(path, mode=mode)
516
687
        return len(self._iterate_over(relpaths, mkdir, pb, 'mkdir', expand=False))
517
688
 
 
689
    @deprecated_method(zero_eleven)
518
690
    def append(self, relpath, f, mode=None):
519
 
        """Append the text in the file-like or string object to 
520
 
        the supplied location.
 
691
        """Append the text in the file-like object to the supplied location.
521
692
 
522
 
        returns the length of f before the content was written to it.
 
693
        returns the length of relpath before the content was written to it.
523
694
        
524
695
        If the file does not exist, it is created with the supplied mode.
525
696
        """
526
 
        raise NotImplementedError(self.append)
 
697
        return self.append_file(relpath, f, mode=mode)
 
698
 
 
699
    def append_file(self, relpath, f, mode=None):
 
700
        """Append bytes from a file-like object to a file at relpath.
 
701
 
 
702
        The file is created if it does not already exist.
 
703
 
 
704
        :param f: a file-like object of the bytes to append.
 
705
        :param mode: Unix mode for newly created files.  This is not used for
 
706
            existing files.
 
707
 
 
708
        :returns: the length of relpath before the content was written to it.
 
709
        """
 
710
        symbol_versioning.warn('Transport %s should implement append_file,'
 
711
                               ' rather than implementing append() as of'
 
712
                               ' version 0.11.'
 
713
                               % (self.__class__.__name__,),
 
714
                               DeprecationWarning)
 
715
        return self.append(relpath, f, mode=mode)
 
716
 
 
717
    def append_bytes(self, relpath, bytes, mode=None):
 
718
        """Append bytes to a file at relpath.
 
719
 
 
720
        The file is created if it does not already exist.
 
721
 
 
722
        :type f: str
 
723
        :param f: a string of the bytes to append.
 
724
        :param mode: Unix mode for newly created files.  This is not used for
 
725
            existing files.
 
726
 
 
727
        :returns: the length of relpath before the content was written to it.
 
728
        """
 
729
        assert isinstance(bytes, str), \
 
730
            'bytes must be a plain string, not %s' % type(bytes)
 
731
        return self.append_file(relpath, StringIO(bytes), mode=mode)
527
732
 
528
733
    def append_multi(self, files, pb=None):
529
734
        """Append the text in each file-like or string object to
532
737
        :param files: A set of (path, f) entries
533
738
        :param pb:  An optional ProgressBar for indicating percent done.
534
739
        """
535
 
        return self._iterate_over(files, self.append, pb, 'append', expand=True)
 
740
        return self._iterate_over(files, self.append_file, pb, 'append', expand=True)
536
741
 
537
742
    def copy(self, rel_from, rel_to):
538
743
        """Copy the item at rel_from to the location at rel_to.
540
745
        Override this for efficiency if a specific transport can do it 
541
746
        faster than this default implementation.
542
747
        """
543
 
        self.put(rel_to, self.get(rel_from))
 
748
        self.put_file(rel_to, self.get(rel_from))
544
749
 
545
750
    def copy_multi(self, relpaths, pb=None):
546
751
        """Copy a bunch of entries.
561
766
        """
562
767
        # The dummy implementation just does a simple get + put
563
768
        def copy_entry(path):
564
 
            other.put(path, self.get(path), mode=mode)
 
769
            other.put_file(path, self.get(path), mode=mode)
565
770
 
566
771
        return len(self._iterate_over(relpaths, copy_entry, pb, 'copy_to', expand=False))
567
772
 
715
920
        WARNING: many transports do not support this, so trying avoid using
716
921
        it if at all possible.
717
922
        """
718
 
        raise errors.TransportNotPossible("This transport has not "
 
923
        raise errors.TransportNotPossible("Transport %r has not "
719
924
                                          "implemented list_dir "
720
925
                                          "(but must claim to be listable "
721
 
                                          "to trigger this error).")
 
926
                                          "to trigger this error)."
 
927
                                          % (self))
722
928
 
723
929
    def lock_read(self, relpath):
724
930
        """Lock the given file for shared (read) access.
725
 
        WARNING: many transports do not support this, so trying avoid using it
 
931
 
 
932
        WARNING: many transports do not support this, so trying avoid using it.
 
933
        These methods may be removed in the future.
 
934
 
 
935
        Transports may raise TransportNotPossible if OS-level locks cannot be
 
936
        taken over this transport.  
726
937
 
727
938
        :return: A lock object, which should contain an unlock() function.
728
939
        """
729
 
        raise NotImplementedError(self.lock_read)
 
940
        raise errors.TransportNotPossible("transport locks not supported on %s" % self)
730
941
 
731
942
    def lock_write(self, relpath):
732
943
        """Lock the given file for exclusive (write) access.
733
 
        WARNING: many transports do not support this, so trying avoid using it
 
944
 
 
945
        WARNING: many transports do not support this, so trying avoid using it.
 
946
        These methods may be removed in the future.
 
947
 
 
948
        Transports may raise TransportNotPossible if OS-level locks cannot be
 
949
        taken over this transport.
734
950
 
735
951
        :return: A lock object, which should contain an unlock() function.
736
952
        """
737
 
        raise NotImplementedError(self.lock_write)
 
953
        raise errors.TransportNotPossible("transport locks not supported on %s" % self)
738
954
 
739
955
    def is_readonly(self):
740
956
        """Return true if this connection cannot be written to."""
882
1098
 
883
1099
    def get_transport_test_permutations(self, module):
884
1100
        """Get the permutations module wants to have tested."""
885
 
        if not hasattr(module, 'get_test_permutations'):
 
1101
        if getattr(module, 'get_test_permutations', None) is None:
886
1102
            warning("transport module %s doesn't provide get_test_permutations()"
887
1103
                    % module.__name__)
888
1104
            return []
950
1166
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
951
1167
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
952
1168
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
953
 
register_lazy_transport('vfat+', 
 
1169
register_lazy_transport('vfat+',
954
1170
                        'bzrlib.transport.fakevfat',
955
1171
                        'FakeVFATTransportDecorator')
 
1172
register_lazy_transport('bzr://',
 
1173
                        'bzrlib.transport.smart',
 
1174
                        'SmartTCPTransport')
 
1175
register_lazy_transport('bzr+ssh://',
 
1176
                        'bzrlib.transport.smart',
 
1177
                        'SmartSSHTransport')