~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
99
99
                modules.add(factory._module_name)
100
100
            else:
101
101
                modules.add(factory._obj.__module__)
102
 
    # Add chroot directly, because there is not handler registered for it.
 
102
    # Add chroot directly, because there is no handler registered for it.
103
103
    modules.add('bzrlib.transport.chroot')
104
104
    result = list(modules)
105
105
    result.sort()
125
125
        self.get(key).insert(0, registry._ObjectGetter(obj))
126
126
 
127
127
    def register_lazy_transport_provider(self, key, module_name, member_name):
128
 
        self.get(key).insert(0, 
 
128
        self.get(key).insert(0,
129
129
                registry._LazyObjectGetter(module_name, member_name))
130
130
 
131
 
    def register_transport(self, key, help=None, info=None):
132
 
        self.register(key, [], help, info)
 
131
    def register_transport(self, key, help=None, default_port=None):
 
132
        self.register(key, [], help, default_port)
133
133
 
134
134
    def set_default_transport(self, key=None):
135
135
        """Return either 'key' or the default key if key is None"""
136
136
        self._default_key = key
137
137
 
138
 
 
139
 
transport_list_registry = TransportListRegistry( )
140
 
 
141
 
 
142
 
def register_transport_proto(prefix, help=None, info=None):
143
 
    transport_list_registry.register_transport(prefix, help, info)
 
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
 
 
146
transport_list_registry = TransportListRegistry()
 
147
 
 
148
 
 
149
def register_transport_proto(prefix, help=None, info=None, default_port=None):
 
150
    transport_list_registry.register_transport(prefix, help, default_port)
144
151
 
145
152
 
146
153
def register_lazy_transport(prefix, module, classname):
369
376
        """
370
377
        # The default implementation just uses "Easier to ask for forgiveness
371
378
        # than permission". We attempt to create the directory, and just
372
 
        # suppress a FileExists exception.
 
379
        # suppress FileExists and PermissionDenied (for Windows) exceptions.
373
380
        try:
374
381
            self.mkdir('.')
375
 
        except errors.FileExists:
 
382
        except (errors.FileExists, errors.PermissionDenied):
376
383
            return False
377
384
        else:
378
385
            return True
406
413
        """
407
414
        assert not isinstance(from_file, basestring), \
408
415
            '_pump should only be called on files not %s' % (type(from_file,))
409
 
        osutils.pumpfile(from_file, to_file)
 
416
        return osutils.pumpfile(from_file, to_file)
410
417
 
411
418
    def _get_total(self, multi):
412
419
        """Try to figure out how many entries are in multi,
635
642
        """
636
643
        raise errors.NoSmartMedium(self)
637
644
 
638
 
    def readv(self, relpath, offsets):
639
 
        """Get parts of the file at the given relative path.
640
 
 
641
 
        :offsets: A list of (offset, size) tuples.
 
645
    def readv(self, relpath, offsets, adjust_for_latency=False,
 
646
        upper_limit=None):
 
647
        """Get parts of the file at the given relative path.
 
648
 
 
649
        :param relpath: The path to read data from.
 
650
        :param offsets: A list of (offset, size) tuples.
 
651
        :param adjust_for_latency: Adjust the requested offsets to accomdate
 
652
            transport latency. This may re-order the offsets, expand them to
 
653
            grab adjacent data when there is likely a high cost to requesting
 
654
            data relative to delivering it.
 
655
        :param upper_limit: When adjust_for_latency is True setting upper_limit
 
656
            allows the caller to tell the transport about the length of the
 
657
            file, so that requests are not issued for ranges beyond the end of
 
658
            the file. This matters because some servers and/or transports error
 
659
            in such a case rather than just satisfying the available ranges.
 
660
            upper_limit should always be provided when adjust_for_latency is
 
661
            True, and should be the size of the file in bytes.
 
662
        :return: A list or generator of (offset, data) tuples
 
663
        """
 
664
        if adjust_for_latency:
 
665
            offsets = sorted(offsets)
 
666
            # short circuit empty requests
 
667
            if len(offsets) == 0:
 
668
                def empty_yielder():
 
669
                    # Quick thunk to stop this function becoming a generator
 
670
                    # itself, rather we return a generator that has nothing to
 
671
                    # yield.
 
672
                    if False:
 
673
                        yield None
 
674
                return empty_yielder()
 
675
            # expand by page size at either end
 
676
            expansion = self.recommended_page_size()
 
677
            reduction = expansion / 2
 
678
            new_offsets = []
 
679
            for offset, length in offsets:
 
680
                new_offset = offset - reduction
 
681
                new_length = length + expansion
 
682
                if new_offset < 0:
 
683
                    # don't ask for anything < 0
 
684
                    new_length -= new_offset
 
685
                    new_offset = 0
 
686
                if (upper_limit is not None and
 
687
                    new_offset + new_length > upper_limit):
 
688
                    new_length = upper_limit - new_offset
 
689
                new_offsets.append((new_offset, new_length))
 
690
            # combine the expanded offsets
 
691
            offsets = []
 
692
            current_offset, current_length = new_offsets[0]
 
693
            current_finish = current_length + current_offset
 
694
            for offset, length in new_offsets[1:]:
 
695
                if offset > current_finish:
 
696
                    offsets.append((current_offset, current_length))
 
697
                    current_offset = offset
 
698
                    current_length = length
 
699
                    continue
 
700
                finish = offset + length
 
701
                if finish > current_finish:
 
702
                    current_finish = finish
 
703
            offsets.append((current_offset, current_length))
 
704
        return self._readv(relpath, offsets)
 
705
 
 
706
    def _readv(self, relpath, offsets):
 
707
        """Get parts of the file at the given relative path.
 
708
 
 
709
        :param relpath: The path to read.
 
710
        :param offsets: A list of (offset, size) tuples.
642
711
        :return: A list or generator of (offset, data) tuples
643
712
        """
644
713
        if not offsets:
794
863
        :param f:       File-like object.
795
864
        :param mode: The mode for the newly created file,
796
865
                     None means just use the default.
 
866
        :return: The length of the file that was written.
797
867
        """
798
868
        # We would like to mark this as NotImplemented, but most likely
799
869
        # transports have defined it in terms of the old api.
1255
1325
        host = urllib.unquote(host)
1256
1326
        path = urllib.unquote(path)
1257
1327
 
 
1328
        if port is None:
 
1329
            # The port isn't explicitly specified, so return the default (if
 
1330
            # there is one).
 
1331
            port = transport_list_registry.get_default_port(scheme)
1258
1332
        return (scheme, user, password, host, port, path)
1259
1333
 
1260
1334
    @staticmethod
1285
1359
            # have one so that it doesn't get accidentally
1286
1360
            # exposed.
1287
1361
            netloc = '%s@%s' % (urllib.quote(user), netloc)
1288
 
        if port is not None:
 
1362
        if (port is not None and 
 
1363
            port != transport_list_registry.get_default_port(scheme)):
 
1364
            # Include the port in the netloc (unless it's the same as the
 
1365
            # default, in which case we omit it as it is redundant).
1289
1366
            netloc = '%s:%d' % (netloc, port)
1290
1367
        path = urllib.quote(path)
1291
1368
        return urlparse.urlunparse((scheme, netloc, path, None, None, None))
1594
1671
        raise NotImplementedError
1595
1672
 
1596
1673
 
1597
 
class TransportLogger(object):
1598
 
    """Adapt a transport to get clear logging data on api calls.
1599
 
    
1600
 
    Feel free to extend to log whatever calls are of interest.
1601
 
    """
1602
 
 
1603
 
    def __init__(self, adapted):
1604
 
        self._adapted = adapted
1605
 
        self._calls = []
1606
 
 
1607
 
    def get(self, name):
1608
 
        self._calls.append((name,))
1609
 
        return self._adapted.get(name)
1610
 
 
1611
 
    def __getattr__(self, name):
1612
 
        """Thunk all undefined access through to self._adapted."""
1613
 
        # raise AttributeError, name 
1614
 
        return getattr(self._adapted, name)
1615
 
 
1616
 
    def readv(self, name, offsets):
1617
 
        self._calls.append((name, offsets))
1618
 
        return self._adapted.readv(name, offsets)
1619
 
 
1620
 
 
1621
1674
# None is the default transport, for things with no url scheme
1622
1675
register_transport_proto('file://',
1623
1676
            help="Access using the standard filesystem (default)")
1625
1678
transport_list_registry.set_default_transport("file://")
1626
1679
 
1627
1680
register_transport_proto('sftp://',
1628
 
            help="Access using SFTP (most SSH servers provide SFTP).")
 
1681
            help="Access using SFTP (most SSH servers provide SFTP).",
 
1682
            default_port=22)
