15
15
import functools
16
16
import keyword
17
17
import os
18
- import re
19
18
import subprocess
20
19
import sys
21
20
import tokenize
24
23
25
24
import libcst as cst
26
25
26
+ from .base import Options
27
27
from .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
29
29
30
30
if TYPE_CHECKING :
31
31
from collections .abc import Iterable , Sequence
@@ -76,12 +76,6 @@ def cst_parse_module_native(source: str) -> cst.Module:
76
76
77
77
def main () -> int :
78
78
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
- )
85
79
Plugin .add_options (parser )
86
80
args = parser .parse_args ()
87
81
Plugin .parse_options (args )
@@ -124,7 +118,13 @@ def main() -> int:
124
118
class Plugin :
125
119
name = __name__
126
120
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
128
128
129
129
def __init__ (self , tree : ast .AST , lines : Sequence [str ]):
130
130
super ().__init__ ()
@@ -158,18 +158,64 @@ def run(self) -> Iterable[Error]:
158
158
@staticmethod
159
159
def add_options (option_manager : OptionManager | ArgumentParser ):
160
160
if isinstance (option_manager , ArgumentParser ):
161
- # TODO: disable TRIO9xx calls by default
162
- # if run as standalone
161
+ Plugin .standalone = True
163
162
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
+ )
164
169
else : # if run as a flake8 plugin
170
+ Plugin .standalone = False
165
171
# Disable TRIO9xx calls by default
166
172
option_manager .extend_default_ignore (default_disabled_error_codes )
167
173
# add parameter to parse from flake8 config
168
174
add_argument = functools .partial ( # type: ignore
169
175
option_manager .add_option , parse_from_config = True
170
176
)
171
- add_argument ("--autofix" , action = "store_true" , required = False )
172
177
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
+ )
173
219
add_argument (
174
220
"--no-checkpoint-warning-decorators" ,
175
221
default = "asynccontextmanager" ,
@@ -208,19 +254,6 @@ def add_options(option_manager: OptionManager | ArgumentParser):
208
254
"suggesting it be replaced with {value}"
209
255
),
210
256
)
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
- )
224
257
add_argument (
225
258
"--anyio" ,
226
259
# action=store_true + parse_from_config does seem to work here, despite
@@ -237,7 +270,56 @@ def add_options(option_manager: OptionManager | ArgumentParser):
237
270
238
271
@staticmethod
239
272
def parse_options (options : Namespace ):
240
- Plugin .options = options
273
+ def get_matching_codes (
274
+ patterns : Iterable [str ], codes : Iterable [str ], msg : 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
+ autofix_codes : set [str ] = set ()
282
+ enabled_codes : set [str ] = set ()
283
+ disabled_codes : set [str ] = {
284
+ err_code
285
+ for err_class in (* ERROR_CLASSES , * ERROR_CLASSES_CST )
286
+ for err_code in err_class .error_codes .keys () # type: ignore[attr-defined]
287
+ if len (err_code ) == 7 # exclude e.g. TRIO103_anyio_trio
288
+ }
289
+
290
+ if options .autofix :
291
+ if not Plugin .standalone :
292
+ print ("Cannot autofix when run as a flake8 plugin." , file = sys .stderr )
293
+ sys .exit (1 )
294
+ autofix_codes .update (
295
+ get_matching_codes (options .autofix , disabled_codes , "autofix" )
296
+ )
297
+
298
+ # enable codes
299
+ tmp = set (get_matching_codes (options .enable , disabled_codes , "enable" ))
300
+ enabled_codes |= tmp
301
+ disabled_codes -= tmp
302
+
303
+ # disable codes
304
+ tmp = set (get_matching_codes (options .disable , enabled_codes , "disable" ))
305
+
306
+ # if disable has default value, don't disable explicitly enabled codes
307
+ if options .disable == ["TRIO9" ]:
308
+ tmp -= {code for code in options .enable if len (code ) == 7 }
309
+
310
+ disabled_codes |= tmp
311
+ enabled_codes -= tmp
312
+
313
+ Plugin ._options = Options (
314
+ enable = enabled_codes ,
315
+ disable = disabled_codes ,
316
+ autofix = autofix_codes ,
317
+ error_on_autofix = options .error_on_autofix ,
318
+ no_checkpoint_warning_decorators = options .no_checkpoint_warning_decorators ,
319
+ startable_in_context_manager = options .startable_in_context_manager ,
320
+ trio200_blocking_calls = options .trio200_blocking_calls ,
321
+ anyio = options .anyio ,
322
+ )
241
323
242
324
243
325
def comma_separated_list (raw_value : str ) -> list [str ]:
0 commit comments