|
| 1 | +import json |
1 | 2 | import logging
|
2 | 3 | import shutil
|
3 | 4 | import sys
|
|
7 | 8 | from optparse import Values
|
8 | 9 | from typing import TYPE_CHECKING, Dict, Iterable, List, Optional
|
9 | 10 |
|
| 11 | +from pip._vendor import requests |
10 | 12 | from pip._vendor.packaging.utils import canonicalize_name
|
11 | 13 | from pip._vendor.packaging.version import parse as parse_version
|
| 14 | +from pip._vendor.six.moves.urllib import parse as urllib_parse |
12 | 15 |
|
13 | 16 | from pip._internal.cli.base_command import Command
|
14 | 17 | from pip._internal.cli.req_command import SessionCommandMixin
|
15 | 18 | from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS
|
16 |
| -from pip._internal.exceptions import CommandError |
| 19 | +from pip._internal.exceptions import CommandError, NetworkConnectionError |
17 | 20 | from pip._internal.metadata import get_default_environment
|
18 | 21 | from pip._internal.models.index import PyPI
|
| 22 | +from pip._internal.network.utils import raise_for_status |
19 | 23 | from pip._internal.network.xmlrpc import PipXmlrpcTransport
|
20 | 24 | from pip._internal.utils.logging import indent_log
|
21 | 25 | from pip._internal.utils.misc import write_output
|
@@ -58,22 +62,31 @@ def add_options(self):
|
58 | 62 |
|
59 | 63 | def get_all_package_versions(self, query, options):
|
60 | 64 | # type: (List[str], Values) -> List[Dict[str, str]]
|
61 |
| - pypi = self.get_pypi(options) |
62 |
| - |
63 | 65 | canonized_query = [canonicalize_name(q) for q in query]
|
64 | 66 |
|
65 |
| - result_hits = [] |
| 67 | + result_hits = [] # type: List[Dict[str, str]] |
66 | 68 | for q in canonized_query:
|
67 |
| - versions = pypi.package_releases(q, True) |
68 |
| - if not versions: |
69 |
| - continue |
70 |
| - |
71 |
| - latest = highest_version(versions) |
72 |
| - hit = pypi.release_data(q, latest) |
| 69 | + try: |
| 70 | + package_data = self.get_pypi_json(options, q) |
| 71 | + except (requests.ConnectionError, |
| 72 | + requests.Timeout, |
| 73 | + NetworkConnectionError) as exc: |
| 74 | + exc = str(exc) |
| 75 | + if '404' in exc: |
| 76 | + err = 'Got 404 error, make sure the package name is valid.' |
| 77 | + else: |
| 78 | + err = exc |
| 79 | + logger.error( |
| 80 | + 'Failed to fetch data of package "%s": %s', q, err) |
| 81 | + return result_hits |
| 82 | + |
| 83 | + versions = package_data.get('releases', {}) # type: Dict[str, str] |
| 84 | + |
| 85 | + info = package_data.get('info', {}) # type: Dict[str, str] |
73 | 86 | for version in versions:
|
74 | 87 | result_hits.append(
|
75 |
| - {'name': hit['name'], |
76 |
| - 'summary': hit['summary'], |
| 88 | + {'name': info.get('name', ''), |
| 89 | + 'summary': info.get('summary', ''), |
77 | 90 | 'version': version}
|
78 | 91 | )
|
79 | 92 | return result_hits
|
@@ -109,6 +122,14 @@ def get_pypi(self, options):
|
109 | 122 | transport = PipXmlrpcTransport(index_url, session)
|
110 | 123 | return xmlrpc.client.ServerProxy(index_url, transport)
|
111 | 124 |
|
| 125 | + def get_pypi_json(self, options, query): |
| 126 | + # type: (Values, str) -> Dict[str, Dict[str, str]] |
| 127 | + resp = requests.get( |
| 128 | + urllib_parse.urljoin( |
| 129 | + options.index, '{}/{}/{}'.format('pypi', query, 'json'))) |
| 130 | + raise_for_status(resp) |
| 131 | + return json.loads(resp.content.decode('utf-8')) |
| 132 | + |
112 | 133 | def search(self, query, options):
|
113 | 134 | # type: (List[str], Values) -> List[Dict[str, str]]
|
114 | 135 | pypi = self.get_pypi(options)
|
|
0 commit comments