@@ -183,7 +183,7 @@ def _iter_ireqs(
183183        unsafe_packages : set [str ],
184184        markers : dict [str , Marker ],
185185        hashes : dict [InstallRequirement , set [str ]] |  None  =  None ,
186-     ) ->  Iterator [str ,  dict [str , str ]]:
186+     ) ->  Iterator [str ]  |   Iterator [ dict [str , str   |   list [ str ] ]]:
187187        # default values 
188188        unsafe_packages  =  unsafe_packages  if  self .allow_unsafe  else  set ()
189189        hashes  =  hashes  or  {}
@@ -194,12 +194,13 @@ def _iter_ireqs(
194194        has_hashes  =  hashes  and  any (hash  for  hash  in  hashes .values ())
195195
196196        yielded  =  False 
197-         for  line  in  self .write_header ():
198-             yield  line , {}
199-             yielded  =  True 
200-         for  line  in  self .write_flags ():
201-             yield  line , {}
202-             yielded  =  True 
197+         if  not  self .json_output :
198+             for  line  in  self .write_header ():
199+                 yield  line 
200+                 yielded  =  True 
201+             for  line  in  self .write_flags ():
202+                 yield  line 
203+                 yielded  =  True 
203204
204205        unsafe_requirements  =  unsafe_requirements  or  {
205206            r  for  r  in  results  if  r .name  in  unsafe_packages 
@@ -208,37 +209,39 @@ def _iter_ireqs(
208209
209210        if  packages :
210211            for  ireq  in  sorted (packages , key = self ._sort_key ):
211-                 if  has_hashes  and  not  hashes .get (ireq ):
212-                     yield  MESSAGE_UNHASHED_PACKAGE , {} 
212+                 if  has_hashes  and  not  hashes .get (ireq )  and   not   self . json_output :
213+                     yield  MESSAGE_UNHASHED_PACKAGE 
213214                    warn_uninstallable  =  True 
214-                 line ,  json  =  self ._format_requirement (
215+                 formatted_req  =  self ._format_requirement (
215216                    ireq , markers .get (key_from_ireq (ireq )), hashes = hashes 
216217                )
217-                 yield  line ,  json 
218+                 yield  formatted_req 
218219            yielded  =  True 
219220
220221        if  unsafe_requirements :
221-             yield  "" , {}
222+ 
223+             if  not  self .json_output :
224+                 yield  "" 
222225            yielded  =  True 
223-             if  has_hashes  and  not  self .allow_unsafe :
224-                 yield  MESSAGE_UNSAFE_PACKAGES_UNPINNED , {} 
226+             if  has_hashes  and  not  self .allow_unsafe   and   not   self . json_output :
227+                 yield  MESSAGE_UNSAFE_PACKAGES_UNPINNED 
225228                warn_uninstallable  =  True 
226-             else :
227-                 yield  MESSAGE_UNSAFE_PACKAGES , {} 
229+             elif   not   self . json_output :
230+                 yield  MESSAGE_UNSAFE_PACKAGES 
228231
229232            for  ireq  in  sorted (unsafe_requirements , key = self ._sort_key ):
230233                ireq_key  =  key_from_ireq (ireq )
231-                 if  not  self .allow_unsafe :
232-                     yield  comment (f"# { ireq_key }  ), {} 
234+                 if  not  self .allow_unsafe   and   not   self . json_output :
235+                     yield  comment (f"# { ireq_key }  )
233236                else :
234-                     line ,  json  =  self ._format_requirement (
237+                     formatted_req  =  self ._format_requirement (
235238                        ireq , marker = markers .get (ireq_key ), hashes = hashes 
236239                    )
237-                     yield  line ,  json 
240+                     yield  formatted_req 
238241
239242        # Yield even when there's no real content, so that blank files are written 
240243        if  not  yielded :
241-             yield  "" , {} 
244+             yield  "" 
242245
243246        if  warn_uninstallable :
244247            log .warning (MESSAGE_UNINSTALLABLE )
@@ -252,40 +255,47 @@ def write(
252255        hashes : dict [InstallRequirement , set [str ]] |  None ,
253256    ) ->  None :
254257        output_structure  =  []
255-         if  not  self .dry_run   or   self . json_output :
258+         if  not  self .dry_run :
256259            dst_file  =  io .TextIOWrapper (
257260                self .dst_file ,
258261                encoding = "utf8" ,
259262                newline = self .linesep ,
260263                line_buffering = True ,
261264            )
262265        try :
263-             for  line ,  ireq  in  self ._iter_ireqs (
266+             for  formatted_req  in  self ._iter_ireqs (
264267                results , unsafe_requirements , unsafe_packages , markers , hashes 
265268            ):
266-                 if  self .dry_run :
269+                 if  self .dry_run   and   not   self . json_output :
267270                    # Bypass the log level to always print this during a dry run 
268-                     log .log (line )
271+                     assert  isinstance (formatted_req , str )
272+                     log .log (formatted_req )
269273                else :
270274                    if  not  self .json_output :
271-                         log .info (line )
272-                     dst_file .write (unstyle (line ))
273-                     dst_file .write ("\n " )
274-                 if  self .json_output  and  ireq :
275-                     output_structure .append (ireq )
275+                         assert  isinstance (formatted_req , str )
276+                         log .info (formatted_req )
277+                         dst_file .write (unstyle (formatted_req ))
278+                         dst_file .write ("\n " )
279+                     else :
280+                         output_structure .append (formatted_req )
276281        finally :
277-             if  not  self .dry_run  or  self .json_output :
278-                 dst_file .detach ()
279282            if  self .json_output :
283+                 json .dump (output_structure , dst_file , indent = 4 )
280284                print (json .dumps (output_structure , indent = 4 ))
285+             if  not  self .dry_run :
286+                 dst_file .detach ()
281287
282288    def  _format_requirement (
283289        self ,
284290        ireq : InstallRequirement ,
285291        marker : Marker  |  None  =  None ,
286292        hashes : dict [InstallRequirement , set [str ]] |  None  =  None ,
287293        unsafe : bool  =  False ,
288-     ) ->  tuple [str , dict [str , str  |  list [str ]]]:
294+     ) ->  str  |  dict [str , str  |  list [str ]]:
295+         """Format a given ``InstallRequirement``. 
296+ 
297+         :returns: A line or a JSON structure to be written to the output file. 
298+         """ 
289299        ireq_hashes  =  (hashes  if  hashes  is  not None  else  {}).get (ireq )
290300
291301        line  =  format_requirement (ireq , marker = marker , hashes = ireq_hashes )
@@ -326,36 +336,40 @@ def _format_requirement(
326336            if  self .annotate :
327337                line  =  "\n " .join (ln .rstrip () for  ln  in  lines )
328338
329-         hashable  =  True 
330-         if  ireq .link :
331-             if  ireq .link .is_vcs  or  (ireq .link .is_file  and  ireq .link .is_existing_dir ()):
332-                 hashable  =  False 
333-         output_marker  =  "" 
334-         if  marker :
335-             output_marker  =  str (marker )
336-         via  =  []
337-         for  parent_req  in  required_by :
338-             if  parent_req .startswith ("-r " ):
339-                 # Ensure paths to requirements files given are absolute 
340-                 reqs_in_path  =  os .path .abspath (parent_req [len ("-r " ) :])
341-                 via .append (f"-r { reqs_in_path }  )
342-             else :
343-                 via .append (parent_req )
344-         output_hashes  =  []
345-         if  ireq_hashes :
346-             output_hashes  =  list (ireq_hashes )
347- 
348-         ireq_json  =  {
349-             "name" : ireq .name ,
350-             "version" : str (ireq .specifier ).lstrip ("==" ),
351-             "requirement" : str (ireq .req ),
352-             "via" : via ,
353-             "line" : unstyle (line ),
354-             "hashable" : hashable ,
355-             "editable" : ireq .editable ,
356-             "hashes" : output_hashes ,
357-             "marker" : output_marker ,
358-             "unsafe" : unsafe ,
359-         }
339+         if  self .json_output :
340+             hashable  =  True 
341+             if  ireq .link :
342+                 if  ireq .link .is_vcs  or  (
343+                     ireq .link .is_file  and  ireq .link .is_existing_dir ()
344+                 ):
345+                     hashable  =  False 
346+             output_marker  =  "" 
347+             if  marker :
348+                 output_marker  =  str (marker )
349+             via  =  []
350+             for  parent_req  in  required_by :
351+                 if  parent_req .startswith ("-r " ):
352+                     # Ensure paths to requirements files given are absolute 
353+                     reqs_in_path  =  os .path .abspath (parent_req [len ("-r " ) :])
354+                     via .append (f"-r { reqs_in_path }  )
355+                 else :
356+                     via .append (parent_req )
357+             output_hashes  =  []
358+             if  ireq_hashes :
359+                 output_hashes  =  list (ireq_hashes )
360+ 
361+             ireq_json  =  {
362+                 "name" : ireq .name ,
363+                 "version" : str (ireq .specifier ).lstrip ("==" ),
364+                 "requirement" : str (ireq .req ),
365+                 "via" : via ,
366+                 "line" : unstyle (line ),
367+                 "hashable" : hashable ,
368+                 "editable" : ireq .editable ,
369+                 "hashes" : output_hashes ,
370+                 "marker" : output_marker ,
371+                 "unsafe" : unsafe ,
372+             }
373+             return  ireq_json 
360374
361-         return  line ,  ireq_json 
375+         return  line 
0 commit comments