robotcode-robot 0.93.1__py3-none-any.whl → 0.94.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- robotcode/robot/__version__.py +1 -1
- robotcode/robot/diagnostics/document_cache_helper.py +11 -6
- robotcode/robot/diagnostics/entities.py +2 -2
- robotcode/robot/diagnostics/errors.py +6 -1
- robotcode/robot/diagnostics/imports_manager.py +77 -37
- robotcode/robot/diagnostics/keyword_finder.py +458 -0
- robotcode/robot/diagnostics/library_doc.py +20 -14
- robotcode/robot/diagnostics/model_helper.py +42 -27
- robotcode/robot/diagnostics/namespace.py +142 -552
- robotcode/robot/diagnostics/namespace_analyzer.py +952 -462
- robotcode/robot/utils/markdownformatter.py +8 -11
- robotcode/robot/utils/visitor.py +7 -13
- {robotcode_robot-0.93.1.dist-info → robotcode_robot-0.94.0.dist-info}/METADATA +2 -2
- robotcode_robot-0.94.0.dist-info/RECORD +31 -0
- robotcode_robot-0.93.1.dist-info/RECORD +0 -30
- {robotcode_robot-0.93.1.dist-info → robotcode_robot-0.94.0.dist-info}/WHEEL +0 -0
- {robotcode_robot-0.93.1.dist-info → robotcode_robot-0.94.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -5,7 +5,6 @@ import time
|
|
5
5
|
import weakref
|
6
6
|
from collections import OrderedDict, defaultdict
|
7
7
|
from concurrent.futures import CancelledError
|
8
|
-
from itertools import chain
|
9
8
|
from pathlib import Path
|
10
9
|
from typing import (
|
11
10
|
Any,
|
@@ -15,7 +14,6 @@ from typing import (
|
|
15
14
|
List,
|
16
15
|
NamedTuple,
|
17
16
|
Optional,
|
18
|
-
Sequence,
|
19
17
|
Set,
|
20
18
|
Tuple,
|
21
19
|
Union,
|
@@ -23,7 +21,6 @@ from typing import (
|
|
23
21
|
)
|
24
22
|
|
25
23
|
from robot.errors import VariableError
|
26
|
-
from robot.libraries import STDLIBS
|
27
24
|
from robot.parsing.lexer.tokens import Token
|
28
25
|
from robot.parsing.model.blocks import Keyword, SettingSection, TestCase, VariableSection
|
29
26
|
from robot.parsing.model.statements import Arguments, Setup, Statement, Timeout
|
@@ -54,21 +51,18 @@ from robotcode.core.text_document import TextDocument
|
|
54
51
|
from robotcode.core.uri import Uri
|
55
52
|
from robotcode.core.utils.logging import LoggingDescriptor
|
56
53
|
|
57
|
-
from ..utils import get_robot_version
|
58
54
|
from ..utils.ast import (
|
59
55
|
range_from_node,
|
60
56
|
range_from_token,
|
61
57
|
strip_variable_token,
|
62
58
|
tokenize_variables,
|
63
59
|
)
|
64
|
-
from ..utils.match import eq_namespace
|
65
60
|
from ..utils.stubs import Languages
|
66
61
|
from ..utils.variables import BUILTIN_VARIABLES
|
67
62
|
from ..utils.visitor import Visitor
|
68
63
|
from .entities import (
|
69
64
|
ArgumentDefinition,
|
70
65
|
BuiltInVariableDefinition,
|
71
|
-
CommandLineVariableDefinition,
|
72
66
|
EnvironmentVariableDefinition,
|
73
67
|
GlobalVariableDefinition,
|
74
68
|
Import,
|
@@ -86,15 +80,16 @@ from .entities import (
|
|
86
80
|
)
|
87
81
|
from .errors import DIAGNOSTICS_SOURCE_NAME, Error
|
88
82
|
from .imports_manager import ImportsManager
|
83
|
+
from .keyword_finder import KeywordFinder
|
89
84
|
from .library_doc import (
|
90
85
|
BUILTIN_LIBRARY_NAME,
|
91
86
|
DEFAULT_LIBRARIES,
|
92
87
|
KeywordDoc,
|
93
|
-
KeywordError,
|
94
88
|
KeywordMatcher,
|
95
89
|
LibraryDoc,
|
96
90
|
resolve_robot_variables,
|
97
91
|
)
|
92
|
+
from .namespace_analyzer import NamespaceAnalyzer
|
98
93
|
|
99
94
|
|
100
95
|
class DiagnosticsError(Exception):
|
@@ -174,12 +169,14 @@ class VariablesVisitor(Visitor):
|
|
174
169
|
|
175
170
|
|
176
171
|
class VariableVisitorBase(Visitor):
|
172
|
+
|
177
173
|
def __init__(
|
178
174
|
self,
|
179
175
|
namespace: "Namespace",
|
180
176
|
nodes: Optional[List[ast.AST]] = None,
|
181
177
|
position: Optional[Position] = None,
|
182
178
|
in_args: bool = True,
|
179
|
+
resolved_variables: Any = None,
|
183
180
|
) -> None:
|
184
181
|
super().__init__()
|
185
182
|
self.namespace = namespace
|
@@ -190,6 +187,7 @@ class VariableVisitorBase(Visitor):
|
|
190
187
|
self._results: Dict[str, VariableDefinition] = {}
|
191
188
|
self.current_kw_doc: Optional[KeywordDoc] = None
|
192
189
|
self.current_kw: Optional[Keyword] = None
|
190
|
+
self._resolved_variables: Any = resolved_variables
|
193
191
|
|
194
192
|
def get_variable_token(self, token: Token) -> Optional[Token]:
|
195
193
|
return next(
|
@@ -344,12 +342,15 @@ class BlockVariableVisitor(OnlyArgumentsVisitor):
|
|
344
342
|
pass
|
345
343
|
|
346
344
|
def _get_var_name(self, original: str, position: Position, require_assign: bool = True) -> Optional[str]:
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
345
|
+
if self._resolved_variables is None:
|
346
|
+
self._resolved_variables = resolve_robot_variables(
|
347
|
+
str(self.namespace.imports_manager.root_folder),
|
348
|
+
str(Path(self.namespace.source).parent) if self.namespace.source else ".",
|
349
|
+
self.namespace.imports_manager.get_resolvable_command_line_variables(),
|
350
|
+
variables=self.namespace.get_resolvable_variables(),
|
351
|
+
)
|
352
|
+
|
353
|
+
robot_variables = self._resolved_variables
|
353
354
|
|
354
355
|
try:
|
355
356
|
replaced = robot_variables.replace_string(original)
|
@@ -680,8 +681,9 @@ class Namespace:
|
|
680
681
|
self._namespaces: Optional[Dict[KeywordMatcher, List[LibraryEntry]]] = None
|
681
682
|
self._libraries_matchers: Optional[Dict[KeywordMatcher, LibraryEntry]] = None
|
682
683
|
self._resources: Dict[str, ResourceEntry] = OrderedDict()
|
684
|
+
self._resources_files: Dict[str, ResourceEntry] = OrderedDict()
|
683
685
|
self._resources_matchers: Optional[Dict[KeywordMatcher, ResourceEntry]] = None
|
684
|
-
self.
|
686
|
+
self._variables_imports: Dict[str, VariablesEntry] = OrderedDict()
|
685
687
|
self._initialized = False
|
686
688
|
self._invalid = False
|
687
689
|
self._initialize_lock = RLock(default_timeout=120, name="Namespace.initialize")
|
@@ -694,15 +696,23 @@ class Namespace:
|
|
694
696
|
self._own_variables: Optional[List[VariableDefinition]] = None
|
695
697
|
self._own_variables_lock = RLock(default_timeout=120, name="Namespace.own_variables")
|
696
698
|
self._global_variables: Optional[List[VariableDefinition]] = None
|
697
|
-
self._global_variables_dict: Optional[Dict[VariableMatcher, VariableDefinition]] = None
|
698
699
|
self._global_variables_lock = RLock(default_timeout=120, name="Namespace.global_variables")
|
700
|
+
self._global_variables_dict: Optional[Dict[VariableMatcher, VariableDefinition]] = None
|
699
701
|
self._global_variables_dict_lock = RLock(default_timeout=120, name="Namespace.global_variables_dict")
|
700
702
|
|
703
|
+
self._imported_variables: Optional[List[VariableDefinition]] = None
|
704
|
+
self._imported_variables_lock = RLock(default_timeout=120, name="Namespace._imported_variables_lock")
|
705
|
+
|
701
706
|
self._global_resolvable_variables: Optional[Dict[str, Any]] = None
|
702
707
|
self._global_resolvable_variables_lock = RLock(
|
703
708
|
default_timeout=120, name="Namespace._global_resolvable_variables_lock"
|
704
709
|
)
|
705
710
|
|
711
|
+
self._global_resolved_variables: Any = None
|
712
|
+
self._global_resolved_variables_lock = RLock(
|
713
|
+
default_timeout=120, name="Namespace._global_resolvabled_variables_lock"
|
714
|
+
)
|
715
|
+
|
706
716
|
self._suite_variables: Optional[Dict[str, Any]] = None
|
707
717
|
self._suite_variables_lock = RLock(default_timeout=120, name="Namespace.global_variables")
|
708
718
|
|
@@ -729,8 +739,6 @@ class Namespace:
|
|
729
739
|
|
730
740
|
self._in_initialize = False
|
731
741
|
|
732
|
-
self._ignored_lines: Optional[List[int]] = None
|
733
|
-
|
734
742
|
@event
|
735
743
|
def has_invalidated(sender) -> None: ...
|
736
744
|
|
@@ -740,19 +748,14 @@ class Namespace:
|
|
740
748
|
@event
|
741
749
|
def has_analysed(sender) -> None: ...
|
742
750
|
|
743
|
-
@event
|
744
|
-
def library_import_changed(sender) -> None: ...
|
745
|
-
|
746
|
-
@event
|
747
|
-
def resource_import_changed(sender) -> None: ...
|
748
|
-
|
749
|
-
@event
|
750
|
-
def variables_import_changed(sender) -> None: ...
|
751
|
-
|
752
751
|
@property
|
753
752
|
def document(self) -> Optional[TextDocument]:
|
754
753
|
return self._document() if self._document is not None else None
|
755
754
|
|
755
|
+
@property
|
756
|
+
def document_uri(self) -> str:
|
757
|
+
return self.document.document_uri if self.document is not None else str(Uri.from_path(self.source))
|
758
|
+
|
756
759
|
@property
|
757
760
|
def search_order(self) -> Tuple[str, ...]:
|
758
761
|
if self._search_order is None:
|
@@ -802,7 +805,7 @@ class Namespace:
|
|
802
805
|
invalidate = False
|
803
806
|
|
804
807
|
for p in variables:
|
805
|
-
if any(e for e in self.
|
808
|
+
if any(e for e in self._variables_imports.values() if e.library_doc.source == p.source):
|
806
809
|
invalidate = True
|
807
810
|
break
|
808
811
|
|
@@ -898,10 +901,10 @@ class Namespace:
|
|
898
901
|
|
899
902
|
return self._resources
|
900
903
|
|
901
|
-
def
|
904
|
+
def get_variables_imports(self) -> Dict[str, VariablesEntry]:
|
902
905
|
self.ensure_initialized()
|
903
906
|
|
904
|
-
return self.
|
907
|
+
return self._variables_imports
|
905
908
|
|
906
909
|
@_logger.call
|
907
910
|
def get_library_doc(self) -> LibraryDoc:
|
@@ -910,7 +913,6 @@ class Namespace:
|
|
910
913
|
self._library_doc = self.imports_manager.get_libdoc_from_model(
|
911
914
|
self.model,
|
912
915
|
self.source,
|
913
|
-
model_type="RESOURCE",
|
914
916
|
append_model_errors=self.document_type is not None and self.document_type == DocumentType.RESOURCE,
|
915
917
|
)
|
916
918
|
|
@@ -928,38 +930,38 @@ class Namespace:
|
|
928
930
|
def ensure_initialized(self) -> bool:
|
929
931
|
with self._initialize_lock:
|
930
932
|
if not self._initialized:
|
933
|
+
with self._logger.measure_time(
|
934
|
+
lambda: f"Initialize Namespace for {self.source}", context_name="import"
|
935
|
+
):
|
936
|
+
succeed = False
|
937
|
+
try:
|
938
|
+
imports = self.get_imports()
|
931
939
|
|
932
|
-
|
933
|
-
try:
|
934
|
-
self._logger.debug(lambda: f"initialize {self.document}")
|
935
|
-
|
936
|
-
imports = self.get_imports()
|
937
|
-
|
938
|
-
variables = self.get_suite_variables()
|
940
|
+
variables = self.get_suite_variables()
|
939
941
|
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
942
|
+
self._import_default_libraries(variables)
|
943
|
+
self._import_imports(
|
944
|
+
imports,
|
945
|
+
str(Path(self.source).parent),
|
946
|
+
top_level=True,
|
947
|
+
variables=variables,
|
948
|
+
)
|
947
949
|
|
948
|
-
|
950
|
+
self._reset_global_variables()
|
949
951
|
|
950
|
-
|
951
|
-
|
952
|
+
self._initialized = True
|
953
|
+
succeed = True
|
952
954
|
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
955
|
+
except BaseException:
|
956
|
+
if self.document is not None:
|
957
|
+
self.document.remove_data(Namespace)
|
958
|
+
self.document.remove_data(Namespace.DataEntry)
|
957
959
|
|
958
|
-
|
959
|
-
|
960
|
+
self._invalidate()
|
961
|
+
raise
|
960
962
|
|
961
|
-
|
962
|
-
|
963
|
+
if succeed:
|
964
|
+
self.has_initialized(self)
|
963
965
|
|
964
966
|
return self._initialized
|
965
967
|
|
@@ -986,10 +988,10 @@ class Namespace:
|
|
986
988
|
|
987
989
|
return self._own_variables
|
988
990
|
|
989
|
-
_builtin_variables: Optional[List[
|
991
|
+
_builtin_variables: Optional[List[VariableDefinition]] = None
|
990
992
|
|
991
993
|
@classmethod
|
992
|
-
def get_builtin_variables(cls) -> List[
|
994
|
+
def get_builtin_variables(cls) -> List[VariableDefinition]:
|
993
995
|
if cls._builtin_variables is None:
|
994
996
|
cls._builtin_variables = [BuiltInVariableDefinition(0, 0, 0, 0, "", n, None) for n in BUILTIN_VARIABLES]
|
995
997
|
|
@@ -1001,11 +1003,14 @@ class Namespace:
|
|
1001
1003
|
|
1002
1004
|
def _reset_global_variables(self) -> None:
|
1003
1005
|
with self._global_variables_lock, self._global_variables_dict_lock, self._suite_variables_lock:
|
1004
|
-
with self._global_resolvable_variables_lock:
|
1005
|
-
self.
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1006
|
+
with self._global_resolvable_variables_lock, self._global_resolved_variables_lock:
|
1007
|
+
with self._imported_variables_lock:
|
1008
|
+
self._global_variables = None
|
1009
|
+
self._global_variables_dict = None
|
1010
|
+
self._suite_variables = None
|
1011
|
+
self._global_resolvable_variables = None
|
1012
|
+
self._global_resolved_variables = None
|
1013
|
+
self._imported_variables = None
|
1009
1014
|
|
1010
1015
|
def get_global_variables(self) -> List[VariableDefinition]:
|
1011
1016
|
with self._global_variables_lock:
|
@@ -1015,7 +1020,7 @@ class Namespace:
|
|
1015
1020
|
self.get_command_line_variables(),
|
1016
1021
|
self.get_own_variables(),
|
1017
1022
|
*(e.variables for e in self._resources.values()),
|
1018
|
-
*(e.variables for e in self.
|
1023
|
+
*(e.variables for e in self._variables_imports.values()),
|
1019
1024
|
self.get_builtin_variables(),
|
1020
1025
|
)
|
1021
1026
|
)
|
@@ -1037,8 +1042,6 @@ class Namespace:
|
|
1037
1042
|
skip_local_variables: bool = False,
|
1038
1043
|
skip_global_variables: bool = False,
|
1039
1044
|
) -> Iterator[Tuple[VariableMatcher, VariableDefinition]]:
|
1040
|
-
yielded: Dict[VariableMatcher, VariableDefinition] = {}
|
1041
|
-
|
1042
1045
|
test_or_keyword = None
|
1043
1046
|
test_or_keyword_nodes = None
|
1044
1047
|
|
@@ -1056,43 +1059,41 @@ class Namespace:
|
|
1056
1059
|
isinstance(test_or_keyword_nodes[-1], (Arguments, Setup, Timeout)) if test_or_keyword_nodes else False
|
1057
1060
|
)
|
1058
1061
|
|
1059
|
-
|
1060
|
-
|
1062
|
+
yield from (
|
1063
|
+
(var.matcher, var)
|
1064
|
+
for var in itertools.chain(
|
1061
1065
|
(
|
1062
1066
|
(
|
1063
1067
|
(OnlyArgumentsVisitor if only_args else BlockVariableVisitor)(
|
1064
|
-
self,
|
1065
|
-
nodes,
|
1066
|
-
position,
|
1067
|
-
in_args,
|
1068
|
+
self, nodes, position, in_args, resolved_variables=self.get_global_resolved_variables()
|
1068
1069
|
).get(test_or_keyword)
|
1069
1070
|
)
|
1070
1071
|
if test_or_keyword is not None and not skip_local_variables
|
1071
1072
|
else []
|
1072
|
-
)
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1073
|
+
),
|
1074
|
+
[] if skip_global_variables or skip_commandline_variables else self.get_command_line_variables(),
|
1075
|
+
[] if skip_global_variables else self.get_own_variables(),
|
1076
|
+
[] if skip_global_variables else self.get_imported_variables(),
|
1077
|
+
[] if skip_global_variables else self.get_builtin_variables(),
|
1078
|
+
)
|
1079
|
+
)
|
1079
1080
|
|
1080
|
-
|
1081
|
+
def get_imported_variables(self) -> List[VariableDefinition]:
|
1082
|
+
with self._imported_variables_lock:
|
1083
|
+
if self._imported_variables is None:
|
1084
|
+
self._imported_variables = list(
|
1085
|
+
itertools.chain(
|
1086
|
+
*(e.variables for e in self._resources.values()),
|
1087
|
+
*(e.variables for e in self._variables_imports.values()),
|
1088
|
+
),
|
1089
|
+
)
|
1081
1090
|
|
1082
|
-
|
1091
|
+
return self._imported_variables
|
1083
1092
|
|
1084
1093
|
def get_suite_variables(self) -> Dict[str, Any]:
|
1085
1094
|
with self._suite_variables_lock:
|
1086
|
-
|
1087
|
-
|
1088
|
-
def check_var(var: VariableDefinition) -> bool:
|
1089
|
-
if var.matcher in vars:
|
1090
|
-
return False
|
1091
|
-
vars[var.matcher] = var
|
1092
|
-
|
1093
|
-
return var.has_value
|
1094
|
-
|
1095
|
-
self._suite_variables = {v.name: v.value for v in filter(check_var, self.get_global_variables())}
|
1095
|
+
if self._suite_variables is None:
|
1096
|
+
self._suite_variables = {v.name: v.value for v in reversed(self.get_global_variables())}
|
1096
1097
|
|
1097
1098
|
return self._suite_variables
|
1098
1099
|
|
@@ -1117,6 +1118,19 @@ class Namespace:
|
|
1117
1118
|
}
|
1118
1119
|
return self._global_resolvable_variables
|
1119
1120
|
|
1121
|
+
def get_global_resolved_variables(
|
1122
|
+
self,
|
1123
|
+
) -> Any:
|
1124
|
+
with self._global_resolved_variables_lock:
|
1125
|
+
if self._global_resolved_variables is None:
|
1126
|
+
self._global_resolved_variables = resolve_robot_variables(
|
1127
|
+
str(self.imports_manager.root_folder),
|
1128
|
+
str(Path(self.source).parent) if self.source else ".",
|
1129
|
+
self.imports_manager.get_resolvable_command_line_variables(),
|
1130
|
+
variables=self.get_resolvable_variables(),
|
1131
|
+
)
|
1132
|
+
return self._global_resolved_variables
|
1133
|
+
|
1120
1134
|
def get_variable_matchers(
|
1121
1135
|
self,
|
1122
1136
|
nodes: Optional[List[ast.AST]] = None,
|
@@ -1154,15 +1168,24 @@ class Namespace:
|
|
1154
1168
|
try:
|
1155
1169
|
matcher = VariableMatcher(name)
|
1156
1170
|
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1171
|
+
if nodes:
|
1172
|
+
result = next(
|
1173
|
+
(
|
1174
|
+
v
|
1175
|
+
for m, v in self.yield_variables(
|
1176
|
+
nodes,
|
1177
|
+
position,
|
1178
|
+
skip_commandline_variables=skip_commandline_variables,
|
1179
|
+
skip_local_variables=skip_local_variables,
|
1180
|
+
skip_global_variables=True,
|
1181
|
+
)
|
1182
|
+
if matcher == m
|
1183
|
+
),
|
1184
|
+
None,
|
1185
|
+
)
|
1186
|
+
|
1187
|
+
if result is not None:
|
1188
|
+
return result
|
1166
1189
|
|
1167
1190
|
result = self.get_global_variables_dict().get(matcher, None)
|
1168
1191
|
if matcher is not None:
|
@@ -1223,6 +1246,10 @@ class Namespace:
|
|
1223
1246
|
|
1224
1247
|
source = self.imports_manager.find_resource(value.name, base_dir, variables=variables)
|
1225
1248
|
|
1249
|
+
if source in self._resources_files:
|
1250
|
+
self._logger.debug(lambda: f"Resource '{value.name}' already imported.", context_name="import")
|
1251
|
+
return None
|
1252
|
+
|
1226
1253
|
if self.source == source:
|
1227
1254
|
if parent_import:
|
1228
1255
|
self.append_diagnostics(
|
@@ -1256,6 +1283,9 @@ class Namespace:
|
|
1256
1283
|
|
1257
1284
|
self._import_entries[value] = result
|
1258
1285
|
|
1286
|
+
if result.variables:
|
1287
|
+
self._reset_global_variables()
|
1288
|
+
|
1259
1289
|
if top_level and (
|
1260
1290
|
not result.library_doc.errors
|
1261
1291
|
and top_level
|
@@ -1286,6 +1316,9 @@ class Namespace:
|
|
1286
1316
|
result.import_source = value.source
|
1287
1317
|
|
1288
1318
|
self._import_entries[value] = result
|
1319
|
+
|
1320
|
+
if result.variables:
|
1321
|
+
self._reset_global_variables()
|
1289
1322
|
else:
|
1290
1323
|
raise DiagnosticsError("Unknown import type.")
|
1291
1324
|
|
@@ -1385,8 +1418,6 @@ class Namespace:
|
|
1385
1418
|
else None
|
1386
1419
|
),
|
1387
1420
|
)
|
1388
|
-
finally:
|
1389
|
-
self._reset_global_variables()
|
1390
1421
|
|
1391
1422
|
return result
|
1392
1423
|
|
@@ -1423,13 +1454,11 @@ class Namespace:
|
|
1423
1454
|
if entry is not None:
|
1424
1455
|
if isinstance(entry, ResourceEntry):
|
1425
1456
|
assert entry.library_doc.source is not None
|
1426
|
-
|
1427
|
-
(e for e in self._resources.values() if e.library_doc.source == entry.library_doc.source),
|
1428
|
-
None,
|
1429
|
-
)
|
1457
|
+
allread_imported_resource = self._resources_files.get(entry.library_doc.source, None)
|
1430
1458
|
|
1431
|
-
if
|
1459
|
+
if allread_imported_resource is None and entry.library_doc.source != self.source:
|
1432
1460
|
self._resources[entry.import_name] = entry
|
1461
|
+
self._resources_files[entry.library_doc.source] = entry
|
1433
1462
|
if entry.variables:
|
1434
1463
|
variables = self.get_suite_variables()
|
1435
1464
|
|
@@ -1463,9 +1492,7 @@ class Namespace:
|
|
1463
1492
|
source=DIAGNOSTICS_SOURCE_NAME,
|
1464
1493
|
code=Error.RECURSIVE_IMPORT,
|
1465
1494
|
)
|
1466
|
-
elif
|
1467
|
-
already_imported_resources is not None and already_imported_resources.library_doc.source
|
1468
|
-
):
|
1495
|
+
elif allread_imported_resource is not None and allread_imported_resource.library_doc.source:
|
1469
1496
|
self.append_diagnostics(
|
1470
1497
|
range=entry.import_range,
|
1471
1498
|
message=f"Resource {entry} already imported.",
|
@@ -1475,13 +1502,13 @@ class Namespace:
|
|
1475
1502
|
[
|
1476
1503
|
DiagnosticRelatedInformation(
|
1477
1504
|
location=Location(
|
1478
|
-
uri=str(Uri.from_path(
|
1479
|
-
range=
|
1505
|
+
uri=str(Uri.from_path(allread_imported_resource.import_source)),
|
1506
|
+
range=allread_imported_resource.import_range,
|
1480
1507
|
),
|
1481
1508
|
message="",
|
1482
1509
|
)
|
1483
1510
|
]
|
1484
|
-
if
|
1511
|
+
if allread_imported_resource.import_source
|
1485
1512
|
else None
|
1486
1513
|
),
|
1487
1514
|
code=Error.RESOURCE_ALREADY_IMPORTED,
|
@@ -1491,7 +1518,7 @@ class Namespace:
|
|
1491
1518
|
already_imported_variables = next(
|
1492
1519
|
(
|
1493
1520
|
e
|
1494
|
-
for e in self.
|
1521
|
+
for e in self._variables_imports.values()
|
1495
1522
|
if e.library_doc.source == entry.library_doc.source
|
1496
1523
|
and e.alias == entry.alias
|
1497
1524
|
and e.args == entry.args
|
@@ -1503,7 +1530,7 @@ class Namespace:
|
|
1503
1530
|
and entry.library_doc is not None
|
1504
1531
|
and entry.library_doc.source_or_origin
|
1505
1532
|
):
|
1506
|
-
self.
|
1533
|
+
self._variables_imports[entry.library_doc.source_or_origin] = entry
|
1507
1534
|
if entry.variables:
|
1508
1535
|
variables = self.get_suite_variables()
|
1509
1536
|
elif top_level and already_imported_variables and already_imported_variables.library_doc.source:
|
@@ -1737,7 +1764,7 @@ class Namespace:
|
|
1737
1764
|
)
|
1738
1765
|
|
1739
1766
|
@_logger.call
|
1740
|
-
def
|
1767
|
+
def get_variables_import_libdoc(self, name: str, args: Tuple[str, ...] = ()) -> Optional[LibraryDoc]:
|
1741
1768
|
self.ensure_initialized()
|
1742
1769
|
|
1743
1770
|
return next(
|
@@ -1835,8 +1862,6 @@ class Namespace:
|
|
1835
1862
|
|
1836
1863
|
@_logger.call(condition=lambda self: not self._analyzed)
|
1837
1864
|
def analyze(self) -> None:
|
1838
|
-
from .namespace_analyzer import NamespaceAnalyzer
|
1839
|
-
|
1840
1865
|
with self._analyze_lock:
|
1841
1866
|
if not self._analyzed:
|
1842
1867
|
canceled = False
|
@@ -1907,438 +1932,3 @@ class Namespace:
|
|
1907
1932
|
raise_keyword_error=raise_keyword_error,
|
1908
1933
|
handle_bdd_style=handle_bdd_style,
|
1909
1934
|
)
|
1910
|
-
|
1911
|
-
|
1912
|
-
class DiagnosticsEntry(NamedTuple):
|
1913
|
-
message: str
|
1914
|
-
severity: DiagnosticSeverity
|
1915
|
-
code: Optional[str] = None
|
1916
|
-
|
1917
|
-
|
1918
|
-
class CancelSearchError(Exception):
|
1919
|
-
pass
|
1920
|
-
|
1921
|
-
|
1922
|
-
DEFAULT_BDD_PREFIXES = {"Given ", "When ", "Then ", "And ", "But "}
|
1923
|
-
|
1924
|
-
|
1925
|
-
class KeywordFinder:
|
1926
|
-
def __init__(self, namespace: Namespace, library_doc: LibraryDoc) -> None:
|
1927
|
-
self.namespace = namespace
|
1928
|
-
self.self_library_doc = library_doc
|
1929
|
-
|
1930
|
-
self.diagnostics: List[DiagnosticsEntry] = []
|
1931
|
-
self.multiple_keywords_result: Optional[List[KeywordDoc]] = None
|
1932
|
-
self._cache: Dict[
|
1933
|
-
Tuple[Optional[str], bool],
|
1934
|
-
Tuple[
|
1935
|
-
Optional[KeywordDoc],
|
1936
|
-
List[DiagnosticsEntry],
|
1937
|
-
Optional[List[KeywordDoc]],
|
1938
|
-
],
|
1939
|
-
] = {}
|
1940
|
-
self.handle_bdd_style = True
|
1941
|
-
self._all_keywords: Optional[List[LibraryEntry]] = None
|
1942
|
-
self._resource_keywords: Optional[List[ResourceEntry]] = None
|
1943
|
-
self._library_keywords: Optional[List[LibraryEntry]] = None
|
1944
|
-
|
1945
|
-
def reset_diagnostics(self) -> None:
|
1946
|
-
self.diagnostics = []
|
1947
|
-
self.multiple_keywords_result = None
|
1948
|
-
|
1949
|
-
def find_keyword(
|
1950
|
-
self,
|
1951
|
-
name: Optional[str],
|
1952
|
-
*,
|
1953
|
-
raise_keyword_error: bool = False,
|
1954
|
-
handle_bdd_style: bool = True,
|
1955
|
-
) -> Optional[KeywordDoc]:
|
1956
|
-
try:
|
1957
|
-
self.reset_diagnostics()
|
1958
|
-
|
1959
|
-
self.handle_bdd_style = handle_bdd_style
|
1960
|
-
|
1961
|
-
cached = self._cache.get((name, self.handle_bdd_style), None)
|
1962
|
-
|
1963
|
-
if cached is not None:
|
1964
|
-
self.diagnostics = cached[1]
|
1965
|
-
self.multiple_keywords_result = cached[2]
|
1966
|
-
return cached[0]
|
1967
|
-
|
1968
|
-
try:
|
1969
|
-
result = self._find_keyword(name)
|
1970
|
-
if result is None:
|
1971
|
-
self.diagnostics.append(
|
1972
|
-
DiagnosticsEntry(
|
1973
|
-
f"No keyword with name '{name}' found.",
|
1974
|
-
DiagnosticSeverity.ERROR,
|
1975
|
-
Error.KEYWORD_NOT_FOUND,
|
1976
|
-
)
|
1977
|
-
)
|
1978
|
-
except KeywordError as e:
|
1979
|
-
if e.multiple_keywords:
|
1980
|
-
self._add_to_multiple_keywords_result(e.multiple_keywords)
|
1981
|
-
|
1982
|
-
if raise_keyword_error:
|
1983
|
-
raise
|
1984
|
-
|
1985
|
-
result = None
|
1986
|
-
self.diagnostics.append(DiagnosticsEntry(str(e), DiagnosticSeverity.ERROR, Error.KEYWORD_ERROR))
|
1987
|
-
|
1988
|
-
self._cache[(name, self.handle_bdd_style)] = (
|
1989
|
-
result,
|
1990
|
-
self.diagnostics,
|
1991
|
-
self.multiple_keywords_result,
|
1992
|
-
)
|
1993
|
-
|
1994
|
-
return result
|
1995
|
-
except CancelSearchError:
|
1996
|
-
return None
|
1997
|
-
|
1998
|
-
def _find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]:
|
1999
|
-
if not name:
|
2000
|
-
self.diagnostics.append(
|
2001
|
-
DiagnosticsEntry(
|
2002
|
-
"Keyword name cannot be empty.",
|
2003
|
-
DiagnosticSeverity.ERROR,
|
2004
|
-
Error.KEYWORD_ERROR,
|
2005
|
-
)
|
2006
|
-
)
|
2007
|
-
raise CancelSearchError
|
2008
|
-
if not isinstance(name, str):
|
2009
|
-
self.diagnostics.append( # type: ignore
|
2010
|
-
DiagnosticsEntry(
|
2011
|
-
"Keyword name must be a string.",
|
2012
|
-
DiagnosticSeverity.ERROR,
|
2013
|
-
Error.KEYWORD_ERROR,
|
2014
|
-
)
|
2015
|
-
)
|
2016
|
-
raise CancelSearchError
|
2017
|
-
|
2018
|
-
result = self._get_keyword_from_self(name)
|
2019
|
-
if not result and "." in name:
|
2020
|
-
result = self._get_explicit_keyword(name)
|
2021
|
-
|
2022
|
-
if not result:
|
2023
|
-
result = self._get_implicit_keyword(name)
|
2024
|
-
|
2025
|
-
if not result and self.handle_bdd_style:
|
2026
|
-
return self._get_bdd_style_keyword(name)
|
2027
|
-
|
2028
|
-
return result
|
2029
|
-
|
2030
|
-
def _get_keyword_from_self(self, name: str) -> Optional[KeywordDoc]:
|
2031
|
-
if get_robot_version() >= (6, 0):
|
2032
|
-
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
|
2033
|
-
(None, v) for v in self.self_library_doc.keywords.get_all(name)
|
2034
|
-
]
|
2035
|
-
if len(found) > 1:
|
2036
|
-
found = self._select_best_matches(found)
|
2037
|
-
if len(found) > 1:
|
2038
|
-
self.diagnostics.append(
|
2039
|
-
DiagnosticsEntry(
|
2040
|
-
self._create_multiple_keywords_found_message(name, found, implicit=False),
|
2041
|
-
DiagnosticSeverity.ERROR,
|
2042
|
-
Error.MULTIPLE_KEYWORDS,
|
2043
|
-
)
|
2044
|
-
)
|
2045
|
-
raise CancelSearchError
|
2046
|
-
|
2047
|
-
if len(found) == 1:
|
2048
|
-
# TODO warning if keyword found is defined in resource and suite
|
2049
|
-
return found[0][1]
|
2050
|
-
|
2051
|
-
return None
|
2052
|
-
|
2053
|
-
try:
|
2054
|
-
return self.self_library_doc.keywords.get(name, None)
|
2055
|
-
except KeywordError as e:
|
2056
|
-
self.diagnostics.append(DiagnosticsEntry(str(e), DiagnosticSeverity.ERROR, Error.KEYWORD_ERROR))
|
2057
|
-
raise CancelSearchError from e
|
2058
|
-
|
2059
|
-
def _yield_owner_and_kw_names(self, full_name: str) -> Iterator[Tuple[str, ...]]:
|
2060
|
-
tokens = full_name.split(".")
|
2061
|
-
for i in range(1, len(tokens)):
|
2062
|
-
yield ".".join(tokens[:i]), ".".join(tokens[i:])
|
2063
|
-
|
2064
|
-
def _get_explicit_keyword(self, name: str) -> Optional[KeywordDoc]:
|
2065
|
-
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = []
|
2066
|
-
for owner_name, kw_name in self._yield_owner_and_kw_names(name):
|
2067
|
-
found.extend(self.find_keywords(owner_name, kw_name))
|
2068
|
-
|
2069
|
-
if get_robot_version() >= (6, 0) and len(found) > 1:
|
2070
|
-
found = self._select_best_matches(found)
|
2071
|
-
|
2072
|
-
if len(found) > 1:
|
2073
|
-
self.diagnostics.append(
|
2074
|
-
DiagnosticsEntry(
|
2075
|
-
self._create_multiple_keywords_found_message(name, found, implicit=False),
|
2076
|
-
DiagnosticSeverity.ERROR,
|
2077
|
-
Error.MULTIPLE_KEYWORDS,
|
2078
|
-
)
|
2079
|
-
)
|
2080
|
-
raise CancelSearchError
|
2081
|
-
|
2082
|
-
return found[0][1] if found else None
|
2083
|
-
|
2084
|
-
def find_keywords(self, owner_name: str, name: str) -> List[Tuple[LibraryEntry, KeywordDoc]]:
|
2085
|
-
if self._all_keywords is None:
|
2086
|
-
self._all_keywords = list(
|
2087
|
-
chain(
|
2088
|
-
self.namespace._libraries.values(),
|
2089
|
-
self.namespace._resources.values(),
|
2090
|
-
)
|
2091
|
-
)
|
2092
|
-
|
2093
|
-
if get_robot_version() >= (6, 0):
|
2094
|
-
result: List[Tuple[LibraryEntry, KeywordDoc]] = []
|
2095
|
-
for v in self._all_keywords:
|
2096
|
-
if eq_namespace(v.alias or v.name, owner_name):
|
2097
|
-
result.extend((v, kw) for kw in v.library_doc.keywords.get_all(name))
|
2098
|
-
return result
|
2099
|
-
|
2100
|
-
result = []
|
2101
|
-
for v in self._all_keywords:
|
2102
|
-
if eq_namespace(v.alias or v.name, owner_name):
|
2103
|
-
kw = v.library_doc.keywords.get(name, None)
|
2104
|
-
if kw is not None:
|
2105
|
-
result.append((v, kw))
|
2106
|
-
return result
|
2107
|
-
|
2108
|
-
def _add_to_multiple_keywords_result(self, kw: Iterable[KeywordDoc]) -> None:
|
2109
|
-
if self.multiple_keywords_result is None:
|
2110
|
-
self.multiple_keywords_result = list(kw)
|
2111
|
-
else:
|
2112
|
-
self.multiple_keywords_result.extend(kw)
|
2113
|
-
|
2114
|
-
def _create_multiple_keywords_found_message(
|
2115
|
-
self,
|
2116
|
-
name: str,
|
2117
|
-
found: Sequence[Tuple[Optional[LibraryEntry], KeywordDoc]],
|
2118
|
-
implicit: bool = True,
|
2119
|
-
) -> str:
|
2120
|
-
self._add_to_multiple_keywords_result([k for _, k in found])
|
2121
|
-
|
2122
|
-
if any(e[1].is_embedded for e in found):
|
2123
|
-
error = f"Multiple keywords matching name '{name}' found"
|
2124
|
-
else:
|
2125
|
-
error = f"Multiple keywords with name '{name}' found"
|
2126
|
-
|
2127
|
-
if implicit:
|
2128
|
-
error += ". Give the full name of the keyword you want to use"
|
2129
|
-
|
2130
|
-
names = sorted(f"{e[1].name if e[0] is None else f'{e[0].alias or e[0].name}.{e[1].name}'}" for e in found)
|
2131
|
-
return "\n ".join([f"{error}:", *names])
|
2132
|
-
|
2133
|
-
def _get_implicit_keyword(self, name: str) -> Optional[KeywordDoc]:
|
2134
|
-
result = self._get_keyword_from_resource_files(name)
|
2135
|
-
if not result:
|
2136
|
-
return self._get_keyword_from_libraries(name)
|
2137
|
-
return result
|
2138
|
-
|
2139
|
-
def _prioritize_same_file_or_public(
|
2140
|
-
self, entries: List[Tuple[Optional[LibraryEntry], KeywordDoc]]
|
2141
|
-
) -> List[Tuple[Optional[LibraryEntry], KeywordDoc]]:
|
2142
|
-
matches = [h for h in entries if h[1].source == self.namespace.source]
|
2143
|
-
if matches:
|
2144
|
-
return matches
|
2145
|
-
|
2146
|
-
matches = [handler for handler in entries if not handler[1].is_private()]
|
2147
|
-
|
2148
|
-
return matches or entries
|
2149
|
-
|
2150
|
-
def _select_best_matches(
|
2151
|
-
self, entries: List[Tuple[Optional[LibraryEntry], KeywordDoc]]
|
2152
|
-
) -> List[Tuple[Optional[LibraryEntry], KeywordDoc]]:
|
2153
|
-
normal = [hand for hand in entries if not hand[1].is_embedded]
|
2154
|
-
if normal:
|
2155
|
-
return normal
|
2156
|
-
|
2157
|
-
matches = [hand for hand in entries if not self._is_worse_match_than_others(hand, entries)]
|
2158
|
-
return matches or entries
|
2159
|
-
|
2160
|
-
def _is_worse_match_than_others(
|
2161
|
-
self,
|
2162
|
-
candidate: Tuple[Optional[LibraryEntry], KeywordDoc],
|
2163
|
-
alternatives: List[Tuple[Optional[LibraryEntry], KeywordDoc]],
|
2164
|
-
) -> bool:
|
2165
|
-
for other in alternatives:
|
2166
|
-
if (
|
2167
|
-
candidate[1] is not other[1]
|
2168
|
-
and self._is_better_match(other, candidate)
|
2169
|
-
and not self._is_better_match(candidate, other)
|
2170
|
-
):
|
2171
|
-
return True
|
2172
|
-
return False
|
2173
|
-
|
2174
|
-
def _is_better_match(
|
2175
|
-
self,
|
2176
|
-
candidate: Tuple[Optional[LibraryEntry], KeywordDoc],
|
2177
|
-
other: Tuple[Optional[LibraryEntry], KeywordDoc],
|
2178
|
-
) -> bool:
|
2179
|
-
return (
|
2180
|
-
other[1].matcher.embedded_arguments.match(candidate[1].name) is not None
|
2181
|
-
and candidate[1].matcher.embedded_arguments.match(other[1].name) is None
|
2182
|
-
)
|
2183
|
-
|
2184
|
-
def _get_keyword_from_resource_files(self, name: str) -> Optional[KeywordDoc]:
|
2185
|
-
if self._resource_keywords is None:
|
2186
|
-
self._resource_keywords = list(chain(self.namespace._resources.values()))
|
2187
|
-
|
2188
|
-
if get_robot_version() >= (6, 0):
|
2189
|
-
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = []
|
2190
|
-
for v in self._resource_keywords:
|
2191
|
-
r = v.library_doc.keywords.get_all(name)
|
2192
|
-
if r:
|
2193
|
-
found.extend([(v, k) for k in r])
|
2194
|
-
else:
|
2195
|
-
found = []
|
2196
|
-
for k in self._resource_keywords:
|
2197
|
-
s = k.library_doc.keywords.get(name, None)
|
2198
|
-
if s is not None:
|
2199
|
-
found.append((k, s))
|
2200
|
-
|
2201
|
-
if not found:
|
2202
|
-
return None
|
2203
|
-
|
2204
|
-
if get_robot_version() >= (6, 0):
|
2205
|
-
if len(found) > 1:
|
2206
|
-
found = self._prioritize_same_file_or_public(found)
|
2207
|
-
|
2208
|
-
if len(found) > 1:
|
2209
|
-
found = self._select_best_matches(found)
|
2210
|
-
|
2211
|
-
if len(found) > 1:
|
2212
|
-
found = self._get_keyword_based_on_search_order(found)
|
2213
|
-
|
2214
|
-
else:
|
2215
|
-
if len(found) > 1:
|
2216
|
-
found = self._get_keyword_based_on_search_order(found)
|
2217
|
-
|
2218
|
-
if len(found) == 1:
|
2219
|
-
return found[0][1]
|
2220
|
-
|
2221
|
-
self.diagnostics.append(
|
2222
|
-
DiagnosticsEntry(
|
2223
|
-
self._create_multiple_keywords_found_message(name, found),
|
2224
|
-
DiagnosticSeverity.ERROR,
|
2225
|
-
Error.MULTIPLE_KEYWORDS,
|
2226
|
-
)
|
2227
|
-
)
|
2228
|
-
raise CancelSearchError
|
2229
|
-
|
2230
|
-
def _get_keyword_based_on_search_order(
|
2231
|
-
self, entries: List[Tuple[Optional[LibraryEntry], KeywordDoc]]
|
2232
|
-
) -> List[Tuple[Optional[LibraryEntry], KeywordDoc]]:
|
2233
|
-
for libname in self.namespace.search_order:
|
2234
|
-
for e in entries:
|
2235
|
-
if e[0] is not None and eq_namespace(libname, e[0].alias or e[0].name):
|
2236
|
-
return [e]
|
2237
|
-
|
2238
|
-
return entries
|
2239
|
-
|
2240
|
-
def _get_keyword_from_libraries(self, name: str) -> Optional[KeywordDoc]:
|
2241
|
-
if self._library_keywords is None:
|
2242
|
-
self._library_keywords = list(chain(self.namespace._libraries.values()))
|
2243
|
-
|
2244
|
-
if get_robot_version() >= (6, 0):
|
2245
|
-
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = []
|
2246
|
-
for v in self._library_keywords:
|
2247
|
-
r = v.library_doc.keywords.get_all(name)
|
2248
|
-
if r:
|
2249
|
-
found.extend([(v, k) for k in r])
|
2250
|
-
else:
|
2251
|
-
found = []
|
2252
|
-
|
2253
|
-
for k in self._library_keywords:
|
2254
|
-
s = k.library_doc.keywords.get(name, None)
|
2255
|
-
if s is not None:
|
2256
|
-
found.append((k, s))
|
2257
|
-
|
2258
|
-
if not found:
|
2259
|
-
return None
|
2260
|
-
|
2261
|
-
if get_robot_version() >= (6, 0):
|
2262
|
-
if len(found) > 1:
|
2263
|
-
found = self._select_best_matches(found)
|
2264
|
-
if len(found) > 1:
|
2265
|
-
found = self._get_keyword_based_on_search_order(found)
|
2266
|
-
else:
|
2267
|
-
if len(found) > 1:
|
2268
|
-
found = self._get_keyword_based_on_search_order(found)
|
2269
|
-
if len(found) == 2:
|
2270
|
-
found = self._filter_stdlib_runner(*found)
|
2271
|
-
|
2272
|
-
if len(found) == 1:
|
2273
|
-
return found[0][1]
|
2274
|
-
|
2275
|
-
self.diagnostics.append(
|
2276
|
-
DiagnosticsEntry(
|
2277
|
-
self._create_multiple_keywords_found_message(name, found),
|
2278
|
-
DiagnosticSeverity.ERROR,
|
2279
|
-
Error.MULTIPLE_KEYWORDS,
|
2280
|
-
)
|
2281
|
-
)
|
2282
|
-
raise CancelSearchError
|
2283
|
-
|
2284
|
-
def _filter_stdlib_runner(
|
2285
|
-
self,
|
2286
|
-
entry1: Tuple[Optional[LibraryEntry], KeywordDoc],
|
2287
|
-
entry2: Tuple[Optional[LibraryEntry], KeywordDoc],
|
2288
|
-
) -> List[Tuple[Optional[LibraryEntry], KeywordDoc]]:
|
2289
|
-
stdlibs_without_remote = STDLIBS - {"Remote"}
|
2290
|
-
if entry1[0] is not None and entry1[0].name in stdlibs_without_remote:
|
2291
|
-
standard, custom = entry1, entry2
|
2292
|
-
elif entry2[0] is not None and entry2[0].name in stdlibs_without_remote:
|
2293
|
-
standard, custom = entry2, entry1
|
2294
|
-
else:
|
2295
|
-
return [entry1, entry2]
|
2296
|
-
|
2297
|
-
self.diagnostics.append(
|
2298
|
-
DiagnosticsEntry(
|
2299
|
-
self._create_custom_and_standard_keyword_conflict_warning_message(custom, standard),
|
2300
|
-
DiagnosticSeverity.WARNING,
|
2301
|
-
Error.CONFLICTING_LIBRARY_KEYWORDS,
|
2302
|
-
)
|
2303
|
-
)
|
2304
|
-
|
2305
|
-
return [custom]
|
2306
|
-
|
2307
|
-
def _create_custom_and_standard_keyword_conflict_warning_message(
|
2308
|
-
self,
|
2309
|
-
custom: Tuple[Optional[LibraryEntry], KeywordDoc],
|
2310
|
-
standard: Tuple[Optional[LibraryEntry], KeywordDoc],
|
2311
|
-
) -> str:
|
2312
|
-
custom_with_name = standard_with_name = ""
|
2313
|
-
if custom[0] is not None and custom[0].alias is not None:
|
2314
|
-
custom_with_name = " imported as '%s'" % custom[0].alias
|
2315
|
-
if standard[0] is not None and standard[0].alias is not None:
|
2316
|
-
standard_with_name = " imported as '%s'" % standard[0].alias
|
2317
|
-
return (
|
2318
|
-
f"Keyword '{standard[1].name}' found both from a custom test library "
|
2319
|
-
f"'{'' if custom[0] is None else custom[0].name}'{custom_with_name} "
|
2320
|
-
f"and a standard library '{standard[1].name}'{standard_with_name}. "
|
2321
|
-
f"The custom keyword is used. To select explicitly, and to get "
|
2322
|
-
f"rid of this warning, use either "
|
2323
|
-
f"'{'' if custom[0] is None else custom[0].alias or custom[0].name}.{custom[1].name}' "
|
2324
|
-
f"or '{'' if standard[0] is None else standard[0].alias or standard[0].name}.{standard[1].name}'."
|
2325
|
-
)
|
2326
|
-
|
2327
|
-
def _get_bdd_style_keyword(self, name: str) -> Optional[KeywordDoc]:
|
2328
|
-
if get_robot_version() < (6, 0):
|
2329
|
-
lower = name.lower()
|
2330
|
-
for prefix in ["given ", "when ", "then ", "and ", "but "]:
|
2331
|
-
if lower.startswith(prefix):
|
2332
|
-
return self._find_keyword(name[len(prefix) :])
|
2333
|
-
return None
|
2334
|
-
|
2335
|
-
parts = name.split()
|
2336
|
-
if len(parts) < 2:
|
2337
|
-
return None
|
2338
|
-
for index in range(1, len(parts)):
|
2339
|
-
prefix = " ".join(parts[:index]).title()
|
2340
|
-
if prefix.title() in (
|
2341
|
-
self.namespace.languages.bdd_prefixes if self.namespace.languages is not None else DEFAULT_BDD_PREFIXES
|
2342
|
-
):
|
2343
|
-
return self._find_keyword(" ".join(parts[index:]))
|
2344
|
-
return None
|