Rev 3992: Command for opening Launchpad pages (Jonathan Lange) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue Feb 10 00:15:56 GMT 2009
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 3992
revision-id: pqm at pqm.ubuntu.com-20090210001553-5qi19ufmh3sx18mq
parent: pqm at pqm.ubuntu.com-20090209232557-ll08rw7c9xe43dpe
parent: ian.clatworthy at canonical.com-20090209233358-87e0072zgnkomb6v
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2009-02-10 00:15:53 +0000
message:
Command for opening Launchpad pages (Jonathan Lange)
added:
bzrlib/plugins/launchpad/test_lp_open.py test_lp_open.py-20090125174355-hxrsxh3sj84225qu-1
modified:
bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_directory.py test_lp_indirect.py-20070126002743-oyle362tzv9cd8mi-1
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3991.1.1
revision-id: ian.clatworthy at canonical.com-20090209233358-87e0072zgnkomb6v
parent: pqm at pqm.ubuntu.com-20090209232557-ll08rw7c9xe43dpe
parent: jml at canonical.com-20090125174401-tzky3d674jyo2odk
committer: Ian Clatworthy <ian.clatworthy at canonical.com>
branch nick: ianc-integration
timestamp: Tue 2009-02-10 09:33:58 +1000
message:
Command for opening Launchpad pages (Jonathan Lange)
added:
bzrlib/plugins/launchpad/test_lp_open.py test_lp_open.py-20090125174355-hxrsxh3sj84225qu-1
modified:
bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_directory.py test_lp_indirect.py-20070126002743-oyle362tzv9cd8mi-1
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3955.3.10
revision-id: jml at canonical.com-20090125174401-tzky3d674jyo2odk
parent: jml at canonical.com-20090125174347-yfsnekjimnlsiy1l
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Sun 2009-01-25 15:44:01 -0200
message:
Blackbox tests, forgot to add these earlier.
added:
bzrlib/plugins/launchpad/test_lp_open.py test_lp_open.py-20090125174355-hxrsxh3sj84225qu-1
------------------------------------------------------------
revno: 3955.3.9
revision-id: jml at canonical.com-20090125174347-yfsnekjimnlsiy1l
parent: jml at canonical.com-20090125170734-0mpnb6ovwsd2wv00
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Sun 2009-01-25 15:43:47 -0200
message:
Catch errors.
modified:
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3955.3.8
revision-id: jml at canonical.com-20090125170734-0mpnb6ovwsd2wv00
parent: jml at canonical.com-20090124151018-5vygev25ufrjm25a
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Sun 2009-01-25 15:07:34 -0200
message:
Support lp URL shortcuts.
modified:
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_directory.py test_lp_indirect.py-20070126002743-oyle362tzv9cd8mi-1
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3955.3.7
revision-id: jml at canonical.com-20090124151018-5vygev25ufrjm25a
parent: jml at canonical.com-20090124000001-l8ctacnsspxvnh42
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Sat 2009-01-24 13:10:18 -0200
message:
Test the launchpad-open command. Fix up some minor bugs.
modified:
bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3955.3.6
revision-id: jml at canonical.com-20090124000001-l8ctacnsspxvnh42
parent: jml at canonical.com-20090123235918-qgxnw89348dhyjgo
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Fri 2009-01-23 22:00:01 -0200
message:
Add a TODO.
modified:
bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
------------------------------------------------------------
revno: 3955.3.5
revision-id: jml at canonical.com-20090123235918-qgxnw89348dhyjgo
parent: jml at canonical.com-20090123233648-s3ufv7z20lss0k3t
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Fri 2009-01-23 21:59:18 -0200
message:
Add an untested plugin, make the error handling a little nicer.
modified:
bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3955.3.4
revision-id: jml at canonical.com-20090123233648-s3ufv7z20lss0k3t
parent: jml at canonical.com-20090123232938-efhx2xe8swjkw5t7
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Fri 2009-01-23 21:36:48 -0200
message:
Some error cases, plus a docstring.
modified:
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3955.3.3
revision-id: jml at canonical.com-20090123232938-efhx2xe8swjkw5t7
parent: jml at canonical.com-20090123232743-xet3q9p8fux1csw1
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Fri 2009-01-23 21:29:38 -0200
message:
Test a couple more cases.
modified:
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3955.3.2
revision-id: jml at canonical.com-20090123232743-xet3q9p8fux1csw1
parent: jml at canonical.com-20090123232536-8itvi1bdejkd4gug
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Fri 2009-01-23 21:27:43 -0200
message:
Tighten up the code a little, changing the dev service to use https,
which a) works and b) is more realistic.
modified:
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
------------------------------------------------------------
revno: 3955.3.1
revision-id: jml at canonical.com-20090123232536-8itvi1bdejkd4gug
parent: pqm at pqm.ubuntu.com-20090123103145-yvo3icrif75vkt20
committer: Jonathan Lange <jml at canonical.com>
branch nick: open-in-launchpad
timestamp: Fri 2009-01-23 21:25:36 -0200
message:
Start doing URL stuff, extracting the domain bit out of LaunchpadService,
a little.
modified:
bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
bzrlib/plugins/launchpad/test_lp_service.py test_lp_service.py-20080213034527-drf0ucr2x1js3onb-1
=== modified file 'bzrlib/plugins/launchpad/__init__.py'
--- a/bzrlib/plugins/launchpad/__init__.py 2008-03-06 21:16:38 +0000
+++ b/bzrlib/plugins/launchpad/__init__.py 2009-01-24 15:10:18 +0000
@@ -127,6 +127,37 @@
register_command(cmd_register_branch)
+# XXX: Make notes to test this.
+class cmd_launchpad_open(Command):
+ """Open a Launchpad branch page in your web browser."""
+
+ aliases = ['lp-open']
+ takes_options = [
+ Option('dry-run',
+ 'Do not actually open the browser. Just say the URL we would '
+ 'use.'),
+ ]
+ takes_args = ['location?']
+
+ def run(self, location=None, dry_run=False):
+ from bzrlib.plugins.launchpad.lp_registration import LaunchpadService
+ from bzrlib.trace import note
+ import webbrowser
+ if location is None:
+ location = u'.'
+ branch = Branch.open(location)
+ branch_url = branch.get_public_branch()
+ if branch_url is None:
+ raise NoPublicBranch(branch)
+ service = LaunchpadService()
+ web_url = service.get_web_url_from_branch_url(branch_url)
+ note('Opening %s in web browser' % web_url)
+ if not dry_run:
+ webbrowser.open(web_url)
+
+register_command(cmd_launchpad_open)
+
+
class cmd_launchpad_login(Command):
"""Show or set the Launchpad user ID.
@@ -182,8 +213,12 @@
"""Called by bzrlib to fetch tests for this plugin"""
from unittest import TestSuite, TestLoader
from bzrlib.plugins.launchpad import (
- test_account, test_lp_directory, test_lp_service, test_register,
- )
+ test_account,
+ test_lp_directory,
+ test_lp_open,
+ test_lp_service,
+ test_register,
+ )
loader = TestLoader()
suite = TestSuite()
@@ -191,6 +226,7 @@
test_account,
test_register,
test_lp_directory,
+ test_lp_open,
test_lp_service,
]:
suite.addTests(loader.loadTestsFromModule(module))
=== modified file 'bzrlib/plugins/launchpad/lp_registration.py'
--- a/bzrlib/plugins/launchpad/lp_registration.py 2008-04-24 07:22:53 +0000
+++ b/bzrlib/plugins/launchpad/lp_registration.py 2009-01-25 17:43:47 +0000
@@ -21,6 +21,11 @@
import urllib
import xmlrpclib
+from bzrlib.lazy_import import lazy_import
+lazy_import(globals(), """
+from bzrlib import urlutils
+""")
+
from bzrlib import (
config,
errors,
@@ -40,26 +45,40 @@
errors.BzrError.__init__(self, lp_instance=lp_instance)
+class NotLaunchpadBranch(errors.BzrError):
+
+ _fmt = "%(url)s is not hosted on Launchpad."
+
+ def __init__(self, url):
+ errors.BzrError.__init__(self, url=url)
+
+
class LaunchpadService(object):
"""A service to talk to Launchpad via XMLRPC.
See http://bazaar-vcs.org/Specs/LaunchpadRpc for the methods we can call.
"""
+ LAUNCHPAD_DOMAINS = {
+ 'production': 'launchpad.net',
+ 'edge': 'edge.launchpad.net',
+ 'staging': 'staging.launchpad.net',
+ 'demo': 'demo.launchpad.net',
+ 'dev': 'launchpad.dev',
+ }
+
# NB: these should always end in a slash to avoid xmlrpclib appending
# '/RPC2'
+ LAUNCHPAD_INSTANCE = {}
+ for instance, domain in LAUNCHPAD_DOMAINS.iteritems():
+ LAUNCHPAD_INSTANCE[instance] = 'https://xmlrpc.%s/bazaar/' % domain
+
# We use edge as the default because:
# Beta users get redirected to it
# All users can use it
# There is a bug in the launchpad side where redirection causes an OOPS.
- LAUNCHPAD_INSTANCE = {
- 'production': 'https://xmlrpc.launchpad.net/bazaar/',
- 'edge': 'https://xmlrpc.edge.launchpad.net/bazaar/',
- 'staging': 'https://xmlrpc.staging.launchpad.net/bazaar/',
- 'demo': 'https://xmlrpc.demo.launchpad.net/bazaar/',
- 'dev': 'http://xmlrpc.launchpad.dev/bazaar/',
- }
- DEFAULT_SERVICE_URL = LAUNCHPAD_INSTANCE['edge']
+ DEFAULT_INSTANCE = 'edge'
+ DEFAULT_SERVICE_URL = LAUNCHPAD_INSTANCE[DEFAULT_INSTANCE]
transport = None
registrant_email = None
@@ -160,6 +179,40 @@
% (self.service_url, e.errcode, e.errmsg))
return result
+ @property
+ def domain(self):
+ if self._lp_instance is None:
+ instance = self.DEFAULT_INSTANCE
+ else:
+ instance = self._lp_instance
+ return self.LAUNCHPAD_DOMAINS[instance]
+
+ def get_web_url_from_branch_url(self, branch_url, _request_factory=None):
+ """Get the Launchpad web URL for the given branch URL.
+
+ :raise errors.InvalidURL: if 'branch_url' cannot be identified as a
+ Launchpad branch URL.
+ :return: The URL of the branch on Launchpad.
+ """
+ scheme, hostinfo, path = urlsplit(branch_url)[:3]
+ if _request_factory is None:
+ _request_factory = ResolveLaunchpadPathRequest
+ if scheme == 'lp':
+ resolve = _request_factory(path)
+ try:
+ result = resolve.submit(self)
+ except xmlrpclib.Fault, fault:
+ raise errors.InvalidURL(branch_url, str(fault))
+ branch_url = result['urls'][0]
+ path = urlsplit(branch_url)[2]
+ else:
+ domains = (
+ 'bazaar.%s' % domain
+ for domain in self.LAUNCHPAD_DOMAINS.itervalues())
+ if hostinfo not in domains:
+ raise NotLaunchpadBranch(branch_url)
+ return urlutils.join('https://code.%s' % self.domain, path)
+
class BaseRequest(object):
"""Base request for talking to a XMLRPC server."""
=== modified file 'bzrlib/plugins/launchpad/test_lp_directory.py'
--- a/bzrlib/plugins/launchpad/test_lp_directory.py 2008-10-17 14:21:20 +0000
+++ b/bzrlib/plugins/launchpad/test_lp_directory.py 2009-01-25 17:07:34 +0000
@@ -24,7 +24,6 @@
from bzrlib.branch import Branch
from bzrlib.directory_service import directories
from bzrlib.tests import (
- TestCase,
TestCaseInTempDir,
TestCaseWithMemoryTransport
)
=== added file 'bzrlib/plugins/launchpad/test_lp_open.py'
--- a/bzrlib/plugins/launchpad/test_lp_open.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/plugins/launchpad/test_lp_open.py 2009-01-25 17:44:01 +0000
@@ -0,0 +1,59 @@
+# Copyright (C) 2009 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""Tests for the launchpad-open command."""
+
+from bzrlib.osutils import abspath
+
+from bzrlib.tests import TestCaseWithTransport
+
+
+class TestLaunchpadOpen(TestCaseWithTransport):
+
+ def run_open(self, location, retcode=0):
+ out, err = self.run_bzr(
+ ['launchpad-open', '--dry-run', location], retcode=retcode)
+ return err.splitlines()
+
+ def test_non_branch(self):
+ # Running lp-open on a non-branch prints a simple error.
+ self.assertEqual(
+ ['bzr: ERROR: Not a branch: "%s/".' % abspath('.')],
+ self.run_open('.', retcode=3))
+
+ def test_no_public_location(self):
+ self.make_branch('not-public')
+ self.assertEqual(
+ ['bzr: ERROR: There is no public branch set for "%s/".'
+ % abspath('not-public')],
+ self.run_open('not-public', retcode=3))
+
+ def test_non_launchpad_branch(self):
+ branch = self.make_branch('non-lp')
+ url = 'http://example.com/non-lp'
+ branch.set_public_branch(url)
+ self.assertEqual(
+ ['bzr: ERROR: %s is not hosted on Launchpad.' % url],
+ self.run_open('non-lp', retcode=3))
+
+ def test_launchpad_branch(self):
+ branch = self.make_branch('lp')
+ branch.set_public_branch(
+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ ['Opening https://code.edge.launchpad.net/~foo/bar/baz in web '
+ 'browser'],
+ self.run_open('lp'))
=== modified file 'bzrlib/plugins/launchpad/test_lp_service.py'
--- a/bzrlib/plugins/launchpad/test_lp_service.py 2008-02-13 03:45:58 +0000
+++ b/bzrlib/plugins/launchpad/test_lp_service.py 2009-01-25 17:43:47 +0000
@@ -17,10 +17,13 @@
"""Tests for selection of the right Launchpad service by environment"""
import os
+import xmlrpclib
+from bzrlib import errors
+from bzrlib.plugins.launchpad.lp_registration import (
+ InvalidLaunchpadInstance, LaunchpadService, NotLaunchpadBranch)
+from bzrlib.plugins.launchpad.test_lp_directory import FakeResolveFactory
from bzrlib.tests import TestCase
-from bzrlib.plugins.launchpad.lp_registration import (
- InvalidLaunchpadInstance, LaunchpadService)
class LaunchpadServiceTests(TestCase):
@@ -58,7 +61,7 @@
def test_dev_service(self):
service = LaunchpadService(lp_instance='dev')
- self.assertEqual('http://xmlrpc.launchpad.dev/bazaar/',
+ self.assertEqual('https://xmlrpc.launchpad.dev/bazaar/',
service.service_url)
def test_demo_service(self):
@@ -84,3 +87,107 @@
service = LaunchpadService(lp_instance='staging')
self.assertEqual('http://example.com/',
service.service_url)
+
+
+class TestURLInference(TestCase):
+ """Test the way we infer Launchpad web pages from branch URLs."""
+
+ def test_default_bzr_ssh_url(self):
+ service = LaunchpadService()
+ web_url = service.get_web_url_from_branch_url(
+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ 'https://code.edge.launchpad.net/~foo/bar/baz', web_url)
+
+ def test_product_bzr_ssh_url(self):
+ service = LaunchpadService(lp_instance='production')
+ web_url = service.get_web_url_from_branch_url(
+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ 'https://code.launchpad.net/~foo/bar/baz', web_url)
+
+ def test_sftp_branch_url(self):
+ service = LaunchpadService(lp_instance='production')
+ web_url = service.get_web_url_from_branch_url(
+ 'sftp://bazaar.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ 'https://code.launchpad.net/~foo/bar/baz', web_url)
+
+ def test_staging_branch_url(self):
+ service = LaunchpadService(lp_instance='production')
+ web_url = service.get_web_url_from_branch_url(
+ 'bzr+ssh://bazaar.staging.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ 'https://code.launchpad.net/~foo/bar/baz', web_url)
+
+ def test_non_launchpad_url(self):
+ service = LaunchpadService()
+ error = self.assertRaises(
+ NotLaunchpadBranch, service.get_web_url_from_branch_url,
+ 'bzr+ssh://example.com/~foo/bar/baz')
+ self.assertEqual(
+ 'bzr+ssh://example.com/~foo/bar/baz is not hosted on Launchpad.',
+ str(error))
+
+ def test_dodgy_launchpad_url(self):
+ service = LaunchpadService()
+ self.assertRaises(
+ NotLaunchpadBranch, service.get_web_url_from_branch_url,
+ 'bzr+ssh://launchpad.net/~foo/bar/baz')
+
+ def test_lp_branch_url(self):
+ service = LaunchpadService(lp_instance='production')
+ factory = FakeResolveFactory(
+ self, '~foo/bar/baz',
+ dict(urls=['http://bazaar.launchpad.net/~foo/bar/baz']))
+ web_url = service.get_web_url_from_branch_url(
+ 'lp:~foo/bar/baz', factory)
+ self.assertEqual(
+ 'https://code.launchpad.net/~foo/bar/baz', web_url)
+
+ def test_lp_branch_shortcut(self):
+ service = LaunchpadService()
+ factory = FakeResolveFactory(
+ self, 'foo',
+ dict(urls=['http://bazaar.launchpad.net/~foo/bar/baz']))
+ web_url = service.get_web_url_from_branch_url('lp:foo', factory)
+ self.assertEqual(
+ 'https://code.edge.launchpad.net/~foo/bar/baz', web_url)
+
+ def test_lp_branch_fault(self):
+ service = LaunchpadService()
+ factory = FakeResolveFactory(self, 'foo', None)
+ def submit(service):
+ raise xmlrpclib.Fault(42, 'something went wrong')
+ factory.submit = submit
+ self.assertRaises(
+ errors.InvalidURL, service.get_web_url_from_branch_url, 'lp:foo',
+ factory)
+
+ def test_staging_url(self):
+ service = LaunchpadService(lp_instance='staging')
+ web_url = service.get_web_url_from_branch_url(
+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ 'https://code.staging.launchpad.net/~foo/bar/baz', web_url)
+
+ def test_edge_url(self):
+ service = LaunchpadService(lp_instance='edge')
+ web_url = service.get_web_url_from_branch_url(
+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ 'https://code.edge.launchpad.net/~foo/bar/baz', web_url)
+
+ def test_dev_url(self):
+ service = LaunchpadService(lp_instance='dev')
+ web_url = service.get_web_url_from_branch_url(
+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ 'https://code.launchpad.dev/~foo/bar/baz', web_url)
+
+ def test_demo_url(self):
+ service = LaunchpadService(lp_instance='demo')
+ web_url = service.get_web_url_from_branch_url(
+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
+ self.assertEqual(
+ 'https://code.demo.launchpad.net/~foo/bar/baz', web_url)
More information about the bazaar-commits
mailing list