1629
1683
register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
1630
1684
# Decorated http transport
1631
1685
register_transport_proto('http+urllib://',
1632
1686
#                help="Read-only access of branches exported on the web."
1633
 
            )
 
1687
            default_port=80)
1634
1688
register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
1635
1689
                        'HttpTransport_urllib')
1636
1690
register_transport_proto('https+urllib://',
1637
1691
#                help="Read-only access of branches exported on the web using SSL."
1638
 
            )
 
1692
            default_port=443)
1639
1693
register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
1640
1694
                        'HttpTransport_urllib')
1641
1695
register_transport_proto('http+pycurl://',
1642
1696
#                help="Read-only access of branches exported on the web."
1643
 
            )
 
1697
            default_port=80)
1644
1698
register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
1645
1699
                        'PyCurlTransport')
1646
1700
register_transport_proto('https+pycurl://',
1647
1701
#                help="Read-only access of branches exported on the web using SSL."
1648
 
            )
 
1702
            default_port=443)
1649
1703
register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
1650
1704
                        'PyCurlTransport')
1651
1705
# Default http transports (last declared wins (if it can be imported))
1652
1706
register_transport_proto('http://',
1653
 
            help="Read-only access of branches exported on the web.")
 
1707
            help="Read-only access of branches exported on the web.",
 
