~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
from bzrlib.lazy_import import lazy_import
34
34
lazy_import(globals(), """
35
35
import errno
36
 
from collections import deque
37
36
from stat import S_ISDIR
38
 
import unittest
39
37
import urllib
40
38
import urlparse
41
 
import warnings
42
39
 
43
 
import bzrlib
44
40
from bzrlib import (
45
41
    errors,
46
42
    osutils,
50
46
""")
51
47
 
52
48
from bzrlib.symbol_versioning import (
53
 
        deprecated_passed,
54
49
        deprecated_method,
55
50
        deprecated_function,
56
51
        DEPRECATED_PARAMETER,
57
 
        zero_eight,
58
 
        zero_eleven,
 
52
        one_four,
59
53
        zero_ninety,
60
54
        )
61
55
from bzrlib.trace import (
62
 
    note,
63
56
    mutter,
64
 
    warning,
65
57
    )
66
58
from bzrlib import registry
67
59
 
128
120
        self.get(key).insert(0,
129
121
                registry._LazyObjectGetter(module_name, member_name))
130
122
 
131
 
    def register_transport(self, key, help=None, default_port=None):
132
 
        self.register(key, [], help, default_port)
 
123
    def register_transport(self, key, help=None):
 
124
        self.register(key, [], help)
133
125
 
134
126
    def set_default_transport(self, key=None):
135
127
        """Return either 'key' or the default key if key is None"""
136
128
        self._default_key = key
137
129
 
138
 
    def get_default_port(self, scheme):
139
 
        """Return the registered default port for this protocol scheme."""
140
 
        try:
141
 
            return self.get_info(scheme + '://')
142
 
        except LookupError:
143
 
            return None
144
 
 
145
130
 
146
131
transport_list_registry = TransportListRegistry()
147
132
 
148
133
 
149
 
def register_transport_proto(prefix, help=None, info=None, default_port=None):
150
 
    transport_list_registry.register_transport(prefix, help, default_port)
 
134
def register_transport_proto(prefix, help=None, info=None,
 
135
                             register_netloc=False):
 
136
    transport_list_registry.register_transport(prefix, help)
 
137
    if register_netloc:
 
138
        assert prefix.endswith('://')
 
139
        register_urlparse_netloc_protocol(prefix[:-3])
151
140
 
152
141
 
153
142
def register_lazy_transport(prefix, module, classname):
237
226
        return cmp((self.start, self.length, self.ranges),
238
227
                   (other.start, other.length, other.ranges))
239
228
 
 
229
    def __repr__(self):
 
230
        return '%s(%r, %r, %r)' % (self.__class__.__name__,
 
231
            self.start, self.length, self.ranges)
 
232
 
240
233
 
241
234
class LateReadError(object):
242
235
    """A helper for transports which pretends to be a readable file.
342
335
        This handles things like ENOENT, ENOTDIR, EEXIST, and EACCESS
343
336
        """
344
337
        if getattr(e, 'errno', None) is not None:
345
 
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
 
338
            if e.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
346
339
                raise errors.NoSuchFile(path, extra=e)
347
340
            # I would rather use errno.EFOO, but there doesn't seem to be
348
341
            # any matching for 267
612
605
        """
613
606
        return self.get(relpath).read()
614
607
 
 
608
    @deprecated_method(one_four)
615
609
    def get_smart_client(self):
616
610
        """Return a smart client for this transport if possible.
617
611
 
632
626
        """
633
627
        raise errors.NoSmartMedium(self)
634
628
 
 
629
    @deprecated_method(one_four)
635
630
    def get_shared_medium(self):
636
631
        """Return a smart client shared medium for this transport if possible.
637
632
 
648
643
 
649
644
        :param relpath: The path to read data from.
650
645
        :param offsets: A list of (offset, size) tuples.
651
 
        :param adjust_for_latency: Adjust the requested offsets to accomdate
 
646
        :param adjust_for_latency: Adjust the requested offsets to accomodate
652
647
            transport latency. This may re-order the offsets, expand them to
653
648
            grab adjacent data when there is likely a high cost to requesting
654
649
            data relative to delivering it.
664
659
        if adjust_for_latency:
665
660
            # Design note: We may wish to have different algorithms for the
666
661
            # expansion of the offsets per-transport. E.g. for local disk to
667
 
            # use page-aligned expansion. If that is the case consider the following structure:
668
 
            #  - a test that transport.readv uses self._offset_expander or some similar attribute, to do the expansion
