@@ -201,7 +201,7 @@ def _iter_ireqs(
201201 unsafe_packages : set [str ],
202202 markers : dict [str , Marker ],
203203 hashes : dict [InstallRequirement , set [str ]] | None = None ,
204- ) -> Iterator [str , dict [str , str ]]:
204+ ) -> Iterator [str ] | Iterator [ dict [str , str | list [ str ] ]]:
205205 # default values
206206 unsafe_packages = unsafe_packages if self .allow_unsafe else set ()
207207 hashes = hashes or {}
@@ -212,12 +212,13 @@ def _iter_ireqs(
212212 has_hashes = hashes and any (hash for hash in hashes .values ())
213213
214214 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
221222
222223 unsafe_requirements = unsafe_requirements or {
223224 r for r in results if r .name in unsafe_packages
@@ -226,37 +227,39 @@ def _iter_ireqs(
226227
227228 if packages :
228229 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
231232 warn_uninstallable = True
232- line , json = self ._format_requirement (
233+ formatted_req = self ._format_requirement (
233234 ireq , markers .get (key_from_ireq (ireq )), hashes = hashes
234235 )
235- yield line , json
236+ yield formatted_req
236237 yielded = True
237238
238239 if unsafe_requirements :
239- yield "" , {}
240+
241+ if not self .json_output :
242+ yield ""
240243 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
243246 warn_uninstallable = True
244- else :
245- yield MESSAGE_UNSAFE_PACKAGES , {}
247+ elif not self . json_output :
248+ yield MESSAGE_UNSAFE_PACKAGES
246249
247250 for ireq in sorted (unsafe_requirements , key = self ._sort_key ):
248251 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 } " )
251254 else :
252- line , json = self ._format_requirement (
255+ formatted_req = self ._format_requirement (
253256 ireq , marker = markers .get (ireq_key ), hashes = hashes
254257 )
255- yield line , json
258+ yield formatted_req
256259
257260 # Yield even when there's no real content, so that blank files are written
258261 if not yielded :
259- yield "" , {}
262+ yield ""
260263
261264 if warn_uninstallable :
262265 log .warning (MESSAGE_UNINSTALLABLE )
@@ -270,40 +273,47 @@ def write(
270273 hashes : dict [InstallRequirement , set [str ]] | None ,
271274 ) -> None :
272275 output_structure = []
273- if not self .dry_run or self . json_output :
276+ if not self .dry_run :
274277 dst_file = io .TextIOWrapper (
275278 self .dst_file ,
276279 encoding = "utf8" ,
277280 newline = self .linesep ,
278281 line_buffering = True ,
279282 )
280283 try :
281- for line , ireq in self ._iter_ireqs (
284+ for formatted_req in self ._iter_ireqs (
282285 results , unsafe_requirements , unsafe_packages , markers , hashes
283286 ):
284- if self .dry_run :
287+ if self .dry_run and not self . json_output :
285288 # 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 )
287291 else :
288292 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 )
294299 finally :
295- if not self .dry_run or self .json_output :
296- dst_file .detach ()
297300 if self .json_output :
301+ json .dump (output_structure , dst_file , indent = 4 )
298302 print (json .dumps (output_structure , indent = 4 ))
303+ if not self .dry_run :
304+ dst_file .detach ()
299305
300306 def _format_requirement (
301307 self ,
302308 ireq : InstallRequirement ,
303309 marker : Marker | None = None ,
304310 hashes : dict [InstallRequirement , set [str ]] | None = None ,
305311 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+ """
307317 ireq_hashes = (hashes if hashes is not None else {}).get (ireq )
308318
309319 line = format_requirement (ireq , marker = marker , hashes = ireq_hashes )
@@ -344,36 +354,40 @@ def _format_requirement(
344354 if self .annotate :
345355 line = "\n " .join (ln .rstrip () for ln in lines )
346356
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
378392
379- return line , ireq_json
393+ return line
0 commit comments