robotcode-robot 0.95.1__py3-none-any.whl → 0.96.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/entities.py +6 -6
- robotcode/robot/diagnostics/imports_manager.py +3 -16
- robotcode/robot/diagnostics/keyword_finder.py +28 -26
- robotcode/robot/diagnostics/library_doc.py +183 -173
- robotcode/robot/diagnostics/namespace.py +2 -7
- robotcode/robot/diagnostics/namespace_analyzer.py +8 -5
- robotcode/robot/diagnostics/workspace_config.py +6 -0
- robotcode/robot/utils/ast.py +42 -53
- robotcode/robot/utils/markdownformatter.py +11 -11
- robotcode/robot/utils/match.py +6 -5
- robotcode/robot/utils/stubs.py +1 -7
- robotcode/robot/utils/variables.py +5 -5
- robotcode/robot/utils/visitor.py +2 -1
- {robotcode_robot-0.95.1.dist-info → robotcode_robot-0.96.0.dist-info}/METADATA +3 -3
- robotcode_robot-0.96.0.dist-info/RECORD +32 -0
- robotcode_robot-0.95.1.dist-info/RECORD +0 -32
- {robotcode_robot-0.95.1.dist-info → robotcode_robot-0.96.0.dist-info}/WHEEL +0 -0
- {robotcode_robot-0.95.1.dist-info → robotcode_robot-0.96.0.dist-info}/licenses/LICENSE.txt +0 -0
robotcode/robot/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.
|
1
|
+
__version__ = "0.96.0"
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import functools
|
1
2
|
from dataclasses import dataclass, field
|
2
3
|
from enum import Enum
|
3
4
|
from typing import (
|
@@ -142,7 +143,7 @@ class VariableMatcher:
|
|
142
143
|
|
143
144
|
self.base = match.base
|
144
145
|
|
145
|
-
self.normalized_name =
|
146
|
+
self.normalized_name = normalize(self.base)
|
146
147
|
|
147
148
|
def __eq__(self, o: object) -> bool:
|
148
149
|
if type(o) is VariableMatcher:
|
@@ -154,7 +155,7 @@ class VariableMatcher:
|
|
154
155
|
if base is None:
|
155
156
|
return False
|
156
157
|
|
157
|
-
normalized =
|
158
|
+
normalized = normalize(base)
|
158
159
|
return self.normalized_name == normalized
|
159
160
|
|
160
161
|
return False
|
@@ -194,10 +195,9 @@ class VariableDefinition(SourceEntity):
|
|
194
195
|
value: Any = field(default=None, compare=False)
|
195
196
|
value_is_native: bool = field(default=False, compare=False)
|
196
197
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
self.matcher = VariableMatcher(self.name)
|
198
|
+
@functools.cached_property
|
199
|
+
def matcher(self) -> VariableMatcher:
|
200
|
+
return VariableMatcher(self.name)
|
201
201
|
|
202
202
|
@single_call
|
203
203
|
def __hash__(self) -> int:
|
@@ -573,12 +573,10 @@ class ImportsManager:
|
|
573
573
|
self._resource_document_changed_timer_interval = 1
|
574
574
|
self._resource_document_changed_documents: Set[TextDocument] = set()
|
575
575
|
|
576
|
-
self._resource_libdoc_cache: "weakref.WeakKeyDictionary[ast.AST, Dict[
|
576
|
+
self._resource_libdoc_cache: "weakref.WeakKeyDictionary[ast.AST, Dict[str, LibraryDoc]]" = (
|
577
577
|
weakref.WeakKeyDictionary()
|
578
578
|
)
|
579
579
|
|
580
|
-
self._process_pool_executor: Optional[ProcessPoolExecutor] = None
|
581
|
-
|
582
580
|
def __del__(self) -> None:
|
583
581
|
try:
|
584
582
|
if self._executor is not None:
|
@@ -1229,9 +1227,6 @@ class ImportsManager:
|
|
1229
1227
|
finally:
|
1230
1228
|
executor.shutdown(wait=True)
|
1231
1229
|
|
1232
|
-
if result.stdout:
|
1233
|
-
self._logger.warning(lambda: f"stdout captured at loading library {name}{args!r}:\n{result.stdout}")
|
1234
|
-
|
1235
1230
|
try:
|
1236
1231
|
if meta is not None:
|
1237
1232
|
meta.has_errors = bool(result.errors)
|
@@ -1302,9 +1297,8 @@ class ImportsManager:
|
|
1302
1297
|
self,
|
1303
1298
|
model: ast.AST,
|
1304
1299
|
source: str,
|
1305
|
-
append_model_errors: bool = True,
|
1306
1300
|
) -> LibraryDoc:
|
1307
|
-
key =
|
1301
|
+
key = source
|
1308
1302
|
|
1309
1303
|
entry = None
|
1310
1304
|
if model in self._resource_libdoc_cache:
|
@@ -1313,11 +1307,7 @@ class ImportsManager:
|
|
1313
1307
|
if entry and key in entry:
|
1314
1308
|
return entry[key]
|
1315
1309
|
|
1316
|
-
result = get_model_doc(
|
1317
|
-
model=model,
|
1318
|
-
source=source,
|
1319
|
-
append_model_errors=append_model_errors,
|
1320
|
-
)
|
1310
|
+
result = get_model_doc(model=model, source=source)
|
1321
1311
|
if entry is None:
|
1322
1312
|
entry = {}
|
1323
1313
|
self._resource_libdoc_cache[model] = entry
|
@@ -1409,9 +1399,6 @@ class ImportsManager:
|
|
1409
1399
|
finally:
|
1410
1400
|
executor.shutdown(True)
|
1411
1401
|
|
1412
|
-
if result.stdout:
|
1413
|
-
self._logger.warning(lambda: f"stdout captured at loading variables {name}{args!r}:\n{result.stdout}")
|
1414
|
-
|
1415
1402
|
try:
|
1416
1403
|
if meta is not None:
|
1417
1404
|
meta_file = meta.filepath_base + ".meta"
|
@@ -39,9 +39,8 @@ DEFAULT_BDD_PREFIXES = {"Given ", "When ", "Then ", "And ", "But "}
|
|
39
39
|
|
40
40
|
|
41
41
|
class KeywordFinder:
|
42
|
-
def __init__(self, namespace: "Namespace"
|
43
|
-
self.
|
44
|
-
self.self_library_doc = library_doc
|
42
|
+
def __init__(self, namespace: "Namespace") -> None:
|
43
|
+
self._namespace = namespace
|
45
44
|
|
46
45
|
self.diagnostics: List[DiagnosticsEntry] = []
|
47
46
|
self.result_bdd_prefix: Optional[str] = None
|
@@ -57,9 +56,9 @@ class KeywordFinder:
|
|
57
56
|
],
|
58
57
|
] = {}
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
self.
|
59
|
+
@functools.cached_property
|
60
|
+
def _library_doc(self) -> LibraryDoc:
|
61
|
+
return self._namespace.get_library_doc()
|
63
62
|
|
64
63
|
def reset_diagnostics(self) -> None:
|
65
64
|
self.diagnostics = []
|
@@ -162,7 +161,7 @@ class KeywordFinder:
|
|
162
161
|
def _get_keyword_from_self(self, name: str) -> Optional[KeywordDoc]:
|
163
162
|
if get_robot_version() >= (6, 0):
|
164
163
|
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
|
165
|
-
(None, v) for v in self.
|
164
|
+
(None, v) for v in self._library_doc.keywords.iter_all(name)
|
166
165
|
]
|
167
166
|
if len(found) > 1:
|
168
167
|
found = self._select_best_matches(found)
|
@@ -183,7 +182,7 @@ class KeywordFinder:
|
|
183
182
|
return None
|
184
183
|
|
185
184
|
try:
|
186
|
-
return self.
|
185
|
+
return self._library_doc.keywords.get(name, None)
|
187
186
|
except KeywordError as e:
|
188
187
|
self.diagnostics.append(DiagnosticsEntry(str(e), DiagnosticSeverity.ERROR, Error.KEYWORD_ERROR))
|
189
188
|
raise CancelSearchError from e
|
@@ -213,15 +212,16 @@ class KeywordFinder:
|
|
213
212
|
|
214
213
|
return found[0][1] if found else None
|
215
214
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
)
|
215
|
+
@functools.cached_property
|
216
|
+
def _all_keywords(self) -> List[LibraryEntry]:
|
217
|
+
return list(
|
218
|
+
chain(
|
219
|
+
self._namespace._libraries.values(),
|
220
|
+
self._namespace._resources.values(),
|
223
221
|
)
|
222
|
+
)
|
224
223
|
|
224
|
+
def find_keywords(self, owner_name: str, name: str) -> List[Tuple[LibraryEntry, KeywordDoc]]:
|
225
225
|
if get_robot_version() >= (6, 0):
|
226
226
|
result: List[Tuple[LibraryEntry, KeywordDoc]] = []
|
227
227
|
for v in self._all_keywords:
|
@@ -271,11 +271,11 @@ class KeywordFinder:
|
|
271
271
|
def _prioritize_same_file_or_public(
|
272
272
|
self, entries: List[Tuple[Optional[LibraryEntry], KeywordDoc]]
|
273
273
|
) -> List[Tuple[Optional[LibraryEntry], KeywordDoc]]:
|
274
|
-
matches = [h for h in entries if h[1].source == self.
|
274
|
+
matches = [h for h in entries if h[1].source == self._namespace.source]
|
275
275
|
if matches:
|
276
276
|
return matches
|
277
277
|
|
278
|
-
matches = [handler for handler in entries if not handler[1].is_private
|
278
|
+
matches = [handler for handler in entries if not handler[1].is_private]
|
279
279
|
|
280
280
|
return matches or entries
|
281
281
|
|
@@ -318,10 +318,11 @@ class KeywordFinder:
|
|
318
318
|
and candidate[1].matcher.embedded_arguments.match(other[1].name) is None
|
319
319
|
)
|
320
320
|
|
321
|
-
|
322
|
-
|
323
|
-
|
321
|
+
@functools.cached_property
|
322
|
+
def _resource_imports(self) -> List[ResourceEntry]:
|
323
|
+
return list(chain(self._namespace._resources.values()))
|
324
324
|
|
325
|
+
def _get_keyword_from_resource_files(self, name: str) -> Optional[KeywordDoc]:
|
325
326
|
if get_robot_version() >= (6, 0):
|
326
327
|
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
|
327
328
|
(v, k) for v in self._resource_imports for k in v.library_doc.keywords.iter_all(name)
|
@@ -365,17 +366,18 @@ class KeywordFinder:
|
|
365
366
|
def _get_keyword_based_on_search_order(
|
366
367
|
self, entries: List[Tuple[Optional[LibraryEntry], KeywordDoc]]
|
367
368
|
) -> List[Tuple[Optional[LibraryEntry], KeywordDoc]]:
|
368
|
-
for libname in self.
|
369
|
+
for libname in self._namespace.search_order:
|
369
370
|
for e in entries:
|
370
371
|
if e[0] is not None and eq_namespace(libname, e[0].alias or e[0].name):
|
371
372
|
return [e]
|
372
373
|
|
373
374
|
return entries
|
374
375
|
|
375
|
-
|
376
|
-
|
377
|
-
|
376
|
+
@functools.cached_property
|
377
|
+
def _library_imports(self) -> List[LibraryEntry]:
|
378
|
+
return list(chain(self._namespace._libraries.values()))
|
378
379
|
|
380
|
+
def _get_keyword_from_libraries(self, name: str) -> Optional[KeywordDoc]:
|
379
381
|
if get_robot_version() >= (6, 0):
|
380
382
|
found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
|
381
383
|
(v, k) for v in self._library_imports for k in v.library_doc.keywords.iter_all(name)
|
@@ -462,8 +464,8 @@ class KeywordFinder:
|
|
462
464
|
def bdd_prefix_regexp(self) -> "re.Pattern[str]":
|
463
465
|
prefixes = (
|
464
466
|
"|".join(
|
465
|
-
self.
|
466
|
-
if self.
|
467
|
+
self._namespace.languages.bdd_prefixes
|
468
|
+
if self._namespace.languages is not None
|
467
469
|
else ["given", "when", "then", "and", "but"]
|
468
470
|
)
|
469
471
|
.replace(" ", r"\s")
|
@@ -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
|
-
iter_nodes,
|
80
|
-
range_from_token,
|
81
|
-
strip_variable_token,
|
82
|
-
)
|
83
|
-
from robotcode.robot.utils.markdownformatter import MarkDownFormatter
|
84
|
-
from robotcode.robot.utils.match import normalize, normalize_namespace
|
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:
|
@@ -243,31 +256,20 @@ class KeywordMatcher:
|
|
243
256
|
self.embedded_arguments: Optional[EmbeddedArguments] = (
|
244
257
|
_get_embedded_arguments(self.name) or None if self._can_have_embedded else None
|
245
258
|
)
|
246
|
-
self._match_cache: Dict[str, bool] = {}
|
247
259
|
|
248
260
|
@property
|
249
261
|
def normalized_name(self) -> str:
|
250
262
|
if self._normalized_name is None:
|
251
|
-
self._normalized_name =
|
263
|
+
self._normalized_name = normalize_namespace(self.name) if self._is_namespace else normalize(self.name)
|
252
264
|
|
253
265
|
return self._normalized_name
|
254
266
|
|
255
|
-
if get_robot_version() >= (6, 0):
|
256
|
-
|
257
|
-
def __match_embedded(self, name: str) -> bool:
|
258
|
-
return self.embedded_arguments is not None and self.embedded_arguments.match(name) is not None
|
259
|
-
|
260
|
-
else:
|
261
|
-
|
262
|
-
def __match_embedded(self, name: str) -> bool:
|
263
|
-
return self.embedded_arguments is not None and self.embedded_arguments.name.match(name) is not None
|
264
|
-
|
265
267
|
def __eq__(self, o: object) -> bool:
|
266
268
|
if type(o) is KeywordMatcher:
|
267
269
|
if self._is_namespace != o._is_namespace:
|
268
270
|
return False
|
269
271
|
|
270
|
-
if
|
272
|
+
if self.embedded_arguments is not None:
|
271
273
|
return self.normalized_name == o.normalized_name
|
272
274
|
|
273
275
|
o = o.name
|
@@ -275,17 +277,28 @@ class KeywordMatcher:
|
|
275
277
|
if type(o) is not str:
|
276
278
|
return False
|
277
279
|
|
278
|
-
|
279
|
-
|
280
|
+
return self.match_string(o)
|
281
|
+
|
282
|
+
def match_string(self, o: str) -> bool:
|
283
|
+
if self.embedded_arguments is not None:
|
284
|
+
return _match_embedded(self.embedded_arguments, o)
|
280
285
|
|
281
|
-
return self.normalized_name ==
|
286
|
+
return self.normalized_name == (normalize_namespace(o) if self._is_namespace else normalize(o))
|
282
287
|
|
283
288
|
@single_call
|
284
289
|
def __hash__(self) -> int:
|
285
290
|
return hash(
|
286
|
-
(
|
287
|
-
|
288
|
-
|
291
|
+
(
|
292
|
+
self.normalized_name,
|
293
|
+
self._is_namespace,
|
294
|
+
self._can_have_embedded,
|
295
|
+
self.embedded_arguments.name if self.embedded_arguments else None,
|
296
|
+
(
|
297
|
+
tuple(self.embedded_arguments.args)
|
298
|
+
if self.embedded_arguments and self.embedded_arguments.args
|
299
|
+
else None
|
300
|
+
),
|
301
|
+
)
|
289
302
|
)
|
290
303
|
|
291
304
|
def __str__(self) -> str:
|
@@ -612,7 +625,6 @@ class KeywordDoc(SourceEntity):
|
|
612
625
|
libname: Optional[str] = None
|
613
626
|
libtype: Optional[str] = None
|
614
627
|
longname: Optional[str] = None
|
615
|
-
is_embedded: bool = False
|
616
628
|
errors: Optional[List[Error]] = field(default=None, compare=False)
|
617
629
|
doc_format: str = ROBOT_DOC_FORMAT
|
618
630
|
is_error_handler: bool = False
|
@@ -661,6 +673,10 @@ class KeywordDoc(SourceEntity):
|
|
661
673
|
def __str__(self) -> str:
|
662
674
|
return f"{self.name}({', '.join(str(arg) for arg in self.arguments)})"
|
663
675
|
|
676
|
+
@functools.cached_property
|
677
|
+
def is_embedded(self) -> bool:
|
678
|
+
return self.matcher.embedded_arguments is not None
|
679
|
+
|
664
680
|
@functools.cached_property
|
665
681
|
def matcher(self) -> KeywordMatcher:
|
666
682
|
return KeywordMatcher(self.name)
|
@@ -690,16 +706,16 @@ class KeywordDoc(SourceEntity):
|
|
690
706
|
|
691
707
|
return Range.invalid()
|
692
708
|
|
693
|
-
@
|
709
|
+
@functools.cached_property
|
694
710
|
def normalized_tags(self) -> List[str]:
|
695
711
|
return [normalize(tag) for tag in self.tags]
|
696
712
|
|
697
|
-
@
|
713
|
+
@functools.cached_property
|
698
714
|
def is_private(self) -> bool:
|
699
715
|
if get_robot_version() < (6, 0):
|
700
716
|
return False
|
701
717
|
|
702
|
-
return "robot:private" in self.normalized_tags
|
718
|
+
return "robot:private" in self.normalized_tags
|
703
719
|
|
704
720
|
@functools.cached_property
|
705
721
|
def range(self) -> Range:
|
@@ -885,7 +901,6 @@ class KeywordDoc(SourceEntity):
|
|
885
901
|
self.type,
|
886
902
|
self.libname,
|
887
903
|
self.libtype,
|
888
|
-
self.is_embedded,
|
889
904
|
self.is_initializer,
|
890
905
|
self.is_error_handler,
|
891
906
|
self.doc_format,
|
@@ -969,7 +984,7 @@ class KeywordStore:
|
|
969
984
|
return list(self.iter_all(key))
|
970
985
|
|
971
986
|
def iter_all(self, key: str) -> Iterable[KeywordDoc]:
|
972
|
-
|
987
|
+
return (v for v in self.keywords if v.matcher.match_string(key))
|
973
988
|
|
974
989
|
|
975
990
|
@dataclass
|
@@ -1282,12 +1297,12 @@ class VariablesDoc(LibraryDoc):
|
|
1282
1297
|
return result
|
1283
1298
|
|
1284
1299
|
|
1285
|
-
@functools.lru_cache(maxsize=
|
1300
|
+
@functools.lru_cache(maxsize=8192)
|
1286
1301
|
def is_library_by_path(path: str) -> bool:
|
1287
1302
|
return path.lower().endswith((".py", "/", os.sep))
|
1288
1303
|
|
1289
1304
|
|
1290
|
-
@functools.lru_cache(maxsize=
|
1305
|
+
@functools.lru_cache(maxsize=8192)
|
1291
1306
|
def is_variables_by_path(path: str) -> bool:
|
1292
1307
|
if get_robot_version() >= (6, 1):
|
1293
1308
|
return path.lower().endswith((".py", ".yml", ".yaml", ".json", "/", os.sep))
|
@@ -2016,7 +2031,6 @@ def get_library_doc(
|
|
2016
2031
|
libname=libdoc.name,
|
2017
2032
|
libtype=libdoc.type,
|
2018
2033
|
longname=f"{libdoc.name}.{kw[0].name}",
|
2019
|
-
is_embedded=is_embedded_keyword(kw[0].name),
|
2020
2034
|
doc_format=str(lib.doc_format) or ROBOT_DOC_FORMAT,
|
2021
2035
|
is_error_handler=kw[1].is_error_handler,
|
2022
2036
|
error_handler_message=kw[1].error_handler_message,
|
@@ -2696,133 +2710,146 @@ def complete_variables_import(
|
|
2696
2710
|
return list(set(result))
|
2697
2711
|
|
2698
2712
|
|
2699
|
-
|
2700
|
-
model: ast.AST,
|
2701
|
-
source: str,
|
2702
|
-
append_model_errors: bool = True,
|
2703
|
-
) -> LibraryDoc:
|
2704
|
-
errors: List[Error] = []
|
2705
|
-
keyword_name_nodes: Dict[int, KeywordName] = {}
|
2706
|
-
keywords_nodes: Dict[int, Keyword] = {}
|
2707
|
-
for node in iter_nodes(model):
|
2708
|
-
if cached_isinstance(node, Keyword):
|
2709
|
-
node.lineno
|
2710
|
-
keywords_nodes[node.lineno] = node
|
2711
|
-
if cached_isinstance(node, KeywordName):
|
2712
|
-
keyword_name_nodes[node.lineno] = node
|
2713
|
-
|
2714
|
-
error = getattr(node, "error", None)
|
2715
|
-
if error is not None:
|
2716
|
-
errors.append(
|
2717
|
-
Error(
|
2718
|
-
message=error,
|
2719
|
-
type_name="ModelError",
|
2720
|
-
source=source,
|
2721
|
-
line_no=node.lineno, # type: ignore
|
2722
|
-
)
|
2723
|
-
)
|
2724
|
-
if append_model_errors:
|
2725
|
-
node_errors = getattr(node, "errors", None)
|
2726
|
-
if node_errors is not None:
|
2727
|
-
for e in node_errors:
|
2728
|
-
errors.append(
|
2729
|
-
Error(
|
2730
|
-
message=e,
|
2731
|
-
type_name="ModelError",
|
2732
|
-
source=source,
|
2733
|
-
line_no=node.lineno, # type: ignore
|
2734
|
-
)
|
2735
|
-
)
|
2736
|
-
|
2737
|
-
def get_keyword_name_token_from_line(line: int) -> Optional[Token]:
|
2738
|
-
keyword_name = keyword_name_nodes.get(line, None)
|
2739
|
-
if keyword_name is None:
|
2740
|
-
return None
|
2741
|
-
return cast(Token, keyword_name.get_token(RobotToken.KEYWORD_NAME))
|
2713
|
+
if get_robot_version() < (7, 0):
|
2742
2714
|
|
2743
|
-
|
2744
|
-
|
2745
|
-
) -> List[ArgumentDefinition]:
|
2746
|
-
keyword_node = keywords_nodes.get(line, None)
|
2747
|
-
if keyword_node is None:
|
2748
|
-
return []
|
2715
|
+
class _MyUserLibrary(UserLibrary):
|
2716
|
+
current_kw: Any = None
|
2749
2717
|
|
2750
|
-
|
2751
|
-
|
2752
|
-
|
2753
|
-
)
|
2754
|
-
if arguments_node is None:
|
2755
|
-
return []
|
2718
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
2719
|
+
self.errors: List[Error] = []
|
2720
|
+
super().__init__(*args, **kwargs)
|
2756
2721
|
|
2757
|
-
|
2758
|
-
|
2759
|
-
|
2722
|
+
def _log_creating_failed(self, handler: UserErrorHandler, error: BaseException) -> None:
|
2723
|
+
err = Error(
|
2724
|
+
message=f"Creating keyword '{handler.name}' failed: {error!s}",
|
2725
|
+
type_name=type(error).__qualname__,
|
2726
|
+
source=self.current_kw.source if self.current_kw is not None else None,
|
2727
|
+
line_no=self.current_kw.lineno if self.current_kw is not None else None,
|
2728
|
+
)
|
2729
|
+
self.errors.append(err)
|
2760
2730
|
|
2761
|
-
|
2731
|
+
def _create_handler(self, kw: Any) -> Any:
|
2732
|
+
self.current_kw = kw
|
2762
2733
|
try:
|
2763
|
-
|
2764
|
-
|
2765
|
-
|
2766
|
-
|
2767
|
-
|
2768
|
-
|
2769
|
-
|
2770
|
-
|
2771
|
-
|
2772
|
-
|
2773
|
-
end_line_no=argument.lineno,
|
2774
|
-
end_col_offset=argument.end_col_offset,
|
2775
|
-
source=source,
|
2776
|
-
)
|
2777
|
-
argument_definitions.append(arg_def)
|
2734
|
+
handler = super()._create_handler(kw)
|
2735
|
+
handler.errors = None
|
2736
|
+
except DataError as e:
|
2737
|
+
err = Error(
|
2738
|
+
message=str(e),
|
2739
|
+
type_name=type(e).__qualname__,
|
2740
|
+
source=kw.source,
|
2741
|
+
line_no=kw.lineno,
|
2742
|
+
)
|
2743
|
+
self.errors.append(err)
|
2778
2744
|
|
2779
|
-
|
2780
|
-
|
2745
|
+
handler = UserErrorHandler(e, kw.name, self.name)
|
2746
|
+
handler.source = kw.source
|
2747
|
+
handler.lineno = kw.lineno
|
2781
2748
|
|
2782
|
-
|
2749
|
+
handler.errors = [err]
|
2783
2750
|
|
2784
|
-
|
2751
|
+
return handler
|
2785
2752
|
|
2786
|
-
with LOGGER.cache_only:
|
2787
|
-
ResourceBuilder(res).visit(model)
|
2788
2753
|
|
2789
|
-
|
2754
|
+
def _get_keyword_name_token_from_line(keyword_name_nodes: Dict[int, KeywordName], line: int) -> Optional[Token]:
|
2755
|
+
keyword_name = keyword_name_nodes.get(line, None)
|
2756
|
+
if keyword_name is None:
|
2757
|
+
return None
|
2758
|
+
return cast(Token, keyword_name.get_token(RobotToken.KEYWORD_NAME))
|
2790
2759
|
|
2791
|
-
class MyUserLibrary(UserLibrary):
|
2792
|
-
current_kw: Any = None
|
2793
2760
|
|
2794
|
-
|
2795
|
-
|
2796
|
-
|
2797
|
-
|
2798
|
-
|
2799
|
-
|
2800
|
-
|
2801
|
-
|
2761
|
+
def _get_argument_definitions_from_line(
|
2762
|
+
keywords_nodes: Dict[int, Keyword],
|
2763
|
+
source: Optional[str],
|
2764
|
+
line: int,
|
2765
|
+
) -> List[ArgumentDefinition]:
|
2766
|
+
keyword_node = keywords_nodes.get(line, None)
|
2767
|
+
if keyword_node is None:
|
2768
|
+
return []
|
2802
2769
|
|
2803
|
-
|
2804
|
-
|
2805
|
-
|
2806
|
-
|
2807
|
-
|
2808
|
-
|
2809
|
-
|
2810
|
-
|
2811
|
-
|
2812
|
-
|
2813
|
-
|
2770
|
+
arguments_node = next(
|
2771
|
+
(n for n in ast.walk(keyword_node) if isinstance(n, Arguments)),
|
2772
|
+
None,
|
2773
|
+
)
|
2774
|
+
if arguments_node is None:
|
2775
|
+
return []
|
2776
|
+
|
2777
|
+
args: List[str] = []
|
2778
|
+
arguments = arguments_node.get_tokens(RobotToken.ARGUMENT)
|
2779
|
+
argument_definitions = []
|
2780
|
+
|
2781
|
+
for argument_token in (cast(RobotToken, e) for e in arguments):
|
2782
|
+
try:
|
2783
|
+
argument = get_variable_token(argument_token)
|
2784
|
+
|
2785
|
+
if argument is not None and argument.value != "@{}":
|
2786
|
+
if argument.value not in args:
|
2787
|
+
args.append(argument.value)
|
2788
|
+
arg_def = ArgumentDefinition(
|
2789
|
+
name=argument.value,
|
2790
|
+
name_token=strip_variable_token(argument),
|
2791
|
+
line_no=argument.lineno,
|
2792
|
+
col_offset=argument.col_offset,
|
2793
|
+
end_line_no=argument.lineno,
|
2794
|
+
end_col_offset=argument.end_col_offset,
|
2795
|
+
source=source,
|
2814
2796
|
)
|
2815
|
-
|
2797
|
+
argument_definitions.append(arg_def)
|
2816
2798
|
|
2817
|
-
|
2818
|
-
|
2819
|
-
handler.lineno = kw.lineno
|
2799
|
+
except VariableError:
|
2800
|
+
pass
|
2820
2801
|
|
2821
|
-
|
2802
|
+
return argument_definitions
|
2803
|
+
|
2804
|
+
|
2805
|
+
class _MyResourceBuilder(ResourceBuilder):
|
2806
|
+
def __init__(self, resource: Any) -> None:
|
2807
|
+
super().__init__(resource)
|
2808
|
+
self.keyword_name_nodes: Dict[int, KeywordName] = {}
|
2809
|
+
self.keywords_nodes: Dict[int, Keyword] = {}
|
2810
|
+
|
2811
|
+
def visit_Section(self, node: Section) -> None: # noqa: N802
|
2812
|
+
if isinstance(node, (SettingSection, KeywordSection)):
|
2813
|
+
self.generic_visit(node)
|
2814
|
+
|
2815
|
+
def visit_Keyword(self, node: Keyword) -> None: # noqa: N802
|
2816
|
+
self.keywords_nodes[node.lineno] = node
|
2817
|
+
super().visit_Keyword(node)
|
2818
|
+
if node.header is not None:
|
2819
|
+
self.keyword_name_nodes[node.lineno] = node.header
|
2820
|
+
|
2821
|
+
|
2822
|
+
def _get_kw_errors(kw: Any) -> Any:
|
2823
|
+
r = kw.errors if hasattr(kw, "errors") else None
|
2824
|
+
if get_robot_version() >= (7, 0) and kw.error:
|
2825
|
+
if not r:
|
2826
|
+
r = []
|
2827
|
+
r.append(
|
2828
|
+
Error(
|
2829
|
+
message=str(kw.error),
|
2830
|
+
type_name="KeywordError",
|
2831
|
+
source=kw.source,
|
2832
|
+
line_no=kw.lineno,
|
2833
|
+
)
|
2834
|
+
)
|
2835
|
+
return r
|
2822
2836
|
|
2823
|
-
return handler
|
2824
2837
|
|
2825
|
-
|
2838
|
+
def get_model_doc(
|
2839
|
+
model: ast.AST,
|
2840
|
+
source: str,
|
2841
|
+
) -> LibraryDoc:
|
2842
|
+
res = ResourceFile(source=source)
|
2843
|
+
|
2844
|
+
res_builder = _MyResourceBuilder(res)
|
2845
|
+
with LOGGER.cache_only:
|
2846
|
+
res_builder.visit(model)
|
2847
|
+
|
2848
|
+
keyword_name_nodes: Dict[int, KeywordName] = res_builder.keyword_name_nodes
|
2849
|
+
keywords_nodes: Dict[int, Keyword] = res_builder.keywords_nodes
|
2850
|
+
|
2851
|
+
if get_robot_version() < (7, 0):
|
2852
|
+
lib = _MyUserLibrary(res)
|
2826
2853
|
else:
|
2827
2854
|
lib = res
|
2828
2855
|
|
@@ -2833,24 +2860,8 @@ def get_model_doc(
|
|
2833
2860
|
scope="GLOBAL",
|
2834
2861
|
source=source,
|
2835
2862
|
line_no=1,
|
2836
|
-
errors=errors,
|
2837
2863
|
)
|
2838
2864
|
|
2839
|
-
def get_kw_errors(kw: Any) -> Any:
|
2840
|
-
r = kw.errors if hasattr(kw, "errors") else None
|
2841
|
-
if get_robot_version() >= (7, 0) and kw.error:
|
2842
|
-
if not r:
|
2843
|
-
r = []
|
2844
|
-
r.append(
|
2845
|
-
Error(
|
2846
|
-
message=str(kw.error),
|
2847
|
-
type_name="KeywordError",
|
2848
|
-
source=kw.source,
|
2849
|
-
line_no=kw.lineno,
|
2850
|
-
)
|
2851
|
-
)
|
2852
|
-
return r
|
2853
|
-
|
2854
2865
|
libdoc.keywords = KeywordStore(
|
2855
2866
|
source=libdoc.name,
|
2856
2867
|
source_type=libdoc.type,
|
@@ -2861,7 +2872,7 @@ def get_model_doc(
|
|
2861
2872
|
doc=kw[0].doc,
|
2862
2873
|
tags=list(kw[0].tags),
|
2863
2874
|
source=str(kw[0].source),
|
2864
|
-
name_token=
|
2875
|
+
name_token=_get_keyword_name_token_from_line(keyword_name_nodes, kw[0].lineno),
|
2865
2876
|
line_no=kw[0].lineno if kw[0].lineno is not None else -1,
|
2866
2877
|
col_offset=-1,
|
2867
2878
|
end_col_offset=-1,
|
@@ -2869,8 +2880,7 @@ def get_model_doc(
|
|
2869
2880
|
libname=libdoc.name,
|
2870
2881
|
libtype=libdoc.type,
|
2871
2882
|
longname=f"{libdoc.name}.{kw[0].name}",
|
2872
|
-
|
2873
|
-
errors=get_kw_errors(kw[1]),
|
2883
|
+
errors=_get_kw_errors(kw[1]),
|
2874
2884
|
is_error_handler=isinstance(kw[1], UserErrorHandler),
|
2875
2885
|
error_handler_message=(
|
2876
2886
|
str(cast(UserErrorHandler, kw[1]).error) if isinstance(kw[1], UserErrorHandler) else None
|
@@ -2878,7 +2888,7 @@ def get_model_doc(
|
|
2878
2888
|
arguments_spec=ArgumentSpec.from_robot_argument_spec(
|
2879
2889
|
kw[1].arguments if get_robot_version() < (7, 0) else kw[1].args
|
2880
2890
|
),
|
2881
|
-
argument_definitions=
|
2891
|
+
argument_definitions=_get_argument_definitions_from_line(keywords_nodes, source, kw[0].lineno),
|
2882
2892
|
)
|
2883
2893
|
for kw in [
|
2884
2894
|
(KeywordDocBuilder(resource=True).build_keyword(lw), lw)
|
@@ -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(
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import ast
|
2
|
+
import functools
|
2
3
|
import itertools
|
3
4
|
import os
|
4
5
|
import token as python_token
|
@@ -70,7 +71,7 @@ from .entities import (
|
|
70
71
|
)
|
71
72
|
from .errors import DIAGNOSTICS_SOURCE_NAME, Error
|
72
73
|
from .keyword_finder import KeywordFinder
|
73
|
-
from .library_doc import KeywordDoc, is_embedded_keyword
|
74
|
+
from .library_doc import KeywordDoc, LibraryDoc, is_embedded_keyword
|
74
75
|
from .model_helper import ModelHelper
|
75
76
|
|
76
77
|
if TYPE_CHECKING:
|
@@ -697,7 +698,7 @@ class NamespaceAnalyzer(Visitor):
|
|
697
698
|
code=Error.RESERVED_KEYWORD,
|
698
699
|
)
|
699
700
|
|
700
|
-
if get_robot_version() >= (6, 0) and result.is_resource_keyword and result.is_private
|
701
|
+
if get_robot_version() >= (6, 0) and result.is_resource_keyword and result.is_private:
|
701
702
|
if self._namespace.source != result.source:
|
702
703
|
self._append_diagnostics(
|
703
704
|
range=kw_range,
|
@@ -1042,12 +1043,14 @@ class NamespaceAnalyzer(Visitor):
|
|
1042
1043
|
if name_token is not None and name_token.value:
|
1043
1044
|
self._analyze_token_variables(name_token, DiagnosticSeverity.HINT)
|
1044
1045
|
|
1046
|
+
@functools.cached_property
|
1047
|
+
def _namespace_lib_doc(self) -> LibraryDoc:
|
1048
|
+
return self._namespace.get_library_doc()
|
1049
|
+
|
1045
1050
|
def visit_Keyword(self, node: Keyword) -> None: # noqa: N802
|
1046
1051
|
if node.name:
|
1047
1052
|
name_token = node.header.get_token(Token.KEYWORD_NAME)
|
1048
|
-
self._current_keyword_doc = ModelHelper.get_keyword_definition_at_token(
|
1049
|
-
self._namespace.get_library_doc(), name_token
|
1050
|
-
)
|
1053
|
+
self._current_keyword_doc = ModelHelper.get_keyword_definition_at_token(self._namespace_lib_doc, name_token)
|
1051
1054
|
|
1052
1055
|
if self._current_keyword_doc is not None and self._current_keyword_doc not in self._keyword_references:
|
1053
1056
|
self._keyword_references[self._current_keyword_doc] = set()
|
@@ -67,6 +67,12 @@ class AnalysisDiagnosticModifiersConfig(ConfigBase):
|
|
67
67
|
hint: List[str] = field(default_factory=list)
|
68
68
|
|
69
69
|
|
70
|
+
@config_section("robotcode.workspace")
|
71
|
+
@dataclass
|
72
|
+
class WorkspaceConfig(ConfigBase):
|
73
|
+
exclude_patterns: List[str] = field(default_factory=list)
|
74
|
+
|
75
|
+
|
70
76
|
@dataclass
|
71
77
|
class WorkspaceAnalysisConfig:
|
72
78
|
cache: CacheConfig = field(default_factory=CacheConfig)
|
robotcode/robot/utils/ast.py
CHANGED
@@ -80,23 +80,23 @@ class FirstAndLastRealStatementFinder(Visitor):
|
|
80
80
|
self.last_statement = statement
|
81
81
|
|
82
82
|
|
83
|
+
_NON_DATA_TOKENS = {
|
84
|
+
Token.SEPARATOR,
|
85
|
+
Token.CONTINUATION,
|
86
|
+
Token.EOL,
|
87
|
+
Token.EOS,
|
88
|
+
}
|
89
|
+
|
90
|
+
_NON_DATA_TOKENS_WITH_COMMENT = {*_NON_DATA_TOKENS, Token.COMMENT}
|
91
|
+
|
92
|
+
|
83
93
|
def _get_non_data_range_from_node(
|
84
94
|
node: ast.AST, only_start: bool = False, allow_comments: bool = False
|
85
95
|
) -> Optional[Range]:
|
96
|
+
non_data_tokens = _NON_DATA_TOKENS_WITH_COMMENT if allow_comments else _NON_DATA_TOKENS
|
86
97
|
if cached_isinstance(node, Statement) and node.tokens:
|
87
98
|
start_token = next(
|
88
|
-
(
|
89
|
-
v
|
90
|
-
for v in node.tokens
|
91
|
-
if v.type
|
92
|
-
not in [
|
93
|
-
Token.SEPARATOR,
|
94
|
-
*([] if allow_comments else [Token.COMMENT]),
|
95
|
-
Token.CONTINUATION,
|
96
|
-
Token.EOL,
|
97
|
-
Token.EOS,
|
98
|
-
]
|
99
|
-
),
|
99
|
+
(v for v in node.tokens if v.type not in non_data_tokens),
|
100
100
|
None,
|
101
101
|
)
|
102
102
|
|
@@ -106,18 +106,7 @@ def _get_non_data_range_from_node(
|
|
106
106
|
end_tokens = node.tokens
|
107
107
|
|
108
108
|
end_token = next(
|
109
|
-
(
|
110
|
-
v
|
111
|
-
for v in reversed(end_tokens)
|
112
|
-
if v.type
|
113
|
-
not in [
|
114
|
-
Token.SEPARATOR,
|
115
|
-
*([] if allow_comments else [Token.COMMENT]),
|
116
|
-
Token.CONTINUATION,
|
117
|
-
Token.EOL,
|
118
|
-
Token.EOS,
|
119
|
-
]
|
120
|
-
),
|
109
|
+
(v for v in reversed(end_tokens) if v.type not in non_data_tokens),
|
121
110
|
None,
|
122
111
|
)
|
123
112
|
if start_token is not None and end_token is not None:
|
@@ -282,35 +271,35 @@ def tokenize_variables(
|
|
282
271
|
return _tokenize_variables(token, variables)
|
283
272
|
|
284
273
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
274
|
+
def _tokenize_variables_before7(token: Token, variables: Any) -> Iterator[Token]:
|
275
|
+
lineno = token.lineno
|
276
|
+
col_offset = token.col_offset
|
277
|
+
remaining = ""
|
278
|
+
for before, variable, remaining in variables:
|
279
|
+
if before:
|
280
|
+
yield Token(token.type, before, lineno, col_offset)
|
281
|
+
col_offset += len(before)
|
282
|
+
yield Token(Token.VARIABLE, variable, lineno, col_offset)
|
283
|
+
col_offset += len(variable)
|
284
|
+
if remaining:
|
285
|
+
yield Token(token.type, remaining, lineno, col_offset)
|
286
|
+
|
287
|
+
|
288
|
+
def _tokenize_variables_v7(token: Token, variables: Any) -> Iterator[Token]:
|
289
|
+
lineno = token.lineno
|
290
|
+
col_offset = token.col_offset
|
291
|
+
after = ""
|
292
|
+
for match in variables:
|
293
|
+
if match.before:
|
294
|
+
yield Token(token.type, match.before, lineno, col_offset)
|
295
|
+
yield Token(Token.VARIABLE, match.match, lineno, col_offset + match.start)
|
296
|
+
col_offset += match.end
|
297
|
+
after = match.after
|
298
|
+
if after:
|
299
|
+
yield Token(token.type, after, lineno, col_offset)
|
300
|
+
|
301
|
+
|
302
|
+
_tokenize_variables = _tokenize_variables_before7 if get_robot_version() < (7, 0) else _tokenize_variables_v7
|
314
303
|
|
315
304
|
|
316
305
|
def iter_over_keyword_names_and_owners(
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
3
3
|
import itertools
|
4
4
|
import re
|
5
5
|
from abc import ABC, abstractmethod
|
6
|
-
from typing import Any, Callable, Iterator, List, Optional, Tuple
|
6
|
+
from typing import Any, Callable, Final, Iterator, List, Optional, Tuple
|
7
7
|
|
8
8
|
|
9
9
|
class Formatter(ABC):
|
@@ -87,7 +87,7 @@ class SingleLineFormatter(Formatter):
|
|
87
87
|
|
88
88
|
|
89
89
|
class HeaderFormatter(SingleLineFormatter):
|
90
|
-
_regex = re.compile(r"^(={1,5})\s+(\S.*?)\s+\1$")
|
90
|
+
_regex: Final["re.Pattern[str]"] = re.compile(r"^(={1,5})\s+(\S.*?)\s+\1$")
|
91
91
|
|
92
92
|
def match(self, line: str) -> Optional[re.Match[str]]:
|
93
93
|
return self._regex.match(line)
|
@@ -103,8 +103,8 @@ class HeaderFormatter(SingleLineFormatter):
|
|
103
103
|
|
104
104
|
class LinkFormatter:
|
105
105
|
_image_exts = (".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg")
|
106
|
-
_link = re.compile(r"\[(.+?\|.*?)\]")
|
107
|
-
_url = re.compile(
|
106
|
+
_link: Final["re.Pattern[str]"] = re.compile(r"\[(.+?\|.*?)\]")
|
107
|
+
_url: Final["re.Pattern[str]"] = re.compile(
|
108
108
|
r"""
|
109
109
|
((^|\ ) ["'(\[{]*) # begin of line or space and opt. any char "'([{
|
110
110
|
([a-z][\w+-.]*://[^\s|]+?) # url
|
@@ -177,7 +177,7 @@ class LinkFormatter:
|
|
177
177
|
|
178
178
|
|
179
179
|
class LineFormatter:
|
180
|
-
_bold = re.compile(
|
180
|
+
_bold: Final["re.Pattern[str]"] = re.compile(
|
181
181
|
r"""
|
182
182
|
( # prefix (group 1)
|
183
183
|
(^|\ ) # begin of line or space
|
@@ -193,7 +193,7 @@ class LineFormatter:
|
|
193
193
|
""",
|
194
194
|
re.VERBOSE,
|
195
195
|
)
|
196
|
-
_italic = re.compile(
|
196
|
+
_italic: Final["re.Pattern[str]"] = re.compile(
|
197
197
|
r"""
|
198
198
|
( (^|\ ) ["'(]* ) # begin of line or space and opt. any char "'(
|
199
199
|
_ # start of italic
|
@@ -203,7 +203,7 @@ _ # end of italic
|
|
203
203
|
""",
|
204
204
|
re.VERBOSE,
|
205
205
|
)
|
206
|
-
_code = re.compile(
|
206
|
+
_code: Final["re.Pattern[str]"] = re.compile(
|
207
207
|
r"""
|
208
208
|
( (^|\ ) ["'(]* ) # same as above with _ changed to ``
|
209
209
|
``
|
@@ -296,7 +296,7 @@ class ListFormatter(Formatter):
|
|
296
296
|
|
297
297
|
|
298
298
|
class RulerFormatter(SingleLineFormatter):
|
299
|
-
regex = re.compile("^-{3,}$")
|
299
|
+
regex: Final["re.Pattern[str]"] = re.compile("^-{3,}$")
|
300
300
|
|
301
301
|
def match(self, line: str) -> Optional[re.Match[str]]:
|
302
302
|
return self.regex.match(line)
|
@@ -306,9 +306,9 @@ class RulerFormatter(SingleLineFormatter):
|
|
306
306
|
|
307
307
|
|
308
308
|
class TableFormatter(Formatter):
|
309
|
-
_table_line = re.compile(r"^\| (.* |)\|$")
|
310
|
-
_line_splitter = re.compile(r" \|(?= )")
|
311
|
-
_format_cell_content = _line_formatter.format
|
309
|
+
_table_line: Final["re.Pattern[str]"] = re.compile(r"^\| (.* |)\|$")
|
310
|
+
_line_splitter: Final["re.Pattern[str]"] = re.compile(r" \|(?= )")
|
311
|
+
_format_cell_content: Final[Callable[[str], str]] = _line_formatter.format
|
312
312
|
|
313
313
|
def _handles(self, line: str) -> bool:
|
314
314
|
return self._table_line.match(line) is not None
|
robotcode/robot/utils/match.py
CHANGED
@@ -2,16 +2,17 @@ from functools import lru_cache
|
|
2
2
|
|
3
3
|
_transform_table = str.maketrans("", "", "_ ")
|
4
4
|
|
5
|
+
_transform_table_namespace = str.maketrans("", "", " ")
|
5
6
|
|
6
|
-
|
7
|
+
|
8
|
+
@lru_cache(maxsize=8192)
|
7
9
|
def normalize(text: str) -> str:
|
8
|
-
|
9
|
-
return text.casefold().translate(_transform_table)
|
10
|
+
return text.translate(_transform_table).casefold()
|
10
11
|
|
11
12
|
|
12
|
-
@lru_cache(maxsize=
|
13
|
+
@lru_cache(maxsize=8192)
|
13
14
|
def normalize_namespace(text: str) -> str:
|
14
|
-
return text.
|
15
|
+
return text.translate(_transform_table_namespace).casefold()
|
15
16
|
|
16
17
|
|
17
18
|
def eq(str1: str, str2: str) -> bool:
|
robotcode/robot/utils/stubs.py
CHANGED
@@ -1,12 +1,6 @@
|
|
1
|
-
from typing import Any, Dict, Iterator, List, Protocol, Set
|
1
|
+
from typing import Any, Dict, Iterator, List, Protocol, Set
|
2
2
|
|
3
3
|
|
4
|
-
@runtime_checkable
|
5
|
-
class BodyBlock(Protocol):
|
6
|
-
body: List[Any]
|
7
|
-
|
8
|
-
|
9
|
-
@runtime_checkable
|
10
4
|
class Languages(Protocol):
|
11
5
|
languages: List[Any]
|
12
6
|
headers: Dict[str, str]
|
@@ -47,26 +47,26 @@ BUILTIN_VARIABLES = [
|
|
47
47
|
]
|
48
48
|
|
49
49
|
|
50
|
-
@functools.lru_cache(maxsize=
|
50
|
+
@functools.lru_cache(maxsize=8192)
|
51
51
|
def contains_variable(string: str, identifiers: str = "$@&") -> bool:
|
52
52
|
return cast(bool, robot_contains_variable(string, identifiers))
|
53
53
|
|
54
54
|
|
55
|
-
@functools.lru_cache(maxsize=
|
55
|
+
@functools.lru_cache(maxsize=8192)
|
56
56
|
def is_scalar_assign(string: str, allow_assign_mark: bool = False) -> bool:
|
57
57
|
return cast(bool, robot_is_scalar_assign(string, allow_assign_mark))
|
58
58
|
|
59
59
|
|
60
|
-
@functools.lru_cache(maxsize=
|
60
|
+
@functools.lru_cache(maxsize=8192)
|
61
61
|
def is_variable(string: str, identifiers: str = "$@&") -> bool:
|
62
62
|
return cast(bool, robot_is_variable(string, identifiers))
|
63
63
|
|
64
64
|
|
65
|
-
@functools.lru_cache(maxsize=
|
65
|
+
@functools.lru_cache(maxsize=8192)
|
66
66
|
def search_variable(string: str, identifiers: str = "$@&%*", ignore_errors: bool = False) -> RobotVariableMatch:
|
67
67
|
return robot_search_variable(string, identifiers, ignore_errors)
|
68
68
|
|
69
69
|
|
70
|
-
@functools.lru_cache(maxsize=
|
70
|
+
@functools.lru_cache(maxsize=8192)
|
71
71
|
def split_from_equals(string: str) -> Tuple[str, Optional[str]]:
|
72
72
|
return cast(Tuple[str, Optional[str]], robot_split_from_equals(string))
|
robotcode/robot/utils/visitor.py
CHANGED
@@ -3,6 +3,7 @@ from abc import ABC
|
|
3
3
|
from typing import (
|
4
4
|
Any,
|
5
5
|
Callable,
|
6
|
+
ClassVar,
|
6
7
|
Dict,
|
7
8
|
Iterator,
|
8
9
|
Optional,
|
@@ -37,7 +38,7 @@ def iter_field_values(node: ast.AST) -> Iterator[Any]:
|
|
37
38
|
|
38
39
|
|
39
40
|
class VisitorFinder(ABC):
|
40
|
-
__cls_finder_cache__: Dict[Type[Any], Optional[Callable[..., Any]]]
|
41
|
+
__cls_finder_cache__: ClassVar[Dict[Type[Any], Optional[Callable[..., Any]]]]
|
41
42
|
|
42
43
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
43
44
|
super().__init_subclass__(**kwargs)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: robotcode-robot
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.96.0
|
4
4
|
Summary: Support classes for RobotCode for handling Robot Framework projects.
|
5
5
|
Project-URL: Homepage, https://robotcode.io
|
6
6
|
Project-URL: Donate, https://opencollective.com/robotcode
|
@@ -25,8 +25,8 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
25
25
|
Classifier: Topic :: Utilities
|
26
26
|
Classifier: Typing :: Typed
|
27
27
|
Requires-Python: >=3.8
|
28
|
-
Requires-Dist: platformdirs<4.
|
29
|
-
Requires-Dist: robotcode-core==0.
|
28
|
+
Requires-Dist: platformdirs<4.4.0,>=3.2.0
|
29
|
+
Requires-Dist: robotcode-core==0.96.0
|
30
30
|
Requires-Dist: robotframework>=4.1.0
|
31
31
|
Requires-Dist: tomli>=1.1.0; python_version < '3.11'
|
32
32
|
Description-Content-Type: text/markdown
|
@@ -0,0 +1,32 @@
|
|
1
|
+
robotcode/robot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
robotcode/robot/__version__.py,sha256=B-kjJajURz8O11CWqC3d4KQ5aeL4lukk9XP51Xbpo8o,23
|
3
|
+
robotcode/robot/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
|
4
|
+
robotcode/robot/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
robotcode/robot/config/loader.py,sha256=bNJwr_XdCoUzpG2ag0BH33PIfiCwn0GMxn7q_Sw3zOk,8103
|
6
|
+
robotcode/robot/config/model.py,sha256=sgr6-4_E06g-yIXW41Z-NtIXZ_7JMmR5WvUD7kTUqu4,89106
|
7
|
+
robotcode/robot/config/utils.py,sha256=xY-LH31BidWzonpvSrle-4HvKrp02I7IRqU2JwlL4Ls,2931
|
8
|
+
robotcode/robot/diagnostics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
robotcode/robot/diagnostics/data_cache.py,sha256=Wge9HuxSUiBVMmrmlsYSMmG2ad7f3Texwox0Dm8lN7U,2969
|
10
|
+
robotcode/robot/diagnostics/diagnostics_modifier.py,sha256=3dDsu8-ET6weIvv7Sk3IQaPYFNxnXUs8Y7gpGTjfOBs,9796
|
11
|
+
robotcode/robot/diagnostics/document_cache_helper.py,sha256=n903UxVXM4Uq4fPxN5s-dugQAKcWUwf4Nw4q0CJV7aw,23902
|
12
|
+
robotcode/robot/diagnostics/entities.py,sha256=b4u2yQN8MDg90RoTMaW7iLogiDNwOAtK180KCB94RfE,10970
|
13
|
+
robotcode/robot/diagnostics/errors.py,sha256=vRH7HiZOfQIC-L7ys2Bj9ULYxLpUH7I03qJRSkEx08k,1813
|
14
|
+
robotcode/robot/diagnostics/imports_manager.py,sha256=zMHqs8WGis9r74FR6Jb4AqpR15EmGtdBWpMq9R_GZMA,58604
|
15
|
+
robotcode/robot/diagnostics/keyword_finder.py,sha256=dm4BA0ccp5V4C65CkSYUJUNXegSmvG24uu09T3eL6a4,17319
|
16
|
+
robotcode/robot/diagnostics/library_doc.py,sha256=VPCX7xp-0LJiYSFLO68y8MuNAMIYcnhJTIHRmWPpl30,100507
|
17
|
+
robotcode/robot/diagnostics/model_helper.py,sha256=ltuUNWwZJFBmMFXIomMmW1IP5v7tMpQSoC1YbncgoNI,30985
|
18
|
+
robotcode/robot/diagnostics/namespace.py,sha256=lJOkaS_yCp8SVhURqh5NqAsm394s0cHZUMQwVeh9nno,75159
|
19
|
+
robotcode/robot/diagnostics/namespace_analyzer.py,sha256=MgEoEGH7FvwVYoR3wA0JEGQxMWJTUUHq10NrorJV5LY,74183
|
20
|
+
robotcode/robot/diagnostics/workspace_config.py,sha256=0QLcjyyDHsirH0Y6o1RvBEi3_jgJxcp2zvHT1dSQuWU,2627
|
21
|
+
robotcode/robot/utils/__init__.py,sha256=OjNPMn_XSnfaMCyKd8Kmq6vlRt6mIGlzW4qiiD3ykUg,447
|
22
|
+
robotcode/robot/utils/ast.py,sha256=eqAVVquoRbMw3WvGmK6FnkUjZzAxHAitVjqK-vx-HSY,10764
|
23
|
+
robotcode/robot/utils/markdownformatter.py,sha256=SdHFfK9OdBnljWMP5r5Jy2behtHy-_Myd7GV4hiH-kI,11688
|
24
|
+
robotcode/robot/utils/match.py,sha256=9tG1OD9KS1v9ocWgsERSf6z_w9gAeE5LourNUYHzvTM,653
|
25
|
+
robotcode/robot/utils/robot_path.py,sha256=Z-GVBOPA_xeD20bCJi4_AWaU0eQWvCym-YFtyRpXARE,1767
|
26
|
+
robotcode/robot/utils/stubs.py,sha256=umugZYAyneFNgqRJBRMJPzm0u0B_TH8Sx_y-ykXnxpw,351
|
27
|
+
robotcode/robot/utils/variables.py,sha256=-ldL8mRRSYYW2pwlm8IpoDeQcG6LYBqaYyV_7U3xsIc,2174
|
28
|
+
robotcode/robot/utils/visitor.py,sha256=nP3O0qh3YYuxR6S8wYJRBFfNwIVgsgohURBlrnFkRYQ,2299
|
29
|
+
robotcode_robot-0.96.0.dist-info/METADATA,sha256=PPFZwhpqJldo2MUkr0Kg4Qa_hx4EkAbJIZCqsG124UE,2240
|
30
|
+
robotcode_robot-0.96.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
31
|
+
robotcode_robot-0.96.0.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
|
32
|
+
robotcode_robot-0.96.0.dist-info/RECORD,,
|
@@ -1,32 +0,0 @@
|
|
1
|
-
robotcode/robot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
robotcode/robot/__version__.py,sha256=h3UCuPK_uHJxqKWB4LppsykwINZBaI076ST-mAa3CFU,23
|
3
|
-
robotcode/robot/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
|
4
|
-
robotcode/robot/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
robotcode/robot/config/loader.py,sha256=bNJwr_XdCoUzpG2ag0BH33PIfiCwn0GMxn7q_Sw3zOk,8103
|
6
|
-
robotcode/robot/config/model.py,sha256=sgr6-4_E06g-yIXW41Z-NtIXZ_7JMmR5WvUD7kTUqu4,89106
|
7
|
-
robotcode/robot/config/utils.py,sha256=xY-LH31BidWzonpvSrle-4HvKrp02I7IRqU2JwlL4Ls,2931
|
8
|
-
robotcode/robot/diagnostics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
robotcode/robot/diagnostics/data_cache.py,sha256=Wge9HuxSUiBVMmrmlsYSMmG2ad7f3Texwox0Dm8lN7U,2969
|
10
|
-
robotcode/robot/diagnostics/diagnostics_modifier.py,sha256=3dDsu8-ET6weIvv7Sk3IQaPYFNxnXUs8Y7gpGTjfOBs,9796
|
11
|
-
robotcode/robot/diagnostics/document_cache_helper.py,sha256=n903UxVXM4Uq4fPxN5s-dugQAKcWUwf4Nw4q0CJV7aw,23902
|
12
|
-
robotcode/robot/diagnostics/entities.py,sha256=um9Yes1LUO30cRgL-JCC_WE3zMPJwoEFkFrJdNkzxwY,11000
|
13
|
-
robotcode/robot/diagnostics/errors.py,sha256=vRH7HiZOfQIC-L7ys2Bj9ULYxLpUH7I03qJRSkEx08k,1813
|
14
|
-
robotcode/robot/diagnostics/imports_manager.py,sha256=lmwg_wYFZLNx_o0u856_5JihXHPLBei2vfr6Puhlm-c,59127
|
15
|
-
robotcode/robot/diagnostics/keyword_finder.py,sha256=2FpPgor3RnT17Kor9L1XhLXcKn1DI43AVoYexdcM-Bs,17418
|
16
|
-
robotcode/robot/diagnostics/library_doc.py,sha256=uVRldDJ-cIHxS9hj4QnMAiGPzcrUNVyMGFvRyshU5H4,100520
|
17
|
-
robotcode/robot/diagnostics/model_helper.py,sha256=ltuUNWwZJFBmMFXIomMmW1IP5v7tMpQSoC1YbncgoNI,30985
|
18
|
-
robotcode/robot/diagnostics/namespace.py,sha256=Y6HDBKIYyCc3qCg2TT-orB9mASd-Ii4fkZuIpcFQMbk,75417
|
19
|
-
robotcode/robot/diagnostics/namespace_analyzer.py,sha256=NlvfAEYH_GyE1ZQ1JH9vR9yPfki3Xmw9TyNEc-B0mtM,74067
|
20
|
-
robotcode/robot/diagnostics/workspace_config.py,sha256=3SoewUj_LZB1Ki5hXM8oxQpJr6vyiog66SUw-ibODSA,2478
|
21
|
-
robotcode/robot/utils/__init__.py,sha256=OjNPMn_XSnfaMCyKd8Kmq6vlRt6mIGlzW4qiiD3ykUg,447
|
22
|
-
robotcode/robot/utils/ast.py,sha256=7TxZiQhh4Nk33it0Q9P6nnmmYiB7317SpXR7QB57MiY,11091
|
23
|
-
robotcode/robot/utils/markdownformatter.py,sha256=Cj4NjComTcNZf8uuezvtBbZqPMLjS237RknMopZYETk,11418
|
24
|
-
robotcode/robot/utils/match.py,sha256=Vtz1ueT6DIZZ4hKyXgvTg1A3x2puBwHgvjw1oAYBn5w,632
|
25
|
-
robotcode/robot/utils/robot_path.py,sha256=Z-GVBOPA_xeD20bCJi4_AWaU0eQWvCym-YFtyRpXARE,1767
|
26
|
-
robotcode/robot/utils/stubs.py,sha256=g-DrP8io1Ft5w3flcZXrjkDCCmEQBZDVbzWt4P14Jcs,457
|
27
|
-
robotcode/robot/utils/variables.py,sha256=XNPUDpghGy_f_Fne9lJ4OST-kFi-72Nrr0yJUu6f_Oc,2169
|
28
|
-
robotcode/robot/utils/visitor.py,sha256=GPMHgWZLEgbrmY0AxlhsXrHBY8Fgo5-XmYAJXbmSD8w,2275
|
29
|
-
robotcode_robot-0.95.1.dist-info/METADATA,sha256=9rVUX0KWKqm-PM8ulPubu_EATUd7gOfx_oCF6S8GfPc,2240
|
30
|
-
robotcode_robot-0.95.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
31
|
-
robotcode_robot-0.95.1.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
|
32
|
-
robotcode_robot-0.95.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|