1708
            default_port=80)
1654
1709
register_transport_proto('https://',
1655
 
            help="Read-only access of branches exported on the web using SSL.")
 
1710
            help="Read-only access of branches exported on the web using SSL.",
 
1711
            default_port=443)
1656
1712
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
1657
1713
                        'HttpTransport_urllib')
1658
1714
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1661
1717
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
1662
1718
 
1663
1719
register_transport_proto('ftp://',
1664
 
            help="Access using passive FTP.")
 
1720
            help="Access using passive FTP.",
 
1721
            default_port=21)
1665
1722
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1666
1723
register_transport_proto('aftp://',
1667
 
            help="Access using active FTP.")
 
1724
            help="Access using active FTP.",
 
1725
            default_port=21)
1668
1726
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1669
1727
 
1670
1728
register_transport_proto('memory://')
1671
1729
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
 
1730
 
 
1731
# chroots cannot be implicitly accessed, they must be explicitly created:
1672
1732
register_transport_proto('chroot+')
1673
1733
 
1674
1734
register_transport_proto('readonly+',
1679
1739
register_transport_proto('fakenfs+')
1680
1740
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
1681
1741
 
 
1742
register_transport_proto('trace+')
 
1743
register_lazy_transport('trace+', 'bzrlib.transport.trace', 'TransportTraceDecorator')
 
1744
 
1682
1745
register_transport_proto('unlistable+')
1683
1746
register_lazy_transport('unlistable+', 'bzrlib.transport.unlistable', 'UnlistableTransportDecorator')
1684
1747
 
1691
1754
                        'bzrlib.transport.fakevfat',
1692
1755
                        'FakeVFATTransportDecorator')
1693
1756
register_transport_proto('bzr://',
1694
 
            help="Fast access using the Bazaar smart server.")
 
1757
            help="Fast access using the Bazaar smart server.",
 
1758
            default_port=4155)
1695
1759
 
1696
1760
register_lazy_transport('bzr://',
1697
1761
                        'bzrlib.transport.remote',
1698
1762
                        'RemoteTCPTransport')
1699
1763
register_transport_proto('bzr+http://',
1700
1764
#                help="Fast access using the Bazaar smart server over HTTP."
 
1765
            default_port=80)
 
1766
register_lazy_transport('bzr+http://',
 
1767
                        'bzrlib.transport.remote',
 
1768
                        'RemoteHTTPTransport')
 
1769
register_transport_proto('bzr+https://',
 
1770
#                help="Fast access using the Bazaar smart server over HTTPS."
1701
1771
             )
1702
 
register_lazy_transport('bzr+http://',
 
1772
register_lazy_transport('bzr+https://',
1703
1773
                        'bzrlib.transport.remote',
1704
1774
                        'RemoteHTTPTransport')
1705
1775
register_transport_proto('bzr+ssh://',
1706
 
            help="Fast access using the Bazaar smart server over SSH.")
 
1776
            help="Fast access using the Bazaar smart server over SSH.",
 
1777
            default_port=22)
1707
1778
register_lazy_transport('bzr+ssh://',
1708
1779
                        'bzrlib.transport.remote',
1709
1780
                        'RemoteSSHTransport')