robotcode-robot 0.93.1__py3-none-any.whl → 0.94.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|