[MERGE][0.91][Bug 133965] PathNotChild, port mismatch with "bzr info" for bzr:// smartserver

Andrew Bennetts andrew at canonical.com
Wed Sep 12 03:22:42 BST 2007


Martin Pool wrote:
[...]
> +_default_ports = {}
> +
> +def _get_default_port(scheme):
> +    """Return the registered default port for this protocol scheme."""
> +    try:
> +        port = _default_ports.get(scheme + '://')
> +    except KeyError:
> +        port = None
> +    return port
> +
> 
> The _default_ports dict looks oddly redundant with the 
> dictionary/registry
> already indexing things by protocol.  Can they be combined?  Maybe not,
> if it's pointing to specific implementations and we want it to talk 
> about the protocol in general.

They can be combined, and your next comment pointed me in the right direction to
do so:

> By the way, what is the info parameter to register_transport_proto?
> In a brief search it does not seem to be used.

It turns out that the Registry allows associating an arbitrary “info” object as
well as a value when you register something.  This was unused by the transport
registry before, so my updated patch stores the default_port there now, rather
than creating a separate dictionary to maintain this in parallel with the
transport registry.

The transport registry already has separate concepts for “register a protocol”
vs. “register an implementation of a protocol”, so this fits quite neatly.  The
code size shrinks a bit as a result.  I've also moved _get_default_port to a
public method on TransportListRegistry as a result.

I've also updated this patch to set default port numbers for all protocols where
it makes sense, not just bzr://.

-Andrew.

