99
99
KeywordError ,
100
100
KeywordMatcher ,
101
101
LibraryDoc ,
102
+ resolve_robot_variables ,
102
103
)
103
104
104
105
EXTRACT_COMMENT_PATTERN = re .compile (r".*(?:^ *|\t+| {2,})#(?P<comment>.*)$" )
@@ -184,22 +185,19 @@ def visit_Variable(self, node: Statement) -> None: # noqa: N802
184
185
class BlockVariableVisitor (Visitor ):
185
186
def __init__ (
186
187
self ,
187
- library_doc : LibraryDoc ,
188
- global_variables : List [VariableDefinition ],
189
- source : str ,
188
+ namespace : "Namespace" ,
189
+ nodes : Optional [List [ast .AST ]] = None ,
190
190
position : Optional [Position ] = None ,
191
191
in_args : bool = True ,
192
192
) -> None :
193
193
super ().__init__ ()
194
- self .library_doc = library_doc
195
- self .global_variables = global_variables
196
- self .source = source
194
+ self .namespace = namespace
195
+ self .nodes = nodes
197
196
self .position = position
198
197
self .in_args = in_args
199
198
200
199
self ._results : Dict [str , VariableDefinition ] = {}
201
200
self .current_kw_doc : Optional [KeywordDoc ] = None
202
- self ._var_statements_vars : List [VariableDefinition ] = []
203
201
204
202
def get (self , model : ast .AST ) -> List [VariableDefinition ]:
205
203
self ._results = {}
@@ -224,7 +222,7 @@ def visit_KeywordName(self, node: Statement) -> None: # noqa: N802
224
222
name_token = node .get_token (Token .KEYWORD_NAME )
225
223
226
224
if name_token is not None and name_token .value :
227
- keyword = ModelHelper .get_keyword_definition_at_token (self .library_doc , name_token )
225
+ keyword = ModelHelper .get_keyword_definition_at_token (self .namespace . get_library_doc () , name_token )
228
226
self .current_kw_doc = keyword
229
227
230
228
for variable_token in filter (
@@ -246,7 +244,7 @@ def visit_KeywordName(self, node: Statement) -> None: # noqa: N802
246
244
col_offset = variable_token .col_offset ,
247
245
end_line_no = variable_token .lineno ,
248
246
end_col_offset = variable_token .end_col_offset ,
249
- source = self .source ,
247
+ source = self .namespace . source ,
250
248
keyword_doc = self .current_kw_doc ,
251
249
)
252
250
@@ -290,7 +288,7 @@ def visit_Arguments(self, node: Statement) -> None: # noqa: N802
290
288
col_offset = argument .col_offset ,
291
289
end_line_no = argument .lineno ,
292
290
end_col_offset = argument .end_col_offset ,
293
- source = self .source ,
291
+ source = self .namespace . source ,
294
292
keyword_doc = self .current_kw_doc ,
295
293
)
296
294
self ._results [argument .value ] = arg_def
@@ -312,12 +310,48 @@ def visit_ExceptHeader(self, node: Statement) -> None: # noqa: N802
312
310
col_offset = variable .col_offset ,
313
311
end_line_no = variable .lineno ,
314
312
end_col_offset = variable .end_col_offset ,
315
- source = self .source ,
313
+ source = self .namespace . source ,
316
314
)
317
315
318
316
except VariableError :
319
317
pass
320
318
319
+ def _get_var_name (self , original : str , position : Position , require_assign : bool = True ) -> Optional [str ]:
320
+ robot_variables = resolve_robot_variables (
321
+ str (self .namespace .imports_manager .root_folder ),
322
+ str (Path (self .namespace .source ).parent ) if self .namespace .source else "." ,
323
+ self .namespace .imports_manager .get_resolvable_command_line_variables (),
324
+ variables = self .namespace .get_resolvable_variables (),
325
+ )
326
+
327
+ try :
328
+ replaced = robot_variables .replace_string (original )
329
+ except VariableError :
330
+ replaced = original
331
+ try :
332
+ name = self ._resolve_var_name (replaced , robot_variables )
333
+ except ValueError :
334
+ name = original
335
+ match = search_variable (name , identifiers = "$@&" )
336
+ match .resolve_base (robot_variables )
337
+ valid = match .is_assign () if require_assign else match .is_variable ()
338
+ if not valid :
339
+ return None
340
+ return str (match )
341
+
342
+ def _resolve_var_name (self , name : str , variables : Any ) -> str :
343
+ if name .startswith ("\\ " ):
344
+ name = name [1 :]
345
+ if len (name ) < 2 or name [0 ] not in "$@&" :
346
+ raise ValueError
347
+ if name [1 ] != "{" :
348
+ name = f"{ name [0 ]} {{{ name [1 :]} }}"
349
+ match = search_variable (name , identifiers = "$@&" , ignore_errors = True )
350
+ match .resolve_base (variables )
351
+ if not match .is_assign ():
352
+ raise ValueError
353
+ return str (match )
354
+
321
355
def visit_KeywordCall (self , node : Statement ) -> None : # noqa: N802
322
356
# TODO analyze "Set Local/Global/Suite Variable"
323
357
@@ -341,12 +375,65 @@ def visit_KeywordCall(self, node: Statement) -> None: # noqa: N802
341
375
col_offset = variable_token .col_offset ,
342
376
end_line_no = variable_token .lineno ,
343
377
end_col_offset = variable_token .end_col_offset ,
344
- source = self .source ,
378
+ source = self .namespace . source ,
345
379
)
346
380
347
381
except VariableError :
348
382
pass
349
383
384
+ keyword_token = node .get_token (Token .KEYWORD )
385
+ if keyword_token is None or not keyword_token .value :
386
+ return
387
+
388
+ keyword = self .namespace .find_keyword (keyword_token .value , raise_keyword_error = False )
389
+ if keyword is None :
390
+ return
391
+
392
+ if keyword .libtype == "LIBRARY" and keyword .libname == "BuiltIn" :
393
+ var_type = None
394
+ if keyword .name == "Set Suite Variable" :
395
+ var_type = VariableDefinition
396
+ elif keyword .name == "Set Global Variable" :
397
+ var_type = GlobalVariableDefinition
398
+ elif keyword .name == "Set Test Variable" or keyword .name == "Set Task Variable" :
399
+ var_type = TestVariableDefinition
400
+ elif keyword .name == "Set Local Variable" :
401
+ var_type = LocalVariableDefinition
402
+ else :
403
+ return
404
+ try :
405
+ variable = node .get_token (Token .ARGUMENT )
406
+ if variable is None :
407
+ return
408
+
409
+ position = range_from_node (node ).start
410
+ position .character = 0
411
+ var_name = self ._get_var_name (variable .value , position )
412
+
413
+ if var_name is None or not is_variable (var_name ):
414
+ return
415
+
416
+ var = var_type (
417
+ name = var_name ,
418
+ name_token = strip_variable_token (variable ),
419
+ line_no = variable .lineno ,
420
+ col_offset = variable .col_offset ,
421
+ end_line_no = variable .lineno ,
422
+ end_col_offset = variable .end_col_offset ,
423
+ source = self .namespace .source ,
424
+ )
425
+
426
+ if var_name not in self ._results or type (self ._results [var_name ]) != type (var ):
427
+ if isinstance (var , LocalVariableDefinition ) or not any (
428
+ l for l in self .namespace .get_global_variables () if l .matcher == var .matcher
429
+ ):
430
+ self ._results [var_name ] = var
431
+ else :
432
+ self ._results .pop (var_name , None )
433
+
434
+ except VariableError :
435
+ pass
436
+
350
437
def visit_InlineIfHeader (self , node : Statement ) -> None : # noqa: N802
351
438
for assign_token in node .get_tokens (Token .ASSIGN ):
352
439
variable_token = self .get_variable_token (assign_token )
@@ -368,7 +455,7 @@ def visit_InlineIfHeader(self, node: Statement) -> None: # noqa: N802
368
455
col_offset = variable_token .col_offset ,
369
456
end_line_no = variable_token .lineno ,
370
457
end_col_offset = variable_token .end_col_offset ,
371
- source = self .source ,
458
+ source = self .namespace . source ,
372
459
)
373
460
374
461
except VariableError :
@@ -386,7 +473,7 @@ def visit_ForHeader(self, node: Statement) -> None: # noqa: N802
386
473
col_offset = variable_token .col_offset ,
387
474
end_line_no = variable_token .lineno ,
388
475
end_col_offset = variable_token .end_col_offset ,
389
- source = self .source ,
476
+ source = self .namespace . source ,
390
477
)
391
478
392
479
def visit_Var (self , node : Statement ) -> None : # noqa: N802
@@ -421,14 +508,12 @@ def visit_Var(self, node: Statement) -> None: # noqa: N802
421
508
col_offset = variable .col_offset ,
422
509
end_line_no = variable .lineno ,
423
510
end_col_offset = variable .end_col_offset ,
424
- source = self .source ,
511
+ source = self .namespace . source ,
425
512
)
426
513
427
- self ._var_statements_vars .append (var )
428
-
429
514
if var_name not in self ._results or type (self ._results [var_name ]) != type (var ):
430
515
if isinstance (var , LocalVariableDefinition ) or not any (
431
- l for l in self .global_variables if l .matcher == var .matcher
516
+ l for l in self .namespace . get_global_variables () if l .matcher == var .matcher
432
517
):
433
518
self ._results [var_name ] = var
434
519
else :
@@ -924,9 +1009,8 @@ def yield_variables(
924
1009
(
925
1010
(
926
1011
BlockVariableVisitor (
927
- self .get_library_doc (),
928
- self .get_global_variables (),
929
- self .source ,
1012
+ self ,
1013
+ nodes ,
930
1014
position ,
931
1015
isinstance (test_or_keyword_nodes [- 1 ], Arguments ) if nodes else False ,
932
1016
).get (test_or_keyword )
0 commit comments