Skip to content

Commit 3699993

Browse files
author
Noah Gorny
committed
search: Use json API in list-all-versions instead of xmlrpc API
1 parent ae1a366 commit 3699993

File tree

1 file changed

+33
-13
lines changed

1 file changed

+33
-13
lines changed

src/pip/_internal/commands/search.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
from __future__ import absolute_import
22

3+
import json
34
import logging
45
import sys
56
import textwrap
67
from collections import OrderedDict
78

8-
from pip._vendor import pkg_resources
9+
from pip._vendor import pkg_resources, requests
910
from pip._vendor.packaging.utils import canonicalize_name
1011
from pip._vendor.packaging.version import parse as parse_version
1112

1213
# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is
1314
# why we ignore the type on this import
1415
from pip._vendor.six.moves import xmlrpc_client # type: ignore
16+
from pip._vendor.six.moves.urllib import parse as urllib_parse
1517

1618
from pip._internal.cli.base_command import Command
1719
from pip._internal.cli.req_command import SessionCommandMixin
1820
from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS
19-
from pip._internal.exceptions import CommandError
21+
from pip._internal.exceptions import CommandError, NetworkConnectionError
2022
from pip._internal.models.index import PyPI
23+
from pip._internal.network.utils import raise_for_status
2124
from pip._internal.network.xmlrpc import PipXmlrpcTransport
2225
from pip._internal.utils.compat import get_terminal_size
2326
from pip._internal.utils.logging import indent_log
@@ -64,22 +67,31 @@ def add_options(self):
6467

6568
def get_all_package_versions(self, query, options):
6669
# type: (List[str], Values) -> List[Dict[str, str]]
67-
pypi = self.get_pypi(options)
68-
6970
canonized_query = [canonicalize_name(q) for q in query]
7071

71-
result_hits = []
72+
result_hits = [] # type: List[Dict[str, str]]
7273
for q in canonized_query:
73-
versions = pypi.package_releases(q, True)
74-
if not versions:
75-
continue
76-
77-
latest = highest_version(versions)
78-
hit = pypi.release_data(q, latest)
74+
try:
75+
package_data = self.get_pypi_json(options, q)
76+
except (requests.ConnectionError,
77+
requests.Timeout,
78+
NetworkConnectionError) as exc:
79+
exc = str(exc)
80+
if '404' in exc:
81+
err = 'Got 404 error, make sure the package name is valid.'
82+
else:
83+
err = exc
84+
logger.error(
85+
'Failed to fetch data of package "%s": %s', q, err)
86+
return result_hits
87+
88+
versions = package_data.get('releases', {}) # type: Dict[str, str]
89+
90+
info = package_data.get('info', {}) # type: Dict[str, str]
7991
for version in versions:
8092
result_hits.append(
81-
{'name': hit['name'],
82-
'summary': hit['summary'],
93+
{'name': info.get('name', ''),
94+
'summary': info.get('summary', ''),
8395
'version': version}
8496
)
8597
return result_hits
@@ -115,6 +127,14 @@ def get_pypi(self, options):
115127
transport = PipXmlrpcTransport(index_url, session)
116128
return xmlrpc_client.ServerProxy(index_url, transport)
117129

130+
def get_pypi_json(self, options, query):
131+
# type: (Values, str) -> Dict[str, Dict[str, str]]
132+
resp = requests.get(
133+
urllib_parse.urljoin(
134+
options.index, '{}/{}/{}'.format('pypi', query, 'json')))
135+
raise_for_status(resp)
136+
return json.loads(resp.content.decode('utf-8'))
137+
118138
def search(self, query, options):
119139
# type: (List[str], Values) -> List[Dict[str, str]]
120140
pypi = self.get_pypi(options)

0 commit comments

Comments
 (0)