|
1 | 1 | from __future__ import absolute_import
|
2 | 2 |
|
| 3 | +import json |
3 | 4 | import logging
|
4 | 5 | import sys
|
5 | 6 | import textwrap
|
6 | 7 | from collections import OrderedDict
|
7 | 8 |
|
8 |
| -from pip._vendor import pkg_resources |
| 9 | +from pip._vendor import pkg_resources, requests |
9 | 10 | from pip._vendor.packaging.utils import canonicalize_name
|
10 | 11 | from pip._vendor.packaging.version import parse as parse_version
|
11 | 12 |
|
12 | 13 | # NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is
|
13 | 14 | # why we ignore the type on this import
|
14 | 15 | from pip._vendor.six.moves import xmlrpc_client # type: ignore
|
| 16 | +from pip._vendor.six.moves.urllib import parse as urllib_parse |
15 | 17 |
|
16 | 18 | from pip._internal.cli.base_command import Command
|
17 | 19 | from pip._internal.cli.req_command import SessionCommandMixin
|
18 | 20 | 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 |
20 | 22 | from pip._internal.models.index import PyPI
|
| 23 | +from pip._internal.network.utils import raise_for_status |
21 | 24 | from pip._internal.network.xmlrpc import PipXmlrpcTransport
|
22 | 25 | from pip._internal.utils.compat import get_terminal_size
|
23 | 26 | from pip._internal.utils.logging import indent_log
|
@@ -64,22 +67,31 @@ def add_options(self):
|
64 | 67 |
|
65 | 68 | def get_all_package_versions(self, query, options):
|
66 | 69 | # type: (List[str], Values) -> List[Dict[str, str]]
|
67 |
| - pypi = self.get_pypi(options) |
68 |
| - |
69 | 70 | canonized_query = [canonicalize_name(q) for q in query]
|
70 | 71 |
|
71 |
| - result_hits = [] |
| 72 | + result_hits = [] # type: List[Dict[str, str]] |
72 | 73 | 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] |
79 | 91 | for version in versions:
|
80 | 92 | result_hits.append(
|
81 |
| - {'name': hit['name'], |
82 |
| - 'summary': hit['summary'], |
| 93 | + {'name': info.get('name', ''), |
| 94 | + 'summary': info.get('summary', ''), |
83 | 95 | 'version': version}
|
84 | 96 | )
|
85 | 97 | return result_hits
|
@@ -115,6 +127,14 @@ def get_pypi(self, options):
|
115 | 127 | transport = PipXmlrpcTransport(index_url, session)
|
116 | 128 | return xmlrpc_client.ServerProxy(index_url, transport)
|
117 | 129 |
|
| 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 | + |
118 | 138 | def search(self, query, options):
|
119 | 139 | # type: (List[str], Values) -> List[Dict[str, str]]
|
120 | 140 | pypi = self.get_pypi(options)
|
|
0 commit comments