1
1
from __future__ import absolute_import
2
2
3
+ import json
3
4
import logging
4
5
import warnings
5
6
try :
6
7
from itertools import zip_longest
7
8
except ImportError :
8
9
from itertools import izip_longest as zip_longest
9
10
11
+ from pip ._vendor import six
12
+
10
13
from pip .basecommand import Command
11
14
from pip .exceptions import CommandError
12
15
from pip .index import PackageFinder
15
18
from pip .utils .deprecation import RemovedInPip10Warning
16
19
from pip .cmdoptions import make_option_group , index_group
17
20
18
-
19
21
logger = logging .getLogger (__name__ )
20
22
21
23
@@ -72,19 +74,31 @@ def __init__(self, *args, **kw):
72
74
"pip only finds stable versions." ),
73
75
)
74
76
77
+ cmd_opts .add_option (
78
+ '--format' ,
79
+ action = 'store' ,
80
+ dest = 'list_format' ,
81
+ choices = ('legacy' , 'columns' , 'freeze' , 'json' ),
82
+ default = 'legacy' ,
83
+ help = "Select the output format among: legacy (default), columns, "
84
+ "freeze or json" ,
85
+ )
86
+
75
87
# TODO: When we switch the default, set default=True here.
76
88
cmd_opts .add_option (
77
89
'--columns' ,
78
- action = 'store_true' ,
79
- dest = 'columns' ,
90
+ action = 'store_const' ,
91
+ const = 'columns' ,
92
+ dest = 'list_format' ,
80
93
# default=True,
81
94
help = "Align package names and versions into vertical columns." ,
82
95
)
83
96
84
97
cmd_opts .add_option (
85
98
'--no-columns' ,
86
- action = 'store_false' ,
87
- dest = 'columns' ,
99
+ action = 'store_const' ,
100
+ const = 'legacy' ,
101
+ dest = 'list_format' ,
88
102
help = ("Do not align package names and versions into "
89
103
"vertical columns (old-style formatting)" ),
90
104
)
@@ -132,43 +146,33 @@ def run(self, options, args):
132
146
RemovedInPip10Warning ,
133
147
)
134
148
135
- # TODO: When we switch the default, remove
136
- # ``and options.columns is not None``
137
- if not options .columns and options .columns is not None :
138
- warnings .warn (
139
- "The --no-columns option will be removed in the future." ,
140
- RemovedInPip10Warning ,
141
- )
142
-
143
149
if options .outdated and options .uptodate :
144
150
raise CommandError (
145
151
"Options --outdated and --uptodate cannot be combined." )
146
152
147
153
if options .outdated :
148
- self .run_outdated (options )
154
+ packages = self .get_outdated (options )
149
155
elif options .uptodate :
150
- self .run_uptodate (options )
156
+ packages = self .get_uptodate (options )
151
157
else :
152
- self .run_listing (options )
158
+ packages = self .get_listing (options )
159
+ self .output_package_listing (packages , options )
153
160
154
- def run_outdated (self , options ):
161
+ def get_outdated (self , options ):
155
162
latest_pkgs = []
156
- for dist , latest_version , typ in sorted (
163
+ for dist in sorted (
157
164
self .find_packages_latest_versions (options ),
158
- key = lambda p : p [0 ].project_name .lower ()):
159
- if latest_version > dist .parsed_version :
160
- latest_pkgs .append ((dist , latest_version , typ ))
161
- if (hasattr (options , "columns" ) and
162
- options .columns and
163
- len (latest_pkgs ) > 0 ):
164
- data , header = format_for_columns (latest_pkgs , options )
165
- self .output_package_listing_columns (data , header )
166
- else :
167
- for dist , latest_version , typ in latest_pkgs :
168
- logger .info (
169
- '%s - Latest: %s [%s]' ,
170
- self .output_package (dist ), latest_version , typ ,
171
- )
165
+ key = lambda dist : dist .project_name .lower ()):
166
+ if dist .latest_version > dist .parsed_version :
167
+ latest_pkgs .append (dist )
168
+ return latest_pkgs
169
+
170
+ def get_uptodate (self , options ):
171
+ uptodate = []
172
+ for dist in self .find_packages_latest_versions (options ):
173
+ if dist .parsed_version == dist .latest_version :
174
+ uptodate .append (dist )
175
+ return uptodate
172
176
173
177
def find_packages_latest_versions (self , options ):
174
178
index_urls = [options .index_url ] + options .extra_index_urls
@@ -212,17 +216,21 @@ def find_packages_latest_versions(self, options):
212
216
typ = 'wheel'
213
217
else :
214
218
typ = 'sdist'
215
- yield dist , remote_version , typ
219
+ # This is dirty but makes the rest of the code much cleaner
220
+ dist .latest_version = remote_version
221
+ dist .latest_filetype = typ
222
+ yield dist
216
223
217
- def run_listing (self , options ):
218
- installed_packages = get_installed_distributions (
219
- local_only = options .local ,
220
- user_only = options .user ,
221
- editables_only = options .editable ,
222
- )
223
- self .output_package_listing (installed_packages , options )
224
+ def get_listing (self , options ):
225
+ packages = []
226
+ for dist in get_installed_distributions (
227
+ local_only = options .local ,
228
+ user_only = options .user ,
229
+ editables_only = options .editable ):
230
+ packages .append (dist )
231
+ return packages
224
232
225
- def output_package (self , dist ):
233
+ def output_legacy (self , dist ):
226
234
if dist_is_editable (dist ):
227
235
return '%s (%s, %s)' % (
228
236
dist .project_name ,
@@ -232,19 +240,42 @@ def output_package(self, dist):
232
240
else :
233
241
return '%s (%s)' % (dist .project_name , dist .version )
234
242
235
- def output_package_listing (self , installed_packages , options = None ):
236
- installed_packages = sorted (
237
- installed_packages ,
243
+ def output_legacy_latest (self , dist ):
244
+ return '%s - Latest: %s [%s]' % (
245
+ self .output_legacy (dist ),
246
+ dist .latest_version ,
247
+ dist .latest_filetype ,
248
+ )
249
+
250
+ def output_package_listing (self , packages , options ):
251
+ packages = sorted (
252
+ packages ,
238
253
key = lambda dist : dist .project_name .lower (),
239
254
)
240
- if (hasattr (options , "columns" ) and
241
- options .columns and
242
- len (installed_packages ) > 0 ):
243
- data , header = format_for_columns (installed_packages , options )
255
+ if options .list_format == 'columns' and packages :
256
+ data , header = format_for_columns (packages , options )
244
257
self .output_package_listing_columns (data , header )
245
- else :
246
- for dist in installed_packages :
247
- logger .info (self .output_package (dist ))
258
+ elif options .list_format == 'freeze' :
259
+ for dist in packages :
260
+ logger .info ("%s==%s" , dist .project_name , dist .version )
261
+ elif options .list_format == 'json' :
262
+ data = []
263
+ for dist in packages :
264
+ info = {
265
+ 'name' : dist .project_name ,
266
+ 'version' : six .text_type (dist .version ),
267
+ }
268
+ if options .outdated :
269
+ info ['latest_version' ] = six .text_type (dist .latest_version )
270
+ info ['latest_filetype' ] = dist .latest_filetype
271
+ data .append (info )
272
+ logger .info (json .dumps (data ))
273
+ else : # legacy
274
+ for dist in packages :
275
+ if options .outdated :
276
+ logger .info (self .output_legacy_latest (dist ))
277
+ else :
278
+ logger .info (self .output_legacy (dist ))
248
279
249
280
def output_package_listing_columns (self , data , header ):
250
281
# insert the header first: we need to know the size of column names
@@ -260,13 +291,6 @@ def output_package_listing_columns(self, data, header):
260
291
for val in pkg_strings :
261
292
logger .info (val )
262
293
263
- def run_uptodate (self , options ):
264
- uptodate = []
265
- for dist , version , typ in self .find_packages_latest_versions (options ):
266
- if dist .parsed_version == version :
267
- uptodate .append (dist )
268
- self .output_package_listing (uptodate , options )
269
-
270
294
271
295
def tabulate (vals ):
272
296
# From pfmoore on GitHub:
@@ -291,31 +315,25 @@ def format_for_columns(pkgs, options):
291
315
Convert the package data into something usable
292
316
by output_package_listing_columns.
293
317
"""
294
- header = ["Package" , "Version" ]
295
- running_outdated = False
318
+ running_outdated = options .outdated
296
319
# Adjust the header for the `pip list --outdated` case.
297
- if isinstance (pkgs [0 ], (list , tuple )):
298
- running_outdated = True
320
+ if running_outdated :
299
321
header = ["Package" , "Version" , "Latest" , "Type" ]
322
+ else :
323
+ header = ["Package" , "Version" ]
300
324
301
325
data = []
302
- if any (dist_is_editable (x [0 ])
303
- if running_outdated
304
- else dist_is_editable (x )
305
- for x in pkgs ):
326
+ if any (dist_is_editable (x ) for x in pkgs ):
306
327
header .append ("Location" )
307
328
308
329
for proj in pkgs :
309
330
# if we're working on the 'outdated' list, separate out the
310
331
# latest_version and type
311
- if running_outdated :
312
- proj , latest_version , typ = proj
313
-
314
332
row = [proj .project_name , proj .version ]
315
333
316
334
if running_outdated :
317
- row .append (latest_version )
318
- row .append (typ )
335
+ row .append (proj . latest_version )
336
+ row .append (proj . latest_filetype )
319
337
320
338
if dist_is_editable (proj ):
321
339
row .append (proj .location )
0 commit comments