@@ -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