11
11
12
12
from __future__ import annotations
13
13
14
- import argparse
15
14
import ast
16
15
import keyword
17
16
import tokenize
18
- from argparse import Namespace
19
- from collections .abc import Iterable , Sequence
17
+ from argparse import ArgumentTypeError , Namespace
18
+ from collections .abc import Iterable
20
19
from fnmatch import fnmatch
21
20
from typing import Any , NamedTuple , Union , cast
22
21
@@ -1427,46 +1426,33 @@ def visit_Call(self, node: ast.Call):
1427
1426
self .error (node , key , blocking_calls [key ])
1428
1427
1429
1428
1430
- class ListOfIdentifiers (argparse .Action ):
1431
- def __call__ (
1432
- self ,
1433
- parser : argparse .ArgumentParser ,
1434
- namespace : argparse .Namespace ,
1435
- values : Sequence [str ] | None ,
1436
- option_string : str | None = None ,
1437
- ):
1438
- assert values is not None
1439
- assert option_string is not None
1440
- for value in values :
1441
- if keyword .iskeyword (value ) or not value .isidentifier ():
1442
- raise argparse .ArgumentError (
1443
- self , f"{ value !r} is not a valid method identifier"
1444
- )
1445
- setattr (namespace , self .dest , values )
1446
-
1447
-
1448
- class ParseDict (argparse .Action ):
1449
- def __call__ (
1450
- self ,
1451
- parser : argparse .ArgumentParser ,
1452
- namespace : argparse .Namespace ,
1453
- values : Sequence [str ] | None ,
1454
- option_string : str | None = None ,
1455
- ):
1456
- res : dict [str , str ] = {}
1457
- splitter = "->" # avoid ":" because it's part of .ini file syntax
1458
- assert values is not None
1459
- for value in values :
1460
- split_values = list (map (str .strip , value .split (splitter )))
1461
- if len (split_values ) != 2 :
1462
- raise argparse .ArgumentError (
1463
- self ,
1464
- f"Invalid number ({ len (split_values )- 1 } ) of splitter "
1465
- f"tokens { splitter !r} in { value !r} " ,
1466
- )
1467
- res [split_values [0 ]] = split_values [1 ]
1468
-
1469
- setattr (namespace , self .dest , res )
1429
+ # flake8 ignores type parameters if using comma_separated_list
1430
+ # so we need to reimplement that ourselves if we want to use "type"
1431
+ # to check values
1432
+ def parse_trio114_identifiers (raw_value : str ) -> list [str ]:
1433
+ values = [s .strip () for s in raw_value .split ("," ) if s .strip ()]
1434
+ for value in values :
1435
+ if keyword .iskeyword (value ) or not value .isidentifier ():
1436
+ raise ArgumentTypeError (f"{ value !r} is not a valid method identifier" )
1437
+ return values
1438
+
1439
+
1440
+ def parse_trio200_dict (raw_value : str ) -> dict [str , str ]:
1441
+ res : dict [str , str ] = {}
1442
+ splitter = "->" # avoid ":" because it's part of .ini file syntax
1443
+ values = [s .strip () for s in raw_value .split ("," ) if s .strip ()]
1444
+
1445
+ for value in values :
1446
+ split_values = list (map (str .strip , value .split (splitter )))
1447
+ if len (split_values ) != 2 :
1448
+ # argparse will eat this error message and spit out it's own
1449
+ # if we raise it as ValueError
1450
+ raise ArgumentTypeError (
1451
+ f"Invalid number ({ len (split_values )- 1 } ) of splitter "
1452
+ f"tokens { splitter !r} in { value !r} " ,
1453
+ )
1454
+ res [split_values [0 ]] = split_values [1 ]
1455
+ return res
1470
1456
1471
1457
1472
1458
class Plugin :
@@ -1484,15 +1470,6 @@ def from_filename(cls, filename: str) -> Plugin:
1484
1470
return cls (ast .parse (source ))
1485
1471
1486
1472
def run (self ) -> Iterable [Error ]:
1487
- # temporary workaround, since the Action does not seem to be called properly
1488
- # by flake8 when parsing from config
1489
- if isinstance (self .options .trio200_blocking_calls , list ):
1490
- ParseDict (["" ], dest = "trio200_blocking_calls" )(
1491
- None , # type: ignore
1492
- self .options ,
1493
- self .options .trio200_blocking_calls , # type: ignore
1494
- None ,
1495
- )
1496
1473
yield from Flake8TrioRunner .run (self ._tree , self .options )
1497
1474
1498
1475
@staticmethod
@@ -1513,11 +1490,10 @@ def add_options(option_manager: OptionManager):
1513
1490
)
1514
1491
option_manager .add_option (
1515
1492
"--startable-in-context-manager" ,
1493
+ type = parse_trio114_identifiers ,
1516
1494
default = "" ,
1517
1495
parse_from_config = True ,
1518
1496
required = False ,
1519
- comma_separated_list = True ,
1520
- action = ListOfIdentifiers ,
1521
1497
help = (
1522
1498
"Comma-separated list of method calls to additionally enable TRIO113 "
1523
1499
"warnings for. Will also check for the pattern inside function calls. "
@@ -1529,11 +1505,10 @@ def add_options(option_manager: OptionManager):
1529
1505
)
1530
1506
option_manager .add_option (
1531
1507
"--trio200-blocking-calls" ,
1508
+ type = parse_trio200_dict ,
1532
1509
default = {},
1533
1510
parse_from_config = True ,
1534
1511
required = False ,
1535
- comma_separated_list = True ,
1536
- action = ParseDict ,
1537
1512
help = (
1538
1513
"Comma-separated list of key:value pairs, where key is a [dotted] "
1539
1514
"function that if found inside an async function will raise TRIO200, "
0 commit comments