Merge lp:~allenap/launchpad/kde-bugzilla-being-odd into lp:launchpad

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~allenap/launchpad/kde-bugzilla-being-odd
Merge into: lp:launchpad
Diff against target: 125 lines (+39/-23)
2 files modified
lib/lp/bugs/doc/externalbugtracker-bugzilla.txt (+28/-3)
lib/lp/bugs/externalbugtracker/bugzilla.py (+11/-20)
To merge this branch: bzr merge lp:~allenap/launchpad/kde-bugzilla-being-odd
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+17142@code.launchpad.net

Commit message

Don't break when an XML-RPC reply from a remote Bugzilla instance is not a mapping. Older Bugzilla instances return tuples. Also, simplify Bugzilla version parsing.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

KDE bugs returns tuples instead of mappings to XML-RPC calls. This causes the Bugzilla sniffer to fall over. I've changed it to consider anything other than a dict as unsupported (for API consideration). This means Launchpad will fall-back to the standard boring stupid Bugzilla support, which should work at least. This addresses bug 505958. I discussed this with Graham Binns briefly before implementation.

I also noticed that I could have a go at fixing Bugzilla._parseVersion() to simply extract the numeric parts of the version. This addresses bug 334980.

Lint free.

Test: bin/test -vvt 'external.*bug'

Revision history for this message
Brad Crittenden (bac) wrote :

Looks nice Gavin.

Since 'remote_version_dict' may be a dict or a tuple how about just calling it 'remote_version'?

review: Approve (code)
Revision history for this message
Gavin Panella (allenap) wrote :

Thanks Brad! Good catch; I've changed the name.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla.txt 2009-09-23 12:24:10 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla.txt 2010-01-13 10:28:18 +0000
@@ -24,11 +24,11 @@
2424
25 >>> from canonical.testing import LaunchpadZopelessLayer25 >>> from canonical.testing import LaunchpadZopelessLayer
26 >>> txn = LaunchpadZopelessLayer.txn26 >>> txn = LaunchpadZopelessLayer.txn
27 >>> external_bugzilla = Bugzilla('http://example.com/', version='A.B4')27 >>> external_bugzilla = Bugzilla('http://example.com/', version='A.B')
28 Traceback (most recent call last):28 Traceback (most recent call last):
29 ...29 ...
30 UnparseableBugTrackerVersion:30 UnparseableBugTrackerVersion:
31 Failed to parse version 'A.B4' for http://...31 Failed to parse version 'A.B' for http://...
3232
33The version parsing is carried out by the Bugzilla._parseVersion()33The version parsing is carried out by the Bugzilla._parseVersion()
34method, which takes a version string and returns a tuple of34method, which takes a version string and returns a tuple of
@@ -51,7 +51,7 @@
51+ characters in the version string will be removed.51+ characters in the version string will be removed.
5252
53 >>> print external_bugzilla._parseVersion('3.2+1')53 >>> print external_bugzilla._parseVersion('3.2+1')
54 (3, 21)54 (3, 2, 1)
5555
56Since we don't want to depend on a working network connection, we use a56Since we don't want to depend on a working network connection, we use a
57slightly modified implementation.57slightly modified implementation.
@@ -201,6 +201,31 @@
201 >>> isinstance(bugzilla_to_use, BugzillaLPPlugin)201 >>> isinstance(bugzilla_to_use, BugzillaLPPlugin)
202 True202 True
203203
204Older versions of the Bugzilla API return tuples rather than mappings
205in response to XML-RPC calls. When something other than a mapping is
206returned, the standard non-API non-plugin external bug tracker is
207selected.
208
209 >>> class OldXMLRPCTransport(xmlrpclib.Transport):
210 ... def request(self, host, handler, request, verbose=None):
211 ... args, method_name = xmlrpclib.loads(request)
212 ...
213 ... if method_name == 'Bugzilla.version':
214 ... return ('versionResponse', {'version': '3.2.5+'})
215 ... else:
216 ... raise xmlrpclib.Fault('Client', 'No such method')
217 ...
218 >>> test_transport = OldXMLRPCTransport()
219
220 >>> bugzilla._test_xmlrpc_proxy = xmlrpclib.ServerProxy(
221 ... 'http://example.com/xmlrpc.cgi',
222 ... transport=test_transport)
223
224 >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
225 >>> (isinstance(bugzilla_to_use, BugzillaAPI) or
226 ... isinstance(bugzilla_to_use, BugzillaLPPlugin))
227 False
228
204229
205== Status Conversion ==230== Status Conversion ==
206231
207232
=== modified file 'lib/lp/bugs/externalbugtracker/bugzilla.py'
--- lib/lp/bugs/externalbugtracker/bugzilla.py 2010-01-06 14:35:52 +0000
+++ lib/lp/bugs/externalbugtracker/bugzilla.py 2010-01-13 10:28:18 +0000
@@ -12,6 +12,7 @@
12 ]12 ]
1313
14import pytz14import pytz
15import re
15import xml.parsers.expat16import xml.parsers.expat
16import xmlrpclib17import xmlrpclib
1718
@@ -69,7 +70,7 @@
69 try:70 try:
70 # We try calling Bugzilla.version() on the remote71 # We try calling Bugzilla.version() on the remote
71 # server because it's the most lightweight method there is.72 # server because it's the most lightweight method there is.
72 remote_version_dict = proxy.Bugzilla.version()73 remote_version = proxy.Bugzilla.version()
73 except xmlrpclib.Fault, fault:74 except xmlrpclib.Fault, fault:
74 if fault.faultCode == 'Client':75 if fault.faultCode == 'Client':
75 return False76 return False
@@ -88,10 +89,12 @@
88 # The server returned an unparsable response.89 # The server returned an unparsable response.
89 return False90 return False
90 else:91 else:
91 if remote_version_dict['version'] >= '3.4':92 # Older versions of the Bugzilla API return tuples. We
92 return True93 # consider anything other than a mapping to be unsupported.
93 else:94 if isinstance(remote_version, dict):
94 return False95 if remote_version['version'] >= '3.4':
96 return True
97 return False
9598
96 def _remoteSystemHasPluginAPI(self):99 def _remoteSystemHasPluginAPI(self):
97 """Return True if the remote host has the Launchpad plugin installed.100 """Return True if the remote host has the Launchpad plugin installed.
@@ -191,25 +194,13 @@
191 if version is None:194 if version is None:
192 return None195 return None
193196
194 try:197 version_numbers = re.findall('[0-9]+', version)
195 # XXX 2008-09-15 gmb bug 270695:198 if len(version_numbers) == 0:
196 # We can clean this up by just stripping out anything
197 # not in [0-9\.].
198 # Get rid of trailing -rh, -debian, etc.
199 version = version.split("-")[0]
200 # Ignore plusses in the version.
201 version = version.replace("+", "")
202 # Ignore the 'rc' string in release candidate versions.
203 version = version.replace("rc", "")
204 # We need to convert the version to a tuple of integers if
205 # we are to compare it correctly.
206 version = tuple(int(x) for x in version.split("."))
207 except ValueError:
208 raise UnparseableBugTrackerVersion(199 raise UnparseableBugTrackerVersion(
209 'Failed to parse version %r for %s' %200 'Failed to parse version %r for %s' %
210 (version, self.baseurl))201 (version, self.baseurl))
211202
212 return version203 return tuple(int(number) for number in version_numbers)
213204
214 def convertRemoteImportance(self, remote_importance):205 def convertRemoteImportance(self, remote_importance):
215 """See `ExternalBugTracker`.206 """See `ExternalBugTracker`.