669
 
            #  - a test for each transport that it has some known-good offset expander
 
662
            # use page-aligned expansion. If that is the case consider the
 
663
            # following structure:
 
664
            #  - a test that transport.readv uses self._offset_expander or some
 
665
            #    similar attribute, to do the expansion
 
666
            #  - a test for each transport that it has some known-good offset
 
667
            #    expander
670
668
            #  - unit tests for each offset expander
671
669
            #  - a set of tests for the offset expander interface, giving
672
670
            #    baseline behaviour (which the current transport
788
786
        return offsets
789
787
 
790
788
    @staticmethod
791
 
    def _coalesce_offsets(offsets, limit, fudge_factor):
 
789
    def _coalesce_offsets(offsets, limit=0, fudge_factor=0, max_size=0):
792
790
        """Yield coalesced offsets.
793
791
 
794
792
        With a long list of neighboring requests, combine them
797
795
        Turns  [(15, 10), (25, 10)] => [(15, 20, [(0, 10), (10, 10)])]
798
796
 
799
797
        :param offsets: A list of (start, length) pairs
800
 
        :param limit: Only combine a maximum of this many pairs
801
 
                      Some transports penalize multiple reads more than
802
 
                      others, and sometimes it is better to return early.
803
 
                      0 means no limit
 
798
 
 
799
        :param limit: Only combine a maximum of this many pairs Some transports
 
800
                penalize multiple reads more than others, and sometimes it is
 
801
                better to return early.
 
802
                0 means no limit
 
803
 
804
804
        :param fudge_factor: All transports have some level of 'it is
805
805
                better to read some more data and throw it away rather 
806
806
                than seek', so collapse if we are 'close enough'
 
807
 
 
808
        :param max_size: Create coalesced offsets no bigger than this size.
 
809
                When a single offset is bigger than 'max_size', it will keep
 
810
                its size and be alone in the coalesced offset.
 
811
                0 means no maximum size.
 
812
 
807
813
        :return: yield _CoalescedOffset objects, which have members for where
808
814
                to start, how much to read, and how to split those 
809
815
                chunks back up
813
819
 
814
820
        for start, size in offsets:
815
821
            end = start + size
816
 
            if (last_end is not None 
 
822
            if (last_end is not None
817
823
                and start <= last_end + fudge_factor
818
824
                and start >= cur.start
819
 
                and (limit <= 0 or len(cur.ranges) < limit)):
 
825
                and (limit <= 0 or len(cur.ranges) < limit)
 
826
                and (max_size <= 0 or end - cur.start <= max_size)):
820
827
                cur.length = end - cur.start
821
828
                cur.ranges.append((start-cur.start, size))
822
829
            else:
1249
1256
class _SharedConnection(object):
1250
1257
    """A connection shared between several transports."""
1251
1258
 
1252
 
    def __init__(self, connection=None, credentials=None):
 
1259
    def __init__(self, connection=None, credentials=None, base=None):
1253
1260
        """Constructor.
1254
1261
 
1255
1262
        :param connection: An opaque object specific to each transport.
1259
1266
        """
1260
1267
        self.connection = connection
1261
1268
        self.credentials = credentials
 
1269
        self.base = base
1262
1270
 
1263
1271
 
1264
1272
class ConnectedTransport(Transport):
1352
1360
            except ValueError:
1353
1361
                raise errors.InvalidURL('invalid port number %s in url:\n%s' %
1354
1362
                                        (port, url))
 
1363
        if host == '':
 
1364
            raise errors.InvalidURL('Host empty in: %s' % url)
 
1365
 
1355
1366
        host = urllib.unquote(host)
1356
1367
        path = urllib.unquote(path)
1357
1368
 
1358
 
        if port is None:
1359
 
            # The port isn't explicitly specified, so return the default (if
1360
 
            # there is one).
1361
 
            port = transport_list_registry.get_default_port(scheme)
1362
1369
        return (scheme, user, password, host, port, path)
1363
1370
 
1364
1371
    @staticmethod
1389
1396
            # have one so that it doesn't get accidentally
1390
1397
            # exposed.
1391
1398
            netloc = '%s@%s' % (urllib.quote(user), netloc)
1392
 
        if (port is not None and 
1393
 
            port != transport_list_registry.get_default_port(scheme)):
1394
 
            # Include the port in the netloc (unless it's the same as the
1395
 
            # default, in which case we omit it as it is redundant).
 
1399
        if port is not None:
1396
1400
            netloc = '%s:%d' % (netloc, port)
1397
1401
        path = urllib.quote(path)
1398
1402
        return urlparse.urlunparse((scheme, netloc, path, None, None, None))
1449
1453
        """Get the object shared amongst cloned transports.
