Rev 5133: Bring Maxb's code for querying launchpads api via a REST request. in http://bazaar.launchpad.net/~jameinel/bzr/2.2-is-up-to-date
John Arbash Meinel
john at arbash-meinel.com
Tue Jul 12 15:06:10 UTC 2011
At http://bazaar.launchpad.net/~jameinel/bzr/2.2-is-up-to-date
------------------------------------------------------------
revno: 5133
revision-id: john at arbash-meinel.com-20110712150543-201jymlxh34foi2a
parent: pqm at pqm.ubuntu.com-20110525182215-rdt09veh6tfzmril
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.2-is-up-to-date
timestamp: Tue 2011-07-12 17:05:43 +0200
message:
Bring Maxb's code for querying launchpads api via a REST request.
This can be done without loading launchpadlib, so the next step is to refactor it
for testing. But manual testing against launchpad itself shows it working.
-------------- next part --------------
=== added file 'bzrlib/plugins/launchpad/lp_api_lite.py'
--- a/bzrlib/plugins/launchpad/lp_api_lite.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/plugins/launchpad/lp_api_lite.py 2011-07-12 15:05:43 +0000
@@ -0,0 +1,126 @@
+# Copyright (C) 2009, 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""Tools for dealing with the Launchpad API without using launchpadlib.
+
+The api itself is a RESTful interface, so we can make HTTP queries directly.
+loading launchpadlib itself has a fairly high overhead (just calling
+Launchpad.login_anonymously() takes a 500ms once the WADL is cached, and 5+s to
+get the WADL.
+"""
+
+try:
+ # Use simplejson if available, much faster, and can be easily installed in
+ # older versions of python
+ import simplejson as json
+except ImportError:
+ # Is present since python 2.6
+ try:
+ import json
+ except ImportError:
+ json = None
+
+import urllib
+import urllib2
+
+from bzrlib import trace
+
+
+LP_API_ROOT = 'https://api.launchpad.net/1.0'
+DEFAULT_SERIES = 'oneiric'
+
+def get_latest_publication(archive, series, project):
+ """Get the most recent publication for a given project.
+
+ :param archive: Either 'ubuntu' or 'debian'
+ :param series: Something like 'natty', 'sid', etc. Can be set as None. Can
+ also include a pocket such as 'natty-proposed'.
+ :param project: Something like 'bzr'
+ :return: A version string indicating the most-recent version published in
+ Launchpad. Might return None if there is an error.
+ """
+ if json is None:
+ return None
+ archive_url = '%s/%s/+archive/primary?' % (LP_API_ROOT, archive)
+ status = 'Published'
+ if archive == 'debian':
+ # Launchpad only tracks debian packages as "Pending", it doesn't mark
+ # them Published
+ status = 'Pending'
+ pocket = None
+ # TODO: If series is None, we probably need to hard-code it. I don't have
+ # proof yet, but otherwise we just get the most-recent version in any
+ # series, rather than getting the one for eg 'oneiric'. The problem I
+ # envision is that natty-proposed might have a newer version than
+ # 'oneiric'. Is this a useful distinction in practice?
+ if series is not None and '-' in series:
+ # The lp: URL 'lp:ubuntu/natty-proposed/...' is translated into series
+ # 'natty' pocket 'proposed'
+ try:
+ series, pocket = series.split('-')
+ except ValueError, e:
+ trace.mutter('failed to find series,pocket from %s' % (series,))
+ return None
+ # pocket must be in 'Title' case, so Proposed, not 'proposed'.
+ pocket = pocket.title()
+ params = {'ws.op': 'getPublishedSources',
+ 'exact_match': 'true',
+ # If we need to use "" shouldn't we quote the project somehow?
+ 'source_name': '"%s"' % (project,),
+ 'status': status,
+ # We only need the latest one, the results seem to be properly
+ # most-recent-debian-version sorted
+ 'ws.size': '1',
+ }
+ if series is not None:
+ params['distro_series'] = '/%s/%s' % (archive, series)
+ if pocket is not None:
+ params['pocket'] = pocket
+ query_url = archive_url + urllib.urlencode(params)
+ try:
+ req = urllib2.Request(query_url)
+ response = urllib2.urlopen(req)
+ json_txt = response.read()
+ except urllib2.HTTPError, e:
+ trace.mutter('failed to place query to %r' % (query_url,))
+ trace.log_exception_quietly()
+ return None
+ try:
+ o = json.loads(json_txt)
+ except Exception:
+ # simplejson raises simplejson.decoder.JSONDecodeError,
+ # but json raises ValueError, so we just catch a generic error and move
+ # on
+ trace.log_exception_quietly()
+ return None
+ try:
+ for e in o['entries']:
+ this_name = e['source_package_name']
+ this_ver = e['source_package_version']
+ # this_comp seems to always be 'main', are we supposed to do
+ # something with it?
+ # this_comp = e['component_name']
+ # pocket = e['pocket'].lower()
+ # series = e['distro_series_link'].split('/')[-1]
+ # if pocket != 'release':
+ # series += '-' + pocket
+ return this_ver
+ except KeyError:
+ # Some expected attribute was missing
+ trace.log_exception_quietly()
+ return None
+ trace.mutter('No versions found for: %r', query_url)
+ return None
More information about the bazaar-commits
mailing list