1+ import csv
2+ import json
13import pathlib
4+ import sys
25
36import click
47import rich
58from packaging .version import Version
69from rich .table import Table
710
8- from fromager import context
11+ from fromager import clickext , context
912from fromager .packagesettings import PatchMap
1013
1114
1619 default = False ,
1720 help = "Show more details about the overrides." ,
1821)
22+ @click .option (
23+ "--format" ,
24+ "output_format" ,
25+ type = click .Choice (["table" , "csv" , "json" ], case_sensitive = False ),
26+ default = "table" ,
27+ help = "Output format for detailed view (requires --details, default: table)" ,
28+ )
29+ @click .option (
30+ "-o" ,
31+ "--output" ,
32+ type = clickext .ClickPath (),
33+ help = "Output file to create (requires --details, default: stdout)" ,
34+ )
1935@click .pass_obj
2036def list_overrides (
2137 wkctx : context .WorkContext ,
2238 details : bool ,
39+ output_format : str ,
40+ output : pathlib .Path | None ,
2341) -> None :
2442 """List all of the packages with overrides in the current configuration."""
43+ # Warn if format/output options are used without --details
44+ if not details :
45+ if output_format != "table" :
46+ click .echo (
47+ "Warning: --format option is ignored when --details is not used" ,
48+ err = True ,
49+ )
50+ if output is not None :
51+ click .echo (
52+ "Warning: --output option is ignored when --details is not used" ,
53+ err = True ,
54+ )
55+
2556 overridden_packages = sorted (wkctx .settings .list_overrides ())
2657 if not details :
2758 for name in overridden_packages :
2859 print (name )
2960 return
3061
31- table = Table (title = "Package Overrides" )
32- table .add_column ("Package" , justify = "left" , no_wrap = True )
33- table .add_column ("Version" , justify = "left" , no_wrap = True )
34- table .add_column ("Patches" , justify = "left" , no_wrap = True )
35-
62+ # Collect data for export
3663 variants = sorted (wkctx .settings .all_variants ())
37- for v in variants :
38- table .add_column (v , justify = "left" , no_wrap = True )
39-
40- table .add_column ("Plugin" , justify = "left" )
64+ variant_names = [str (v ) for v in variants ]
65+ export_data = []
4166
4267 for name in overridden_packages :
4368 pbi = wkctx .settings .package_build_info (name )
@@ -68,16 +93,16 @@ def list_overrides(
6893 plugin_hooks .append (hook )
6994 plugin_hooks_str = ", " .join (plugin_hooks )
7095
71- variant_info : list [str ] = []
96+ variant_info : dict [str , str ] = {}
7297 for v in variants :
7398 v_info = ps .variants .get (v )
7499 if v_info :
75100 if v_info .pre_built :
76- variant_info . append ( "pre-built" )
101+ variant_info [ str ( v )] = "pre-built"
77102 else :
78- variant_info . append ( "yes" )
103+ variant_info [ str ( v )] = "yes"
79104 else :
80- variant_info . append ( "" )
105+ variant_info [ str ( v )] = ""
81106
82107 all_patches : PatchMap = pbi .get_all_patches ()
83108 global_patches : list [pathlib .Path ] = all_patches .get (None , [])
@@ -87,38 +112,95 @@ def list_overrides(
87112 [v for v in all_patches .keys () if v is not None ]
88113 )
89114
90- row : list [str ] = []
91- patches_str : str = ""
92-
93115 if not all_pkg_versions :
94116 # This package has overrides, but none are version-specific.
95117 patches_str = str (num_global_patches ) if num_global_patches else ""
96- row = (
97- [
98- name ,
99- "" , # Version
100- patches_str ,
101- ]
102- + variant_info
103- + [plugin_hooks_str ]
104- )
105- table .add_row (* row )
118+ row_data = {
119+ "package" : name ,
120+ "version" : "" ,
121+ "patches" : patches_str ,
122+ "plugin_hooks" : plugin_hooks_str ,
123+ }
124+ # Add variant information
125+ row_data .update (variant_info )
126+ export_data .append (row_data )
106127 else :
107128 # This package has version-specific overrides.
108129 for version in all_pkg_versions :
109130 version_patches : list [pathlib .Path ] = all_patches .get (version , [])
110131 total_patches : int = num_global_patches + len (version_patches )
111132 patches_str = str (total_patches ) if total_patches else ""
112133
113- row = (
114- [
115- name ,
116- str (version ),
117- patches_str ,
118- ]
119- + variant_info
120- + [plugin_hooks_str ]
121- )
122- table .add_row (* row )
134+ row_data = {
135+ "package" : name ,
136+ "version" : str (version ),
137+ "patches" : patches_str ,
138+ "plugin_hooks" : plugin_hooks_str ,
139+ }
140+ # Add variant information
141+ row_data .update (variant_info )
142+ export_data .append (row_data )
143+
144+ # Handle different output formats
145+ match output_format :
146+ case "json" :
147+ _export_json (export_data , output )
148+ case "csv" :
149+ _export_csv (export_data , variant_names , output )
150+ case "table" :
151+ _export_table (export_data , variant_names )
152+ case _:
153+ raise ValueError (f"Invalid output format: { output_format } " )
154+
155+
156+ def _export_json (data : list [dict ], output : pathlib .Path | None ) -> None :
157+ """Export data as JSON."""
158+ if output :
159+ with open (output , "w" ) as outfile :
160+ json .dump (data , outfile , indent = 2 )
161+ else :
162+ json .dump (data , sys .stdout , indent = 2 )
163+
164+
165+ def _export_csv (
166+ data : list [dict ], variants : list [str ], output : pathlib .Path | None
167+ ) -> None :
168+ """Export data as CSV."""
169+ # Define field names in the order we want them
170+ fieldnames = ["package" , "version" , "patches" ] + variants + ["plugin_hooks" ]
171+
172+ if output :
173+ with open (output , "w" , newline = "" ) as outfile :
174+ writer = csv .DictWriter (
175+ outfile , fieldnames = fieldnames , quoting = csv .QUOTE_NONNUMERIC
176+ )
177+ writer .writeheader ()
178+ writer .writerows (data )
179+ else :
180+ writer = csv .DictWriter (
181+ sys .stdout , fieldnames = fieldnames , quoting = csv .QUOTE_NONNUMERIC
182+ )
183+ writer .writeheader ()
184+ writer .writerows (data )
185+
186+
187+ def _export_table (data : list [dict ], variants : list [str ]) -> None :
188+ """Export data as Rich table (original behavior)."""
189+ table = Table (title = "Package Overrides" )
190+ table .add_column ("Package" , justify = "left" , no_wrap = True )
191+ table .add_column ("Version" , justify = "left" , no_wrap = True )
192+ table .add_column ("Patches" , justify = "left" , no_wrap = True )
193+
194+ for v in variants :
195+ table .add_column (v , justify = "left" , no_wrap = True )
196+
197+ table .add_column ("Plugin" , justify = "left" )
198+
199+ # Define column keys in the same order as CSV exporter
200+ column_keys = ["package" , "version" , "patches" ] + variants + ["plugin_hooks" ]
201+
202+ for row_data in data :
203+ row = [row_data .get (key , "" ) for key in column_keys ]
204+ table .add_row (* row )
123205
124206 rich .get_console ().print (table )
0 commit comments