@@ -201,7 +201,7 @@ def _iter_ireqs(
201
201
unsafe_packages : set [str ],
202
202
markers : dict [str , Marker ],
203
203
hashes : dict [InstallRequirement , set [str ]] | None = None ,
204
- ) -> Iterator [str , dict [str , str ]]:
204
+ ) -> Iterator [str ] | Iterator [ dict [str , str | list [ str ] ]]:
205
205
# default values
206
206
unsafe_packages = unsafe_packages if self .allow_unsafe else set ()
207
207
hashes = hashes or {}
@@ -212,12 +212,13 @@ def _iter_ireqs(
212
212
has_hashes = hashes and any (hash for hash in hashes .values ())
213
213
214
214
yielded = False
215
- for line in self .write_header ():
216
- yield line , {}
217
- yielded = True
218
- for line in self .write_flags ():
219
- yield line , {}
220
- yielded = True
215
+ if not self .json_output :
216
+ for line in self .write_header ():
217
+ yield line
218
+ yielded = True
219
+ for line in self .write_flags ():
220
+ yield line
221
+ yielded = True
221
222
222
223
unsafe_requirements = unsafe_requirements or {
223
224
r for r in results if r .name in unsafe_packages
@@ -226,37 +227,39 @@ def _iter_ireqs(
226
227
227
228
if packages :
228
229
for ireq in sorted (packages , key = self ._sort_key ):
229
- if has_hashes and not hashes .get (ireq ):
230
- yield MESSAGE_UNHASHED_PACKAGE , {}
230
+ if has_hashes and not hashes .get (ireq ) and not self . json_output :
231
+ yield MESSAGE_UNHASHED_PACKAGE
231
232
warn_uninstallable = True
232
- line , json = self ._format_requirement (
233
+ formatted_req = self ._format_requirement (
233
234
ireq , markers .get (key_from_ireq (ireq )), hashes = hashes
234
235
)
235
- yield line , json
236
+ yield formatted_req
236
237
yielded = True
237
238
238
239
if unsafe_requirements :
239
- yield "" , {}
240
+
241
+ if not self .json_output :
242
+ yield ""
240
243
yielded = True
241
- if has_hashes and not self .allow_unsafe :
242
- yield MESSAGE_UNSAFE_PACKAGES_UNPINNED , {}
244
+ if has_hashes and not self .allow_unsafe and not self . json_output :
245
+ yield MESSAGE_UNSAFE_PACKAGES_UNPINNED
243
246
warn_uninstallable = True
244
- else :
245
- yield MESSAGE_UNSAFE_PACKAGES , {}
247
+ elif not self . json_output :
248
+ yield MESSAGE_UNSAFE_PACKAGES
246
249
247
250
for ireq in sorted (unsafe_requirements , key = self ._sort_key ):
248
251
ireq_key = key_from_ireq (ireq )
249
- if not self .allow_unsafe :
250
- yield comment (f"# { ireq_key } " ), {}
252
+ if not self .allow_unsafe and not self . json_output :
253
+ yield comment (f"# { ireq_key } " )
251
254
else :
252
- line , json = self ._format_requirement (
255
+ formatted_req = self ._format_requirement (
253
256
ireq , marker = markers .get (ireq_key ), hashes = hashes
254
257
)
255
- yield line , json
258
+ yield formatted_req
256
259
257
260
# Yield even when there's no real content, so that blank files are written
258
261
if not yielded :
259
- yield "" , {}
262
+ yield ""
260
263
261
264
if warn_uninstallable :
262
265
log .warning (MESSAGE_UNINSTALLABLE )
@@ -270,40 +273,47 @@ def write(
270
273
hashes : dict [InstallRequirement , set [str ]] | None ,
271
274
) -> None :
272
275
output_structure = []
273
- if not self .dry_run or self . json_output :
276
+ if not self .dry_run :
274
277
dst_file = io .TextIOWrapper (
275
278
self .dst_file ,
276
279
encoding = "utf8" ,
277
280
newline = self .linesep ,
278
281
line_buffering = True ,
279
282
)
280
283
try :
281
- for line , ireq in self ._iter_ireqs (
284
+ for formatted_req in self ._iter_ireqs (
282
285
results , unsafe_requirements , unsafe_packages , markers , hashes
283
286
):
284
- if self .dry_run :
287
+ if self .dry_run and not self . json_output :
285
288
# Bypass the log level to always print this during a dry run
286
- log .log (line )
289
+ assert isinstance (formatted_req , str )
290
+ log .log (formatted_req )
287
291
else :
288
292
if not self .json_output :
289
- log .info (line )
290
- dst_file .write (unstyle (line ))
291
- dst_file .write ("\n " )
292
- if self .json_output and ireq :
293
- output_structure .append (ireq )
293
+ assert isinstance (formatted_req , str )
294
+ log .info (formatted_req )
295
+ dst_file .write (unstyle (formatted_req ))
296
+ dst_file .write ("\n " )
297
+ else :
298
+ output_structure .append (formatted_req )
294
299
finally :
295
- if not self .dry_run or self .json_output :
296
- dst_file .detach ()
297
300
if self .json_output :
301
+ json .dump (output_structure , dst_file , indent = 4 )
298
302
print (json .dumps (output_structure , indent = 4 ))
303
+ if not self .dry_run :
304
+ dst_file .detach ()
299
305
300
306
def _format_requirement (
301
307
self ,
302
308
ireq : InstallRequirement ,
303
309
marker : Marker | None = None ,
304
310
hashes : dict [InstallRequirement , set [str ]] | None = None ,
305
311
unsafe : bool = False ,
306
- ) -> tuple [str , dict [str , str | list [str ]]]:
312
+ ) -> str | dict [str , str | list [str ]]:
313
+ """Format a given ``InstallRequirement``.
314
+
315
+ :returns: A line or a JSON structure to be written to the output file.
316
+ """
307
317
ireq_hashes = (hashes if hashes is not None else {}).get (ireq )
308
318
309
319
line = format_requirement (ireq , marker = marker , hashes = ireq_hashes )
@@ -344,36 +354,40 @@ def _format_requirement(
344
354
if self .annotate :
345
355
line = "\n " .join (ln .rstrip () for ln in lines )
346
356
347
- hashable = True
348
- if ireq .link :
349
- if ireq .link .is_vcs or (ireq .link .is_file and ireq .link .is_existing_dir ()):
350
- hashable = False
351
- output_marker = ""
352
- if marker :
353
- output_marker = str (marker )
354
- via = []
355
- for parent_req in required_by :
356
- if parent_req .startswith ("-r " ):
357
- # Ensure paths to requirements files given are absolute
358
- reqs_in_path = os .path .abspath (parent_req [len ("-r " ) :])
359
- via .append (f"-r { reqs_in_path } " )
360
- else :
361
- via .append (parent_req )
362
- output_hashes = []
363
- if ireq_hashes :
364
- output_hashes = list (ireq_hashes )
365
-
366
- ireq_json = {
367
- "name" : ireq .name ,
368
- "version" : str (ireq .specifier ).lstrip ("==" ),
369
- "requirement" : str (ireq .req ),
370
- "via" : via ,
371
- "line" : unstyle (line ),
372
- "hashable" : hashable ,
373
- "editable" : ireq .editable ,
374
- "hashes" : output_hashes ,
375
- "marker" : output_marker ,
376
- "unsafe" : unsafe ,
377
- }
357
+ if self .json_output :
358
+ hashable = True
359
+ if ireq .link :
360
+ if ireq .link .is_vcs or (
361
+ ireq .link .is_file and ireq .link .is_existing_dir ()
362
+ ):
363
+ hashable = False
364
+ output_marker = ""
365
+ if marker :
366
+ output_marker = str (marker )
367
+ via = []
368
+ for parent_req in required_by :
369
+ if parent_req .startswith ("-r " ):
370
+ # Ensure paths to requirements files given are absolute
371
+ reqs_in_path = os .path .abspath (parent_req [len ("-r " ) :])
372
+ via .append (f"-r { reqs_in_path } " )
373
+ else :
374
+ via .append (parent_req )
375
+ output_hashes = []
376
+ if ireq_hashes :
377
+ output_hashes = list (ireq_hashes )
378
+
379
+ ireq_json = {
380
+ "name" : ireq .name ,
381
+ "version" : str (ireq .specifier ).lstrip ("==" ),
382
+ "requirement" : str (ireq .req ),
383
+ "via" : via ,
384
+ "line" : unstyle (line ),
385
+ "hashable" : hashable ,
386
+ "editable" : ireq .editable ,
387
+ "hashes" : output_hashes ,
388
+ "marker" : output_marker ,
389
+ "unsafe" : unsafe ,
390
+ }
391
+ return ireq_json
378
392
379
- return line , ireq_json
393
+ return line
0 commit comments