launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #22658
[Merge] lp:~cjwatson/launchpad/explicit-proxy-bugtracker-cleanup into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/explicit-proxy-bugtracker-cleanup into lp:launchpad with lp:~cjwatson/launchpad/explicit-proxy-trac as a prerequisite.
Commit message:
Remove urllib2-based external bug tracker code, now that everything has been ported to requests.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/explicit-proxy-bugtracker-cleanup/+merge/348436
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/explicit-proxy-bugtracker-cleanup into lp:launchpad.
=== modified file 'lib/lp/bugs/externalbugtracker/__init__.py'
--- lib/lp/bugs/externalbugtracker/__init__.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/__init__.py 2018-06-23 10:08:23 +0000
@@ -14,7 +14,6 @@
'DebBugs',
'DebBugsDatabaseNotFound',
'ExternalBugTracker',
- 'ExternalBugTrackerRequests',
'GitHub',
'InvalidBugId',
'LookupTree',
@@ -39,7 +38,6 @@
BugWatchUpdateError,
BugWatchUpdateWarning,
ExternalBugTracker,
- ExternalBugTrackerRequests,
InvalidBugId,
LookupTree,
PrivateRemoteBug,
=== modified file 'lib/lp/bugs/externalbugtracker/base.py'
--- lib/lp/bugs/externalbugtracker/base.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/base.py 2018-06-23 10:08:23 +0000
@@ -12,7 +12,6 @@
'BugWatchUpdateError',
'BugWatchUpdateWarning',
'ExternalBugTracker',
- 'ExternalBugTrackerRequests',
'InvalidBugId',
'LookupTree',
'LP_USER_AGENT',
@@ -28,9 +27,6 @@
]
-import urllib
-import urllib2
-
import requests
from six.moves.urllib_parse import (
urljoin,
@@ -176,14 +172,6 @@
ISupportsCommentImport.providedBy(self) or
ISupportsBackLinking.providedBy(self)))
- @ensure_no_transaction
- def urlopen(self, request, data=None):
- if self.url_opener:
- func = self.url_opener.open
- else:
- func = urllib2.urlopen
- return func(request, data, self.timeout)
-
def getExternalBugTrackerToUse(self):
"""See `IExternalBugTracker`."""
return self
@@ -261,56 +249,6 @@
# user-agent string (Python-urllib/2.x) to access their bugzilla.
return {'User-Agent': LP_USER_AGENT, 'Host': self.basehost}
- def _fetchPage(self, page, data=None):
- """Fetch a page from the remote server.
-
- A BugTrackerConnectError will be raised if anything goes wrong.
- """
- if not isinstance(page, urllib2.Request):
- page = urllib2.Request(page, headers=self._getHeaders())
- try:
- return self.urlopen(page, data)
- except (urllib2.HTTPError, urllib2.URLError) as val:
- raise BugTrackerConnectError(self.baseurl, val)
-
- def _getPage(self, page):
- """GET the specified page on the remote HTTP server."""
- request = urllib2.Request(
- "%s/%s" % (self.baseurl, page), headers=self._getHeaders())
- return self._fetchPage(request).read()
-
- def _post(self, url, data):
- """Post to a given URL."""
- request = urllib2.Request(url, headers=self._getHeaders())
- return self._fetchPage(request, data=data)
-
- def _postPage(self, page, form, repost_on_redirect=False):
- """POST to the specified page and form.
-
- :param form: is a dict of form variables being POSTed.
- :param repost_on_redirect: override RFC-compliant redirect handling.
- By default, if the POST receives a redirect response, the
- request to the redirection's target URL will be a GET. If
- `repost_on_redirect` is True, this method will do a second POST
- instead. Do this only if you are sure that repeated POST to
- this page is safe, as is usually the case with search forms.
- """
- url = "%s/%s" % (self.baseurl, page)
- post_data = urllib.urlencode(form)
- response = self._post(url, data=post_data)
-
- if repost_on_redirect and response.url != url:
- response = self._post(response.url, data=post_data)
-
- return response.read()
-
-
-class ExternalBugTrackerRequests(ExternalBugTracker):
- """An external bug tracker that uses `requests`.
-
- This is temporary until all bug tracker types have been converted.
- """
-
@ensure_no_transaction
def makeRequest(self, method, url, **kwargs):
"""Make a request.
=== modified file 'lib/lp/bugs/externalbugtracker/bugzilla.py'
--- lib/lp/bugs/externalbugtracker/bugzilla.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/bugzilla.py 2018-06-23 10:08:23 +0000
@@ -32,7 +32,7 @@
BugNotFound,
BugTrackerAuthenticationError,
BugTrackerConnectError,
- ExternalBugTrackerRequests,
+ ExternalBugTracker,
InvalidBugId,
LookupTree,
UnknownRemoteImportanceError,
@@ -61,7 +61,7 @@
)
-class Bugzilla(ExternalBugTrackerRequests):
+class Bugzilla(ExternalBugTracker):
"""An ExternalBugTracker for dealing with remote Bugzilla systems."""
batch_query_threshold = 0 # Always use the batch method.
=== modified file 'lib/lp/bugs/externalbugtracker/github.py'
--- lib/lp/bugs/externalbugtracker/github.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/github.py 2018-06-23 10:08:23 +0000
@@ -25,7 +25,7 @@
from lp.bugs.externalbugtracker import (
BugTrackerConnectError,
BugWatchUpdateError,
- ExternalBugTrackerRequests,
+ ExternalBugTracker,
UnknownRemoteStatusError,
UnparsableBugTrackerVersion,
)
@@ -120,7 +120,7 @@
"""The GitHub Issues URL is malformed."""
-class GitHub(ExternalBugTrackerRequests):
+class GitHub(ExternalBugTracker):
"""An `ExternalBugTracker` for dealing with GitHub issues."""
# Avoid eating through our rate limit unnecessarily.
=== modified file 'lib/lp/bugs/externalbugtracker/mantis.py'
--- lib/lp/bugs/externalbugtracker/mantis.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/mantis.py 2018-06-23 10:08:23 +0000
@@ -24,7 +24,7 @@
BugNotFound,
BugTrackerConnectError,
BugWatchUpdateError,
- ExternalBugTrackerRequests,
+ ExternalBugTracker,
InvalidBugId,
LookupTree,
UnknownRemoteStatusError,
@@ -165,7 +165,7 @@
raise UnparsableBugData("Exception parsing CSV file: %s." % error)
-class Mantis(ExternalBugTrackerRequests):
+class Mantis(ExternalBugTracker):
"""An `ExternalBugTracker` for dealing with Mantis instances.
For a list of tested Mantis instances and their behaviour when
=== modified file 'lib/lp/bugs/externalbugtracker/roundup.py'
--- lib/lp/bugs/externalbugtracker/roundup.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/roundup.py 2018-06-23 10:08:23 +0000
@@ -13,7 +13,7 @@
from lp.bugs.externalbugtracker import (
BugNotFound,
- ExternalBugTrackerRequests,
+ ExternalBugTracker,
InvalidBugId,
LookupTree,
UnknownRemoteStatusError,
@@ -42,7 +42,7 @@
for (key, value) in items)
-class Roundup(ExternalBugTrackerRequests):
+class Roundup(ExternalBugTracker):
"""An ExternalBugTracker descendant for handling Roundup bug trackers."""
_status_fields_map = {
=== modified file 'lib/lp/bugs/externalbugtracker/rt.py'
--- lib/lp/bugs/externalbugtracker/rt.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/rt.py 2018-06-23 10:08:23 +0000
@@ -15,7 +15,7 @@
from lp.bugs.externalbugtracker import (
BugNotFound,
BugTrackerConnectError,
- ExternalBugTrackerRequests,
+ ExternalBugTracker,
InvalidBugId,
LookupTree,
UnknownRemoteStatusError,
@@ -29,7 +29,7 @@
from lp.services.webapp.url import urlparse
-class RequestTracker(ExternalBugTrackerRequests):
+class RequestTracker(ExternalBugTracker):
"""`ExternalBugTracker` subclass for handling RT imports."""
ticket_url = 'REST/1.0/ticket/%s/show'
=== modified file 'lib/lp/bugs/externalbugtracker/sourceforge.py'
--- lib/lp/bugs/externalbugtracker/sourceforge.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/sourceforge.py 2018-06-23 10:08:23 +0000
@@ -11,7 +11,7 @@
from lp.bugs.externalbugtracker import (
BugNotFound,
- ExternalBugTrackerRequests,
+ ExternalBugTracker,
InvalidBugId,
LookupTree,
PrivateRemoteBug,
@@ -27,7 +27,7 @@
from lp.services.webapp import urlsplit
-class SourceForge(ExternalBugTrackerRequests):
+class SourceForge(ExternalBugTracker):
"""An ExternalBugTracker for SourceForge bugs."""
# We only allow ourselves to update one SourceForge bug at a time to
=== modified file 'lib/lp/bugs/externalbugtracker/tests/test_externalbugtracker.py'
--- lib/lp/bugs/externalbugtracker/tests/test_externalbugtracker.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/tests/test_externalbugtracker.py 2018-06-23 10:08:23 +0000
@@ -5,9 +5,6 @@
__metaclass__ = type
-from StringIO import StringIO
-import urllib2
-
import responses
from testtools.matchers import (
ContainsDict,
@@ -21,7 +18,6 @@
from lp.bugs.externalbugtracker.base import (
BugTrackerConnectError,
ExternalBugTracker,
- ExternalBugTrackerRequests,
LP_USER_AGENT,
)
from lp.bugs.externalbugtracker.debbugs import DebBugs
@@ -30,15 +26,8 @@
ISupportsCommentImport,
ISupportsCommentPushing,
)
-from lp.testing import (
- monkey_patch,
- TestCase,
- )
-from lp.testing.fakemethod import FakeMethod
-from lp.testing.layers import (
- ZopelessDatabaseLayer,
- ZopelessLayer,
- )
+from lp.testing import TestCase
+from lp.testing.layers import ZopelessDatabaseLayer
@implementer(ISupportsBackLinking)
@@ -110,81 +99,27 @@
tracker = DebBugs(self.base_url)
self.assertFalse(tracker.sync_comments)
- def _makeFakePostForm(self, base_url, page=None):
- """Create a fake `urllib2.urlopen` result."""
- content = "<bugzilla>%s</bugzilla>" % self.factory.getUniqueString()
- fake_form = StringIO(content)
- if page is None:
- page = self.factory.getUniqueString()
- fake_form.url = base_url + page
- return fake_form
-
- def _fakeExternalBugTracker(self, base_url, fake_form):
- """Create an `ExternalBugTracker` with a fake `_post` method."""
- bugtracker = ExternalBugTracker(base_url)
- bugtracker._post = FakeMethod(result=fake_form)
- return bugtracker
-
+ @responses.activate
def test_postPage_returns_response_page(self):
# _postPage posts, then returns the page text it gets back from
# the server.
base_url = "http://example.com/"
form = self.factory.getUniqueString()
- fake_form = self._makeFakePostForm(base_url, page=form)
- bugtracker = self._fakeExternalBugTracker(base_url, fake_form)
- self.assertEqual(fake_form.getvalue(), bugtracker._postPage(form, {}))
-
- def test_postPage_does_not_repost_on_redirect(self):
- # By default, if the POST redirects, _postPage leaves urllib2 to
- # handle it in the normal, RFC-compliant way.
- base_url = "http://example.com/"
- form = self.factory.getUniqueString()
- fake_form = self._makeFakePostForm(base_url)
- bugtracker = self._fakeExternalBugTracker(base_url, fake_form)
-
- bugtracker._postPage(form, {})
-
- self.assertEqual(1, bugtracker._post.call_count)
- args, kwargs = bugtracker._post.calls[0]
- self.assertEqual((base_url + form, ), args)
-
- def test_postPage_can_repost_on_redirect(self):
- # Some pages (that means you, BugZilla bug-search page!) can
- # redirect on POST, but without honouring the POST. Standard
- # urllib2 behaviour is to redirect to a GET, but if the caller
- # says it's safe, _postPage can re-do the POST at the new URL.
- base_url = "http://example.com/"
- form = self.factory.getUniqueString()
- fake_form = self._makeFakePostForm(base_url)
- bugtracker = self._fakeExternalBugTracker(base_url, fake_form)
-
- bugtracker._postPage(form, form={}, repost_on_redirect=True)
-
- self.assertEqual(2, bugtracker._post.call_count)
- last_args, last_kwargs = bugtracker._post.calls[-1]
- self.assertEqual((fake_form.url, ), last_args)
-
- @responses.activate
- def test_requests_postPage_returns_response_page(self):
- # _postPage posts, then returns the page text it gets back from
- # the server.
- base_url = "http://example.com/"
- form = self.factory.getUniqueString()
fake_form = "<bugzilla>%s</bugzilla>" % self.factory.getUniqueString()
- bugtracker = ExternalBugTrackerRequests(base_url)
+ bugtracker = ExternalBugTracker(base_url)
transaction.commit()
responses.add("POST", base_url + form, body=fake_form)
self.assertEqual(fake_form, bugtracker._postPage(form, {}).text)
@responses.activate
- def test_requests_postPage_does_not_repost_on_redirect(self):
+ def test_postPage_does_not_repost_on_redirect(self):
# By default, if the POST redirects, _postPage leaves requests to
# handle it in the normal, RFC-compliant way.
base_url = "http://example.com/"
form = self.factory.getUniqueString()
target = self.factory.getUniqueString()
fake_form = "<bugzilla>%s</bugzilla>" % self.factory.getUniqueString()
- bugtracker = ExternalBugTrackerRequests(base_url)
+ bugtracker = ExternalBugTracker(base_url)
transaction.commit()
responses.add(
"POST", base_url + form, status=302,
@@ -200,7 +135,7 @@
]))
@responses.activate
- def test_requests_postPage_can_repost_on_redirect(self):
+ def test_postPage_can_repost_on_redirect(self):
# Some pages (that means you, BugZilla bug-search page!) can
# redirect on POST, but without honouring the POST. Standard
# requests behaviour is to redirect to a GET, but if the caller
@@ -209,7 +144,7 @@
form = self.factory.getUniqueString()
target = self.factory.getUniqueString()
fake_form = "<bugzilla>%s</bugzilla>" % self.factory.getUniqueString()
- bugtracker = ExternalBugTrackerRequests(base_url)
+ bugtracker = ExternalBugTracker(base_url)
transaction.commit()
responses.add(
"POST", base_url + form, status=302,
@@ -228,46 +163,13 @@
class TestExternalBugTracker(TestCase):
"""Tests for various methods of the ExternalBugTracker."""
- layer = ZopelessLayer
-
- def test_post_raises_on_404(self):
- # When posting, a 404 is converted to a BugTrackerConnectError.
- base_url = "http://example.com/"
- bugtracker = ExternalBugTracker(base_url)
-
- def raise404(request, data, timeout=None):
- raise urllib2.HTTPError('url', 404, 'Not Found', None, None)
-
- with monkey_patch(urllib2, urlopen=raise404):
- self.assertRaises(
- BugTrackerConnectError,
- bugtracker._post, 'some-url', {'post-data': 'here'})
-
- def test_post_sends_host(self):
- # When posting, a Host header is sent.
- base_host = 'example.com'
- base_url = 'http://%s/' % base_host
- bugtracker = ExternalBugTracker(base_url)
-
- def assert_headers(request, data, timeout=None):
- self.assertContentEqual(
- [('User-agent', LP_USER_AGENT), ('Host', base_host)],
- request.header_items())
-
- with monkey_patch(urllib2, urlopen=assert_headers):
- bugtracker._post('some-url', {'post-data': 'here'})
-
-
-class TestExternalBugTrackerRequests(TestCase):
- """Tests for various methods of the ExternalBugTrackerRequests."""
-
layer = ZopelessDatabaseLayer
@responses.activate
def test_postPage_raises_on_404(self):
# When posting, a 404 is converted to a BugTrackerConnectError.
base_url = "http://example.com/"
- bugtracker = ExternalBugTrackerRequests(base_url)
+ bugtracker = ExternalBugTracker(base_url)
transaction.commit()
responses.add("POST", base_url + "some-url", status=404)
self.assertRaises(
@@ -279,7 +181,7 @@
# When posting, a Host header is sent.
base_host = 'example.com'
base_url = 'http://%s/' % base_host
- bugtracker = ExternalBugTrackerRequests(base_url)
+ bugtracker = ExternalBugTracker(base_url)
transaction.commit()
responses.add("POST", base_url + "some-url")
bugtracker._postPage('some-url', {'post-data': 'here'})
=== modified file 'lib/lp/bugs/externalbugtracker/tests/test_xmlrpc.py'
--- lib/lp/bugs/externalbugtracker/tests/test_xmlrpc.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/tests/test_xmlrpc.py 2018-06-23 10:08:23 +0000
@@ -6,58 +6,17 @@
__metaclass__ = type
import socket
-from urllib2 import URLError
from xml.parsers.expat import ExpatError
from fixtures import MockPatch
import requests
import responses
-from lp.bugs.externalbugtracker.xmlrpc import (
- RequestsTransport,
- UrlLib2Transport,
- )
-from lp.bugs.tests.externalbugtracker import (
- ensure_response_parser_is_expat,
- UrlLib2TransportTestHandler,
- )
+from lp.bugs.externalbugtracker.xmlrpc import RequestsTransport
+from lp.bugs.tests.externalbugtracker import ensure_response_parser_is_expat
from lp.testing import TestCase
-class TestUrlLib2Transport(TestCase):
- """Tests for `UrlLib2Transport`."""
-
- def test_expat_error(self):
- # Malformed XML-RPC responses cause xmlrpclib to raise an ExpatError.
- handler = UrlLib2TransportTestHandler()
- handler.setResponse("<params><mis></match></params>")
- transport = UrlLib2Transport("http://not.real/")
- transport.opener.add_handler(handler)
-
- # The Launchpad production environment selects Expat at present. This
- # is quite strict compared to the other parsers that xmlrpclib can
- # possibly select.
- ensure_response_parser_is_expat(transport)
-
- self.assertRaises(
- ExpatError, transport.request,
- 'www.example.com', 'xmlrpc', "<methodCall />")
-
- def test_unicode_url(self):
- # Python's httplib doesn't like Unicode URLs much. Ensure that
- # they don't cause it to crash, and we get a post-serialisation
- # connection error instead.
- self.useFixture(MockPatch(
- "socket.getaddrinfo",
- side_effect=socket.gaierror(
- socket.EAI_NONAME, "Name or service not known")))
- transport = UrlLib2Transport(u"http://test.invalid/")
- self.assertRaisesWithContent(
- URLError, '<urlopen error [Errno -2] Name or service not known>',
- transport.request, u"test.invalid", u"xmlrpc",
- u"\N{SNOWMAN}".encode('utf-8'))
-
-
class TestRequestsTransport(TestCase):
"""Tests for `RequestsTransport`."""
=== modified file 'lib/lp/bugs/externalbugtracker/trac.py'
--- lib/lp/bugs/externalbugtracker/trac.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/trac.py 2018-06-23 10:08:23 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Trac ExternalBugTracker implementation."""
@@ -24,7 +24,7 @@
BugNotFound,
BugTrackerAuthenticationError,
BugTrackerConnectError,
- ExternalBugTrackerRequests,
+ ExternalBugTracker,
InvalidBugId,
LookupTree,
UnknownRemoteStatusError,
@@ -60,7 +60,7 @@
FAULT_TICKET_NOT_FOUND = 1001
-class Trac(ExternalBugTrackerRequests):
+class Trac(ExternalBugTracker):
"""An ExternalBugTracker instance for handling Trac bugtrackers."""
ticket_url = 'ticket/%i?format=csv'
=== modified file 'lib/lp/bugs/externalbugtracker/xmlrpc.py'
--- lib/lp/bugs/externalbugtracker/xmlrpc.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/externalbugtracker/xmlrpc.py 2018-06-23 10:08:23 +0000
@@ -1,26 +1,15 @@
# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
-"""XMLRPC transports which use urllib2 or requests."""
+"""An XMLRPC transport which uses requests."""
__metaclass__ = type
__all__ = [
'RequestsTransport',
- 'UrlLib2Transport',
- 'XMLRPCRedirectHandler',
]
-from cookielib import Cookie
-from cStringIO import StringIO
from io import BytesIO
-from urllib2 import (
- build_opener,
- HTTPCookieProcessor,
- HTTPError,
- HTTPRedirectHandler,
- Request,
- )
from urlparse import (
urlparse,
urlunparse,
@@ -42,98 +31,6 @@
from lp.services.utils import traceback_info
-class XMLRPCRedirectHandler(HTTPRedirectHandler):
- """A handler for HTTP redirections of XML-RPC requests."""
-
- def redirect_request(self, req, fp, code, msg, headers, newurl):
- """Return a Request or None in response to a redirect.
-
- See `urllib2.HTTPRedirectHandler`.
-
- If the original request is a POST request, the request's payload
- will be preserved in the redirect and the returned request will
- also be a POST request.
- """
- # If we can't handle this redirect,
- # HTTPRedirectHandler.redirect_request() will raise an
- # HTTPError. We call the superclass here in the old fashion
- # since HTTPRedirectHandler isn't a new-style class.
- new_request = HTTPRedirectHandler.redirect_request(
- self, req, fp, code, msg, headers, newurl)
-
- # If the old request is a POST request, the payload will be
- # preserved. Note that we don't need to test for the POST-ness
- # of the old request; if its data attribute - its payload - is
- # not None it's a POST request, if it's None it's a GET request.
- # We can therefore just copy the data from the old request to
- # the new without worrying about breaking things.
- new_request.data = req.data
- new_request.timeout = req.timeout
- return new_request
-
-
-class UrlLib2Transport(Transport):
- """An XMLRPC transport which uses urllib2.
-
- This XMLRPC transport uses the Python urllib2 module to make the request,
- with proxying handled by that module's semantics (though underdocumented).
- It also handles cookies correctly, and in addition allows specifying the
- cookie explicitly by setting `self.auth_cookie`.
-
- Note: this transport isn't fit for general XMLRPC use. It is just good
- enough for some of our external bug tracker implementations.
-
- :param endpoint: The URL of the XMLRPC server.
- """
-
- verbose = False
-
- def __init__(self, endpoint, cookie_jar=None):
- Transport.__init__(self, use_datetime=True)
- self.scheme, self.host = urlparse(endpoint)[:2]
- assert self.scheme in ('http', 'https'), (
- "Unsupported URL scheme: %s" % self.scheme)
- self.cookie_processor = HTTPCookieProcessor(cookie_jar)
- self.redirect_handler = XMLRPCRedirectHandler()
- self.opener = build_opener(
- self.cookie_processor, self.redirect_handler)
- self.timeout = config.checkwatches.default_socket_timeout
-
- def setCookie(self, cookie_str):
- """Set a cookie for the transport to use in future connections."""
- name, value = cookie_str.split('=')
- cookie = Cookie(
- version=0, name=name, value=value,
- port=None, port_specified=False,
- domain=self.host, domain_specified=True,
- domain_initial_dot=None,
- path='', path_specified=False,
- secure=False, expires=False, discard=None,
- comment=None, comment_url=None, rest=None)
- self.cookie_processor.cookiejar.set_cookie(cookie)
-
- def request(self, host, handler, request_body, verbose=0):
- """Make an XMLRPC request.
-
- Uses the configured proxy server to make the connection.
- """
- url = urlunparse((self.scheme, host, handler, '', '', ''))
- # httplib can raise a UnicodeDecodeError when using a Unicode
- # URL, a non-ASCII body and a proxy. http://bugs.python.org/issue12398
- if isinstance(url, unicode):
- url = url.encode('utf-8')
- headers = {'Content-type': 'text/xml'}
- request = Request(url, request_body, headers)
- try:
- response = self.opener.open(request, timeout=self.timeout).read()
- except HTTPError as he:
- raise ProtocolError(
- request.get_full_url(), he.code, he.msg, he.hdrs)
- else:
- traceback_info(response)
- return self.parse_response(StringIO(response))
-
-
class RequestsTransport(Transport):
"""An XML-RPC transport which uses requests.
=== modified file 'lib/lp/bugs/tests/externalbugtracker-xmlrpc-transport.txt'
--- lib/lp/bugs/tests/externalbugtracker-xmlrpc-transport.txt 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/tests/externalbugtracker-xmlrpc-transport.txt 2018-06-23 10:08:23 +0000
@@ -1,200 +1,3 @@
-XMLRPC urllib2 transport
-------------------------
-
-When using XMLRPC for connecting to external bug trackers, we need to
-use a special transport, which processes http cookies correctly, and
-which can connect through an http proxy server.
-
- >>> from lp.bugs.tests.externalbugtracker import (
- ... UrlLib2TransportTestHandler)
- >>> from lp.bugs.externalbugtracker.xmlrpc import (
- ... UrlLib2Transport)
-
-UrlLib2Transport accepts a CookieJar as an optional parameter upon creation.
-This allows us to share a CookieJar - and therefore the cookie it contains -
-between different transports or URL openers.
-
- >>> from cookielib import CookieJar
- >>> jar = CookieJar()
- >>> transport = UrlLib2Transport('http://example.com', jar)
- >>> transport.cookie_processor.cookiejar == jar
- True
-
-We patch the opener to return a fixed response without actually opening a
-connection. The response returns the request-url as an XMLRPC parameter, and
-sets a cookie from the server, 'foo=bar'.
-
- >>> test_handler = UrlLib2TransportTestHandler()
- >>> transport.opener.add_handler(test_handler)
-
-Before sending the request, the transport's cookie jar is empty.
-
- >>> transport.cookie_processor.cookiejar
- <...CookieJar[]>
-
- >>> request_body = """<?xml version="1.0"?>
- ... <methodCall>
- ... <methodName>examples.testMethod</methodName>
- ... <params>
- ... <param>
- ... <value>
- ... <int>42</int>
- ... </value>
- ... </param>
- ... </params>
- ... </methodCall>
- ... """
- >>> transport.request('www.example.com', 'xmlrpc', request_body)
- ('http://www.example.com/xmlrpc',)
-
-We received the url as the single XMLRPC result, and the cookie jar now
-contains the 'foo=bar' cookie sent by the server.
-
- >>> transport.cookie_processor.cookiejar
- <...CookieJar[Cookie(version=0, name='foo', value='bar'...)]>
-
-In addition to cookies sent by the server, we can set cookies locally.
-
- >>> transport.setCookie('ding=dong')
- >>> transport.cookie_processor.cookiejar
- <...CookieJar[Cookie(version=0, name='ding', value='dong'...),
- Cookie(version=0, name='foo', value='bar'...)]>
-
-If an error occurs trying to make the request, an
-``xmlrpclib.ProtocolError`` is raised.
-
- >>> from urllib2 import HTTPError
- >>> test_handler.setError(
- ... HTTPError(
- ... 'http://www.example.com/xmlrpc', 500, 'Internal Error', {},
- ... None),
- ... 'http://www.example.com/xmlrpc')
- >>> request_body = """<?xml version="1.0"?>
- ... <methodCall>
- ... <methodName>examples.testError</methodName>
- ... <params>
- ... <param>
- ... <value>
- ... <int>42</int>
- ... </value>
- ... </param>
- ... </params>
- ... </methodCall>
- ... """
- >>> transport.request('www.example.com', 'xmlrpc', request_body)
- Traceback (most recent call last):
- ...
- ProtocolError: <ProtocolError for http://www.example.com/xmlrpc: 500
- Internal Error>
-
-If the transport encounters a redirect response it will make its request
-to the location indicated in that response rather than the original
-location.
-
- >>> test_handler.setRedirect('http://www.example.com/xmlrpc/redirected')
- >>> request_body = """<?xml version="1.0"?>
- ... <methodCall>
- ... <methodName>examples.whatever</methodName>
- ... <params>
- ... <param>
- ... <value>
- ... <int>42</int>
- ... </value>
- ... </param>
- ... </params>
- ... </methodCall>
- ... """
- >>> transport.request('www.example.com', 'xmlrpc', request_body)
- ('http://www.example.com/xmlrpc/redirected',)
-
-
-The XMLRPCRedirectHandler
-=========================
-
-The UrlLib2Transport uses a custom HTTP redirection handler to handle
-redirect responses. This is a subclass of urllib2's HTTPRedirectHandler.
-
- >>> from lp.bugs.externalbugtracker.xmlrpc import XMLRPCRedirectHandler
- >>> from urllib2 import HTTPRedirectHandler, Request
-
- >>> transport.opener.handlers
- [... <lp.bugs...XMLRPCRedirectHandler instance ...]
-
- >>> issubclass(XMLRPCRedirectHandler, HTTPRedirectHandler)
- True
-
-XMLRPCRedirectHandler overrides HTTPRedirectHandler's redirect_request()
-method. XMLRPCRedirectHandler.redirect_request() will return a
-urllib2.Request object that is set to POST to the new target URL
-specified by an HTTP 30x redirect response (as opposed to
-HTTPRedirectHandler.redirect_request(), which will return a GET request
-to the new target URL).
-
- >>> request_body = """<?xml version="1.0"?>
- ... <methodCall>
- ... <methodName>examples.exampleRequest</methodName>
- ... <params>
- ... <param>
- ... <value>
- ... <int>42</int>
- ... </value>
- ... </param>
- ... </params>
- ... </methodCall>
- ... """
- >>> request_to_be_redirected = Request(
- ... 'http://www.example.com', data=request_body)
- >>> request_to_be_redirected.timeout = 30
-
- >>> handler = XMLRPCRedirectHandler()
- >>> redirected_request = handler.redirect_request(
- ... request_to_be_redirected, None, 302, 'Moved', {},
- ... newurl='http://www.example.com/redirected')
-
-The new request will be a POST request to the URL specified in
-redirect_request()'s newurl parameter. The payload of the request will
-be the XML-RPC method call.
-
- >>> print redirected_request.get_method()
- POST
-
- >>> print redirected_request.get_full_url()
- http://www.example.com/redirected
-
- >>> print redirected_request.data
- <?xml version="1.0"?>
- <methodCall>
- <methodName>examples.exampleRequest</methodName>
- <params>
- <param>
- <value>
- <int>42</int>
- </value>
- </param>
- </params>
- </methodCall>
- >>> redirected_request.timeout == request_to_be_redirected.timeout
- True
-
-If an XMLRPCRedirectHandler is passed a GET request to redirect, the new
-request will be a GET request with no payload.
-
- >>> request_to_be_redirected = Request('http://www.example.com')
- >>> request_to_be_redirected.timeout = 30
- >>> redirected_request = handler.redirect_request(
- ... request_to_be_redirected, None, 302, 'Moved', {},
- ... newurl='http://www.example.com/redirected')
-
- >>> print redirected_request.get_method()
- GET
-
- >>> print redirected_request.get_full_url()
- http://www.example.com/redirected
-
- >>> print redirected_request.data
- None
-
-
XMLRPC requests transport
-------------------------
=== modified file 'lib/lp/bugs/tests/externalbugtracker.py'
--- lib/lp/bugs/tests/externalbugtracker.py 2018-06-23 10:08:23 +0000
+++ lib/lp/bugs/tests/externalbugtracker.py 2018-06-23 10:08:23 +0000
@@ -11,16 +11,10 @@
datetime,
timedelta,
)
-from httplib import HTTPMessage
import os
import random
import re
-from StringIO import StringIO
import time
-from urllib2 import (
- BaseHandler,
- Request,
- )
import xmlrpclib
import responses
@@ -1698,86 +1692,6 @@
return bug
-class UrlLib2TransportTestInfo:
- """A url info object for use in the test, returning
- a hard-coded cookie header.
- """
- cookies = 'foo=bar'
-
- def getheaders(self, header):
- """Return the hard-coded cookie header."""
- if header.lower() in ('cookie', 'set-cookie', 'set-cookie2'):
- return [self.cookies]
-
-
-class UrlLib2TransportTestHandler(BaseHandler):
- """A test urllib2 handler returning a hard-coded response."""
-
- def __init__(self):
- self.redirect_url = None
- self.raise_error = None
- self.response = None
- self.accessed_urls = []
-
- def setRedirect(self, new_url):
- """The next call of default_open() will redirect to `url`."""
- self.redirect_url = new_url
-
- def setError(self, error, url):
- """Raise `error` when `url` is accessed."""
- self.raise_error = error
- self.raise_url = url
-
- def setResponse(self, response):
- self.response = response
-
- def default_open(self, req):
- """Catch all requests and return a hard-coded response.
-
- The response body is an XMLRPC response. In addition we set the
- info of the response to contain a cookie.
- """
- assert isinstance(req, Request), (
- 'Expected a urllib2.Request, got %s' % req)
-
- self.accessed_urls.append(req.get_full_url())
- if (self.raise_error is not None and
- req.get_full_url() == self.raise_url):
- error = self.raise_error
- self.raise_error = None
- raise error
- elif self.redirect_url is not None:
- headers = HTTPMessage(StringIO())
- headers['location'] = self.redirect_url
- response = StringIO()
- response.info = lambda: headers
- response.geturl = req.get_full_url
- response.code = 302
- response.msg = 'Moved'
- self.redirect_url = None
- response = self.parent.error(
- 'http', req, response, 302, 'Moved', headers)
- elif self.response is not None:
- response = StringIO(self.response)
- info = UrlLib2TransportTestInfo()
- response.info = lambda: info
- response.code = 200
- response.geturl = req.get_full_url
- response.msg = ''
- self.response = None
- else:
- xmlrpc_response = xmlrpclib.dumps(
- (req.get_full_url(), ), methodresponse=True)
- response = StringIO(xmlrpc_response)
- info = UrlLib2TransportTestInfo()
- response.info = lambda: info
- response.code = 200
- response.geturl = req.get_full_url
- response.msg = ''
-
- return response
-
-
def ensure_response_parser_is_expat(transport):
"""Ensure the transport always selects the Expat-based response parser.
Follow ups