robotcode-robot 1.2.0__py3-none-any.whl → 1.3.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/config/model.py +2 -2
- robotcode/robot/diagnostics/entities.py +18 -45
- robotcode/robot/diagnostics/errors.py +1 -0
- robotcode/robot/diagnostics/imports_manager.py +3 -3
- robotcode/robot/diagnostics/keyword_finder.py +43 -12
- robotcode/robot/diagnostics/library_doc.py +11 -5
- robotcode/robot/diagnostics/model_helper.py +4 -4
- robotcode/robot/diagnostics/namespace.py +227 -196
- robotcode/robot/diagnostics/namespace_analyzer.py +194 -136
- robotcode/robot/utils/__init__.py +4 -7
- robotcode/robot/utils/ast.py +21 -19
- robotcode/robot/utils/variables.py +159 -4
- {robotcode_robot-1.2.0.dist-info → robotcode_robot-1.3.0.dist-info}/METADATA +2 -2
- robotcode_robot-1.3.0.dist-info/RECORD +32 -0
- robotcode_robot-1.2.0.dist-info/RECORD +0 -32
- {robotcode_robot-1.2.0.dist-info → robotcode_robot-1.3.0.dist-info}/WHEEL +0 -0
- {robotcode_robot-1.2.0.dist-info → robotcode_robot-1.3.0.dist-info}/licenses/LICENSE.txt +0 -0
robotcode/robot/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "1.
|
1
|
+
__version__ = "1.3.0"
|
robotcode/robot/config/model.py
CHANGED
@@ -2371,11 +2371,11 @@ class RobotConfig(RobotExtendBaseProfile):
|
|
2371
2371
|
|
2372
2372
|
Examples:
|
2373
2373
|
```toml
|
2374
|
-
|
2374
|
+
default-profiles = "default"
|
2375
2375
|
```
|
2376
2376
|
|
2377
2377
|
```toml
|
2378
|
-
|
2378
|
+
default-profiles = ["default", "Firefox"]
|
2379
2379
|
```
|
2380
2380
|
"""
|
2381
2381
|
)
|
@@ -17,10 +17,9 @@ from typing_extensions import Concatenate, ParamSpec
|
|
17
17
|
|
18
18
|
from robot.parsing.lexer.tokens import Token
|
19
19
|
from robotcode.core.lsp.types import Position, Range
|
20
|
-
from robotcode.robot.utils.match import normalize
|
21
20
|
|
22
21
|
from ..utils.ast import range_from_token
|
23
|
-
from ..utils.variables import search_variable
|
22
|
+
from ..utils.variables import VariableMatcher, search_variable
|
24
23
|
|
25
24
|
if TYPE_CHECKING:
|
26
25
|
from robotcode.robot.diagnostics.library_doc import KeywordDoc, LibraryDoc
|
@@ -164,48 +163,6 @@ class VariablesImport(Import):
|
|
164
163
|
return hash((type(self), self.name, self.args))
|
165
164
|
|
166
165
|
|
167
|
-
class InvalidVariableError(Exception):
|
168
|
-
pass
|
169
|
-
|
170
|
-
|
171
|
-
class VariableMatcher:
|
172
|
-
def __init__(self, name: str) -> None:
|
173
|
-
self.name = name
|
174
|
-
|
175
|
-
match = search_variable(name, "$@&%", ignore_errors=True)
|
176
|
-
|
177
|
-
if match.base is None:
|
178
|
-
raise InvalidVariableError(f"Invalid variable '{name}'")
|
179
|
-
|
180
|
-
self.base = match.base
|
181
|
-
|
182
|
-
self.normalized_name = normalize(self.base)
|
183
|
-
|
184
|
-
def __eq__(self, o: object) -> bool:
|
185
|
-
if type(o) is VariableMatcher:
|
186
|
-
return o.normalized_name == self.normalized_name
|
187
|
-
|
188
|
-
if type(o) is str:
|
189
|
-
match = search_variable(o, "$@&%", ignore_errors=True)
|
190
|
-
base = match.base
|
191
|
-
if base is None:
|
192
|
-
return False
|
193
|
-
|
194
|
-
normalized = normalize(base)
|
195
|
-
return self.normalized_name == normalized
|
196
|
-
|
197
|
-
return False
|
198
|
-
|
199
|
-
def __hash__(self) -> int:
|
200
|
-
return hash(self.normalized_name)
|
201
|
-
|
202
|
-
def __str__(self) -> str:
|
203
|
-
return self.name
|
204
|
-
|
205
|
-
def __repr__(self) -> str:
|
206
|
-
return f"{type(self).__name__}(name={self.name!r})"
|
207
|
-
|
208
|
-
|
209
166
|
class VariableDefinitionType(Enum):
|
210
167
|
VARIABLE = "suite variable"
|
211
168
|
LOCAL_VARIABLE = "local variable"
|
@@ -230,10 +187,17 @@ class VariableDefinition(SourceEntity):
|
|
230
187
|
|
231
188
|
value: Any = field(default=None, compare=False)
|
232
189
|
value_is_native: bool = field(default=False, compare=False)
|
190
|
+
value_type: Optional[str] = field(default=None, compare=False)
|
233
191
|
|
234
192
|
@functools.cached_property
|
235
193
|
def matcher(self) -> VariableMatcher:
|
236
|
-
return
|
194
|
+
return search_variable(self.name)
|
195
|
+
|
196
|
+
@functools.cached_property
|
197
|
+
def convertable_name(self) -> str:
|
198
|
+
m = self.matcher
|
199
|
+
value_type = f": {self.value_type}" if self.value_type else ""
|
200
|
+
return f"{m.identifier}{{{m.base.strip()}{value_type}}}"
|
237
201
|
|
238
202
|
@single_call
|
239
203
|
def __hash__(self) -> int:
|
@@ -311,6 +275,15 @@ class ArgumentDefinition(LocalVariableDefinition):
|
|
311
275
|
return hash((type(self), self.name, self.type, self.range, self.source))
|
312
276
|
|
313
277
|
|
278
|
+
@dataclass
|
279
|
+
class EmbeddedArgumentDefinition(ArgumentDefinition):
|
280
|
+
pattern: Optional[str] = field(default=None, compare=False)
|
281
|
+
|
282
|
+
@single_call
|
283
|
+
def __hash__(self) -> int:
|
284
|
+
return hash((type(self), self.name, self.type, self.range, self.source))
|
285
|
+
|
286
|
+
|
314
287
|
@dataclass
|
315
288
|
class LibraryArgumentDefinition(ArgumentDefinition):
|
316
289
|
@single_call
|
@@ -1527,11 +1527,11 @@ class ImportsManager:
|
|
1527
1527
|
variables: Optional[Dict[str, Any]] = None,
|
1528
1528
|
) -> _ResourcesEntry:
|
1529
1529
|
source = self.find_resource(name, base_dir, variables=variables)
|
1530
|
+
source_path = normalized_path(Path(source))
|
1530
1531
|
|
1531
1532
|
def _get_document() -> TextDocument:
|
1532
|
-
self._logger.debug(lambda: f"Load resource {name} from source {
|
1533
|
+
self._logger.debug(lambda: f"Load resource {name} from source {source_path}", context_name="import")
|
1533
1534
|
|
1534
|
-
source_path = normalized_path(Path(source))
|
1535
1535
|
extension = source_path.suffix
|
1536
1536
|
if extension.lower() not in RESOURCE_EXTENSIONS:
|
1537
1537
|
raise ImportError(
|
@@ -1541,7 +1541,7 @@ class ImportsManager:
|
|
1541
1541
|
|
1542
1542
|
return self.documents_manager.get_or_open_document(source_path)
|
1543
1543
|
|
1544
|
-
entry_key = _ResourcesEntryKey(
|
1544
|
+
entry_key = _ResourcesEntryKey(str(source_path))
|
1545
1545
|
|
1546
1546
|
with self._resources_lock:
|
1547
1547
|
if entry_key not in self._resources:
|
@@ -87,9 +87,25 @@ class KeywordFinder:
|
|
87
87
|
try:
|
88
88
|
result = self._find_keyword(name, handle_bdd_style)
|
89
89
|
if result is None:
|
90
|
+
error_message = "No keyword with found."
|
91
|
+
|
92
|
+
if name and name.strip(": ").upper() == "FOR":
|
93
|
+
error_message = (
|
94
|
+
f"Support for the old FOR loop syntax has been removed. "
|
95
|
+
f"Replace '{name}' with 'FOR', end the loop with 'END', and "
|
96
|
+
f"remove escaping backslashes."
|
97
|
+
)
|
98
|
+
elif name and name == "\\":
|
99
|
+
error_message = (
|
100
|
+
"No keyword with name '\\' found. If it is used inside a for "
|
101
|
+
"loop, remove escaping backslashes and end the loop with 'END'."
|
102
|
+
)
|
103
|
+
else:
|
104
|
+
error_message = f"No keyword with name '{name}' found."
|
105
|
+
|
90
106
|
self.diagnostics.append(
|
91
107
|
DiagnosticsEntry(
|
92
|
-
|
108
|
+
error_message,
|
93
109
|
DiagnosticSeverity.ERROR,
|
94
110
|
Error.KEYWORD_NOT_FOUND,
|
95
111
|
)
|
@@ -306,17 +322,32 @@ class KeywordFinder:
|
|
306
322
|
return True
|
307
323
|
return False
|
308
324
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
325
|
+
if get_robot_version() >= (7, 3):
|
326
|
+
|
327
|
+
def _is_better_match(
|
328
|
+
self,
|
329
|
+
candidate: Tuple[Optional[LibraryEntry], KeywordDoc],
|
330
|
+
other: Tuple[Optional[LibraryEntry], KeywordDoc],
|
331
|
+
) -> bool:
|
332
|
+
return (
|
333
|
+
other[1].matcher.embedded_arguments is not None
|
334
|
+
and candidate[1].matcher.embedded_arguments is not None
|
335
|
+
and other[1].matcher.embedded_arguments.matches(candidate[1].name)
|
336
|
+
and not candidate[1].matcher.embedded_arguments.matches(other[1].name)
|
337
|
+
)
|
338
|
+
else:
|
339
|
+
|
340
|
+
def _is_better_match(
|
341
|
+
self,
|
342
|
+
candidate: Tuple[Optional[LibraryEntry], KeywordDoc],
|
343
|
+
other: Tuple[Optional[LibraryEntry], KeywordDoc],
|
344
|
+
) -> bool:
|
345
|
+
return (
|
346
|
+
other[1].matcher.embedded_arguments is not None
|
347
|
+
and candidate[1].matcher.embedded_arguments is not None
|
348
|
+
and other[1].matcher.embedded_arguments.match(candidate[1].name) is not None
|
349
|
+
and candidate[1].matcher.embedded_arguments.match(other[1].name) is None
|
350
|
+
)
|
320
351
|
|
321
352
|
@functools.cached_property
|
322
353
|
def _resource_imports(self) -> List[ResourceEntry]:
|
@@ -67,7 +67,7 @@ from robotcode.core.utils.path import normalized_path
|
|
67
67
|
from ..utils import get_robot_version
|
68
68
|
from ..utils.ast import (
|
69
69
|
cached_isinstance,
|
70
|
-
|
70
|
+
get_first_variable_token,
|
71
71
|
range_from_token,
|
72
72
|
strip_variable_token,
|
73
73
|
)
|
@@ -199,7 +199,7 @@ def convert_from_rest(text: str) -> str:
|
|
199
199
|
|
200
200
|
|
201
201
|
if get_robot_version() >= (6, 0):
|
202
|
-
# monkey patch robot framework
|
202
|
+
# monkey patch robot framework for performance reasons
|
203
203
|
_old_from_name = EmbeddedArguments.from_name
|
204
204
|
|
205
205
|
@functools.lru_cache(maxsize=8192)
|
@@ -214,8 +214,14 @@ if get_robot_version() >= (6, 0):
|
|
214
214
|
except (VariableError, DataError):
|
215
215
|
return ()
|
216
216
|
|
217
|
-
|
218
|
-
|
217
|
+
if get_robot_version() >= (7, 3):
|
218
|
+
|
219
|
+
def _match_embedded(embedded_arguments: EmbeddedArguments, name: str) -> bool:
|
220
|
+
return bool(embedded_arguments.matches(name))
|
221
|
+
else:
|
222
|
+
|
223
|
+
def _match_embedded(embedded_arguments: EmbeddedArguments, name: str) -> bool:
|
224
|
+
return embedded_arguments.match(name) is not None
|
219
225
|
|
220
226
|
else:
|
221
227
|
|
@@ -2779,7 +2785,7 @@ def _get_argument_definitions_from_line(
|
|
2779
2785
|
|
2780
2786
|
for argument_token in (cast(RobotToken, e) for e in arguments):
|
2781
2787
|
try:
|
2782
|
-
argument =
|
2788
|
+
argument = get_first_variable_token(argument_token)
|
2783
2789
|
|
2784
2790
|
if argument is not None and argument.value != "@{}":
|
2785
2791
|
if argument.value not in args:
|
@@ -253,7 +253,7 @@ class ModelHelper:
|
|
253
253
|
|
254
254
|
return lib_entry, kw_namespace
|
255
255
|
|
256
|
-
|
256
|
+
MATCH_EXTENDED = re.compile(
|
257
257
|
r"""
|
258
258
|
(.+?) # base name (group 1)
|
259
259
|
([^\s\w].+) # extended part (group 2)
|
@@ -500,9 +500,9 @@ class ModelHelper:
|
|
500
500
|
and sub_token.value[1:2] == "{"
|
501
501
|
and sub_token.value[-1:] == "}"
|
502
502
|
):
|
503
|
-
|
504
|
-
if
|
505
|
-
base_name, _ =
|
503
|
+
extended_match = cls.MATCH_EXTENDED.match(name[2:-1])
|
504
|
+
if extended_match is not None:
|
505
|
+
base_name, _ = extended_match.groups()
|
506
506
|
name = f"{name[0]}{{{base_name.strip()}}}"
|
507
507
|
var = namespace.find_variable(
|
508
508
|
name,
|