11import argparse
2+ import enum
23import multiprocessing
34import os
45import sys
910from sphinxlint .sphinxlint import CheckersOptions
1011
1112
13+ class SortField (enum .Enum ):
14+ """Fields available for sorting error reports"""
15+
16+ FILENAME = 0
17+ LINE = 1
18+ ERROR_TYPE = 2
19+
20+ @staticmethod
21+ def as_supported_options ():
22+ return "," .join (field .name .lower () for field in SortField )
23+
24+
1225def parse_args (argv = None ):
1326 """Parse command line argument."""
1427 if argv is None :
@@ -33,6 +46,18 @@ def __call__(self, parser, namespace, values, option_string=None):
3346 else :
3447 enabled_checkers_names .difference_update (values .split ("," ))
3548
49+ class StoreSortFieldAction (argparse .Action ):
50+ def __call__ (self , parser , namespace , values , option_string = None ):
51+ sort_fields = []
52+ for field_name in values .split ("," ):
53+ try :
54+ sort_fields .append (SortField [field_name .upper ()])
55+ except KeyError :
56+ raise ValueError (
57+ f"Unsupported sort field: { field_name } , supported values are { SortField .as_supported_options ()} "
58+ ) from None
59+ setattr (namespace , self .dest , sort_fields )
60+
3661 parser .add_argument (
3762 "-v" ,
3863 "--verbose" ,
@@ -77,6 +102,14 @@ def __call__(self, parser, namespace, values, option_string=None):
77102 default = 80 ,
78103 type = int ,
79104 )
105+ parser .add_argument (
106+ "-s" ,
107+ "--sort-by" ,
108+ action = StoreSortFieldAction ,
109+ help = "comma-separated list of fields used to sort errors by. Available "
110+ f"fields are: { SortField .as_supported_options ()} " ,
111+ )
112+
80113 parser .add_argument ("paths" , default = "." , nargs = "*" )
81114 args = parser .parse_args (argv [1 :])
82115 try :
@@ -116,13 +149,31 @@ def _check_file(todo):
116149 return check_file (* todo )
117150
118151
119- def print_results (results ):
120- """Print results (or a message if nothing is to be printed)."""
152+ def sort_errors (results , sorted_by ):
153+ """Flattens and potentially sorts errors based on user prefernces"""
154+ if not sorted_by :
155+ for results in results :
156+ yield from results
157+ return
158+ errors = list (error for errors in results for error in errors )
159+ # sorting is stable in python, so we can sort in reverse order to get the
160+ # ordering specified by the user
161+ for sort_field in reversed (sorted_by ):
162+ if sort_field == SortField .ERROR_TYPE :
163+ errors .sort (key = lambda error : error .checker_name )
164+ elif sort_field == SortField .FILENAME :
165+ errors .sort (key = lambda error : error .filename )
166+ elif sort_field == SortField .LINE :
167+ errors .sort (key = lambda error : error .line_no )
168+ yield from errors
169+
170+
171+ def print_errors (errors ):
172+ """Print errors (or a message if nothing is to be printed)."""
121173 qty = 0
122- for result in results :
123- for error in result :
124- print (error )
125- qty += 1
174+ for error in errors :
175+ print (error )
176+ qty += 1
126177 if qty == 0 :
127178 print ("No problems found." )
128179 return qty
@@ -156,10 +207,12 @@ def main(argv=None):
156207 ]
157208
158209 if len (todo ) < 8 :
159- count = print_results ( starmap (check_file , todo ))
210+ count = print_errors ( sort_errors ( starmap (check_file , todo ), args . sort_by ))
160211 else :
161212 with multiprocessing .Pool () as pool :
162- count = print_results (pool .imap_unordered (_check_file , todo ))
213+ count = print_errors (
214+ sort_errors (pool .imap_unordered (_check_file , todo ), args .sort_by )
215+ )
163216 pool .close ()
164217 pool .join ()
165218
0 commit comments