1450
1454
 
1451
1455
        This should be used only by classes that needs to extend the sharing
1452
 
        with other objects than tramsports.
 
1456
        with objects other than transports.
1453
1457
 
1454
1458
        Use _get_connection to get the connection itself.
1455
1459
        """
1507
1511
 
1508
1512
        :return: A new transport or None if the connection cannot be shared.
1509
1513
        """
1510
 
        (scheme, user, password, host, port, path) = self._split_url(other_base)
 
1514
        try:
 
1515
            (scheme, user, password,
 
1516
             host, port, path) = self._split_url(other_base)
 
1517
        except errors.InvalidURL:
 
1518
            # No hope in trying to reuse an existing transport for an invalid
 
1519
            # URL
 
1520
            return None
 
1521
 
1511
1522
        transport = None
1512
1523
        # Don't compare passwords, they may be absent from other_base or from
1513
1524
        # self and they don't carry more information than user anyway.
1555
1566
    if base is None:
1556
1567
        base = '.'
1557
1568
    last_err = None
 
1569
    from bzrlib.directory_service import directories
 
1570
    base = directories.dereference(base)
1558
1571
 
1559
1572
    def convert_path_to_url(base, error_str):
1560
1573
        m = _urlRE.match(base)
1707
1720
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
1708
1721
transport_list_registry.set_default_transport("file://")
1709
1722
 
1710
 
# Note that sftp:// has no default_port, because the user's ~/.ssh/config
1711
 
# can set it to arbitrary values based on hostname.
1712
1723
register_transport_proto('sftp://',
1713
 
            help="Access using SFTP (most SSH servers provide SFTP).")
 
1724
            help="Access using SFTP (most SSH servers provide SFTP).",
 
1725
            register_netloc=True)
1714
1726
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
1715
1727
# Decorated http transport
1716
1728
register_transport_proto('http+urllib://',
1717
1729
#                help="Read-only access of branches exported on the web."
1718
 
            default_port=80)
 
1730
                         register_netloc=True)
1719
1731
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
1720
1732
                        'HttpTransport_urllib')
1721
1733
register_transport_proto('https+urllib://',
1722
1734
#                help="Read-only access of branches exported on the web using SSL."
1723
 
            default_port=443)
 
1735
                         register_netloc=True)
1724
1736
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
1725
1737
                        'HttpTransport_urllib')
1726
1738
register_transport_proto('http+pycurl://',
1727
1739
#                help="Read-only access of branches exported on the web."
1728
 
            default_port=80)
 
1740
                         register_netloc=True)
1729
1741
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
1730
1742
                        'PyCurlTransport')
1731
1743
register_transport_proto('https+pycurl://',
1732
1744
#                help="Read-only access of branches exported on the web using SSL."
1733
 
            default_port=443)
 
1745
                         register_netloc=True)
1734
1746
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
1735
1747
                        'PyCurlTransport')
1736
1748
# Default http transports (last declared wins (if it can be imported))
1737
1749
register_transport_proto('http://',
1738
 
            help="Read-only access of branches exported on the web.",
1739
 
            default_port=80)
 
1750
                 help="Read-only access of branches exported on the web.")
1740
1751
register_transport_proto('https://',
1741
 
            help="Read-only access of branches exported on the web using SSL.",
1742
 
            default_port=443)
 
1752
            help="Read-only access of branches exported on the web using SSL.")
1743
1753
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
1744
1754
                        'HttpTransport_urllib')
1745
1755
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1746
1756
                        'HttpTransport_urllib')
1747
 
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
1748
 
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
 
1757
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl',
 
1758
                        'PyCurlTransport')
 
1759
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl',
 
1760
                        'PyCurlTransport')
1749
1761
 
1750
 
register_transport_proto('ftp://',
1751
 
            help="Access using passive FTP.",
1752
 
            default_port=21)
 
1762
register_transport_proto('ftp://', help="Access using passive FTP.")
1753
1763
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1754
 
register_transport_proto('aftp://',
1755
 
            help="Access using active FTP.",
1756
 
            default_port=21)
 
1764
register_transport_proto('aftp://', help="Access using active FTP.")
1757
1765
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1758
1766
 
