[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