1515import functools
1616import keyword
1717import os
18- import re
1918import subprocess
2019import sys
2120import tokenize
2423
2524import libcst as cst
2625
26+ from .base import Options
2727from .runner import Flake8TrioRunner , Flake8TrioRunner_cst
28- from .visitors import default_disabled_error_codes
28+ from .visitors import ERROR_CLASSES , ERROR_CLASSES_CST , default_disabled_error_codes
2929
3030if TYPE_CHECKING :
3131 from collections .abc import Iterable , Sequence
@@ -76,12 +76,6 @@ def cst_parse_module_native(source: str) -> cst.Module:
7676
7777def main () -> int :
7878 parser = ArgumentParser (prog = "flake8_trio" )
79- parser .add_argument (
80- nargs = "*" ,
81- metavar = "file" ,
82- dest = "files" ,
83- help = "Files(s) to format, instead of autodetection." ,
84- )
8579 Plugin .add_options (parser )
8680 args = parser .parse_args ()
8781 Plugin .parse_options (args )
@@ -115,7 +109,7 @@ def main() -> int:
115109 for error in sorted (plugin .run ()):
116110 print (f"{ file } :{ error } " )
117111 any_error = True
118- if plugin .options .autofix :
112+ if plugin .options .autofix_codes :
119113 with open (file , "w" ) as file :
120114 file .write (plugin .module .code )
121115 return 1 if any_error else 0
@@ -124,7 +118,13 @@ def main() -> int:
124118class Plugin :
125119 name = __name__
126120 version = __version__
127- options : Namespace = Namespace ()
121+ standalone = True
122+ _options : Options | None = None
123+
124+ @property
125+ def options (self ) -> Options :
126+ assert self ._options is not None
127+ return self ._options
128128
129129 def __init__ (self , tree : ast .AST , lines : Sequence [str ]):
130130 super ().__init__ ()
@@ -158,18 +158,64 @@ def run(self) -> Iterable[Error]:
158158 @staticmethod
159159 def add_options (option_manager : OptionManager | ArgumentParser ):
160160 if isinstance (option_manager , ArgumentParser ):
161- # TODO: disable TRIO9xx calls by default
162- # if run as standalone
161+ Plugin .standalone = True
163162 add_argument = option_manager .add_argument
163+ add_argument (
164+ nargs = "*" ,
165+ metavar = "file" ,
166+ dest = "files" ,
167+ help = "Files(s) to format, instead of autodetection." ,
168+ )
164169 else : # if run as a flake8 plugin
170+ Plugin .standalone = False
165171 # Disable TRIO9xx calls by default
166172 option_manager .extend_default_ignore (default_disabled_error_codes )
167173 # add parameter to parse from flake8 config
168174 add_argument = functools .partial ( # type: ignore
169175 option_manager .add_option , parse_from_config = True
170176 )
171- add_argument ("--autofix" , action = "store_true" , required = False )
172177
178+ add_argument (
179+ "--enable" ,
180+ type = comma_separated_list ,
181+ default = "TRIO" ,
182+ required = False ,
183+ help = (
184+ "Comma-separated list of error codes to enable, similar to flake8"
185+ " --select but is additionally more performant as it will disable"
186+ " non-enabled visitors from running instead of just silencing their"
187+ " errors."
188+ ),
189+ )
190+ add_argument (
191+ "--disable" ,
192+ type = comma_separated_list ,
193+ default = "TRIO9" if Plugin .standalone else "" ,
194+ required = False ,
195+ help = (
196+ "Comma-separated list of error codes to disable, similar to flake8"
197+ " --ignore but is additionally more performant as it will disable"
198+ " non-enabled visitors from running instead of just silencing their"
199+ " errors."
200+ ),
201+ )
202+ add_argument (
203+ "--autofix" ,
204+ type = comma_separated_list ,
205+ default = "" ,
206+ required = False ,
207+ help = (
208+ "Comma-separated list of error-codes to enable autofixing for"
209+ "if implemented. Requires running as a standalone program."
210+ ),
211+ )
212+ add_argument (
213+ "--error-on-autofix" ,
214+ action = "store_true" ,
215+ required = False ,
216+ default = False ,
217+ help = "Whether to also print an error message for autofixed errors" ,
218+ )
173219 add_argument (
174220 "--no-checkpoint-warning-decorators" ,
175221 default = "asynccontextmanager" ,
@@ -208,19 +254,6 @@ def add_options(option_manager: OptionManager | ArgumentParser):
208254 "suggesting it be replaced with {value}"
209255 ),
210256 )
211- add_argument (
212- "--enable-visitor-codes-regex" ,
213- type = re .compile , # type: ignore[arg-type]
214- default = ".*" ,
215- required = False ,
216- help = (
217- "Regex string of visitors to enable. Can be used to disable broken "
218- "visitors, or instead of --select/--disable to select error codes "
219- "in a way that is more performant. If a visitor raises multiple codes "
220- "it will not be disabled unless all codes are disabled, but it will "
221- "not report codes matching this regex."
222- ),
223- )
224257 add_argument (
225258 "--anyio" ,
226259 # action=store_true + parse_from_config does seem to work here, despite
@@ -237,7 +270,45 @@ def add_options(option_manager: OptionManager | ArgumentParser):
237270
238271 @staticmethod
239272 def parse_options (options : Namespace ):
240- Plugin .options = options
273+ def get_matching_codes (
274+ patterns : Iterable [str ], codes : Iterable [str ]
275+ ) -> Iterable [str ]:
276+ for pattern in patterns :
277+ for code in codes :
278+ if code .lower ().startswith (pattern .lower ()):
279+ yield code
280+
281+ all_codes : set [str ] = {
282+ err_code
283+ for err_class in (* ERROR_CLASSES , * ERROR_CLASSES_CST )
284+ for err_code in err_class .error_codes .keys () # type: ignore[attr-defined]
285+ if len (err_code ) == 7 # exclude e.g. TRIO103_anyio_trio
286+ }
287+
288+ if options .autofix and not Plugin .standalone :
289+ print ("Cannot autofix when run as a flake8 plugin." , file = sys .stderr )
290+ sys .exit (1 )
291+ autofix_codes = set (get_matching_codes (options .autofix , all_codes ))
292+
293+ # enable codes
294+ enabled_codes = set (get_matching_codes (options .enable , all_codes ))
295+
296+ # disable codes
297+ enabled_codes -= set (get_matching_codes (options .disable , enabled_codes ))
298+
299+ # if disable has default value, re-enable explicitly enabled codes
300+ if options .disable == ["TRIO9" ]:
301+ enabled_codes .update (code for code in options .enable if len (code ) == 7 )
302+
303+ Plugin ._options = Options (
304+ enabled_codes = enabled_codes ,
305+ autofix_codes = autofix_codes ,
306+ error_on_autofix = options .error_on_autofix ,
307+ no_checkpoint_warning_decorators = options .no_checkpoint_warning_decorators ,
308+ startable_in_context_manager = options .startable_in_context_manager ,
309+ trio200_blocking_calls = options .trio200_blocking_calls ,
310+ anyio = options .anyio ,
311+ )
241312
242313
243314def comma_separated_list (raw_value : str ) -> list [str ]:
0 commit comments