1759
1767
register_transport_proto('memory://')
1760
 
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
 
1768
register_lazy_transport('memory://', 'bzrlib.transport.memory',
 
1769
                        'MemoryTransport')
1761
1770
 
1762
1771
# chroots cannot be implicitly accessed, they must be explicitly created:
1763
1772
register_transport_proto('chroot+')
1765
1774
register_transport_proto('readonly+',
1766
1775
#              help="This modifier converts any transport to be readonly."
1767
1776
            )
1768
 
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
 
1777
register_lazy_transport('readonly+', 'bzrlib.transport.readonly',
 
1778
                        'ReadonlyTransportDecorator')
1769
1779
 
1770
1780
register_transport_proto('fakenfs+')
1771
 
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
 
1781
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs',
 
1782
                        'FakeNFSTransportDecorator')
1772
1783
 
1773
1784
register_transport_proto('trace+')
1774
 
register_lazy_transport('trace+', 'bzrlib.transport.trace', 'TransportTraceDecorator')
 
1785
register_lazy_transport('trace+', 'bzrlib.transport.trace',
 
1786
                        'TransportTraceDecorator')
1775
1787
 
1776
1788
register_transport_proto('unlistable+')
1777
 
register_lazy_transport('unlistable+', 'bzrlib.transport.unlistable', 'UnlistableTransportDecorator')
 
1789
register_lazy_transport('unlistable+', 'bzrlib.transport.unlistable',
 
1790
                        'UnlistableTransportDecorator')
1778
1791
 
1779
1792
register_transport_proto('brokenrename+')
1780
1793
register_lazy_transport('brokenrename+', 'bzrlib.transport.brokenrename',
1781
 
        'BrokenRenameTransportDecorator')
 
1794
                        'BrokenRenameTransportDecorator')
1782
1795
 
1783
1796
register_transport_proto('vfat+')
1784
1797
register_lazy_transport('vfat+',
1785
1798
                        'bzrlib.transport.fakevfat',
1786
1799
                        'FakeVFATTransportDecorator')
 
1800
 
 
1801
register_transport_proto('nosmart+')
 
1802
register_lazy_transport('nosmart+', 'bzrlib.transport.nosmart',
 
1803
                        'NoSmartTransportDecorator')
 
1804
 
 
1805
# These two schemes were registered, but don't seem to have an actual transport
 
1806
# protocol registered
 
1807
for scheme in ['ssh', 'bzr+loopback']:
 
1808
    register_urlparse_netloc_protocol(scheme)
 
1809
del scheme
 
1810
 
1787
1811
register_transport_proto('bzr://',
1788
1812
            help="Fast access using the Bazaar smart server.",
1789
 
            default_port=4155)
 
1813
                         register_netloc=True)
1790
1814
 
1791
 
register_lazy_transport('bzr://',
1792
 
                        'bzrlib.transport.remote',
 
1815
register_lazy_transport('bzr://', 'bzrlib.transport.remote',
1793
1816
                        'RemoteTCPTransport')
1794
1817
register_transport_proto('bzr+http://',
1795
1818
#                help="Fast access using the Bazaar smart server over HTTP."
1796
 
            default_port=80)
1797
 
register_lazy_transport('bzr+http://',
1798
 
                        'bzrlib.transport.remote',
 
1819
                         register_netloc=True)
 
1820
register_lazy_transport('bzr+http://', 'bzrlib.transport.remote',
1799
1821
                        'RemoteHTTPTransport')
1800
1822
register_transport_proto('bzr+https://',
1801
1823
#                help="Fast access using the Bazaar smart server over HTTPS."
1802
 
             )
 
1824
                         register_netloc=True)
1803
1825
register_lazy_transport('bzr+https://',
1804
1826
                        'bzrlib.transport.remote',
1805
1827
                        'RemoteHTTPTransport')
1806
 
# Note that bzr+ssh:// has no default_port, because the user's ~/.ssh/config
1807
 
# can set it to arbitrary values based on hostname.
1808
1828
register_transport_proto('bzr+ssh://',
1809
 
            help="Fast access using the Bazaar smart server over SSH.")
1810
 
register_lazy_transport('bzr+ssh://',
1811
 
                        'bzrlib.transport.remote',
 
1829
            help="Fast access using the Bazaar smart server over SSH.",
 
1830
            register_netloc=True)
 
1831
register_lazy_transport('bzr+ssh://', 'bzrlib.transport.remote',
1812
1832
                        'RemoteSSHTransport')