robotcode-robot 0.92.0__py3-none-any.whl → 0.93.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/loader.py +12 -4
- robotcode/robot/config/utils.py +4 -2
- robotcode/robot/diagnostics/entities.py +2 -2
- robotcode/robot/diagnostics/imports_manager.py +79 -64
- robotcode/robot/diagnostics/library_doc.py +25 -8
- robotcode/robot/diagnostics/namespace.py +105 -102
- robotcode/robot/utils/ast.py +34 -7
- {robotcode_robot-0.92.0.dist-info → robotcode_robot-0.93.0.dist-info}/METADATA +2 -2
- {robotcode_robot-0.92.0.dist-info → robotcode_robot-0.93.0.dist-info}/RECORD +12 -12
- {robotcode_robot-0.92.0.dist-info → robotcode_robot-0.93.0.dist-info}/WHEEL +0 -0
- {robotcode_robot-0.92.0.dist-info → robotcode_robot-0.93.0.dist-info}/licenses/LICENSE.txt +0 -0
robotcode/robot/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.
|
1
|
+
__version__ = "0.93.0"
|
robotcode/robot/config/loader.py
CHANGED
@@ -28,6 +28,7 @@ class DiscoverdBy(str, Enum):
|
|
28
28
|
LOCAL_ROBOT_TOML = ".robot.toml (local file)"
|
29
29
|
USER_DEFAULT_CONFIG_TOML = "robot.toml (user default config)"
|
30
30
|
NOT_FOUND = "not found"
|
31
|
+
COMMAND_LINE = "command line"
|
31
32
|
|
32
33
|
|
33
34
|
class ConfigType(str, Enum):
|
@@ -191,7 +192,13 @@ def load_robot_config_from_path(
|
|
191
192
|
|
192
193
|
def find_project_root(
|
193
194
|
*sources: Union[str, Path],
|
195
|
+
root_folder: Optional[Path] = None,
|
196
|
+
no_vcs: bool = False,
|
194
197
|
) -> Tuple[Optional[Path], DiscoverdBy]:
|
198
|
+
|
199
|
+
if root_folder:
|
200
|
+
return root_folder.absolute(), DiscoverdBy.COMMAND_LINE
|
201
|
+
|
195
202
|
if not sources:
|
196
203
|
sources = (str(Path.cwd().absolute()),)
|
197
204
|
|
@@ -214,11 +221,12 @@ def find_project_root(
|
|
214
221
|
if (directory / PYPROJECT_TOML).is_file():
|
215
222
|
return directory, DiscoverdBy.PYPROJECT_TOML
|
216
223
|
|
217
|
-
if
|
218
|
-
|
224
|
+
if not no_vcs:
|
225
|
+
if (directory / ".git").exists():
|
226
|
+
return directory, DiscoverdBy.GIT
|
219
227
|
|
220
|
-
|
221
|
-
|
228
|
+
if (directory / ".hg").is_dir():
|
229
|
+
return directory, DiscoverdBy.HG
|
222
230
|
|
223
231
|
return None, DiscoverdBy.NOT_FOUND
|
224
232
|
|
robotcode/robot/config/utils.py
CHANGED
@@ -43,10 +43,12 @@ def get_user_config_file(
|
|
43
43
|
def get_config_files(
|
44
44
|
paths: Optional[Sequence[Union[str, Path]]] = None,
|
45
45
|
config_files: Optional[Sequence[Path]] = None,
|
46
|
+
root_folder: Optional[Path] = None,
|
47
|
+
no_vcs: bool = False,
|
46
48
|
*,
|
47
49
|
verbose_callback: Optional[Callable[[str], None]] = None,
|
48
50
|
) -> Tuple[Sequence[Tuple[Path, ConfigType]], Optional[Path], DiscoverdBy]:
|
49
|
-
root_folder, discovered_by = find_project_root(*(paths or []))
|
51
|
+
root_folder, discovered_by = find_project_root(*(paths or []), root_folder=root_folder, no_vcs=no_vcs)
|
50
52
|
|
51
53
|
if root_folder is None:
|
52
54
|
root_folder = Path.cwd()
|
@@ -54,7 +56,7 @@ def get_config_files(
|
|
54
56
|
verbose_callback(f"Cannot detect root folder. Use current folder '{root_folder}' as root.")
|
55
57
|
|
56
58
|
if verbose_callback:
|
57
|
-
verbose_callback(f"
|
59
|
+
verbose_callback(f"Use root at:\n {root_folder} ({discovered_by.value})")
|
58
60
|
|
59
61
|
if config_files:
|
60
62
|
if verbose_callback:
|
@@ -160,7 +160,7 @@ class VariableMatcher:
|
|
160
160
|
return False
|
161
161
|
|
162
162
|
def __hash__(self) -> int:
|
163
|
-
return hash(self.
|
163
|
+
return hash(self.normalized_name)
|
164
164
|
|
165
165
|
def __str__(self) -> str:
|
166
166
|
return self.name
|
@@ -252,7 +252,7 @@ class BuiltInVariableDefinition(VariableDefinition):
|
|
252
252
|
|
253
253
|
@single_call
|
254
254
|
def __hash__(self) -> int:
|
255
|
-
return hash((type(self), self.name, self.type))
|
255
|
+
return hash((type(self), self.name, self.type, None, None))
|
256
256
|
|
257
257
|
|
258
258
|
@dataclass
|
@@ -64,6 +64,7 @@ from .library_doc import (
|
|
64
64
|
get_variables_doc,
|
65
65
|
is_library_by_path,
|
66
66
|
is_variables_by_path,
|
67
|
+
replace_variables_scalar,
|
67
68
|
resolve_args,
|
68
69
|
resolve_variable,
|
69
70
|
)
|
@@ -905,7 +906,8 @@ class ImportsManager:
|
|
905
906
|
):
|
906
907
|
self._logger.debug(
|
907
908
|
lambda: f"Ignore library {result.name or '' if result is not None else ''}"
|
908
|
-
f" {result.origin or '' if result is not None else ''} for caching."
|
909
|
+
f" {result.origin or '' if result is not None else ''} for caching.",
|
910
|
+
context_name="import",
|
909
911
|
)
|
910
912
|
return None, import_name, ignore_arguments
|
911
913
|
|
@@ -1143,9 +1145,7 @@ class ImportsManager:
|
|
1143
1145
|
base_dir: str,
|
1144
1146
|
variables: Optional[Dict[str, Any]] = None,
|
1145
1147
|
) -> LibraryDoc:
|
1146
|
-
meta,
|
1147
|
-
|
1148
|
-
self._logger.debug(lambda: f"Load Library {source}{args!r}")
|
1148
|
+
meta, _source, ignore_arguments = self.get_library_meta(name, base_dir, variables)
|
1149
1149
|
|
1150
1150
|
if meta is not None and not meta.has_errors:
|
1151
1151
|
meta_file = Path(self.lib_doc_cache_path, meta.filepath_base + ".meta.json")
|
@@ -1219,7 +1219,7 @@ class ImportsManager:
|
|
1219
1219
|
|
1220
1220
|
meta_file.write_text(as_json(meta), "utf-8")
|
1221
1221
|
else:
|
1222
|
-
self._logger.debug(lambda: f"Skip caching library {name}{args!r}")
|
1222
|
+
self._logger.debug(lambda: f"Skip caching library {name}{args!r}", context_name="import")
|
1223
1223
|
except (SystemExit, KeyboardInterrupt):
|
1224
1224
|
raise
|
1225
1225
|
except BaseException as e:
|
@@ -1236,37 +1236,37 @@ class ImportsManager:
|
|
1236
1236
|
sentinel: Any = None,
|
1237
1237
|
variables: Optional[Dict[str, Any]] = None,
|
1238
1238
|
) -> LibraryDoc:
|
1239
|
+
with self._logger.measure_time(lambda: f"loading library {name}{args!r}", context_name="import"):
|
1240
|
+
source = self.find_library(name, base_dir, variables)
|
1239
1241
|
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
ignore_reference=sentinel is None,
|
1261
|
-
)
|
1242
|
+
resolved_args = resolve_args(
|
1243
|
+
args,
|
1244
|
+
str(self.root_folder),
|
1245
|
+
base_dir,
|
1246
|
+
self.get_resolvable_command_line_variables(),
|
1247
|
+
variables,
|
1248
|
+
)
|
1249
|
+
entry_key = _LibrariesEntryKey(source, resolved_args)
|
1250
|
+
|
1251
|
+
with self._libaries_lock:
|
1252
|
+
if entry_key not in self._libaries:
|
1253
|
+
self._libaries[entry_key] = _LibrariesEntry(
|
1254
|
+
self,
|
1255
|
+
name,
|
1256
|
+
args,
|
1257
|
+
str(self.root_folder),
|
1258
|
+
base_dir,
|
1259
|
+
self._get_library_libdoc_handler(variables),
|
1260
|
+
ignore_reference=sentinel is None,
|
1261
|
+
)
|
1262
1262
|
|
1263
|
-
|
1263
|
+
entry = self._libaries[entry_key]
|
1264
1264
|
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1265
|
+
if not entry.ignore_reference and sentinel is not None and sentinel not in entry.references:
|
1266
|
+
weakref.finalize(sentinel, self.__remove_library_entry, entry_key, entry)
|
1267
|
+
entry.references.add(sentinel)
|
1268
1268
|
|
1269
|
-
|
1269
|
+
return entry.get_libdoc()
|
1270
1270
|
|
1271
1271
|
@_logger.call
|
1272
1272
|
def get_libdoc_from_model(
|
@@ -1313,7 +1313,7 @@ class ImportsManager:
|
|
1313
1313
|
resolve_variables: bool = True,
|
1314
1314
|
resolve_command_line_vars: bool = True,
|
1315
1315
|
) -> VariablesDoc:
|
1316
|
-
meta,
|
1316
|
+
meta, _source = self.get_variables_meta(
|
1317
1317
|
name,
|
1318
1318
|
base_dir,
|
1319
1319
|
variables,
|
@@ -1321,7 +1321,6 @@ class ImportsManager:
|
|
1321
1321
|
resolve_command_line_vars=resolve_command_line_vars,
|
1322
1322
|
)
|
1323
1323
|
|
1324
|
-
self._logger.debug(lambda: f"Load variables {source}{args!r}")
|
1325
1324
|
if meta is not None:
|
1326
1325
|
meta_file = Path(
|
1327
1326
|
self.variables_doc_cache_path,
|
@@ -1391,7 +1390,7 @@ class ImportsManager:
|
|
1391
1390
|
raise RuntimeError(f"Cannot write spec file for variables '{name}' to '{spec_file}'") from e
|
1392
1391
|
meta_file.write_text(as_json(meta), "utf-8")
|
1393
1392
|
else:
|
1394
|
-
self._logger.debug(lambda: f"Skip caching variables {name}{args!r}")
|
1393
|
+
self._logger.debug(lambda: f"Skip caching variables {name}{args!r}", context_name="import")
|
1395
1394
|
except (SystemExit, KeyboardInterrupt):
|
1396
1395
|
raise
|
1397
1396
|
except BaseException as e:
|
@@ -1410,39 +1409,40 @@ class ImportsManager:
|
|
1410
1409
|
resolve_variables: bool = True,
|
1411
1410
|
resolve_command_line_vars: bool = True,
|
1412
1411
|
) -> VariablesDoc:
|
1413
|
-
|
1414
|
-
|
1415
|
-
if args:
|
1416
|
-
resolved_args = resolve_args(
|
1417
|
-
args,
|
1418
|
-
str(self.root_folder),
|
1419
|
-
base_dir,
|
1420
|
-
self.get_resolvable_command_line_variables() if resolve_command_line_vars else None,
|
1421
|
-
variables,
|
1422
|
-
)
|
1423
|
-
else:
|
1424
|
-
resolved_args = ()
|
1412
|
+
with self._logger.measure_time(lambda: f"getting libdoc for variables import {name}", context_name="import"):
|
1413
|
+
source = self.find_variables(name, base_dir, variables, resolve_variables, resolve_command_line_vars)
|
1425
1414
|
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
if entry_key not in self._variables:
|
1430
|
-
self._variables[entry_key] = _VariablesEntry(
|
1431
|
-
name,
|
1432
|
-
resolved_args,
|
1415
|
+
if args:
|
1416
|
+
resolved_args = resolve_args(
|
1417
|
+
args,
|
1433
1418
|
str(self.root_folder),
|
1434
1419
|
base_dir,
|
1435
|
-
self,
|
1436
|
-
|
1420
|
+
self.get_resolvable_command_line_variables() if resolve_command_line_vars else None,
|
1421
|
+
variables,
|
1437
1422
|
)
|
1423
|
+
else:
|
1424
|
+
resolved_args = ()
|
1425
|
+
|
1426
|
+
entry_key = _VariablesEntryKey(source, resolved_args)
|
1427
|
+
|
1428
|
+
with self._variables_lock:
|
1429
|
+
if entry_key not in self._variables:
|
1430
|
+
self._variables[entry_key] = _VariablesEntry(
|
1431
|
+
name,
|
1432
|
+
resolved_args,
|
1433
|
+
str(self.root_folder),
|
1434
|
+
base_dir,
|
1435
|
+
self,
|
1436
|
+
self._get_variables_libdoc_handler(variables, resolve_variables, resolve_command_line_vars),
|
1437
|
+
)
|
1438
1438
|
|
1439
|
-
|
1439
|
+
entry = self._variables[entry_key]
|
1440
1440
|
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1441
|
+
if sentinel is not None and sentinel not in entry.references:
|
1442
|
+
entry.references.add(sentinel)
|
1443
|
+
weakref.finalize(sentinel, self.__remove_variables_entry, entry_key, entry)
|
1444
1444
|
|
1445
|
-
|
1445
|
+
return entry.get_libdoc()
|
1446
1446
|
|
1447
1447
|
@_logger.call
|
1448
1448
|
def _get_entry_for_resource_import(
|
@@ -1455,7 +1455,7 @@ class ImportsManager:
|
|
1455
1455
|
source = self.find_resource(name, base_dir, variables=variables)
|
1456
1456
|
|
1457
1457
|
def _get_document() -> TextDocument:
|
1458
|
-
self._logger.debug(lambda: f"Load resource {name} from source {source}")
|
1458
|
+
self._logger.debug(lambda: f"Load resource {name} from source {source}", context_name="import")
|
1459
1459
|
|
1460
1460
|
source_path = normalized_path(Path(source))
|
1461
1461
|
extension = source_path.suffix
|
@@ -1488,9 +1488,10 @@ class ImportsManager:
|
|
1488
1488
|
sentinel: Any = None,
|
1489
1489
|
variables: Optional[Dict[str, Any]] = None,
|
1490
1490
|
) -> Tuple["Namespace", LibraryDoc]:
|
1491
|
-
|
1491
|
+
with self._logger.measure_time(lambda: f"getting namespace and libdoc for {name}", context_name="import"):
|
1492
|
+
entry = self._get_entry_for_resource_import(name, base_dir, sentinel, variables)
|
1492
1493
|
|
1493
|
-
|
1494
|
+
return entry.get_namespace(), entry.get_libdoc()
|
1494
1495
|
|
1495
1496
|
def get_namespace_for_resource_import(
|
1496
1497
|
self,
|
@@ -1572,3 +1573,17 @@ class ImportsManager:
|
|
1572
1573
|
self.get_resolvable_command_line_variables(),
|
1573
1574
|
variables,
|
1574
1575
|
)
|
1576
|
+
|
1577
|
+
def replace_variables_scalar(
|
1578
|
+
self,
|
1579
|
+
scalar: str,
|
1580
|
+
base_dir: str = ".",
|
1581
|
+
variables: Optional[Dict[str, Any]] = None,
|
1582
|
+
) -> Any:
|
1583
|
+
return replace_variables_scalar(
|
1584
|
+
scalar,
|
1585
|
+
str(self.root_folder),
|
1586
|
+
base_dir,
|
1587
|
+
self.get_resolvable_command_line_variables(),
|
1588
|
+
variables,
|
1589
|
+
)
|
@@ -45,12 +45,8 @@ from robot.parsing.lexer.tokens import Token
|
|
45
45
|
from robot.parsing.lexer.tokens import Token as RobotToken
|
46
46
|
from robot.parsing.model.blocks import Keyword
|
47
47
|
from robot.parsing.model.statements import Arguments, KeywordName
|
48
|
-
from robot.running.arguments.argumentresolver import
|
49
|
-
|
50
|
-
DictToKwargs,
|
51
|
-
NamedArgumentResolver,
|
52
|
-
VariableReplacer,
|
53
|
-
)
|
48
|
+
from robot.running.arguments.argumentresolver import ArgumentResolver, DictToKwargs, NamedArgumentResolver
|
49
|
+
from robot.running.arguments.argumentresolver import VariableReplacer as ArgumentsVariableReplacer
|
54
50
|
from robot.running.arguments.argumentspec import ArgInfo
|
55
51
|
from robot.running.arguments.argumentspec import (
|
56
52
|
ArgumentSpec as RobotArgumentSpec,
|
@@ -64,6 +60,7 @@ from robot.utils.robotpath import find_file as robot_find_file
|
|
64
60
|
from robot.variables import Variables
|
65
61
|
from robot.variables.filesetter import PythonImporter, YamlImporter
|
66
62
|
from robot.variables.finders import VariableFinder
|
63
|
+
from robot.variables.replacer import VariableReplacer
|
67
64
|
from robot.variables.search import contains_variable
|
68
65
|
from robotcode.core.lsp.types import Position, Range
|
69
66
|
from robotcode.core.utils.path import normalized_path
|
@@ -578,9 +575,9 @@ class ArgumentSpec:
|
|
578
575
|
|
579
576
|
positional, named = MyNamedArgumentResolver(self.__robot_arguments).resolve(arguments, variables)
|
580
577
|
if get_robot_version() < (7, 0):
|
581
|
-
positional, named =
|
578
|
+
positional, named = ArgumentsVariableReplacer(resolve_variables_until).replace(positional, named, variables)
|
582
579
|
else:
|
583
|
-
positional, named =
|
580
|
+
positional, named = ArgumentsVariableReplacer(self.__robot_arguments, resolve_variables_until).replace(
|
584
581
|
positional, named, variables
|
585
582
|
)
|
586
583
|
positional, named = DictToKwargs(self.__robot_arguments, dict_to_kwargs).handle(positional, named)
|
@@ -1585,6 +1582,26 @@ def resolve_variable(
|
|
1585
1582
|
return name.replace("\\", "\\\\")
|
1586
1583
|
|
1587
1584
|
|
1585
|
+
def replace_variables_scalar(
|
1586
|
+
scalar: str,
|
1587
|
+
working_dir: str = ".",
|
1588
|
+
base_dir: str = ".",
|
1589
|
+
command_line_variables: Optional[Dict[str, Optional[Any]]] = None,
|
1590
|
+
variables: Optional[Dict[str, Optional[Any]]] = None,
|
1591
|
+
) -> Any:
|
1592
|
+
|
1593
|
+
_update_env(working_dir)
|
1594
|
+
|
1595
|
+
if contains_variable(scalar, "$@&%"):
|
1596
|
+
robot_variables = resolve_robot_variables(working_dir, base_dir, command_line_variables, variables)
|
1597
|
+
if get_robot_version() >= (6, 1):
|
1598
|
+
return VariableReplacer(robot_variables).replace_scalar(scalar.replace("\\", "\\\\"))
|
1599
|
+
|
1600
|
+
return VariableReplacer(robot_variables.store).replace_scalar(scalar.replace("\\", "\\\\"))
|
1601
|
+
|
1602
|
+
return scalar.replace("\\", "\\\\")
|
1603
|
+
|
1604
|
+
|
1588
1605
|
@contextmanager
|
1589
1606
|
def _std_capture() -> Iterator[io.StringIO]:
|
1590
1607
|
old__stdout__ = sys.__stdout__
|
@@ -694,7 +694,14 @@ class Namespace:
|
|
694
694
|
self._own_variables: Optional[List[VariableDefinition]] = None
|
695
695
|
self._own_variables_lock = RLock(default_timeout=120, name="Namespace.own_variables")
|
696
696
|
self._global_variables: Optional[List[VariableDefinition]] = None
|
697
|
+
self._global_variables_dict: Optional[Dict[VariableMatcher, VariableDefinition]] = None
|
697
698
|
self._global_variables_lock = RLock(default_timeout=120, name="Namespace.global_variables")
|
699
|
+
self._global_variables_dict_lock = RLock(default_timeout=120, name="Namespace.global_variables_dict")
|
700
|
+
|
701
|
+
self._global_resolvable_variables: Optional[Dict[str, Any]] = None
|
702
|
+
self._global_resolvable_variables_lock = RLock(
|
703
|
+
default_timeout=120, name="Namespace._global_resolvable_variables_lock"
|
704
|
+
)
|
698
705
|
|
699
706
|
self._suite_variables: Optional[Dict[str, Any]] = None
|
700
707
|
self._suite_variables_lock = RLock(default_timeout=120, name="Namespace.global_variables")
|
@@ -993,9 +1000,12 @@ class Namespace:
|
|
993
1000
|
return self.imports_manager.get_command_line_variables()
|
994
1001
|
|
995
1002
|
def _reset_global_variables(self) -> None:
|
996
|
-
with self._global_variables_lock, self._suite_variables_lock:
|
997
|
-
self.
|
998
|
-
|
1003
|
+
with self._global_variables_lock, self._global_variables_dict_lock, self._suite_variables_lock:
|
1004
|
+
with self._global_resolvable_variables_lock:
|
1005
|
+
self._global_variables = None
|
1006
|
+
self._global_variables_dict = None
|
1007
|
+
self._suite_variables = None
|
1008
|
+
self._global_resolvable_variables = None
|
999
1009
|
|
1000
1010
|
def get_global_variables(self) -> List[VariableDefinition]:
|
1001
1011
|
with self._global_variables_lock:
|
@@ -1012,12 +1022,20 @@ class Namespace:
|
|
1012
1022
|
|
1013
1023
|
return self._global_variables
|
1014
1024
|
|
1025
|
+
def get_global_variables_dict(self) -> Dict[VariableMatcher, VariableDefinition]:
|
1026
|
+
with self._global_variables_dict_lock:
|
1027
|
+
if self._global_variables_dict is None:
|
1028
|
+
self._global_variables_dict = {m: v for m, v in self.yield_variables()}
|
1029
|
+
|
1030
|
+
return self._global_variables_dict
|
1031
|
+
|
1015
1032
|
def yield_variables(
|
1016
1033
|
self,
|
1017
1034
|
nodes: Optional[List[ast.AST]] = None,
|
1018
1035
|
position: Optional[Position] = None,
|
1019
1036
|
skip_commandline_variables: bool = False,
|
1020
1037
|
skip_local_variables: bool = False,
|
1038
|
+
skip_global_variables: bool = False,
|
1021
1039
|
) -> Iterator[Tuple[VariableMatcher, VariableDefinition]]:
|
1022
1040
|
yielded: Dict[VariableMatcher, VariableDefinition] = {}
|
1023
1041
|
|
@@ -1053,7 +1071,7 @@ class Namespace:
|
|
1053
1071
|
else []
|
1054
1072
|
)
|
1055
1073
|
],
|
1056
|
-
self.get_global_variables(),
|
1074
|
+
self.get_global_variables() if not skip_global_variables else [],
|
1057
1075
|
):
|
1058
1076
|
if var.matcher not in yielded:
|
1059
1077
|
if skip_commandline_variables and isinstance(var, CommandLineVariableDefinition):
|
@@ -1063,17 +1081,7 @@ class Namespace:
|
|
1063
1081
|
|
1064
1082
|
yield var.matcher, var
|
1065
1083
|
|
1066
|
-
def get_suite_variables(
|
1067
|
-
self,
|
1068
|
-
nodes: Optional[List[ast.AST]] = None,
|
1069
|
-
position: Optional[Position] = None,
|
1070
|
-
) -> Dict[str, Any]:
|
1071
|
-
if nodes:
|
1072
|
-
return {
|
1073
|
-
v.name: v.value
|
1074
|
-
for k, v in self.yield_variables(nodes, position, skip_commandline_variables=True)
|
1075
|
-
if v.has_value
|
1076
|
-
}
|
1084
|
+
def get_suite_variables(self) -> Dict[str, Any]:
|
1077
1085
|
with self._suite_variables_lock:
|
1078
1086
|
vars = {}
|
1079
1087
|
|
@@ -1093,11 +1101,21 @@ class Namespace:
|
|
1093
1101
|
nodes: Optional[List[ast.AST]] = None,
|
1094
1102
|
position: Optional[Position] = None,
|
1095
1103
|
) -> Dict[str, Any]:
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1104
|
+
if nodes:
|
1105
|
+
return {
|
1106
|
+
v.name: v.value
|
1107
|
+
for k, v in self.yield_variables(nodes, position, skip_commandline_variables=True)
|
1108
|
+
if v.has_value
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
with self._global_resolvable_variables_lock:
|
1112
|
+
if self._global_resolvable_variables is None:
|
1113
|
+
self._global_resolvable_variables = {
|
1114
|
+
v.name: v.value
|
1115
|
+
for k, v in self.yield_variables(nodes, position, skip_commandline_variables=True)
|
1116
|
+
if v.has_value
|
1117
|
+
}
|
1118
|
+
return self._global_resolvable_variables
|
1101
1119
|
|
1102
1120
|
def get_variable_matchers(
|
1103
1121
|
self,
|
@@ -1141,9 +1159,15 @@ class Namespace:
|
|
1141
1159
|
position,
|
1142
1160
|
skip_commandline_variables=skip_commandline_variables,
|
1143
1161
|
skip_local_variables=skip_local_variables,
|
1162
|
+
skip_global_variables=True,
|
1144
1163
|
):
|
1145
1164
|
if matcher == m:
|
1146
1165
|
return v
|
1166
|
+
|
1167
|
+
result = self.get_global_variables_dict().get(matcher, None)
|
1168
|
+
if matcher is not None:
|
1169
|
+
return result
|
1170
|
+
|
1147
1171
|
except InvalidVariableError:
|
1148
1172
|
if not ignore_error:
|
1149
1173
|
raise
|
@@ -1376,12 +1400,12 @@ class Namespace:
|
|
1376
1400
|
source: Optional[str] = None,
|
1377
1401
|
parent_import: Optional[Import] = None,
|
1378
1402
|
parent_source: Optional[str] = None,
|
1379
|
-
depth: int = 0,
|
1380
1403
|
) -> Optional[Dict[str, Any]]:
|
1381
1404
|
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1405
|
+
with self._logger.measure_time(
|
1406
|
+
lambda: f"loading imports for {self.source if top_level else source}",
|
1407
|
+
context_name="import",
|
1408
|
+
):
|
1385
1409
|
for imp in imports:
|
1386
1410
|
if variables is None:
|
1387
1411
|
variables = self.get_suite_variables()
|
@@ -1418,7 +1442,6 @@ class Namespace:
|
|
1418
1442
|
source=entry.library_doc.source,
|
1419
1443
|
parent_import=imp if top_level else parent_import,
|
1420
1444
|
parent_source=parent_source if top_level else source,
|
1421
|
-
depth=depth + 1,
|
1422
1445
|
)
|
1423
1446
|
except (SystemExit, KeyboardInterrupt):
|
1424
1447
|
raise
|
@@ -1568,48 +1591,40 @@ class Namespace:
|
|
1568
1591
|
code=Error.LIBRARY_ALREADY_IMPORTED,
|
1569
1592
|
)
|
1570
1593
|
|
1571
|
-
finally:
|
1572
|
-
self._logger.debug(
|
1573
|
-
lambda: f"{' '*depth}end imports for "
|
1574
|
-
f"{self.document if top_level else source} in {time.monotonic() - current_time}s"
|
1575
|
-
)
|
1576
|
-
|
1577
1594
|
return variables
|
1578
1595
|
|
1596
|
+
def _import_lib(self, library: str, variables: Optional[Dict[str, Any]] = None) -> Optional[LibraryEntry]:
|
1597
|
+
try:
|
1598
|
+
return self._get_library_entry(
|
1599
|
+
library,
|
1600
|
+
(),
|
1601
|
+
None,
|
1602
|
+
str(Path(self.source).parent),
|
1603
|
+
is_default_library=True,
|
1604
|
+
variables=variables,
|
1605
|
+
)
|
1606
|
+
except (SystemExit, KeyboardInterrupt):
|
1607
|
+
raise
|
1608
|
+
except BaseException as e:
|
1609
|
+
self.append_diagnostics(
|
1610
|
+
range=Range.zero(),
|
1611
|
+
message=f"Can't import default library '{library}': {str(e) or type(e).__name__}",
|
1612
|
+
severity=DiagnosticSeverity.ERROR,
|
1613
|
+
source="Robot",
|
1614
|
+
code=type(e).__qualname__,
|
1615
|
+
)
|
1616
|
+
return None
|
1617
|
+
|
1579
1618
|
def _import_default_libraries(self, variables: Optional[Dict[str, Any]] = None) -> None:
|
1580
|
-
def _import_lib(library: str, variables: Optional[Dict[str, Any]] = None) -> Optional[LibraryEntry]:
|
1581
|
-
try:
|
1582
|
-
return self._get_library_entry(
|
1583
|
-
library,
|
1584
|
-
(),
|
1585
|
-
None,
|
1586
|
-
str(Path(self.source).parent),
|
1587
|
-
is_default_library=True,
|
1588
|
-
variables=variables,
|
1589
|
-
)
|
1590
|
-
except (SystemExit, KeyboardInterrupt):
|
1591
|
-
raise
|
1592
|
-
except BaseException as e:
|
1593
|
-
self.append_diagnostics(
|
1594
|
-
range=Range.zero(),
|
1595
|
-
message=f"Can't import default library '{library}': {str(e) or type(e).__name__}",
|
1596
|
-
severity=DiagnosticSeverity.ERROR,
|
1597
|
-
source="Robot",
|
1598
|
-
code=type(e).__qualname__,
|
1599
|
-
)
|
1600
|
-
return None
|
1601
1619
|
|
1602
|
-
self._logger.
|
1603
|
-
try:
|
1620
|
+
with self._logger.measure_time(lambda: f"importing default libraries for {self.source}", context_name="import"):
|
1604
1621
|
if variables is None:
|
1605
1622
|
variables = self.get_suite_variables()
|
1606
1623
|
|
1607
1624
|
for library in DEFAULT_LIBRARIES:
|
1608
|
-
e = _import_lib(library, variables)
|
1625
|
+
e = self._import_lib(library, variables)
|
1609
1626
|
if e is not None:
|
1610
1627
|
self._libraries[e.alias or e.name or e.import_name] = e
|
1611
|
-
finally:
|
1612
|
-
self._logger.debug(lambda: f"end import default libraries for document {self.document}")
|
1613
1628
|
|
1614
1629
|
@_logger.call
|
1615
1630
|
def _get_library_entry(
|
@@ -1820,8 +1835,6 @@ class Namespace:
|
|
1820
1835
|
|
1821
1836
|
@_logger.call(condition=lambda self: not self._analyzed)
|
1822
1837
|
def analyze(self) -> None:
|
1823
|
-
import time
|
1824
|
-
|
1825
1838
|
from .namespace_analyzer import NamespaceAnalyzer
|
1826
1839
|
|
1827
1840
|
with self._analyze_lock:
|
@@ -1830,53 +1843,43 @@ class Namespace:
|
|
1830
1843
|
|
1831
1844
|
self.ensure_initialized()
|
1832
1845
|
|
1833
|
-
self._logger.
|
1834
|
-
|
1835
|
-
|
1836
|
-
try:
|
1837
|
-
result = NamespaceAnalyzer(self.model, self, self.create_finder()).run()
|
1846
|
+
with self._logger.measure_time(lambda: f"analyzing document {self.source}", context_name="analyze"):
|
1847
|
+
try:
|
1848
|
+
result = NamespaceAnalyzer(self.model, self, self.create_finder()).run()
|
1838
1849
|
|
1839
|
-
|
1840
|
-
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1850
|
+
self._diagnostics += result.diagnostics
|
1851
|
+
self._keyword_references = result.keyword_references
|
1852
|
+
self._variable_references = result.variable_references
|
1853
|
+
self._local_variable_assignments = result.local_variable_assignments
|
1854
|
+
self._namespace_references = result.namespace_references
|
1844
1855
|
|
1845
|
-
|
1856
|
+
lib_doc = self.get_library_doc()
|
1846
1857
|
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
1850
|
-
|
1851
|
-
|
1852
|
-
|
1853
|
-
|
1854
|
-
|
1855
|
-
|
1856
|
-
|
1857
|
-
|
1858
|
+
if lib_doc.errors is not None:
|
1859
|
+
for err in lib_doc.errors:
|
1860
|
+
self.append_diagnostics(
|
1861
|
+
range=Range(
|
1862
|
+
start=Position(
|
1863
|
+
line=((err.line_no - 1) if err.line_no is not None else 0),
|
1864
|
+
character=0,
|
1865
|
+
),
|
1866
|
+
end=Position(
|
1867
|
+
line=((err.line_no - 1) if err.line_no is not None else 0),
|
1868
|
+
character=0,
|
1869
|
+
),
|
1858
1870
|
),
|
1859
|
-
|
1860
|
-
|
1861
|
-
|
1862
|
-
|
1863
|
-
|
1864
|
-
|
1865
|
-
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
|
1870
|
-
|
1871
|
-
self._analyzed = not canceled
|
1872
|
-
|
1873
|
-
self._logger.debug(
|
1874
|
-
lambda: (
|
1875
|
-
f"end analyzed {self.document} succeed in {time.monotonic() - start_time}s"
|
1876
|
-
if self._analyzed
|
1877
|
-
else f"end analyzed {self.document} failed in {time.monotonic() - start_time}s"
|
1878
|
-
)
|
1879
|
-
)
|
1871
|
+
message=err.message,
|
1872
|
+
severity=DiagnosticSeverity.ERROR,
|
1873
|
+
source=DIAGNOSTICS_SOURCE_NAME,
|
1874
|
+
code=err.type_name,
|
1875
|
+
)
|
1876
|
+
# TODO: implement CancelationToken
|
1877
|
+
except CancelledError:
|
1878
|
+
canceled = True
|
1879
|
+
self._logger.debug("analyzing canceled")
|
1880
|
+
raise
|
1881
|
+
finally:
|
1882
|
+
self._analyzed = not canceled
|
1880
1883
|
|
1881
1884
|
self.has_analysed(self)
|
1882
1885
|
|
robotcode/robot/utils/ast.py
CHANGED
@@ -2,7 +2,9 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import ast
|
4
4
|
import itertools
|
5
|
-
from typing import Any, Iterator, List, Optional, Sequence, Set, Tuple
|
5
|
+
from typing import Any, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, TypeVar, Union
|
6
|
+
|
7
|
+
from typing_extensions import TypeGuard
|
6
8
|
|
7
9
|
from robot.errors import VariableError
|
8
10
|
from robot.parsing.lexer.tokens import Token
|
@@ -17,17 +19,42 @@ if get_robot_version() < (7, 0):
|
|
17
19
|
else:
|
18
20
|
from robot.variables.search import VariableMatches as VariableIterator
|
19
21
|
|
22
|
+
_cached_isinstance_cache: Dict[Tuple[type, Tuple[type, ...]], bool] = {}
|
23
|
+
|
24
|
+
_T = TypeVar("_T")
|
25
|
+
|
26
|
+
|
27
|
+
def cached_isinstance(obj: Any, *expected_types: Type[_T]) -> TypeGuard[Union[_T]]:
|
28
|
+
try:
|
29
|
+
t = type(obj)
|
30
|
+
if (t, expected_types) in _cached_isinstance_cache:
|
31
|
+
return _cached_isinstance_cache[(t, expected_types)]
|
32
|
+
|
33
|
+
_cached_isinstance_cache[(t, expected_types)] = result = isinstance(obj, expected_types)
|
34
|
+
|
35
|
+
return result
|
36
|
+
|
37
|
+
except TypeError:
|
38
|
+
return False
|
39
|
+
|
40
|
+
|
41
|
+
# def cached_isinstance(obj: Any, *expected_types: type) -> bool:
|
42
|
+
# try:
|
43
|
+
# return isinstance(obj, expected_types)
|
44
|
+
# except TypeError:
|
45
|
+
# return False
|
46
|
+
|
20
47
|
|
21
48
|
def iter_nodes(node: ast.AST, descendants: bool = True) -> Iterator[ast.AST]:
|
22
49
|
for _field, value in ast.iter_fields(node):
|
23
|
-
if
|
50
|
+
if cached_isinstance(value, list):
|
24
51
|
for item in value:
|
25
|
-
if
|
52
|
+
if cached_isinstance(item, ast.AST):
|
26
53
|
yield item
|
27
54
|
if descendants:
|
28
55
|
yield from iter_nodes(item)
|
29
56
|
|
30
|
-
elif
|
57
|
+
elif cached_isinstance(value, ast.AST):
|
31
58
|
yield value
|
32
59
|
if descendants:
|
33
60
|
yield from iter_nodes(value)
|
@@ -53,7 +80,7 @@ class FirstAndLastRealStatementFinder(Visitor):
|
|
53
80
|
return finder.first_statement, finder.last_statement
|
54
81
|
|
55
82
|
def visit_Statement(self, statement: ast.AST) -> None: # noqa: N802
|
56
|
-
if not
|
83
|
+
if not cached_isinstance(statement, EmptyLine):
|
57
84
|
if self.first_statement is None:
|
58
85
|
self.first_statement = statement
|
59
86
|
|
@@ -63,7 +90,7 @@ class FirstAndLastRealStatementFinder(Visitor):
|
|
63
90
|
def _get_non_data_range_from_node(
|
64
91
|
node: ast.AST, only_start: bool = False, allow_comments: bool = False
|
65
92
|
) -> Optional[Range]:
|
66
|
-
if
|
93
|
+
if cached_isinstance(node, Statement) and node.tokens:
|
67
94
|
start_token = next(
|
68
95
|
(
|
69
96
|
v
|
@@ -115,7 +142,7 @@ def range_from_node(
|
|
115
142
|
allow_comments: bool = False,
|
116
143
|
) -> Range:
|
117
144
|
if skip_non_data:
|
118
|
-
if
|
145
|
+
if cached_isinstance(node, Statement) and node.tokens:
|
119
146
|
result = _get_non_data_range_from_node(node, only_start, allow_comments)
|
120
147
|
if result is not None:
|
121
148
|
return result
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: robotcode-robot
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.93.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
|
@@ -26,7 +26,7 @@ Classifier: Topic :: Utilities
|
|
26
26
|
Classifier: Typing :: Typed
|
27
27
|
Requires-Python: >=3.8
|
28
28
|
Requires-Dist: platformdirs<4.2.0,>=3.2.0
|
29
|
-
Requires-Dist: robotcode-core==0.
|
29
|
+
Requires-Dist: robotcode-core==0.93.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
|
@@ -1,30 +1,30 @@
|
|
1
1
|
robotcode/robot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
robotcode/robot/__version__.py,sha256=
|
2
|
+
robotcode/robot/__version__.py,sha256=W-KEJT0wpRrG8XJ2aDG4NSyzaUucgZR9y6mwfCTlku4,23
|
3
3
|
robotcode/robot/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
|
4
4
|
robotcode/robot/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
robotcode/robot/config/loader.py,sha256=
|
5
|
+
robotcode/robot/config/loader.py,sha256=bNJwr_XdCoUzpG2ag0BH33PIfiCwn0GMxn7q_Sw3zOk,8103
|
6
6
|
robotcode/robot/config/model.py,sha256=sgr6-4_E06g-yIXW41Z-NtIXZ_7JMmR5WvUD7kTUqu4,89106
|
7
|
-
robotcode/robot/config/utils.py,sha256=
|
7
|
+
robotcode/robot/config/utils.py,sha256=xY-LH31BidWzonpvSrle-4HvKrp02I7IRqU2JwlL4Ls,2931
|
8
8
|
robotcode/robot/diagnostics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
robotcode/robot/diagnostics/diagnostics_modifier.py,sha256=3dDsu8-ET6weIvv7Sk3IQaPYFNxnXUs8Y7gpGTjfOBs,9796
|
10
10
|
robotcode/robot/diagnostics/document_cache_helper.py,sha256=MlOPXC3mUVjNB2rHn5UJK7NnUPa8S4mE3qojILOi_Mk,23756
|
11
|
-
robotcode/robot/diagnostics/entities.py,sha256=
|
11
|
+
robotcode/robot/diagnostics/entities.py,sha256=dWZFSrAuW9wzW9AEDSr-stseexLbJIYfdScfAbBj4FY,11002
|
12
12
|
robotcode/robot/diagnostics/errors.py,sha256=HCE0h0_ui1tx-6pJHs0r4s09ZHmArYwKUo-_lBl9K-4,1600
|
13
|
-
robotcode/robot/diagnostics/imports_manager.py,sha256=
|
14
|
-
robotcode/robot/diagnostics/library_doc.py,sha256=
|
13
|
+
robotcode/robot/diagnostics/imports_manager.py,sha256=GvcIEqghVjtliTWUwQo4N-xier00CGnEh_eO7l05cJg,57187
|
14
|
+
robotcode/robot/diagnostics/library_doc.py,sha256=7m29COvY_YlK_n9dj57q0c7sFRThoR_X4bgdZapWwaY,98160
|
15
15
|
robotcode/robot/diagnostics/model_helper.py,sha256=_5ixKKMrb-nY-uvV8_WjJ1rlNlz7gT7kHM5NYi_hjVg,30232
|
16
|
-
robotcode/robot/diagnostics/namespace.py,sha256=
|
16
|
+
robotcode/robot/diagnostics/namespace.py,sha256=lH5RReu-fW0g8CZBh9LKWHItCZh14D-XFvFdYUvBoDg,90151
|
17
17
|
robotcode/robot/diagnostics/namespace_analyzer.py,sha256=3eC1xEBm_foCnqoHk8Cj6O11UXOHcHxWnfuKP8M5KIQ,51369
|
18
18
|
robotcode/robot/diagnostics/workspace_config.py,sha256=3SoewUj_LZB1Ki5hXM8oxQpJr6vyiog66SUw-ibODSA,2478
|
19
19
|
robotcode/robot/utils/__init__.py,sha256=OjNPMn_XSnfaMCyKd8Kmq6vlRt6mIGlzW4qiiD3ykUg,447
|
20
|
-
robotcode/robot/utils/ast.py,sha256=
|
20
|
+
robotcode/robot/utils/ast.py,sha256=_ob36KHFY776n9dhljn0xAWVoUDb7pV86fPW40vIirY,11266
|
21
21
|
robotcode/robot/utils/markdownformatter.py,sha256=0XZZ5wDU2yfzIuShjG7h79PgzMaQJ501WH4YRFcG1VM,11615
|
22
22
|
robotcode/robot/utils/match.py,sha256=ofyfXgrvVddl7a064Dk5Kiqp3a-n_6gTIgqDbL3E80Q,632
|
23
23
|
robotcode/robot/utils/robot_path.py,sha256=qKBh1cEnReBBLKkWu4gB9EzM-scAwE4xJc1m6v2LRN0,1786
|
24
24
|
robotcode/robot/utils/stubs.py,sha256=6-DMI_CQVJHDgG13t-zINKGCRb_Q7MQPm0_AkfhAEvE,748
|
25
25
|
robotcode/robot/utils/variables.py,sha256=fEl8S37lb_mD4hn2MZRAlkiuLGBjAOeZVK0r2o2CfPw,742
|
26
26
|
robotcode/robot/utils/visitor.py,sha256=uYLqEhGPmzWKWI3SSrmCaYMwtKvNShvbiPZ4b3FavX8,3241
|
27
|
-
robotcode_robot-0.
|
28
|
-
robotcode_robot-0.
|
29
|
-
robotcode_robot-0.
|
30
|
-
robotcode_robot-0.
|
27
|
+
robotcode_robot-0.93.0.dist-info/METADATA,sha256=64V1dXSzlmT6KArsIP0YqCm79s_oPXVm3MXBlenwx2E,2240
|
28
|
+
robotcode_robot-0.93.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
29
|
+
robotcode_robot-0.93.0.dist-info/licenses/LICENSE.txt,sha256=B05uMshqTA74s-0ltyHKI6yoPfJ3zYgQbvcXfDVGFf8,10280
|
30
|
+
robotcode_robot-0.93.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|