11from collections import Counter
22from collections .abc import Iterable
33from dataclasses import dataclass
4- from typing import cast
4+ from typing import cast , override
55
6- from mypy .checker import TypeChecker
7- from mypy .nodes import (
8- Context ,
9- Expression ,
10- ListExpr ,
11- StrExpr ,
12- TupleExpr ,
13- )
6+ from mypy .nodes import Context , Expression , ListExpr , StrExpr , TupleExpr
147
15- from .checker_wrapper import CheckerWrapper
168from .error_codes import (
179 DUPLICATE_ARGNAME ,
1810 INVALID_ARGNAME ,
1911 REQUEST_KEYWORD ,
2012 UNREADABLE_ARGNAME ,
2113 UNREADABLE_ARGNAMES ,
2214)
15+ from .names_parser import NamesParser
2316from .utils import cache_by_id
2417
2518
2619@dataclass (frozen = True )
27- class ArgnamesParser (CheckerWrapper ):
28- checker : TypeChecker
29-
20+ class ArgnamesParser (NamesParser ):
3021 def __post_init__ (self ) -> None :
3122 object .__setattr__ (self , "parse_names" , cache_by_id (self .parse_names ))
3223
@@ -37,29 +28,17 @@ def parse_names(self, expression: Expression) -> str | list[str] | None:
3728 case ListExpr () | TupleExpr ():
3829 argnames = self .parse_names_sequence (expression )
3930 case _:
40- self .fail (
41- "Unable to identify argnames. (Use a comma-separated string, list of strings or tuple of strings)." ,
42- context = expression ,
43- code = UNREADABLE_ARGNAMES ,
44- )
31+ self ._fail_unreadable_argnames (expression )
4532 return None
4633 argnames = self ._check_duplicate_argnames (argnames , expression )
4734 return argnames
4835
49- def _check_valid_identifier (self , name : str , context : StrExpr ) -> bool :
50- if not (valid_identifier := name .isidentifier ()):
51- self .fail (
52- f"Invalid identifier { name !r} ." ,
53- context = context ,
54- code = INVALID_ARGNAME ,
55- )
56- elif not (valid_identifier := (name != "request" )):
57- self .fail (
58- f"{ name !r} is not allowed as an argname; it is a reserved word in Pytest." ,
59- context = context ,
60- code = REQUEST_KEYWORD ,
61- )
62- return valid_identifier
36+ def _fail_unreadable_argnames (self , context : Context ) -> None :
37+ self .fail (
38+ "Unable to identify argnames. (Use a comma-separated string, list of strings or tuple of strings)." ,
39+ context = context ,
40+ code = UNREADABLE_ARGNAMES ,
41+ )
6342
6443 def parse_names_string (self , expression : StrExpr ) -> str | list [str ] | None :
6544 individual_names = [name .strip () for name in expression .value .split ("," )]
@@ -71,21 +50,8 @@ def parse_names_string(self, expression: StrExpr) -> str | list[str] | None:
7150 return name
7251 return filtered_names
7352
74- def _parse_name (self , expression : Expression ) -> str | None :
75- if isinstance (expression , StrExpr ):
76- name = expression .value
77- if self ._check_valid_identifier (name , expression ):
78- return name
79- else :
80- self .fail (
81- "Unable to read identifier. (Use a sequence of strings instead.)" ,
82- context = expression ,
83- code = UNREADABLE_ARGNAME ,
84- )
85- return None
86-
8753 def parse_names_sequence (self , expr : TupleExpr | ListExpr ) -> list [str ] | None :
88- names = [self ._parse_name (item ) for item in expr .items ]
54+ names = [self .parse_name (item ) for item in expr .items ]
8955 if all ([isinstance (name , str ) for name in names ]):
9056 return cast (list [str ], names )
9157 return None
@@ -109,8 +75,30 @@ def _check_duplicate_argnames_sequence(
10975
11076 def _warn_duplicate_argnames (self , duplicates : Iterable [str ], context : Context ) -> None :
11177 for argname in duplicates :
112- self .fail (
113- f"Duplicated argname { argname !r} ." ,
114- context = context ,
115- code = DUPLICATE_ARGNAME ,
116- )
78+ self .fail (f"Duplicated argname { argname !r} ." , context = context , code = DUPLICATE_ARGNAME )
79+
80+ @override
81+ def _fail_invalid_identifier (self , name : str , context : Context ) -> None :
82+ self .fail (
83+ f"Invalid identifier { name !r} for argname." , context = context , code = INVALID_ARGNAME
84+ )
85+
86+ @override
87+ def _fail_keyword_identifier (self , name : str , context : Context ) -> None :
88+ self .fail (f"Keyword { name !r} used as an argname." , context = context , code = INVALID_ARGNAME )
89+
90+ @override
91+ def _fail_unreadable_identifier (self , context : Context ) -> None :
92+ self .fail (
93+ "Unable to read argname. (Use string literals instead.)" ,
94+ context = context ,
95+ code = UNREADABLE_ARGNAME ,
96+ )
97+
98+ @override
99+ def _fail_request_identifier (self , name : str , context : Context ) -> None :
100+ self .fail (
101+ f"{ name !r} is not allowed as an argname; it is a reserved word in Pytest." ,
102+ context = context ,
103+ code = REQUEST_KEYWORD ,
104+ )
0 commit comments