diff --git a/CHANGES.txt b/CHANGES.txt index 02fcfa8e8a3..d71b10a791d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -59,6 +59,9 @@ * Allow the use of ``--no-use-wheel`` within a requirements file. (:pull:`1859`) +* Fixed :issue:`1180`. Added support to respect proxies in ``pip search``. It + also fixes :issue:`932` and :issue:`1104`. (:pull:`1902`) + **1.5.7** diff --git a/pip/commands/search.py b/pip/commands/search.py index 6de76eed3ad..6476d43aeba 100644 --- a/pip/commands/search.py +++ b/pip/commands/search.py @@ -5,6 +5,7 @@ from functools import reduce from pip.basecommand import Command, SUCCESS +from pip.download import PipXmlrpcTransport from pip.util import get_terminal_size from pip.log import logger from pip.compat import cmp @@ -36,9 +37,7 @@ def run(self, options, args): if not args: raise CommandError('Missing required argument (search query).') query = args - index_url = options.index - - pypi_hits = self.search(query, index_url) + pypi_hits = self.search(query, options) hits = transform_hits(pypi_hits) terminal_width = None @@ -50,10 +49,13 @@ def run(self, options, args): return SUCCESS return NO_MATCHES_FOUND - def search(self, query, index_url): - pypi = xmlrpc_client.ServerProxy(index_url) - hits = pypi.search({'name': query, 'summary': query}, 'or') - return hits + def search(self, query, options): + index_url = options.index + with self._build_session(options) as session: + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc_client.ServerProxy(index_url, transport) + hits = pypi.search({'name': query, 'summary': query}, 'or') + return hits def transform_hits(hits): diff --git a/pip/download.py b/pip/download.py index 23a650e01b5..f1c2e3a2d0d 100644 --- a/pip/download.py +++ b/pip/download.py @@ -28,6 +28,7 @@ from pip._vendor.cachecontrol import CacheControlAdapter from pip._vendor.cachecontrol.caches import FileCache from pip._vendor.lockfile import LockError +from pip._vendor.six.moves import xmlrpc_client __all__ = ['get_file_content', @@ -695,3 +696,29 @@ def unpack_file_url(link, location, download_dir=None): # a download dir is specified and not already downloaded if download_dir and not already_downloaded: _copy_file(from_path, download_dir, content_type, link) + + +class PipXmlrpcTransport(xmlrpc_client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + def __init__(self, index_url, session, use_datetime=False): + xmlrpc_client.Transport.__init__(self, use_datetime) + index_parts = urlparse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request(self, host, handler, request_body, verbose=False): + parts = (self._scheme, host, handler, None, None, None) + url = urlparse.urlunparse(parts) + try: + headers = {'Content-Type': 'text/xml'} + response = self._session.post(url, data=request_body, + headers=headers, stream=True) + response.raise_for_status() + self.verbose = verbose + return self.parse_response(response.raw) + except requests.HTTPError as exc: + logger.fatal("HTTP error {0} while getting {1}".format( + exc.response.status_code, url)) + raise diff --git a/tests/functional/test_search.py b/tests/functional/test_search.py index dee6b319dca..77b4f43bf1c 100644 --- a/tests/functional/test_search.py +++ b/tests/functional/test_search.py @@ -3,7 +3,6 @@ transform_hits, SearchCommand) from pip.status_codes import NO_MATCHES_FOUND, SUCCESS -from mock import Mock from tests.lib import pyversion @@ -139,10 +138,10 @@ def test_run_method_should_return_sucess_when_find_packages(): """ Test SearchCommand.run for found package """ - options_mock = Mock() - options_mock.index = 'http://pypi.python.org/pypi' - search_cmd = SearchCommand() - status = search_cmd.run(options_mock, ('pip',)) + command = SearchCommand() + cmdline = "--index=http://pypi.python.org/pypi pip" + options, args = command.parse_args(cmdline.split()) + status = command.run(options, args) assert status == SUCCESS @@ -150,11 +149,11 @@ def test_run_method_should_return_no_matches_found_when_does_not_find_pkgs(): """ Test SearchCommand.run for no matches """ - options_mock = Mock() - options_mock.index = 'https://pypi.python.org/pypi' - search_cmd = SearchCommand() - status = search_cmd.run(options_mock, ('non-existent-package',)) - assert status == NO_MATCHES_FOUND, status + command = SearchCommand() + cmdline = "--index=http://pypi.python.org/pypi non-existent-package" + options, args = command.parse_args(cmdline.split()) + status = command.run(options, args) + assert status == NO_MATCHES_FOUND def test_search_should_exit_status_code_zero_when_find_packages(script):