comparison env/lib/python3.9/site-packages/pip/_internal/commands/list.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 import json
2 import logging
3
4 from pip._internal.cli import cmdoptions
5 from pip._internal.cli.req_command import IndexGroupCommand
6 from pip._internal.cli.status_codes import SUCCESS
7 from pip._internal.exceptions import CommandError
8 from pip._internal.index.collector import LinkCollector
9 from pip._internal.index.package_finder import PackageFinder
10 from pip._internal.models.selection_prefs import SelectionPreferences
11 from pip._internal.utils.compat import stdlib_pkgs
12 from pip._internal.utils.misc import (
13 dist_is_editable,
14 get_installed_distributions,
15 tabulate,
16 write_output,
17 )
18 from pip._internal.utils.packaging import get_installer
19 from pip._internal.utils.parallel import map_multithread
20 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
21
22 if MYPY_CHECK_RUNNING:
23 from optparse import Values
24 from typing import Iterator, List, Set, Tuple
25
26 from pip._vendor.pkg_resources import Distribution
27
28 from pip._internal.network.session import PipSession
29
30 logger = logging.getLogger(__name__)
31
32
33 class ListCommand(IndexGroupCommand):
34 """
35 List installed packages, including editables.
36
37 Packages are listed in a case-insensitive sorted order.
38 """
39
40 ignore_require_venv = True
41 usage = """
42 %prog [options]"""
43
44 def add_options(self):
45 # type: () -> None
46 self.cmd_opts.add_option(
47 '-o', '--outdated',
48 action='store_true',
49 default=False,
50 help='List outdated packages')
51 self.cmd_opts.add_option(
52 '-u', '--uptodate',
53 action='store_true',
54 default=False,
55 help='List uptodate packages')
56 self.cmd_opts.add_option(
57 '-e', '--editable',
58 action='store_true',
59 default=False,
60 help='List editable projects.')
61 self.cmd_opts.add_option(
62 '-l', '--local',
63 action='store_true',
64 default=False,
65 help=('If in a virtualenv that has global access, do not list '
66 'globally-installed packages.'),
67 )
68 self.cmd_opts.add_option(
69 '--user',
70 dest='user',
71 action='store_true',
72 default=False,
73 help='Only output packages installed in user-site.')
74 self.cmd_opts.add_option(cmdoptions.list_path())
75 self.cmd_opts.add_option(
76 '--pre',
77 action='store_true',
78 default=False,
79 help=("Include pre-release and development versions. By default, "
80 "pip only finds stable versions."),
81 )
82
83 self.cmd_opts.add_option(
84 '--format',
85 action='store',
86 dest='list_format',
87 default="columns",
88 choices=('columns', 'freeze', 'json'),
89 help="Select the output format among: columns (default), freeze, "
90 "or json",
91 )
92
93 self.cmd_opts.add_option(
94 '--not-required',
95 action='store_true',
96 dest='not_required',
97 help="List packages that are not dependencies of "
98 "installed packages.",
99 )
100
101 self.cmd_opts.add_option(
102 '--exclude-editable',
103 action='store_false',
104 dest='include_editable',
105 help='Exclude editable package from output.',
106 )
107 self.cmd_opts.add_option(
108 '--include-editable',
109 action='store_true',
110 dest='include_editable',
111 help='Include editable package from output.',
112 default=True,
113 )
114 self.cmd_opts.add_option(cmdoptions.list_exclude())
115 index_opts = cmdoptions.make_option_group(
116 cmdoptions.index_group, self.parser
117 )
118
119 self.parser.insert_option_group(0, index_opts)
120 self.parser.insert_option_group(0, self.cmd_opts)
121
122 def _build_package_finder(self, options, session):
123 # type: (Values, PipSession) -> PackageFinder
124 """
125 Create a package finder appropriate to this list command.
126 """
127 link_collector = LinkCollector.create(session, options=options)
128
129 # Pass allow_yanked=False to ignore yanked versions.
130 selection_prefs = SelectionPreferences(
131 allow_yanked=False,
132 allow_all_prereleases=options.pre,
133 )
134
135 return PackageFinder.create(
136 link_collector=link_collector,
137 selection_prefs=selection_prefs,
138 )
139
140 def run(self, options, args):
141 # type: (Values, List[str]) -> int
142 if options.outdated and options.uptodate:
143 raise CommandError(
144 "Options --outdated and --uptodate cannot be combined.")
145
146 cmdoptions.check_list_path_option(options)
147
148 skip = set(stdlib_pkgs)
149 if options.excludes:
150 skip.update(options.excludes)
151
152 packages = get_installed_distributions(
153 local_only=options.local,
154 user_only=options.user,
155 editables_only=options.editable,
156 include_editables=options.include_editable,
157 paths=options.path,
158 skip=skip,
159 )
160
161 # get_not_required must be called firstly in order to find and
162 # filter out all dependencies correctly. Otherwise a package
163 # can't be identified as requirement because some parent packages
164 # could be filtered out before.
165 if options.not_required:
166 packages = self.get_not_required(packages, options)
167
168 if options.outdated:
169 packages = self.get_outdated(packages, options)
170 elif options.uptodate:
171 packages = self.get_uptodate(packages, options)
172
173 self.output_package_listing(packages, options)
174 return SUCCESS
175
176 def get_outdated(self, packages, options):
177 # type: (List[Distribution], Values) -> List[Distribution]
178 return [
179 dist for dist in self.iter_packages_latest_infos(packages, options)
180 if dist.latest_version > dist.parsed_version
181 ]
182
183 def get_uptodate(self, packages, options):
184 # type: (List[Distribution], Values) -> List[Distribution]
185 return [
186 dist for dist in self.iter_packages_latest_infos(packages, options)
187 if dist.latest_version == dist.parsed_version
188 ]
189
190 def get_not_required(self, packages, options):
191 # type: (List[Distribution], Values) -> List[Distribution]
192 dep_keys = set() # type: Set[Distribution]
193 for dist in packages:
194 dep_keys.update(requirement.key for requirement in dist.requires())
195
196 # Create a set to remove duplicate packages, and cast it to a list
197 # to keep the return type consistent with get_outdated and
198 # get_uptodate
199 return list({pkg for pkg in packages if pkg.key not in dep_keys})
200
201 def iter_packages_latest_infos(self, packages, options):
202 # type: (List[Distribution], Values) -> Iterator[Distribution]
203 with self._build_session(options) as session:
204 finder = self._build_package_finder(options, session)
205
206 def latest_info(dist):
207 # type: (Distribution) -> Distribution
208 all_candidates = finder.find_all_candidates(dist.key)
209 if not options.pre:
210 # Remove prereleases
211 all_candidates = [candidate for candidate in all_candidates
212 if not candidate.version.is_prerelease]
213
214 evaluator = finder.make_candidate_evaluator(
215 project_name=dist.project_name,
216 )
217 best_candidate = evaluator.sort_best_candidate(all_candidates)
218 if best_candidate is None:
219 return None
220
221 remote_version = best_candidate.version
222 if best_candidate.link.is_wheel:
223 typ = 'wheel'
224 else:
225 typ = 'sdist'
226 # This is dirty but makes the rest of the code much cleaner
227 dist.latest_version = remote_version
228 dist.latest_filetype = typ
229 return dist
230
231 for dist in map_multithread(latest_info, packages):
232 if dist is not None:
233 yield dist
234
235 def output_package_listing(self, packages, options):
236 # type: (List[Distribution], Values) -> None
237 packages = sorted(
238 packages,
239 key=lambda dist: dist.project_name.lower(),
240 )
241 if options.list_format == 'columns' and packages:
242 data, header = format_for_columns(packages, options)
243 self.output_package_listing_columns(data, header)
244 elif options.list_format == 'freeze':
245 for dist in packages:
246 if options.verbose >= 1:
247 write_output("%s==%s (%s)", dist.project_name,
248 dist.version, dist.location)
249 else:
250 write_output("%s==%s", dist.project_name, dist.version)
251 elif options.list_format == 'json':
252 write_output(format_for_json(packages, options))
253
254 def output_package_listing_columns(self, data, header):
255 # type: (List[List[str]], List[str]) -> None
256 # insert the header first: we need to know the size of column names
257 if len(data) > 0:
258 data.insert(0, header)
259
260 pkg_strings, sizes = tabulate(data)
261
262 # Create and add a separator.
263 if len(data) > 0:
264 pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes)))
265
266 for val in pkg_strings:
267 write_output(val)
268
269
270 def format_for_columns(pkgs, options):
271 # type: (List[Distribution], Values) -> Tuple[List[List[str]], List[str]]
272 """
273 Convert the package data into something usable
274 by output_package_listing_columns.
275 """
276 running_outdated = options.outdated
277 # Adjust the header for the `pip list --outdated` case.
278 if running_outdated:
279 header = ["Package", "Version", "Latest", "Type"]
280 else:
281 header = ["Package", "Version"]
282
283 data = []
284 if options.verbose >= 1 or any(dist_is_editable(x) for x in pkgs):
285 header.append("Location")
286 if options.verbose >= 1:
287 header.append("Installer")
288
289 for proj in pkgs:
290 # if we're working on the 'outdated' list, separate out the
291 # latest_version and type
292 row = [proj.project_name, proj.version]
293
294 if running_outdated:
295 row.append(proj.latest_version)
296 row.append(proj.latest_filetype)
297
298 if options.verbose >= 1 or dist_is_editable(proj):
299 row.append(proj.location)
300 if options.verbose >= 1:
301 row.append(get_installer(proj))
302
303 data.append(row)
304
305 return data, header
306
307
308 def format_for_json(packages, options):
309 # type: (List[Distribution], Values) -> str
310 data = []
311 for dist in packages:
312 info = {
313 'name': dist.project_name,
314 'version': str(dist.version),
315 }
316 if options.verbose >= 1:
317 info['location'] = dist.location
318 info['installer'] = get_installer(dist)
319 if options.outdated:
320 info['latest_version'] = str(dist.latest_version)
321 info['latest_filetype'] = dist.latest_filetype
322 data.append(info)
323 return json.dumps(data)