2626from robot .errors import VariableError
2727from robot .libraries import STDLIBS
2828from robot .parsing .lexer .tokens import Token
29- from robot .parsing .model .blocks import (
30- Keyword ,
31- SettingSection ,
32- TestCase ,
33- VariableSection ,
34- )
35- from robot .parsing .model .statements import Arguments , Statement
29+ from robot .parsing .model .blocks import Keyword , SettingSection , TestCase , VariableSection
30+ from robot .parsing .model .statements import Arguments , Fixture , Statement , Timeout
3631from robot .parsing .model .statements import LibraryImport as RobotLibraryImport
3732from robot .parsing .model .statements import ResourceImport as RobotResourceImport
3833from robot .parsing .model .statements import (
@@ -182,7 +177,7 @@ def visit_Variable(self, node: Statement) -> None: # noqa: N802
182177 )
183178
184179
185- class BlockVariableVisitor (Visitor ):
180+ class VariableVisitorBase (Visitor ):
186181 def __init__ (
187182 self ,
188183 namespace : "Namespace" ,
@@ -198,7 +193,79 @@ def __init__(
198193
199194 self ._results : Dict [str , VariableDefinition ] = {}
200195 self .current_kw_doc : Optional [KeywordDoc ] = None
196+ self .current_kw : Optional [Keyword ] = None
197+
198+ def get_variable_token (self , token : Token ) -> Optional [Token ]:
199+ return next (
200+ (
201+ v
202+ for v in itertools .dropwhile (
203+ lambda t : t .type in Token .NON_DATA_TOKENS ,
204+ tokenize_variables (token , ignore_errors = True , extra_types = {Token .VARIABLE }),
205+ )
206+ if v .type == Token .VARIABLE
207+ ),
208+ None ,
209+ )
210+
211+
212+ class ArgumentVisitor (VariableVisitorBase ):
213+ def __init__ (
214+ self ,
215+ namespace : "Namespace" ,
216+ nodes : Optional [List [ast .AST ]],
217+ position : Optional [Position ],
218+ in_args : bool ,
219+ current_kw_doc : Optional [KeywordDoc ],
220+ ) -> None :
221+ super ().__init__ (namespace , nodes , position , in_args )
222+
223+ self .current_kw_doc : Optional [KeywordDoc ] = current_kw_doc
224+
225+ def get (self , model : ast .AST ) -> Dict [str , VariableDefinition ]:
226+ self ._results = {}
227+
228+ self .visit (model )
229+
230+ return self ._results
231+
232+ def visit_Arguments (self , node : Statement ) -> None : # noqa: N802
233+ args : List [str ] = []
234+
235+ arguments = node .get_tokens (Token .ARGUMENT )
236+
237+ for argument_token in arguments :
238+ try :
239+ argument = self .get_variable_token (argument_token )
240+
241+ if argument is not None and argument .value != "@{}" :
242+ if (
243+ self .in_args
244+ and self .position is not None
245+ and self .position in range_from_token (argument_token )
246+ and self .position > range_from_token (argument ).end
247+ ):
248+ break
249+
250+ if argument .value not in args :
251+ args .append (argument .value )
252+ arg_def = ArgumentDefinition (
253+ name = argument .value ,
254+ name_token = strip_variable_token (argument ),
255+ line_no = argument .lineno ,
256+ col_offset = argument .col_offset ,
257+ end_line_no = argument .lineno ,
258+ end_col_offset = argument .end_col_offset ,
259+ source = self .namespace .source ,
260+ keyword_doc = self .current_kw_doc ,
261+ )
262+ self ._results [argument .value ] = arg_def
263+
264+ except VariableError :
265+ pass
201266
267+
268+ class OnlyArgumentsVisitor (VariableVisitorBase ):
202269 def get (self , model : ast .AST ) -> List [VariableDefinition ]:
203270 self ._results = {}
204271
@@ -211,9 +278,11 @@ def visit(self, node: ast.AST) -> None:
211278 super ().visit (node )
212279
213280 def visit_Keyword (self , node : ast .AST ) -> None : # noqa: N802
281+ self .current_kw = cast (Keyword , node )
214282 try :
215283 self .generic_visit (node )
216284 finally :
285+ self .current_kw = None
217286 self .current_kw_doc = None
218287
219288 def visit_KeywordName (self , node : Statement ) -> None : # noqa: N802
@@ -248,53 +317,15 @@ def visit_KeywordName(self, node: Statement) -> None: # noqa: N802
248317 keyword_doc = self .current_kw_doc ,
249318 )
250319
251- def get_variable_token (self , token : Token ) -> Optional [Token ]:
252- return next (
253- (
254- v
255- for v in itertools .dropwhile (
256- lambda t : t .type in Token .NON_DATA_TOKENS ,
257- tokenize_variables (token , ignore_errors = True , extra_types = {Token .VARIABLE }),
258- )
259- if v .type == Token .VARIABLE
260- ),
261- None ,
262- )
263-
264- def visit_Arguments (self , node : Statement ) -> None : # noqa: N802
265- args : List [str ] = []
320+ if self .current_kw is not None :
321+ args = ArgumentVisitor (
322+ self .namespace , self .nodes , self .position , self .in_args , self .current_kw_doc
323+ ).get (self .current_kw )
324+ if args :
325+ self ._results .update (args )
266326
267- arguments = node .get_tokens (Token .ARGUMENT )
268327
269- for argument_token in arguments :
270- try :
271- argument = self .get_variable_token (argument_token )
272-
273- if argument is not None and argument .value != "@{}" :
274- if (
275- self .in_args
276- and self .position is not None
277- and self .position in range_from_token (argument_token )
278- and self .position > range_from_token (argument ).end
279- ):
280- break
281-
282- if argument .value not in args :
283- args .append (argument .value )
284- arg_def = ArgumentDefinition (
285- name = argument .value ,
286- name_token = strip_variable_token (argument ),
287- line_no = argument .lineno ,
288- col_offset = argument .col_offset ,
289- end_line_no = argument .lineno ,
290- end_col_offset = argument .end_col_offset ,
291- source = self .namespace .source ,
292- keyword_doc = self .current_kw_doc ,
293- )
294- self ._results [argument .value ] = arg_def
295-
296- except VariableError :
297- pass
328+ class BlockVariableVisitor (OnlyArgumentsVisitor ):
298329
299330 def visit_ExceptHeader (self , node : Statement ) -> None : # noqa: N802
300331 variables = node .get_tokens (Token .VARIABLE )[:1 ]
@@ -990,10 +1021,12 @@ def yield_variables(
9901021 nodes : Optional [List [ast .AST ]] = None ,
9911022 position : Optional [Position ] = None ,
9921023 skip_commandline_variables : bool = False ,
1024+ skip_local_variables : bool = False ,
9931025 ) -> Iterator [Tuple [VariableMatcher , VariableDefinition ]]:
9941026 yielded : Dict [VariableMatcher , VariableDefinition ] = {}
9951027
9961028 test_or_keyword = None
1029+ test_or_keyword_nodes = None
9971030
9981031 if nodes :
9991032 test_or_keyword_nodes = list (
@@ -1004,18 +1037,23 @@ def yield_variables(
10041037 )
10051038 test_or_keyword = test_or_keyword_nodes [0 ] if test_or_keyword_nodes else None
10061039
1040+ in_args = isinstance (test_or_keyword_nodes [- 1 ], Arguments ) if test_or_keyword_nodes else False
1041+ only_args = (
1042+ isinstance (test_or_keyword_nodes [- 1 ], (Arguments , Fixture , Timeout )) if test_or_keyword_nodes else False
1043+ )
1044+
10071045 for var in chain (
10081046 * [
10091047 (
10101048 (
1011- BlockVariableVisitor (
1049+ ( OnlyArgumentsVisitor if only_args else BlockVariableVisitor ) (
10121050 self ,
10131051 nodes ,
10141052 position ,
1015- isinstance ( test_or_keyword_nodes [ - 1 ], Arguments ) if nodes else False ,
1053+ in_args ,
10161054 ).get (test_or_keyword )
10171055 )
1018- if test_or_keyword is not None
1056+ if test_or_keyword is not None and not skip_local_variables
10191057 else []
10201058 )
10211059 ],
@@ -1081,6 +1119,7 @@ def find_variable(
10811119 nodes : Optional [List [ast .AST ]] = None ,
10821120 position : Optional [Position ] = None ,
10831121 skip_commandline_variables : bool = False ,
1122+ skip_local_variables : bool = False ,
10841123 ignore_error : bool = False ,
10851124 ) -> Optional [VariableDefinition ]:
10861125 self .ensure_initialized ()
@@ -1105,6 +1144,7 @@ def find_variable(
11051144 nodes ,
11061145 position ,
11071146 skip_commandline_variables = skip_commandline_variables ,
1147+ skip_local_variables = skip_local_variables ,
11081148 ):
11091149 if matcher == m :
11101150 return v
0 commit comments