-------------- next part --------------
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: andrew.bennetts at canonical.com-20070912021105-\
#   0n78a0y6qe37efwy
# target_branch: http://bazaar-vcs.org/bzr/bzr.dev
# testament_sha1: e8246f97260cf8ea79b8ddfba8a19ccb4a23c7a7
# timestamp: 2007-09-12 12:11:28 +1000
# source_branch: http://people.ubuntu.com/~andrew/bzr/port-mismatch-\
#   bug-133965
# base_revision_id: pqm at pqm.ubuntu.com-20070911010353-6lu7ek40rbjhj86o
# 
# Begin patch
=== modified file 'NEWS'
--- NEWS	2007-09-11 00:15:56 +0000
+++ NEWS	2007-09-12 02:11:05 +0000
@@ -129,6 +129,11 @@
    * Overwrite conflicting tags by ``push`` and ``pull`` if the
      ``--overwrite`` option is specified.  (Lukáš Lalinský, #93947)
 
+   * Fix ''bzr info bzr://host/'' and other operations on ''bzr://' URLs with
+     an implicit port.  We were incorrectly raising PathNotChild due to
+     inconsistent treatment of the ''_port'' attribute on the Transport object.
+     (Andrew Bennetts, #133965)
+
   IMPROVEMENTS:
 
    * Add the option "--show-diff" to the commit command in order to display

=== modified file 'bzrlib/tests/test_transport.py'
--- bzrlib/tests/test_transport.py	2007-08-19 20:38:10 +0000
+++ bzrlib/tests/test_transport.py	2007-09-12 02:05:23 +0000
@@ -54,6 +54,10 @@
 from bzrlib.transport.memory import MemoryTransport
 from bzrlib.transport.local import (LocalTransport,
                                     EmulatedWin32LocalTransport)
+from bzrlib.transport.remote import (
+    BZR_DEFAULT_PORT,
+    RemoteTCPTransport
+    )
 
 
 # TODO: Should possibly split transport-specific tests into their own files.
@@ -617,7 +621,7 @@
     def test_parse_url(self):
         t = ConnectedTransport('sftp://simple.example.com/home/source')
         self.assertEquals(t._host, 'simple.example.com')
-        self.assertEquals(t._port, None)
+        self.assertEquals(t._port, 22)
         self.assertEquals(t._path, '/home/source/')
         self.failUnless(t._user is None)
         self.failUnless(t._password is None)
@@ -712,6 +716,50 @@
         self.assertIsNot(t1, t2)
 
 
+class TestRemoteTCPTransport(TestCase):
+    """Tests for bzr:// transport (RemoteTCPTransport)."""
+
+    def test_relpath_with_implicit_port(self):
+        """Connected transports with the same URL are the same, even if the
+        port is implicit.
+
+        So t.relpath(url) should always be '' if t.base is the same as url, or
+        if the only difference is that one explicitly specifies the default
+        port and the other doesn't specify a port.
+        """
+        t_implicit_port = RemoteTCPTransport('bzr://host.com/')
+        self.assertEquals('', t_implicit_port.relpath('bzr://host.com/'))
+        self.assertEquals('', t_implicit_port.relpath('bzr://host.com:4155/'))
+        t_explicit_port = RemoteTCPTransport('bzr://host.com:4155/')
+        self.assertEquals('', t_explicit_port.relpath('bzr://host.com/'))
+        self.assertEquals('', t_explicit_port.relpath('bzr://host.com:4155/'))
+
+    def test_construct_uses_default_port(self):
+        """If no port is specified, then RemoteTCPTransport uses
+        BZR_DEFAULT_PORT.
+        """
+        t = get_transport('bzr://host.com/')
+        self.assertEquals(BZR_DEFAULT_PORT, t._port)
+
+    def test_url_omits_default_port(self):
+        """If a RemoteTCPTransport uses the default port, then its base URL
+        will omit the port.
+
+        This is like how ":80" is omitted from "http://example.com/".
+        """
+        t = get_transport('bzr://host.com:4155/')
+        self.assertEquals('bzr://host.com/', t.base)
+
+    def test_url_includes_non_default_port(self):
+        """Non-default ports are included in the transport's URL.
+
+        Contrast this to `test_url_omits_default_port`.
+        """
+        t = get_transport('bzr://host.com:666/')
+        self.assertEquals('bzr://host.com:666/', t.base)
+
+
+
 def get_test_permutations():
     """Return transport permutations to be used in testing.
 

=== modified file 'bzrlib/transport/__init__.py'
--- bzrlib/transport/__init__.py	2007-09-10 09:27:54 +0000
+++ bzrlib/transport/__init__.py	2007-09-12 02:05:23 +0000
@@ -128,19 +128,26 @@
         self.get(key).insert(0, 
                 registry._LazyObjectGetter(module_name, member_name))
 
-    def register_transport(self, key, help=None, info=None):
-        self.register(key, [], help, info)
+    def register_transport(self, key, help=None, default_port=None):
+        self.register(key, [], help, default_port)
 
     def set_default_transport(self, key=None):
         """Return either 'key' or the default key if key is None"""
         self._default_key = key
 
+    def get_default_port(self, scheme):
+        """Return the registered default port for this protocol scheme."""
+        try:
+            return self.get_info(scheme + '://')
+        except LookupError:
+            return None
+
 
 transport_list_registry = TransportListRegistry( )
 
 
-def register_transport_proto(prefix, help=None, info=None):
-    transport_list_registry.register_transport(prefix, help, info)
+def register_transport_proto(prefix, help=None, info=None, default_port=None):
+    transport_list_registry.register_transport(prefix, help, default_port)
 
 
 def register_lazy_transport(prefix, module, classname):
@@ -1255,6 +1262,10 @@
         host = urllib.unquote(host)
         path = urllib.unquote(path)
 
+        if port is None:
+            # The port isn't explicitly specified, so return the default (if
+            # there is one).
+            port = transport_list_registry.get_default_port(scheme)
         return (scheme, user, password, host, port, path)
 
     @staticmethod
@@ -1285,7 +1296,10 @@
             # have one so that it doesn't get accidentally
             # exposed.
             netloc = '%s@%s' % (urllib.quote(user), netloc)
-        if port is not None:
+        if (port is not None and 
+            port != transport_list_registry.get_default_port(scheme)):
+            # Include the port in the netloc (unless it's the same as the
+            # default, in which case we omit it as it is redundant).
             netloc = '%s:%d' % (netloc, port)
         path = urllib.quote(path)
         return urlparse.urlunparse((scheme, netloc, path, None, None, None))
@@ -1625,34 +1639,37 @@
 transport_list_registry.set_default_transport("file://")
 
 register_transport_proto('sftp://',
-            help="Access using SFTP (most SSH servers provide SFTP).")
+            help="Access using SFTP (most SSH servers provide SFTP).",
+            default_port=22)
 register_lazy_transport('sftp://', 'bzrlib.transport.sftp', 'SFTPTransport')
 # Decorated http transport
 register_transport_proto('http+urllib://',
 #                help="Read-only access of branches exported on the web."
-            )
+            default_port=80)
 register_lazy_transport('http+urllib://', 'bzrlib.transport.http._urllib',
                         'HttpTransport_urllib')
 register_transport_proto('https+urllib://',
 #                help="Read-only access of branches exported on the web using SSL."
-            )
+            default_port=443)
 register_lazy_transport('https+urllib://', 'bzrlib.transport.http._urllib',
                         'HttpTransport_urllib')
 register_transport_proto('http+pycurl://',
 #                help="Read-only access of branches exported on the web."
-            )
+            default_port=80)
 register_lazy_transport('http+pycurl://', 'bzrlib.transport.http._pycurl',
                         'PyCurlTransport')
 register_transport_proto('https+pycurl://',
 #                help="Read-only access of branches exported on the web using SSL."
-            )
+            default_port=443)
 register_lazy_transport('https+pycurl://', 'bzrlib.transport.http._pycurl',
                         'PyCurlTransport')
 # Default http transports (last declared wins (if it can be imported))
 register_transport_proto('http://',
-            help="Read-only access of branches exported on the web.")
+            help="Read-only access of branches exported on the web.",
+            default_port=80)
 register_transport_proto('https://',
-            help="Read-only access of branches exported on the web using SSL.")
+            help="Read-only access of branches exported on the web using SSL.",
+            default_port=443)
 register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
                         'HttpTransport_urllib')
 register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
@@ -1661,10 +1678,12 @@
 register_lazy_transport('https://', 'bzrlib.transport.http._pycurl', 'PyCurlTransport')
 
 register_transport_proto('ftp://',
-            help="Access using passive FTP.")
+            help="Access using passive FTP.",
+            default_port=21)
 register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
 register_transport_proto('aftp://',
-            help="Access using active FTP.")
+            help="Access using active FTP.",
+            default_port=21)
 register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
 
 register_transport_proto('memory://')
@@ -1691,19 +1710,21 @@
                         'bzrlib.transport.fakevfat',
                         'FakeVFATTransportDecorator')
 register_transport_proto('bzr://',
-            help="Fast access using the Bazaar smart server.")
+            help="Fast access using the Bazaar smart server.",
+            default_port=4155)
 
 register_lazy_transport('bzr://',
                         'bzrlib.transport.remote',
                         'RemoteTCPTransport')
 register_transport_proto('bzr+http://',
 #                help="Fast access using the Bazaar smart server over HTTP."
-             )
+            default_port=80)
 register_lazy_transport('bzr+http://',
                         'bzrlib.transport.remote',
                         'RemoteHTTPTransport')
 register_transport_proto('bzr+ssh://',
-            help="Fast access using the Bazaar smart server over SSH.")
+            help="Fast access using the Bazaar smart server over SSH.",
+            default_port=22)
 register_lazy_transport('bzr+ssh://',
                         'bzrlib.transport.remote',
                         'RemoteSSHTransport')

=== modified file 'bzrlib/transport/remote.py'
--- bzrlib/transport/remote.py	2007-08-15 06:53:07 +0000
+++ bzrlib/transport/remote.py	2007-09-11 02:23:26 +0000
@@ -450,8 +450,6 @@
 
     def _build_medium(self):
         assert self.base.startswith('bzr://')
-        if self._port is None:
-            self._port = BZR_DEFAULT_PORT
         return medium.SmartTCPClientMedium(self._host, self._port), None
 
 

# Begin bundle
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
3zAzEfXt9tEKiNkgl6ub+f/i7kinChIPfXqLQA==


More information about the bazaar mailing list