Skip to content

Commit cc73f9b

Browse files
committed
Separate pip search command from operation
By extracting the logic into `pip.operations.search`, the hope is that folks could do a `search` programmatically more easily. It also has greater separation of concerns and should allow people to work in parallel with less chance of merge conflicts. Continuing work started in pypa#2173 and pypa#2404.
1 parent 7538303 commit cc73f9b

File tree

2 files changed

+63
-57
lines changed

2 files changed

+63
-57
lines changed

pip/commands/search.py

+3-57
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
import textwrap
66

77
from pip.basecommand import Command, SUCCESS
8-
from pip.download import PipXmlrpcTransport
98
from pip.index import PyPI
109
from pip.utils import get_terminal_size
1110
from pip.utils.logging import indent_log
1211
from pip.exceptions import CommandError
1312
from pip.status_codes import NO_MATCHES_FOUND
1413
from pip._vendor import pkg_resources
15-
from pip._vendor.six.moves import xmlrpc_client
14+
15+
from pip.operations.search import highest_version, search, transform_hits
1616

1717

1818
logger = logging.getLogger(__name__)
@@ -40,7 +40,7 @@ def run(self, options, args):
4040
if not args:
4141
raise CommandError('Missing required argument (search query).')
4242
query = args
43-
pypi_hits = self.search(query, options)
43+
pypi_hits = search(query, options, self._build_session)
4444
hits = transform_hits(pypi_hits)
4545

4646
terminal_width = None
@@ -52,54 +52,6 @@ def run(self, options, args):
5252
return SUCCESS
5353
return NO_MATCHES_FOUND
5454

55-
def search(self, query, options):
56-
index_url = options.index
57-
with self._build_session(options) as session:
58-
transport = PipXmlrpcTransport(index_url, session)
59-
pypi = xmlrpc_client.ServerProxy(index_url, transport)
60-
hits = pypi.search({'name': query, 'summary': query}, 'or')
61-
return hits
62-
63-
64-
def transform_hits(hits):
65-
"""
66-
The list from pypi is really a list of versions. We want a list of
67-
packages with the list of versions stored inline. This converts the
68-
list from pypi into one we can use.
69-
"""
70-
packages = {}
71-
for hit in hits:
72-
name = hit['name']
73-
summary = hit['summary']
74-
version = hit['version']
75-
score = hit['_pypi_ordering']
76-
if score is None:
77-
score = 0
78-
79-
if name not in packages.keys():
80-
packages[name] = {
81-
'name': name,
82-
'summary': summary,
83-
'versions': [version],
84-
'score': score,
85-
}
86-
else:
87-
packages[name]['versions'].append(version)
88-
89-
# if this is the highest version, replace summary and score
90-
if version == highest_version(packages[name]['versions']):
91-
packages[name]['summary'] = summary
92-
packages[name]['score'] = score
93-
94-
# each record has a unique name now, so we will convert the dict into a
95-
# list sorted by score
96-
package_list = sorted(
97-
packages.values(),
98-
key=lambda x: x['score'],
99-
reverse=True,
100-
)
101-
return package_list
102-
10355

10456
def print_results(hits, name_column_width=None, terminal_width=None):
10557
if not hits:
@@ -131,9 +83,3 @@ def print_results(hits, name_column_width=None, terminal_width=None):
13183
logger.info('LATEST: %s', latest)
13284
except UnicodeEncodeError:
13385
pass
134-
135-
136-
def highest_version(versions):
137-
return next(iter(
138-
sorted(versions, key=pkg_resources.parse_version, reverse=True)
139-
))

pip/operations/search.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# The guts of search
2+
3+
from pip.download import PipXmlrpcTransport
4+
from pip._vendor.six.moves import xmlrpc_client
5+
from pip._vendor import pkg_resources
6+
7+
8+
def search(query, options, build_session_func):
9+
index_url = options.index
10+
with build_session_func(options) as session:
11+
transport = PipXmlrpcTransport(index_url, session)
12+
pypi = xmlrpc_client.ServerProxy(index_url, transport)
13+
hits = pypi.search({'name': query, 'summary': query}, 'or')
14+
return hits
15+
16+
17+
def transform_hits(hits):
18+
"""
19+
The list from pypi is really a list of versions. We want a list of
20+
packages with the list of versions stored inline. This converts the
21+
list from pypi into one we can use.
22+
"""
23+
packages = {}
24+
for hit in hits:
25+
name = hit['name']
26+
summary = hit['summary']
27+
version = hit['version']
28+
score = hit['_pypi_ordering']
29+
if score is None:
30+
score = 0
31+
32+
if name not in packages.keys():
33+
packages[name] = {
34+
'name': name,
35+
'summary': summary,
36+
'versions': [version],
37+
'score': score,
38+
}
39+
else:
40+
packages[name]['versions'].append(version)
41+
42+
# if this is the highest version, replace summary and score
43+
if version == highest_version(packages[name]['versions']):
44+
packages[name]['summary'] = summary
45+
packages[name]['score'] = score
46+
47+
# each record has a unique name now, so we will convert the dict into a
48+
# list sorted by score
49+
package_list = sorted(
50+
packages.values(),
51+
key=lambda x: x['score'],
52+
reverse=True,
53+
)
54+
return package_list
55+
56+
57+
def highest_version(versions):
58+
return next(iter(
59+
sorted(versions, key=pkg_resources.parse_version, reverse=True)
60+
))

0 commit comments

Comments
 (0)