robotcode-robot 0.95.0__py3-none-any.whl → 0.95.2__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/entities.py +8 -8
- robotcode/robot/diagnostics/imports_manager.py +3 -10
- robotcode/robot/diagnostics/keyword_finder.py +40 -39
- robotcode/robot/diagnostics/library_doc.py +197 -204
- robotcode/robot/diagnostics/model_helper.py +3 -3
- robotcode/robot/diagnostics/namespace.py +2 -7
- robotcode/robot/diagnostics/namespace_analyzer.py +8 -5
- robotcode/robot/utils/ast.py +42 -60
- robotcode/robot/utils/markdownformatter.py +11 -11
- robotcode/robot/utils/match.py +6 -5
- robotcode/robot/utils/robot_path.py +2 -2
- robotcode/robot/utils/stubs.py +1 -25
- robotcode/robot/utils/variables.py +5 -5
- robotcode/robot/utils/visitor.py +2 -28
- {robotcode_robot-0.95.0.dist-info → robotcode_robot-0.95.2.dist-info}/METADATA +2 -2
- robotcode_robot-0.95.2.dist-info/RECORD +32 -0
- robotcode_robot-0.95.0.dist-info/RECORD +0 -32
- {robotcode_robot-0.95.0.dist-info → robotcode_robot-0.95.2.dist-info}/WHEEL +0 -0
- {robotcode_robot-0.95.0.dist-info → robotcode_robot-0.95.2.dist-info}/licenses/LICENSE.txt +0 -0
@@ -44,7 +44,7 @@ from robot.output.logger import LOGGER
|
|
44
44
|
from robot.output.loggerhelper import AbstractLogger
|
45
45
|
from robot.parsing.lexer.tokens import Token
|
46
46
|
from robot.parsing.lexer.tokens import Token as RobotToken
|
47
|
-
from robot.parsing.model.blocks import Keyword
|
47
|
+
from robot.parsing.model.blocks import Keyword, KeywordSection, Section, SettingSection
|
48
48
|
from robot.parsing.model.statements import Arguments, KeywordName
|
49
49
|
from robot.running.arguments.argumentresolver import ArgumentResolver, DictToKwargs, NamedArgumentResolver
|
50
50
|
from robot.running.arguments.argumentresolver import VariableReplacer as ArgumentsVariableReplacer
|
@@ -64,7 +64,18 @@ from robot.variables.finders import VariableFinder
|
|
64
64
|
from robot.variables.replacer import VariableReplacer
|
65
65
|
from robotcode.core.lsp.types import Position, Range
|
66
66
|
from robotcode.core.utils.path import normalized_path
|
67
|
-
|
67
|
+
|
68
|
+
from ..utils import get_robot_version
|
69
|
+
from ..utils.ast import (
|
70
|
+
cached_isinstance,
|
71
|
+
get_variable_token,
|
72
|
+
range_from_token,
|
73
|
+
strip_variable_token,
|
74
|
+
)
|
75
|
+
from ..utils.markdownformatter import MarkDownFormatter
|
76
|
+
from ..utils.match import normalize, normalize_namespace
|
77
|
+
from ..utils.variables import contains_variable
|
78
|
+
from .entities import (
|
68
79
|
ArgumentDefinition,
|
69
80
|
ImportedVariableDefinition,
|
70
81
|
LibraryArgumentDefinition,
|
@@ -72,18 +83,6 @@ from robotcode.robot.diagnostics.entities import (
|
|
72
83
|
SourceEntity,
|
73
84
|
single_call,
|
74
85
|
)
|
75
|
-
from robotcode.robot.utils import get_robot_version
|
76
|
-
from robotcode.robot.utils.ast import (
|
77
|
-
cached_isinstance,
|
78
|
-
get_variable_token,
|
79
|
-
range_from_token,
|
80
|
-
strip_variable_token,
|
81
|
-
)
|
82
|
-
from robotcode.robot.utils.markdownformatter import MarkDownFormatter
|
83
|
-
from robotcode.robot.utils.match import normalize, normalize_namespace
|
84
|
-
from robotcode.robot.utils.stubs import HasError, HasErrors
|
85
|
-
|
86
|
-
from ..utils.variables import contains_variable
|
87
86
|
|
88
87
|
if get_robot_version() < (7, 0):
|
89
88
|
from robot.running.handlers import _PythonHandler, _PythonInitHandler # pyright: ignore[reportMissingImports]
|
@@ -201,22 +200,36 @@ def convert_from_rest(text: str) -> str:
|
|
201
200
|
|
202
201
|
if get_robot_version() >= (6, 0):
|
203
202
|
|
204
|
-
|
203
|
+
# monkey patch robot framework
|
204
|
+
_old_from_name = EmbeddedArguments.from_name
|
205
|
+
|
206
|
+
@functools.lru_cache(maxsize=8192)
|
207
|
+
def _new_from_name(name: str) -> EmbeddedArguments:
|
208
|
+
return _old_from_name(name)
|
209
|
+
|
210
|
+
EmbeddedArguments.from_name = _new_from_name
|
211
|
+
|
205
212
|
def _get_embedded_arguments(name: str) -> Any:
|
206
213
|
try:
|
207
214
|
return EmbeddedArguments.from_name(name)
|
208
215
|
except (VariableError, DataError):
|
209
216
|
return ()
|
210
217
|
|
218
|
+
def _match_embedded(embedded_arguments: EmbeddedArguments, name: str) -> bool:
|
219
|
+
return embedded_arguments.match(name) is not None
|
220
|
+
|
211
221
|
else:
|
212
222
|
|
213
|
-
@functools.lru_cache(maxsize=
|
223
|
+
@functools.lru_cache(maxsize=8192)
|
214
224
|
def _get_embedded_arguments(name: str) -> Any:
|
215
225
|
try:
|
216
226
|
return EmbeddedArguments(name)
|
217
227
|
except (VariableError, DataError):
|
218
228
|
return ()
|
219
229
|
|
230
|
+
def _match_embedded(embedded_arguments: EmbeddedArguments, name: str) -> bool:
|
231
|
+
return embedded_arguments.name.match(name) is not None
|
232
|
+
|
220
233
|
|
221
234
|
def is_embedded_keyword(name: str) -> bool:
|
222
235
|
try:
|
@@ -239,59 +252,50 @@ class KeywordMatcher:
|
|
239
252
|
self._can_have_embedded = can_have_embedded and not is_namespace
|
240
253
|
self._is_namespace = is_namespace
|
241
254
|
self._normalized_name: Optional[str] = None
|
242
|
-
|
255
|
+
|
256
|
+
self.embedded_arguments: Optional[EmbeddedArguments] = (
|
257
|
+
_get_embedded_arguments(self.name) or None if self._can_have_embedded else None
|
258
|
+
)
|
243
259
|
|
244
260
|
@property
|
245
261
|
def normalized_name(self) -> str:
|
246
262
|
if self._normalized_name is None:
|
247
|
-
self._normalized_name =
|
263
|
+
self._normalized_name = normalize_namespace(self.name) if self._is_namespace else normalize(self.name)
|
248
264
|
|
249
265
|
return self._normalized_name
|
250
266
|
|
251
|
-
@property
|
252
|
-
def embedded_arguments(self) -> Any:
|
253
|
-
if self._embedded_arguments is None:
|
254
|
-
if self._can_have_embedded:
|
255
|
-
self._embedded_arguments = _get_embedded_arguments(self.name)
|
256
|
-
else:
|
257
|
-
self._embedded_arguments = ()
|
258
|
-
|
259
|
-
return self._embedded_arguments
|
260
|
-
|
261
|
-
if get_robot_version() >= (6, 0):
|
262
|
-
|
263
|
-
def __match_embedded(self, name: str) -> bool:
|
264
|
-
return self.embedded_arguments.match(name) is not None
|
265
|
-
|
266
|
-
else:
|
267
|
-
|
268
|
-
def __match_embedded(self, name: str) -> bool:
|
269
|
-
return self.embedded_arguments.name.match(name) is not None
|
270
|
-
|
271
267
|
def __eq__(self, o: object) -> bool:
|
272
|
-
if
|
268
|
+
if type(o) is KeywordMatcher:
|
273
269
|
if self._is_namespace != o._is_namespace:
|
274
270
|
return False
|
275
271
|
|
276
|
-
if
|
272
|
+
if self.embedded_arguments is not None:
|
277
273
|
return self.normalized_name == o.normalized_name
|
278
274
|
|
279
275
|
o = o.name
|
280
276
|
|
281
|
-
if
|
277
|
+
if type(o) is not str:
|
282
278
|
return False
|
283
279
|
|
284
|
-
|
285
|
-
return self.__match_embedded(o)
|
280
|
+
return self.match_string(o)
|
286
281
|
|
287
|
-
|
282
|
+
def match_string(self, o: str) -> bool:
|
283
|
+
if self.embedded_arguments is not None:
|
284
|
+
return _match_embedded(self.embedded_arguments, o)
|
285
|
+
|
286
|
+
return self.normalized_name == (normalize_namespace(o) if self._is_namespace else normalize(o))
|
288
287
|
|
289
288
|
@single_call
|
290
289
|
def __hash__(self) -> int:
|
291
290
|
return hash(
|
292
|
-
(
|
293
|
-
|
294
|
-
|
291
|
+
(
|
292
|
+
self.normalized_name,
|
293
|
+
self._is_namespace,
|
294
|
+
self._can_have_embedded,
|
295
|
+
self.embedded_arguments,
|
296
|
+
self.embedded_arguments.name if self.embedded_arguments else None,
|
297
|
+
self.embedded_arguments.args if self.embedded_arguments else None,
|
298
|
+
)
|
295
299
|
)
|
296
300
|
|
297
301
|
def __str__(self) -> str:
|
@@ -618,7 +622,6 @@ class KeywordDoc(SourceEntity):
|
|
618
622
|
libname: Optional[str] = None
|
619
623
|
libtype: Optional[str] = None
|
620
624
|
longname: Optional[str] = None
|
621
|
-
is_embedded: bool = False
|
622
625
|
errors: Optional[List[Error]] = field(default=None, compare=False)
|
623
626
|
doc_format: str = ROBOT_DOC_FORMAT
|
624
627
|
is_error_handler: bool = False
|
@@ -667,13 +670,15 @@ class KeywordDoc(SourceEntity):
|
|
667
670
|
def __str__(self) -> str:
|
668
671
|
return f"{self.name}({', '.join(str(arg) for arg in self.arguments)})"
|
669
672
|
|
670
|
-
@
|
673
|
+
@functools.cached_property
|
674
|
+
def is_embedded(self) -> bool:
|
675
|
+
return self.matcher.embedded_arguments is not None
|
676
|
+
|
677
|
+
@functools.cached_property
|
671
678
|
def matcher(self) -> KeywordMatcher:
|
672
|
-
|
673
|
-
self.__matcher = KeywordMatcher(self.name)
|
674
|
-
return self.__matcher
|
679
|
+
return KeywordMatcher(self.name)
|
675
680
|
|
676
|
-
@
|
681
|
+
@functools.cached_property
|
677
682
|
def is_deprecated(self) -> bool:
|
678
683
|
return self.deprecated or DEPRECATED_PATTERN.match(self.doc) is not None
|
679
684
|
|
@@ -685,31 +690,31 @@ class KeywordDoc(SourceEntity):
|
|
685
690
|
def is_library_keyword(self) -> bool:
|
686
691
|
return self.libtype == "LIBRARY"
|
687
692
|
|
688
|
-
@
|
693
|
+
@functools.cached_property
|
689
694
|
def deprecated_message(self) -> str:
|
690
695
|
if (m := DEPRECATED_PATTERN.match(self.doc)) is not None:
|
691
696
|
return m.group("message").strip()
|
692
697
|
return ""
|
693
698
|
|
694
|
-
@
|
699
|
+
@functools.cached_property
|
695
700
|
def name_range(self) -> Range:
|
696
701
|
if self.name_token is not None:
|
697
702
|
return range_from_token(self.name_token)
|
698
703
|
|
699
704
|
return Range.invalid()
|
700
705
|
|
701
|
-
@
|
706
|
+
@functools.cached_property
|
702
707
|
def normalized_tags(self) -> List[str]:
|
703
708
|
return [normalize(tag) for tag in self.tags]
|
704
709
|
|
705
|
-
@
|
710
|
+
@functools.cached_property
|
706
711
|
def is_private(self) -> bool:
|
707
712
|
if get_robot_version() < (6, 0):
|
708
713
|
return False
|
709
714
|
|
710
|
-
return "robot:private" in self.normalized_tags
|
715
|
+
return "robot:private" in self.normalized_tags
|
711
716
|
|
712
|
-
@
|
717
|
+
@functools.cached_property
|
713
718
|
def range(self) -> Range:
|
714
719
|
if self.name_token is not None:
|
715
720
|
return range_from_token(self.name_token)
|
@@ -820,7 +825,7 @@ class KeywordDoc(SourceEntity):
|
|
820
825
|
|
821
826
|
return result
|
822
827
|
|
823
|
-
@
|
828
|
+
@functools.cached_property
|
824
829
|
def signature(self) -> str:
|
825
830
|
return (
|
826
831
|
f'({self.type}) "{self.name}": ('
|
@@ -893,7 +898,6 @@ class KeywordDoc(SourceEntity):
|
|
893
898
|
self.type,
|
894
899
|
self.libname,
|
895
900
|
self.libtype,
|
896
|
-
self.is_embedded,
|
897
901
|
self.is_initializer,
|
898
902
|
self.is_error_handler,
|
899
903
|
self.doc_format,
|
@@ -919,19 +923,13 @@ class KeywordStore:
|
|
919
923
|
source_type: Optional[str] = None
|
920
924
|
keywords: List[KeywordDoc] = field(default_factory=list)
|
921
925
|
|
922
|
-
@property
|
923
|
-
def _matchers(self) -> Dict[KeywordMatcher, KeywordDoc]:
|
924
|
-
if not hasattr(self, "__matchers"):
|
925
|
-
self.__matchers = {v.matcher: v for v in self.keywords}
|
926
|
-
return self.__matchers
|
927
|
-
|
928
926
|
def __getitem__(self, key: str) -> KeywordDoc:
|
929
|
-
items = [
|
927
|
+
items = [v for v in self.keywords if v.matcher == key]
|
930
928
|
|
931
929
|
if not items:
|
932
930
|
raise KeyError
|
933
931
|
if len(items) == 1:
|
934
|
-
return items[0]
|
932
|
+
return items[0]
|
935
933
|
|
936
934
|
if self.source and self.source_type:
|
937
935
|
file_info = ""
|
@@ -946,14 +944,14 @@ class KeywordStore:
|
|
946
944
|
else:
|
947
945
|
file_info = "File"
|
948
946
|
error = [f"{file_info} contains multiple keywords matching name '{key}':"]
|
949
|
-
names = sorted(
|
947
|
+
names = sorted(v.name for v in items)
|
950
948
|
raise KeywordError(
|
951
949
|
"\n ".join(error + names),
|
952
|
-
multiple_keywords=[v for
|
950
|
+
multiple_keywords=[v for v in items],
|
953
951
|
)
|
954
952
|
|
955
953
|
def __contains__(self, _x: object) -> bool:
|
956
|
-
return any(
|
954
|
+
return any(v.matcher == _x for v in self.keywords)
|
957
955
|
|
958
956
|
def __len__(self) -> int:
|
959
957
|
return len(self.keywords)
|
@@ -983,7 +981,7 @@ class KeywordStore:
|
|
983
981
|
return list(self.iter_all(key))
|
984
982
|
|
985
983
|
def iter_all(self, key: str) -> Iterable[KeywordDoc]:
|
986
|
-
|
984
|
+
return (v for v in self.keywords if v.matcher.match_string(key))
|
987
985
|
|
988
986
|
|
989
987
|
@dataclass
|
@@ -1296,12 +1294,12 @@ class VariablesDoc(LibraryDoc):
|
|
1296
1294
|
return result
|
1297
1295
|
|
1298
1296
|
|
1299
|
-
@functools.lru_cache(maxsize=
|
1297
|
+
@functools.lru_cache(maxsize=8192)
|
1300
1298
|
def is_library_by_path(path: str) -> bool:
|
1301
1299
|
return path.lower().endswith((".py", "/", os.sep))
|
1302
1300
|
|
1303
1301
|
|
1304
|
-
@functools.lru_cache(maxsize=
|
1302
|
+
@functools.lru_cache(maxsize=8192)
|
1305
1303
|
def is_variables_by_path(path: str) -> bool:
|
1306
1304
|
if get_robot_version() >= (6, 1):
|
1307
1305
|
return path.lower().endswith((".py", ".yml", ".yaml", ".json", "/", os.sep))
|
@@ -2030,7 +2028,6 @@ def get_library_doc(
|
|
2030
2028
|
libname=libdoc.name,
|
2031
2029
|
libtype=libdoc.type,
|
2032
2030
|
longname=f"{libdoc.name}.{kw[0].name}",
|
2033
|
-
is_embedded=is_embedded_keyword(kw[0].name),
|
2034
2031
|
doc_format=str(lib.doc_format) or ROBOT_DOC_FORMAT,
|
2035
2032
|
is_error_handler=kw[1].is_error_handler,
|
2036
2033
|
error_handler_message=kw[1].error_handler_message,
|
@@ -2710,133 +2707,146 @@ def complete_variables_import(
|
|
2710
2707
|
return list(set(result))
|
2711
2708
|
|
2712
2709
|
|
2713
|
-
|
2714
|
-
model: ast.AST,
|
2715
|
-
source: str,
|
2716
|
-
append_model_errors: bool = True,
|
2717
|
-
) -> LibraryDoc:
|
2718
|
-
errors: List[Error] = []
|
2719
|
-
keyword_name_nodes: List[KeywordName] = []
|
2720
|
-
keywords_nodes: List[Keyword] = []
|
2721
|
-
for node in ast.walk(model):
|
2722
|
-
if isinstance(node, Keyword):
|
2723
|
-
keywords_nodes.append(node)
|
2724
|
-
if isinstance(node, KeywordName):
|
2725
|
-
keyword_name_nodes.append(node)
|
2726
|
-
|
2727
|
-
error = node.error if isinstance(node, HasError) else None
|
2728
|
-
if error is not None:
|
2729
|
-
errors.append(
|
2730
|
-
Error(
|
2731
|
-
message=error,
|
2732
|
-
type_name="ModelError",
|
2733
|
-
source=source,
|
2734
|
-
line_no=node.lineno, # type: ignore
|
2735
|
-
)
|
2736
|
-
)
|
2737
|
-
if append_model_errors:
|
2738
|
-
node_errors = node.errors if isinstance(node, HasErrors) else None
|
2739
|
-
if node_errors is not None:
|
2740
|
-
for e in node_errors:
|
2741
|
-
errors.append(
|
2742
|
-
Error(
|
2743
|
-
message=e,
|
2744
|
-
type_name="ModelError",
|
2745
|
-
source=source,
|
2746
|
-
line_no=node.lineno, # type: ignore
|
2747
|
-
)
|
2748
|
-
)
|
2710
|
+
if get_robot_version() < (7, 0):
|
2749
2711
|
|
2750
|
-
|
2751
|
-
|
2752
|
-
if keyword_name.lineno == line:
|
2753
|
-
return cast(Token, keyword_name.get_token(RobotToken.KEYWORD_NAME))
|
2712
|
+
class _MyUserLibrary(UserLibrary):
|
2713
|
+
current_kw: Any = None
|
2754
2714
|
|
2755
|
-
|
2715
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
2716
|
+
self.errors: List[Error] = []
|
2717
|
+
super().__init__(*args, **kwargs)
|
2756
2718
|
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2762
|
-
|
2719
|
+
def _log_creating_failed(self, handler: UserErrorHandler, error: BaseException) -> None:
|
2720
|
+
err = Error(
|
2721
|
+
message=f"Creating keyword '{handler.name}' failed: {error!s}",
|
2722
|
+
type_name=type(error).__qualname__,
|
2723
|
+
source=self.current_kw.source if self.current_kw is not None else None,
|
2724
|
+
line_no=self.current_kw.lineno if self.current_kw is not None else None,
|
2725
|
+
)
|
2726
|
+
self.errors.append(err)
|
2763
2727
|
|
2764
|
-
|
2765
|
-
|
2766
|
-
|
2767
|
-
|
2768
|
-
|
2769
|
-
|
2728
|
+
def _create_handler(self, kw: Any) -> Any:
|
2729
|
+
self.current_kw = kw
|
2730
|
+
try:
|
2731
|
+
handler = super()._create_handler(kw)
|
2732
|
+
handler.errors = None
|
2733
|
+
except DataError as e:
|
2734
|
+
err = Error(
|
2735
|
+
message=str(e),
|
2736
|
+
type_name=type(e).__qualname__,
|
2737
|
+
source=kw.source,
|
2738
|
+
line_no=kw.lineno,
|
2739
|
+
)
|
2740
|
+
self.errors.append(err)
|
2770
2741
|
|
2771
|
-
|
2772
|
-
|
2773
|
-
|
2742
|
+
handler = UserErrorHandler(e, kw.name, self.name)
|
2743
|
+
handler.source = kw.source
|
2744
|
+
handler.lineno = kw.lineno
|
2774
2745
|
|
2775
|
-
|
2776
|
-
try:
|
2777
|
-
argument = get_variable_token(argument_token)
|
2778
|
-
|
2779
|
-
if argument is not None and argument.value != "@{}":
|
2780
|
-
if argument.value not in args:
|
2781
|
-
args.append(argument.value)
|
2782
|
-
arg_def = ArgumentDefinition(
|
2783
|
-
name=argument.value,
|
2784
|
-
name_token=strip_variable_token(argument),
|
2785
|
-
line_no=argument.lineno,
|
2786
|
-
col_offset=argument.col_offset,
|
2787
|
-
end_line_no=argument.lineno,
|
2788
|
-
end_col_offset=argument.end_col_offset,
|
2789
|
-
source=source,
|
2790
|
-
)
|
2791
|
-
argument_definitions.append(arg_def)
|
2746
|
+
handler.errors = [err]
|
2792
2747
|
|
2793
|
-
|
2794
|
-
pass
|
2748
|
+
return handler
|
2795
2749
|
|
2796
|
-
return argument_definitions
|
2797
2750
|
|
2798
|
-
|
2751
|
+
def _get_keyword_name_token_from_line(keyword_name_nodes: Dict[int, KeywordName], line: int) -> Optional[Token]:
|
2752
|
+
keyword_name = keyword_name_nodes.get(line, None)
|
2753
|
+
if keyword_name is None:
|
2754
|
+
return None
|
2755
|
+
return cast(Token, keyword_name.get_token(RobotToken.KEYWORD_NAME))
|
2799
2756
|
|
2800
|
-
with LOGGER.cache_only:
|
2801
|
-
ResourceBuilder(res).visit(model)
|
2802
2757
|
|
2803
|
-
|
2758
|
+
def _get_argument_definitions_from_line(
|
2759
|
+
keywords_nodes: Dict[int, Keyword],
|
2760
|
+
source: Optional[str],
|
2761
|
+
line: int,
|
2762
|
+
) -> List[ArgumentDefinition]:
|
2763
|
+
keyword_node = keywords_nodes.get(line, None)
|
2764
|
+
if keyword_node is None:
|
2765
|
+
return []
|
2804
2766
|
|
2805
|
-
|
2806
|
-
|
2767
|
+
arguments_node = next(
|
2768
|
+
(n for n in ast.walk(keyword_node) if isinstance(n, Arguments)),
|
2769
|
+
None,
|
2770
|
+
)
|
2771
|
+
if arguments_node is None:
|
2772
|
+
return []
|
2807
2773
|
|
2808
|
-
|
2809
|
-
|
2810
|
-
|
2811
|
-
type_name=type(error).__qualname__,
|
2812
|
-
source=self.current_kw.source if self.current_kw is not None else None,
|
2813
|
-
line_no=self.current_kw.lineno if self.current_kw is not None else None,
|
2814
|
-
)
|
2815
|
-
errors.append(err)
|
2774
|
+
args: List[str] = []
|
2775
|
+
arguments = arguments_node.get_tokens(RobotToken.ARGUMENT)
|
2776
|
+
argument_definitions = []
|
2816
2777
|
|
2817
|
-
|
2818
|
-
|
2819
|
-
|
2820
|
-
|
2821
|
-
|
2822
|
-
|
2823
|
-
|
2824
|
-
|
2825
|
-
|
2826
|
-
|
2827
|
-
line_no=
|
2778
|
+
for argument_token in (cast(RobotToken, e) for e in arguments):
|
2779
|
+
try:
|
2780
|
+
argument = get_variable_token(argument_token)
|
2781
|
+
|
2782
|
+
if argument is not None and argument.value != "@{}":
|
2783
|
+
if argument.value not in args:
|
2784
|
+
args.append(argument.value)
|
2785
|
+
arg_def = ArgumentDefinition(
|
2786
|
+
name=argument.value,
|
2787
|
+
name_token=strip_variable_token(argument),
|
2788
|
+
line_no=argument.lineno,
|
2789
|
+
col_offset=argument.col_offset,
|
2790
|
+
end_line_no=argument.lineno,
|
2791
|
+
end_col_offset=argument.end_col_offset,
|
2792
|
+
source=source,
|
2828
2793
|
)
|
2829
|
-
|
2794
|
+
argument_definitions.append(arg_def)
|
2830
2795
|
|
2831
|
-
|
2832
|
-
|
2833
|
-
handler.lineno = kw.lineno
|
2796
|
+
except VariableError:
|
2797
|
+
pass
|
2834
2798
|
|
2835
|
-
|
2799
|
+
return argument_definitions
|
2800
|
+
|
2801
|
+
|
2802
|
+
class _MyResourceBuilder(ResourceBuilder):
|
2803
|
+
def __init__(self, resource: Any) -> None:
|
2804
|
+
super().__init__(resource)
|
2805
|
+
self.keyword_name_nodes: Dict[int, KeywordName] = {}
|
2806
|
+
self.keywords_nodes: Dict[int, Keyword] = {}
|
2807
|
+
|
2808
|
+
def visit_Section(self, node: Section) -> None: # noqa: N802
|
2809
|
+
if isinstance(node, (SettingSection, KeywordSection)):
|
2810
|
+
self.generic_visit(node)
|
2811
|
+
|
2812
|
+
def visit_Keyword(self, node: Keyword) -> None: # noqa: N802
|
2813
|
+
self.keywords_nodes[node.lineno] = node
|
2814
|
+
super().visit_Keyword(node)
|
2815
|
+
if node.header is not None:
|
2816
|
+
self.keyword_name_nodes[node.lineno] = node.header
|
2817
|
+
|
2818
|
+
|
2819
|
+
def _get_kw_errors(kw: Any) -> Any:
|
2820
|
+
r = kw.errors if hasattr(kw, "errors") else None
|
2821
|
+
if get_robot_version() >= (7, 0) and kw.error:
|
2822
|
+
if not r:
|
2823
|
+
r = []
|
2824
|
+
r.append(
|
2825
|
+
Error(
|
2826
|
+
message=str(kw.error),
|
2827
|
+
type_name="KeywordError",
|
2828
|
+
source=kw.source,
|
2829
|
+
line_no=kw.lineno,
|
2830
|
+
)
|
2831
|
+
)
|
2832
|
+
return r
|
2836
2833
|
|
2837
|
-
return handler
|
2838
2834
|
|
2839
|
-
|
2835
|
+
def get_model_doc(
|
2836
|
+
model: ast.AST,
|
2837
|
+
source: str,
|
2838
|
+
) -> LibraryDoc:
|
2839
|
+
res = ResourceFile(source=source)
|
2840
|
+
|
2841
|
+
res_builder = _MyResourceBuilder(res)
|
2842
|
+
with LOGGER.cache_only:
|
2843
|
+
res_builder.visit(model)
|
2844
|
+
|
2845
|
+
keyword_name_nodes: Dict[int, KeywordName] = res_builder.keyword_name_nodes
|
2846
|
+
keywords_nodes: Dict[int, Keyword] = res_builder.keywords_nodes
|
2847
|
+
|
2848
|
+
if get_robot_version() < (7, 0):
|
2849
|
+
lib = _MyUserLibrary(res)
|
2840
2850
|
else:
|
2841
2851
|
lib = res
|
2842
2852
|
|
@@ -2847,24 +2857,8 @@ def get_model_doc(
|
|
2847
2857
|
scope="GLOBAL",
|
2848
2858
|
source=source,
|
2849
2859
|
line_no=1,
|
2850
|
-
errors=errors,
|
2851
2860
|
)
|
2852
2861
|
|
2853
|
-
def get_kw_errors(kw: Any) -> Any:
|
2854
|
-
r = kw.errors if hasattr(kw, "errors") else None
|
2855
|
-
if get_robot_version() >= (7, 0) and kw.error:
|
2856
|
-
if not r:
|
2857
|
-
r = []
|
2858
|
-
r.append(
|
2859
|
-
Error(
|
2860
|
-
message=str(kw.error),
|
2861
|
-
type_name="KeywordError",
|
2862
|
-
source=kw.source,
|
2863
|
-
line_no=kw.lineno,
|
2864
|
-
)
|
2865
|
-
)
|
2866
|
-
return r
|
2867
|
-
|
2868
2862
|
libdoc.keywords = KeywordStore(
|
2869
2863
|
source=libdoc.name,
|
2870
2864
|
source_type=libdoc.type,
|
@@ -2875,7 +2869,7 @@ def get_model_doc(
|
|
2875
2869
|
doc=kw[0].doc,
|
2876
2870
|
tags=list(kw[0].tags),
|
2877
2871
|
source=str(kw[0].source),
|
2878
|
-
name_token=
|
2872
|
+
name_token=_get_keyword_name_token_from_line(keyword_name_nodes, kw[0].lineno),
|
2879
2873
|
line_no=kw[0].lineno if kw[0].lineno is not None else -1,
|
2880
2874
|
col_offset=-1,
|
2881
2875
|
end_col_offset=-1,
|
@@ -2883,8 +2877,7 @@ def get_model_doc(
|
|
2883
2877
|
libname=libdoc.name,
|
2884
2878
|
libtype=libdoc.type,
|
2885
2879
|
longname=f"{libdoc.name}.{kw[0].name}",
|
2886
|
-
|
2887
|
-
errors=get_kw_errors(kw[1]),
|
2880
|
+
errors=_get_kw_errors(kw[1]),
|
2888
2881
|
is_error_handler=isinstance(kw[1], UserErrorHandler),
|
2889
2882
|
error_handler_message=(
|
2890
2883
|
str(cast(UserErrorHandler, kw[1]).error) if isinstance(kw[1], UserErrorHandler) else None
|
@@ -2892,7 +2885,7 @@ def get_model_doc(
|
|
2892
2885
|
arguments_spec=ArgumentSpec.from_robot_argument_spec(
|
2893
2886
|
kw[1].arguments if get_robot_version() < (7, 0) else kw[1].args
|
2894
2887
|
),
|
2895
|
-
argument_definitions=
|
2888
|
+
argument_definitions=_get_argument_definitions_from_line(keywords_nodes, source, kw[0].lineno),
|
2896
2889
|
)
|
2897
2890
|
for kw in [
|
2898
2891
|
(KeywordDocBuilder(resource=True).build_keyword(lw), lw)
|
@@ -665,12 +665,12 @@ class ModelHelper:
|
|
665
665
|
|
666
666
|
@classmethod
|
667
667
|
def get_keyword_definition_at_token(cls, library_doc: LibraryDoc, token: Token) -> Optional[KeywordDoc]:
|
668
|
-
return cls.get_keyword_definition_at_line(library_doc, token.
|
668
|
+
return cls.get_keyword_definition_at_line(library_doc, token.lineno)
|
669
669
|
|
670
670
|
@classmethod
|
671
|
-
def get_keyword_definition_at_line(cls, library_doc: LibraryDoc,
|
671
|
+
def get_keyword_definition_at_line(cls, library_doc: LibraryDoc, line: int) -> Optional[KeywordDoc]:
|
672
672
|
return next(
|
673
|
-
(k for k in library_doc.keywords.
|
673
|
+
(k for k in library_doc.keywords.keywords if k.line_no == line),
|
674
674
|
None,
|
675
675
|
)
|
676
676
|
|
@@ -910,11 +910,7 @@ class Namespace:
|
|
910
910
|
def get_library_doc(self) -> LibraryDoc:
|
911
911
|
with self._library_doc_lock:
|
912
912
|
if self._library_doc is None:
|
913
|
-
self._library_doc = self.imports_manager.get_libdoc_from_model(
|
914
|
-
self.model,
|
915
|
-
self.source,
|
916
|
-
append_model_errors=self.document_type is not None and self.document_type == DocumentType.RESOURCE,
|
917
|
-
)
|
913
|
+
self._library_doc = self.imports_manager.get_libdoc_from_model(self.model, self.source)
|
918
914
|
|
919
915
|
return self._library_doc
|
920
916
|
|
@@ -1887,7 +1883,6 @@ class Namespace:
|
|
1887
1883
|
source=DIAGNOSTICS_SOURCE_NAME,
|
1888
1884
|
code=err.type_name,
|
1889
1885
|
)
|
1890
|
-
# TODO: implement CancelationToken
|
1891
1886
|
except CancelledError:
|
1892
1887
|
canceled = True
|
1893
1888
|
self._logger.debug("analyzing canceled")
|
@@ -1904,7 +1899,7 @@ class Namespace:
|
|
1904
1899
|
|
1905
1900
|
def create_finder(self) -> "KeywordFinder":
|
1906
1901
|
self.ensure_initialized()
|
1907
|
-
return KeywordFinder(self
|
1902
|
+
return KeywordFinder(self)
|
1908
1903
|
|
1909
1904
|
@_logger.call(condition=lambda self, name, **kwargs: self._finder is not None and name not in self._finder._cache)
|
1910
1905
|
def find_keyword(
|