robotcode-robot 2.8.0.dev670__py3-none-any.whl → 2.9.0.dev51__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 +89 -16
- robotcode/robot/diagnostics/analyzer_result.py +5 -1
- robotcode/robot/diagnostics/document_cache_helper.py +10 -0
- robotcode/robot/diagnostics/entities.py +0 -4
- robotcode/robot/diagnostics/import_resolver.py +1 -14
- robotcode/robot/diagnostics/imports_manager.py +34 -19
- robotcode/robot/diagnostics/library_doc.py +1 -1
- robotcode/robot/diagnostics/namespace.py +17 -14
- robotcode/robot/diagnostics/namespace_analyzer.py +11 -1
- robotcode/robot/diagnostics/semantic_analyzer/analyzer.py +11 -1
- robotcode/robot/utils/markdownformatter.py +8 -4
- robotcode/robot/utils/variables.py +9 -0
- {robotcode_robot-2.8.0.dev670.dist-info → robotcode_robot-2.9.0.dev51.dist-info}/METADATA +2 -2
- {robotcode_robot-2.8.0.dev670.dist-info → robotcode_robot-2.9.0.dev51.dist-info}/RECORD +16 -16
- {robotcode_robot-2.8.0.dev670.dist-info → robotcode_robot-2.9.0.dev51.dist-info}/WHEEL +1 -1
robotcode/robot/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.
|
|
1
|
+
__version__ = "2.9.0-dev.51"
|
robotcode/robot/config/model.py
CHANGED
|
@@ -3222,6 +3222,42 @@ class RobotProfile(RobotExtendBaseProfile):
|
|
|
3222
3222
|
)
|
|
3223
3223
|
|
|
3224
3224
|
|
|
3225
|
+
@dataclass(frozen=True)
|
|
3226
|
+
class UnknownProfile:
|
|
3227
|
+
"""A profile name or pattern that selects no profile, and where it comes from."""
|
|
3228
|
+
|
|
3229
|
+
name: str
|
|
3230
|
+
# the profile whose `inherits` names it; None for a name of the selection or of `default-profiles`
|
|
3231
|
+
inherited_by: Optional[str] = None
|
|
3232
|
+
# whether it comes from `default-profiles`
|
|
3233
|
+
default: bool = False
|
|
3234
|
+
|
|
3235
|
+
@property
|
|
3236
|
+
def selected(self) -> bool:
|
|
3237
|
+
"""Whether the name comes from the selection: `-p`, `ROBOTCODE_PROFILES` or an editor."""
|
|
3238
|
+
return self.inherited_by is None and not self.default
|
|
3239
|
+
|
|
3240
|
+
@property
|
|
3241
|
+
def message(self) -> str:
|
|
3242
|
+
return f"No profile matches '{self.name}'."
|
|
3243
|
+
|
|
3244
|
+
|
|
3245
|
+
class ProfileNotFoundError(ValueError):
|
|
3246
|
+
"""A profile name or pattern that selects no profile."""
|
|
3247
|
+
|
|
3248
|
+
def __init__(self, unknown: UnknownProfile) -> None:
|
|
3249
|
+
super().__init__(unknown.message)
|
|
3250
|
+
self.unknown = unknown
|
|
3251
|
+
|
|
3252
|
+
|
|
3253
|
+
class ProfileInheritanceError(ValueError):
|
|
3254
|
+
"""A profile that inherits itself, directly or through other profiles."""
|
|
3255
|
+
|
|
3256
|
+
def __init__(self, path: Tuple[str, ...]) -> None:
|
|
3257
|
+
super().__init__(f"The profile '{path[0]}' inherits itself: {' -> '.join(path)}.")
|
|
3258
|
+
self.path = path
|
|
3259
|
+
|
|
3260
|
+
|
|
3225
3261
|
@dataclass
|
|
3226
3262
|
class RobotConfig(RobotExtendBaseProfile):
|
|
3227
3263
|
"""Robot Framework configuration."""
|
|
@@ -3288,11 +3324,13 @@ class RobotConfig(RobotExtendBaseProfile):
|
|
|
3288
3324
|
*names: str,
|
|
3289
3325
|
verbose_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
|
3290
3326
|
error_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
|
3327
|
+
unknown_profile_callback: Optional[Callable[[UnknownProfile], None]] = None,
|
|
3291
3328
|
) -> Dict[str, Tuple[RobotProfile, Optional[Set[Tuple[str, RobotProfile]]]]]:
|
|
3292
3329
|
result: Dict[str, Tuple[RobotProfile, Optional[Set[Tuple[str, RobotProfile]]]]] = {}
|
|
3293
3330
|
|
|
3294
3331
|
profiles = self.profiles or {}
|
|
3295
3332
|
|
|
3333
|
+
from_default_profiles = not names
|
|
3296
3334
|
if not names:
|
|
3297
3335
|
if verbose_callback:
|
|
3298
3336
|
verbose_callback("No profiles given, try to check if there are default profiles specified.")
|
|
@@ -3306,40 +3344,65 @@ class RobotConfig(RobotExtendBaseProfile):
|
|
|
3306
3344
|
|
|
3307
3345
|
names = (*(default_profile or ()),)
|
|
3308
3346
|
|
|
3309
|
-
|
|
3347
|
+
# each name that selects no profile is reported once for each place it comes from
|
|
3348
|
+
reported: Set[UnknownProfile] = set()
|
|
3349
|
+
|
|
3350
|
+
def report(unknown: UnknownProfile) -> None:
|
|
3351
|
+
if unknown in reported:
|
|
3352
|
+
return
|
|
3353
|
+
reported.add(unknown)
|
|
3354
|
+
|
|
3355
|
+
# a name that selects no profile is an error of the configuration; only callers that must go on, such as
|
|
3356
|
+
# the language server, take it and go on without it
|
|
3357
|
+
if unknown_profile_callback is not None:
|
|
3358
|
+
unknown_profile_callback(unknown)
|
|
3359
|
+
# a name of `default-profiles` is only reported: the `default-profiles` of the user configuration can name
|
|
3360
|
+
# a profile of that file, which the `[profiles]` of a project replace
|
|
3361
|
+
elif unknown.default and error_callback is not None:
|
|
3362
|
+
error_callback(unknown.message)
|
|
3363
|
+
else:
|
|
3364
|
+
raise ProfileNotFoundError(unknown)
|
|
3365
|
+
|
|
3366
|
+
# `path` holds the profiles whose `inherits` lead to the name; the last one names it
|
|
3367
|
+
def select(name: str, path: Tuple[str, ...] = ()) -> None:
|
|
3310
3368
|
if not name:
|
|
3311
3369
|
return
|
|
3312
3370
|
|
|
3313
3371
|
nonlocal result
|
|
3314
3372
|
|
|
3373
|
+
parent = path[-1] if path else None
|
|
3374
|
+
|
|
3315
3375
|
if verbose_callback:
|
|
3316
|
-
if
|
|
3317
|
-
verbose_callback(
|
|
3318
|
-
f"Selecting profiles matching '{name}'"
|
|
3319
|
-
f" for parent profile '{next(f for f in parent_profiles)[0]}'."
|
|
3320
|
-
)
|
|
3376
|
+
if parent is not None:
|
|
3377
|
+
verbose_callback(f"Selecting profiles matching '{name}' for parent profile '{parent}'.")
|
|
3321
3378
|
else:
|
|
3322
3379
|
verbose_callback(f"Selecting profiles matching '{name}'.")
|
|
3323
3380
|
|
|
3324
|
-
|
|
3381
|
+
if name in profiles:
|
|
3382
|
+
# an exact name selects its profile, also when it contains `*`, `?` or `[`
|
|
3383
|
+
profile_names = [name]
|
|
3384
|
+
else:
|
|
3385
|
+
# a pattern in `inherits` does not match the profile that has it
|
|
3386
|
+
profile_names = [p for p in profiles.keys() if p != parent and fnmatch.fnmatchcase(p, name)]
|
|
3325
3387
|
|
|
3326
3388
|
if not profile_names:
|
|
3327
|
-
|
|
3328
|
-
if error_callback is None:
|
|
3329
|
-
raise ValueError(message)
|
|
3330
|
-
|
|
3331
|
-
error_callback(message)
|
|
3389
|
+
report(UnknownProfile(name, inherited_by=parent, default=from_default_profiles and parent is None))
|
|
3332
3390
|
return
|
|
3333
3391
|
|
|
3392
|
+
parent_profiles = {(parent, profiles[parent])} if parent is not None else None
|
|
3393
|
+
|
|
3334
3394
|
for v in profile_names:
|
|
3395
|
+
if v in path:
|
|
3396
|
+
raise ProfileInheritanceError((*path[path.index(v) :], v))
|
|
3397
|
+
|
|
3335
3398
|
p = profiles[v]
|
|
3336
3399
|
|
|
3337
3400
|
if p.inherits:
|
|
3338
3401
|
if isinstance(p.inherits, list):
|
|
3339
3402
|
for i in p.inherits:
|
|
3340
|
-
select(str(i),
|
|
3403
|
+
select(str(i), (*path, v))
|
|
3341
3404
|
else:
|
|
3342
|
-
select(str(p.inherits),
|
|
3405
|
+
select(str(p.inherits), (*path, v))
|
|
3343
3406
|
|
|
3344
3407
|
if v in result:
|
|
3345
3408
|
if parent_profiles is None:
|
|
@@ -3362,14 +3425,21 @@ class RobotConfig(RobotExtendBaseProfile):
|
|
|
3362
3425
|
*names: str,
|
|
3363
3426
|
verbose_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
|
3364
3427
|
error_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
|
3428
|
+
unknown_profile_callback: Optional[Callable[[UnknownProfile], None]] = None,
|
|
3365
3429
|
) -> RobotBaseProfile:
|
|
3366
|
-
return self.combine_profiles_ex(
|
|
3430
|
+
return self.combine_profiles_ex(
|
|
3431
|
+
*names,
|
|
3432
|
+
verbose_callback=verbose_callback,
|
|
3433
|
+
error_callback=error_callback,
|
|
3434
|
+
unknown_profile_callback=unknown_profile_callback,
|
|
3435
|
+
)[0]
|
|
3367
3436
|
|
|
3368
3437
|
def combine_profiles_ex(
|
|
3369
3438
|
self,
|
|
3370
3439
|
*names: str,
|
|
3371
3440
|
verbose_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
|
3372
3441
|
error_callback: Optional[Callable[[Union[str, Callable[[], Any]]], None]] = None,
|
|
3442
|
+
unknown_profile_callback: Optional[Callable[[UnknownProfile], None]] = None,
|
|
3373
3443
|
) -> Tuple[RobotBaseProfile, Dict[str, RobotProfile], List[str]]:
|
|
3374
3444
|
enabled_profiles = []
|
|
3375
3445
|
type_hints = get_type_hints(RobotBaseProfile)
|
|
@@ -3390,7 +3460,10 @@ class RobotConfig(RobotExtendBaseProfile):
|
|
|
3390
3460
|
)
|
|
3391
3461
|
|
|
3392
3462
|
selected_profiles = self._select_profiles(
|
|
3393
|
-
*names,
|
|
3463
|
+
*names,
|
|
3464
|
+
verbose_callback=verbose_callback,
|
|
3465
|
+
error_callback=error_callback,
|
|
3466
|
+
unknown_profile_callback=unknown_profile_callback,
|
|
3394
3467
|
)
|
|
3395
3468
|
if verbose_callback:
|
|
3396
3469
|
if selected_profiles:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from typing import TYPE_CHECKING, Dict, List, Optional, Set
|
|
3
3
|
|
|
4
4
|
from robotcode.core.lsp.types import Diagnostic, Location, Range
|
|
@@ -25,3 +25,7 @@ class AnalyzerResult:
|
|
|
25
25
|
testcase_metadata_references: Dict[str, Set[Location]]
|
|
26
26
|
scope_tree: ScopeTree
|
|
27
27
|
semantic_model: Optional["SemanticModel"] = None
|
|
28
|
+
# the environment variables the analysis read, which the namespace cache compares when it restores the analysis:
|
|
29
|
+
# whether each one checked without a default value is set, and the hash of each one whose value the analysis used
|
|
30
|
+
environment_set: Dict[str, bool] = field(default_factory=dict)
|
|
31
|
+
environment_values: Dict[str, Optional[str]] = field(default_factory=dict)
|
|
@@ -640,6 +640,16 @@ class DocumentsCacheHelper:
|
|
|
640
640
|
)
|
|
641
641
|
return None
|
|
642
642
|
|
|
643
|
+
# from_data resolved the imports again with the current Python path and environment variables. A namespace
|
|
644
|
+
# with a failed import is never stored and the dependency keys name the files found, so a failed import or
|
|
645
|
+
# other keys mean that an import finds another file or none now.
|
|
646
|
+
if result.failed_imports or set(result.dependency_metas or {}) != set(meta.dependency_fingerprints):
|
|
647
|
+
self._logger.debug(
|
|
648
|
+
lambda: f"Cache miss for {source}: the imports find other files now, re-analyzing",
|
|
649
|
+
context_name="cache",
|
|
650
|
+
)
|
|
651
|
+
return None
|
|
652
|
+
|
|
643
653
|
self._logger.debug(
|
|
644
654
|
lambda: f"Loaded namespace from disk cache for {source}",
|
|
645
655
|
context_name="import",
|
|
@@ -9,7 +9,6 @@ The resolver takes imports + a VariableScope and returns ResolvedImports
|
|
|
9
9
|
containing all resolved entries and collected diagnostics.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
import os
|
|
13
12
|
from dataclasses import dataclass, field
|
|
14
13
|
from pathlib import Path
|
|
15
14
|
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
|
@@ -129,7 +128,6 @@ class ImportResolver:
|
|
|
129
128
|
"_scope",
|
|
130
129
|
"_sentinel",
|
|
131
130
|
"_source",
|
|
132
|
-
"_source_hints",
|
|
133
131
|
"_source_id",
|
|
134
132
|
"_variables",
|
|
135
133
|
"_variables_dirty",
|
|
@@ -159,14 +157,11 @@ class ImportResolver:
|
|
|
159
157
|
self._diagnostics: List[Diagnostic] = []
|
|
160
158
|
self._variables: Dict[str, Any] = {}
|
|
161
159
|
self._variables_dirty = True
|
|
162
|
-
self._source_hints: Dict[str, str] = {}
|
|
163
160
|
self._dependency_metas: Dict[str, Optional[Any]] = {}
|
|
164
161
|
self._failed_imports: List[FailedImport] = []
|
|
165
162
|
|
|
166
|
-
def resolve(self, imports: Iterable[Import]
|
|
163
|
+
def resolve(self, imports: Iterable[Import]) -> ResolvedImports:
|
|
167
164
|
"""Resolve all imports and return the result."""
|
|
168
|
-
if source_hints:
|
|
169
|
-
self._source_hints = source_hints
|
|
170
165
|
self._refresh_variables()
|
|
171
166
|
self._import_default_libraries()
|
|
172
167
|
self._handle_imports(imports, self._base_dir, top_level=True)
|
|
@@ -542,19 +537,11 @@ class ImportResolver:
|
|
|
542
537
|
) -> None:
|
|
543
538
|
assert imp.name is not None
|
|
544
539
|
|
|
545
|
-
# Look up cached source hint to skip find_resource() filesystem search
|
|
546
|
-
source_hint: Optional[str] = None
|
|
547
|
-
if self._source_hints:
|
|
548
|
-
hint = self._source_hints.get(imp.hint_key)
|
|
549
|
-
if hint is not None and os.path.isfile(hint):
|
|
550
|
-
source_hint = hint
|
|
551
|
-
|
|
552
540
|
resource_doc, meta = self._imports_manager.get_resource_doc_for_resource_import_with_meta(
|
|
553
541
|
imp.name,
|
|
554
542
|
base_dir,
|
|
555
543
|
sentinel=self._sentinel,
|
|
556
544
|
variables=self._variables,
|
|
557
|
-
source=source_hint,
|
|
558
545
|
)
|
|
559
546
|
if resource_doc.source is not None:
|
|
560
547
|
# setdefault: on re-imports of the same resource the meta captured
|
|
@@ -59,7 +59,7 @@ from ..__version__ import __version__
|
|
|
59
59
|
from ..utils import RF_VERSION
|
|
60
60
|
from ..utils.robot_path import find_file_ex
|
|
61
61
|
from ..utils.stubs import Languages
|
|
62
|
-
from ..utils.variables import contains_variable
|
|
62
|
+
from ..utils.variables import contains_variable, environment_value_hash
|
|
63
63
|
from .data_cache import CACHE_DIR_NAME, CacheSection, build_cache_dir
|
|
64
64
|
from .data_cache import SqliteDataCache as DefaultDataCache
|
|
65
65
|
from .entities import (
|
|
@@ -722,6 +722,10 @@ class NamespaceMetaData:
|
|
|
722
722
|
config_fingerprint: Any
|
|
723
723
|
dependency_fingerprints: Dict[str, Any] = field(default_factory=dict)
|
|
724
724
|
semantic_model_enabled: bool = False
|
|
725
|
+
# the environment variables the analysis read: whether each one checked without a default value was set, and the
|
|
726
|
+
# hash of each one whose value it used (see environment_value_hash)
|
|
727
|
+
environment_set: Dict[str, bool] = field(default_factory=dict)
|
|
728
|
+
environment_values: Dict[str, Optional[str]] = field(default_factory=dict)
|
|
725
729
|
|
|
726
730
|
|
|
727
731
|
class ImportsManager:
|
|
@@ -808,7 +812,6 @@ class ImportsManager:
|
|
|
808
812
|
self._config_fingerprint: Any = (
|
|
809
813
|
tuple(sorted(self.cmd_variables.items())),
|
|
810
814
|
tuple(self.cmd_variable_files),
|
|
811
|
-
tuple(sorted((k, v) for k, v in self._environment.items() if k not in os.environ)),
|
|
812
815
|
tuple(self.global_library_search_order),
|
|
813
816
|
self._languages_fingerprint,
|
|
814
817
|
)
|
|
@@ -895,8 +898,9 @@ class ImportsManager:
|
|
|
895
898
|
"""Cached configuration snapshot.
|
|
896
899
|
|
|
897
900
|
Computed once at init from cmd_variables, cmd_variable_files,
|
|
898
|
-
|
|
899
|
-
detected via direct ``==`` comparison.
|
|
901
|
+
global_library_search_order and the language settings. Changes are
|
|
902
|
+
detected via direct ``==`` comparison. Environment variables are
|
|
903
|
+
not part of it: each namespace keeps the ones its analysis read.
|
|
900
904
|
"""
|
|
901
905
|
return self._config_fingerprint
|
|
902
906
|
|
|
@@ -949,13 +953,15 @@ class ImportsManager:
|
|
|
949
953
|
config_fingerprint=self.config_fingerprint,
|
|
950
954
|
dependency_fingerprints=dict(dependency_metas),
|
|
951
955
|
semantic_model_enabled=semantic_model_enabled,
|
|
956
|
+
environment_set=dict(namespace.environment_set),
|
|
957
|
+
environment_values=dict(namespace.environment_values),
|
|
952
958
|
)
|
|
953
959
|
|
|
954
960
|
def validate_namespace_meta(self, meta: NamespaceMetaData, source_disk_info: Optional[DiskInfo]) -> bool:
|
|
955
961
|
"""Check whether a cached NamespaceMetaData is fresh for the given content.
|
|
956
962
|
|
|
957
963
|
Performs a 2-level validation:
|
|
958
|
-
Level 1 (fast): source_disk_info, config_fingerprint
|
|
964
|
+
Level 1 (fast): source_disk_info, config_fingerprint, the environment variables the analysis read
|
|
959
965
|
Level 2 (dependency check): each dependency fingerprint
|
|
960
966
|
|
|
961
967
|
Validation is against the *effective* content: the source is compared
|
|
@@ -990,6 +996,25 @@ class ImportsManager:
|
|
|
990
996
|
)
|
|
991
997
|
return False
|
|
992
998
|
|
|
999
|
+
# the analysis reads os.environ itself, so it does not matter where a variable was set
|
|
1000
|
+
for env_name, was_set in meta.environment_set.items():
|
|
1001
|
+
if (os.environ.get(env_name) is not None) != was_set:
|
|
1002
|
+
name = env_name
|
|
1003
|
+
self._logger.debug(
|
|
1004
|
+
lambda: f"Cache miss for {source}: environment variable {name!r} was set or unset",
|
|
1005
|
+
context_name="cache",
|
|
1006
|
+
)
|
|
1007
|
+
return False
|
|
1008
|
+
|
|
1009
|
+
for env_name, value_hash in meta.environment_values.items():
|
|
1010
|
+
if environment_value_hash(os.environ.get(env_name)) != value_hash:
|
|
1011
|
+
name = env_name
|
|
1012
|
+
self._logger.debug(
|
|
1013
|
+
lambda: f"Cache miss for {source}: environment variable {name!r} changed",
|
|
1014
|
+
context_name="cache",
|
|
1015
|
+
)
|
|
1016
|
+
return False
|
|
1017
|
+
|
|
993
1018
|
# Level 2: dependency checks — direct comparison, no hashing
|
|
994
1019
|
for key, saved_value in meta.dependency_fingerprints.items():
|
|
995
1020
|
if key.startswith("lib:"):
|
|
@@ -2211,10 +2236,8 @@ class ImportsManager:
|
|
|
2211
2236
|
base_dir: str,
|
|
2212
2237
|
sentinel: Any = None,
|
|
2213
2238
|
variables: Optional[Dict[str, Any]] = None,
|
|
2214
|
-
*,
|
|
2215
|
-
source: Optional[str] = None,
|
|
2216
2239
|
) -> _ResourcesEntry:
|
|
2217
|
-
source =
|
|
2240
|
+
source = self.find_resource(name, base_dir, variables=variables)
|
|
2218
2241
|
normalized_source = os.path.normpath(os.path.abspath(source))
|
|
2219
2242
|
|
|
2220
2243
|
entry_key = _ResourcesEntryKey(normalized_source)
|
|
@@ -2238,12 +2261,10 @@ class ImportsManager:
|
|
|
2238
2261
|
base_dir: str,
|
|
2239
2262
|
sentinel: Any = None,
|
|
2240
2263
|
variables: Optional[Dict[str, Any]] = None,
|
|
2241
|
-
*,
|
|
2242
|
-
source: Optional[str] = None,
|
|
2243
2264
|
) -> Tuple["Namespace", LibraryDoc]:
|
|
2244
2265
|
with self._logger.measure_time(lambda: f"getting namespace and libdoc for {name}", context_name="import"):
|
|
2245
2266
|
with self._logger.measure_time(lambda: f"getting resource entry {name}", context_name="import"):
|
|
2246
|
-
entry = self._get_entry_for_resource_import(name, base_dir, sentinel, variables
|
|
2267
|
+
entry = self._get_entry_for_resource_import(name, base_dir, sentinel, variables)
|
|
2247
2268
|
|
|
2248
2269
|
with self._logger.measure_time(lambda: f"getting namespace {name}", context_name="import"):
|
|
2249
2270
|
namespace = entry.get_namespace()
|
|
@@ -2258,11 +2279,9 @@ class ImportsManager:
|
|
|
2258
2279
|
base_dir: str,
|
|
2259
2280
|
sentinel: Any = None,
|
|
2260
2281
|
variables: Optional[Dict[str, Any]] = None,
|
|
2261
|
-
*,
|
|
2262
|
-
source: Optional[str] = None,
|
|
2263
2282
|
) -> Tuple[ResourceDoc, Optional[RobotFileMeta]]:
|
|
2264
2283
|
with self._logger.measure_time(lambda: f"getting resource doc for {name}", context_name="import"):
|
|
2265
|
-
entry = self._get_entry_for_resource_import(name, base_dir, sentinel, variables
|
|
2284
|
+
entry = self._get_entry_for_resource_import(name, base_dir, sentinel, variables)
|
|
2266
2285
|
|
|
2267
2286
|
return entry.get_resource_doc_with_meta()
|
|
2268
2287
|
|
|
@@ -2272,12 +2291,8 @@ class ImportsManager:
|
|
|
2272
2291
|
base_dir: str,
|
|
2273
2292
|
sentinel: Any = None,
|
|
2274
2293
|
variables: Optional[Dict[str, Any]] = None,
|
|
2275
|
-
*,
|
|
2276
|
-
source: Optional[str] = None,
|
|
2277
2294
|
) -> ResourceDoc:
|
|
2278
|
-
return self.get_resource_doc_for_resource_import_with_meta(name, base_dir, sentinel, variables
|
|
2279
|
-
0
|
|
2280
|
-
]
|
|
2295
|
+
return self.get_resource_doc_for_resource_import_with_meta(name, base_dir, sentinel, variables)[0]
|
|
2281
2296
|
|
|
2282
2297
|
def complete_library_import(
|
|
2283
2298
|
self,
|
|
@@ -2728,7 +2728,7 @@ class _Logger(AbstractLogger):
|
|
|
2728
2728
|
|
|
2729
2729
|
def _import_test_library(name: str) -> Union[Any, Tuple[Any, str]]:
|
|
2730
2730
|
with OutputCapturer(library_import=True):
|
|
2731
|
-
importer = Importer("
|
|
2731
|
+
importer = Importer("library", LOGGER)
|
|
2732
2732
|
return importer.import_class_or_module(name, return_source=True)
|
|
2733
2733
|
|
|
2734
2734
|
|
|
@@ -133,12 +133,6 @@ class NamespaceData:
|
|
|
133
133
|
# --- ScopeTree (local scopes only, file_scope is reconstructed) ---
|
|
134
134
|
local_scopes: List[LocalScope] = field(default_factory=list)
|
|
135
135
|
|
|
136
|
-
# --- Resolved resource import sources ---
|
|
137
|
-
# Maps (import_name\0source_file) → resolved absolute path for resources.
|
|
138
|
-
# Used as source hints in from_data() to skip find_resource() filesystem
|
|
139
|
-
# lookups during import re-resolution.
|
|
140
|
-
resolved_resource_sources: Dict[str, str] = field(default_factory=dict)
|
|
141
|
-
|
|
142
136
|
# --- Authoritative variable definitions (stable_id → VariableDefinition) ---
|
|
143
137
|
# Stores the exact VariableDefinition objects that were referenced during
|
|
144
138
|
# analysis. Used as fallback in from_data() when the re-parsed resource doc
|
|
@@ -189,6 +183,8 @@ class Namespace:
|
|
|
189
183
|
semantic_model: Optional["SemanticModel"] = None,
|
|
190
184
|
dependency_metas: Optional[Dict[str, Optional[Any]]] = None,
|
|
191
185
|
failed_imports: Optional[List[FailedImport]] = None,
|
|
186
|
+
environment_set: Optional[Dict[str, bool]] = None,
|
|
187
|
+
environment_values: Optional[Dict[str, Optional[str]]] = None,
|
|
192
188
|
) -> None:
|
|
193
189
|
self.imports_manager = imports_manager
|
|
194
190
|
self.source = source
|
|
@@ -219,6 +215,8 @@ class Namespace:
|
|
|
219
215
|
self._semantic_model = semantic_model
|
|
220
216
|
self._dependency_metas = dependency_metas
|
|
221
217
|
self._failed_imports = failed_imports or []
|
|
218
|
+
self._environment_set = environment_set or {}
|
|
219
|
+
self._environment_values = environment_values or {}
|
|
222
220
|
|
|
223
221
|
# Lazy-computed caches
|
|
224
222
|
self._namespaces: Optional[Dict[KeywordMatcher, List[LibraryEntry]]] = None
|
|
@@ -244,6 +242,16 @@ class Namespace:
|
|
|
244
242
|
"""The imports whose resolution raised or whose library or variables documentation has errors."""
|
|
245
243
|
return self._failed_imports
|
|
246
244
|
|
|
245
|
+
@property
|
|
246
|
+
def environment_set(self) -> Dict[str, bool]:
|
|
247
|
+
"""Whether each environment variable the analysis checked without a default value was set."""
|
|
248
|
+
return self._environment_set
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def environment_values(self) -> Dict[str, Optional[str]]:
|
|
252
|
+
"""The hash of each environment variable whose value the analysis used, see `environment_value_hash`."""
|
|
253
|
+
return self._environment_values
|
|
254
|
+
|
|
247
255
|
@property
|
|
248
256
|
def document(self) -> Optional[TextDocument]:
|
|
249
257
|
return self._document() if self._document is not None else None
|
|
@@ -538,12 +546,6 @@ class Namespace:
|
|
|
538
546
|
else:
|
|
539
547
|
var_assigns_merged[sid] = set(ranges)
|
|
540
548
|
|
|
541
|
-
# Build resolved resource source hints for from_data() optimization
|
|
542
|
-
resolved_res_sources: Dict[str, str] = {}
|
|
543
|
-
for imp, entry in self._import_entries.items():
|
|
544
|
-
if isinstance(entry, ResourceEntry) and entry.library_doc.source:
|
|
545
|
-
resolved_res_sources[imp.hint_key] = entry.library_doc.source
|
|
546
|
-
|
|
547
549
|
return NamespaceData(
|
|
548
550
|
source=self.source,
|
|
549
551
|
source_id=str(self.source_id) if self.source_id else None,
|
|
@@ -563,7 +565,6 @@ class Namespace:
|
|
|
563
565
|
metadata_references={k: set(v) for k, v in self._metadata_references.items()},
|
|
564
566
|
testcase_metadata_references={k: set(v) for k, v in self._testcase_metadata_references.items()},
|
|
565
567
|
local_scopes=list(self._scope_tree.local_scopes),
|
|
566
|
-
resolved_resource_sources=resolved_res_sources,
|
|
567
568
|
variable_definitions=all_var_defs,
|
|
568
569
|
)
|
|
569
570
|
|
|
@@ -595,7 +596,7 @@ class Namespace:
|
|
|
595
596
|
)
|
|
596
597
|
|
|
597
598
|
resolver = ImportResolver(imports_manager, data.source, scope, sentinel=sentinel)
|
|
598
|
-
resolved = resolver.resolve(data.imports
|
|
599
|
+
resolved = resolver.resolve(data.imports)
|
|
599
600
|
|
|
600
601
|
# Add imported variables to scope (needed for variable lookup)
|
|
601
602
|
for resource_entry in resolved.resources.values():
|
|
@@ -893,4 +894,6 @@ class NamespaceBuilder:
|
|
|
893
894
|
semantic_model=analyzer_result.semantic_model,
|
|
894
895
|
dependency_metas=resolved.dependency_metas,
|
|
895
896
|
failed_imports=resolved.failed_imports,
|
|
897
|
+
environment_set=analyzer_result.environment_set,
|
|
898
|
+
environment_values=analyzer_result.environment_values,
|
|
896
899
|
)
|
|
@@ -61,6 +61,7 @@ from ..utils.variables import (
|
|
|
61
61
|
InvalidVariableError,
|
|
62
62
|
VariableMatcher,
|
|
63
63
|
contains_variable,
|
|
64
|
+
environment_value_hash,
|
|
64
65
|
is_number_literal,
|
|
65
66
|
replace_curdir_in_variable_values,
|
|
66
67
|
search_variable,
|
|
@@ -145,6 +146,8 @@ class NamespaceAnalyzer(Visitor):
|
|
|
145
146
|
self._testcase_tag_references: Dict[str, Set[Location]] = defaultdict(set)
|
|
146
147
|
self._metadata_references: Dict[str, Set[Location]] = defaultdict(set)
|
|
147
148
|
self._testcase_metadata_references: Dict[str, Set[Location]] = defaultdict(set)
|
|
149
|
+
self._environment_set: Dict[str, bool] = {}
|
|
150
|
+
self._environment_values: Dict[str, Optional[str]] = {}
|
|
148
151
|
|
|
149
152
|
# Phase 1+2 results (set by resolve())
|
|
150
153
|
self._library_doc: ResourceDoc = cast(ResourceDoc, None) # set by resolve()
|
|
@@ -245,6 +248,8 @@ class NamespaceAnalyzer(Visitor):
|
|
|
245
248
|
self._metadata_references,
|
|
246
249
|
self._testcase_metadata_references,
|
|
247
250
|
self._scope_builder.build(self._variable_scope),
|
|
251
|
+
environment_set=self._environment_set,
|
|
252
|
+
environment_values=self._environment_values,
|
|
248
253
|
)
|
|
249
254
|
|
|
250
255
|
def _visit_VariableSection(self, node: VariableSection) -> None: # noqa: N802
|
|
@@ -673,7 +678,10 @@ class NamespaceAnalyzer(Visitor):
|
|
|
673
678
|
and cast(EnvironmentVariableDefinition, var).default_value is None
|
|
674
679
|
):
|
|
675
680
|
env_name = var.name[2:-1]
|
|
676
|
-
|
|
681
|
+
# the namespace cache compares every read of the environment when it restores the analysis
|
|
682
|
+
env_value = os.environ.get(env_name, None)
|
|
683
|
+
self._environment_set[env_name] = env_value is not None
|
|
684
|
+
if env_value is None:
|
|
677
685
|
self._append_diagnostics(
|
|
678
686
|
range=range_from_token(var_token),
|
|
679
687
|
message=(
|
|
@@ -2039,7 +2047,9 @@ class NamespaceAnalyzer(Visitor):
|
|
|
2039
2047
|
return name_default
|
|
2040
2048
|
return False
|
|
2041
2049
|
env_name, sep, default = env_inner.partition("=")
|
|
2050
|
+
# the namespace cache compares every read of the environment when it restores the analysis
|
|
2042
2051
|
env_val = os.environ.get(env_name)
|
|
2052
|
+
self._environment_values[env_name] = environment_value_hash(env_val)
|
|
2043
2053
|
if env_val is not None:
|
|
2044
2054
|
return env_val
|
|
2045
2055
|
if sep:
|
|
@@ -98,6 +98,7 @@ from ...utils.variables import (
|
|
|
98
98
|
InvalidVariableError,
|
|
99
99
|
VariableMatcher,
|
|
100
100
|
contains_variable,
|
|
101
|
+
environment_value_hash,
|
|
101
102
|
is_number_literal,
|
|
102
103
|
replace_curdir_in_variable_values,
|
|
103
104
|
search_variable,
|
|
@@ -280,6 +281,8 @@ class SemanticAnalyzer(Visitor):
|
|
|
280
281
|
self._testcase_tag_references: Dict[str, Set[Location]] = defaultdict(set)
|
|
281
282
|
self._metadata_references: Dict[str, Set[Location]] = defaultdict(set)
|
|
282
283
|
self._testcase_metadata_references: Dict[str, Set[Location]] = defaultdict(set)
|
|
284
|
+
self._environment_set: Dict[str, bool] = {}
|
|
285
|
+
self._environment_values: Dict[str, Optional[str]] = {}
|
|
283
286
|
|
|
284
287
|
# Phase 1+2 results (set by resolve())
|
|
285
288
|
self._library_doc: ResourceDoc = cast(ResourceDoc, None)
|
|
@@ -422,6 +425,8 @@ class SemanticAnalyzer(Visitor):
|
|
|
422
425
|
self._metadata_references,
|
|
423
426
|
self._testcase_metadata_references,
|
|
424
427
|
self._scope_builder.build(self._variable_scope),
|
|
428
|
+
environment_set=self._environment_set,
|
|
429
|
+
environment_values=self._environment_values,
|
|
425
430
|
semantic_model=self._semantic_model,
|
|
426
431
|
)
|
|
427
432
|
|
|
@@ -2004,7 +2009,10 @@ class SemanticAnalyzer(Visitor):
|
|
|
2004
2009
|
and cast(EnvironmentVariableDefinition, var).default_value is None
|
|
2005
2010
|
):
|
|
2006
2011
|
env_name = var.name[2:-1]
|
|
2007
|
-
|
|
2012
|
+
# the namespace cache compares every read of the environment when it restores the analysis
|
|
2013
|
+
env_value = os.environ.get(env_name, None)
|
|
2014
|
+
self._environment_set[env_name] = env_value is not None
|
|
2015
|
+
if env_value is None:
|
|
2008
2016
|
self._append_diagnostics(
|
|
2009
2017
|
range=range_from_token(var_token),
|
|
2010
2018
|
message=(
|
|
@@ -4456,7 +4464,9 @@ class SemanticAnalyzer(Visitor):
|
|
|
4456
4464
|
return name_default
|
|
4457
4465
|
return False
|
|
4458
4466
|
env_name, sep, default = env_inner.partition("=")
|
|
4467
|
+
# the namespace cache compares every read of the environment when it restores the analysis
|
|
4459
4468
|
env_val = os.environ.get(env_name)
|
|
4469
|
+
self._environment_values[env_name] = environment_value_hash(env_val)
|
|
4460
4470
|
if env_val is not None:
|
|
4461
4471
|
return env_val
|
|
4462
4472
|
if sep:
|
|
@@ -144,14 +144,18 @@ class LinkFormatter:
|
|
|
144
144
|
return pre + self._get_link(url)
|
|
145
145
|
|
|
146
146
|
def _get_image(self, src: str, title: Optional[str] = None) -> str:
|
|
147
|
-
return f"})"
|
|
148
148
|
|
|
149
149
|
def _get_link(self, href: str, content: Optional[str] = None) -> str:
|
|
150
|
-
return f"[{content or href}]({self.
|
|
150
|
+
return f"[{content or href}]({self._target(href)})"
|
|
151
151
|
|
|
152
|
-
def
|
|
152
|
+
def _target(self, target: str) -> str:
|
|
153
153
|
# `#` is escaped in the whole line before links are formatted, a link target needs it as it is
|
|
154
|
-
|
|
154
|
+
target = target.replace("\\#", "#")
|
|
155
|
+
# Markdown takes a target with spaces only in angle brackets; `<` is already escaped in the whole line
|
|
156
|
+
if " " in target:
|
|
157
|
+
return "<" + target.replace(">", "\\>") + ">"
|
|
158
|
+
return target
|
|
155
159
|
|
|
156
160
|
def _quot(self, attr: str) -> str:
|
|
157
161
|
return attr if '"' not in attr else attr.replace('"', """)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import functools
|
|
2
|
+
import hashlib
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Any, Optional, Sequence, Tuple, cast
|
|
4
5
|
|
|
@@ -275,3 +276,11 @@ def replace_curdir_in_variable_values(values: Sequence[str], source: str) -> Tup
|
|
|
275
276
|
"""Replace ${CURDIR} in variable values with the escaped parent directory of source."""
|
|
276
277
|
curdir = str(Path(source).parent).replace("\\", "\\\\")
|
|
277
278
|
return tuple(s.replace("${CURDIR}", curdir) for s in values)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def environment_value_hash(value: Optional[str]) -> Optional[str]:
|
|
282
|
+
"""The SHA-256 of the value of an environment variable, or None when it is not set.
|
|
283
|
+
|
|
284
|
+
What the analysis cache keeps instead of the value, so that no value of the environment ends up in it.
|
|
285
|
+
"""
|
|
286
|
+
return hashlib.sha256(value.encode("utf-8", "surrogateescape")).hexdigest() if value is not None else None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: robotcode-robot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0-dev.51
|
|
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
|
|
@@ -27,7 +27,7 @@ Classifier: Topic :: Utilities
|
|
|
27
27
|
Classifier: Typing :: Typed
|
|
28
28
|
Requires-Python: >=3.10
|
|
29
29
|
Requires-Dist: platformdirs>=4.3
|
|
30
|
-
Requires-Dist: robotcode-core==2.
|
|
30
|
+
Requires-Dist: robotcode-core==2.9.0-dev.51
|
|
31
31
|
Requires-Dist: robotframework>=5.0.0
|
|
32
32
|
Requires-Dist: tomli>=1.1.0; python_version < '3.11'
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
robotcode/robot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
robotcode/robot/__version__.py,sha256=
|
|
2
|
+
robotcode/robot/__version__.py,sha256=68lI1Ud2IVxiSKwJTXlHsfwV7s6ASsCia4CPMEdJ8Ls,29
|
|
3
3
|
robotcode/robot/py.typed,sha256=bWew9mHgMy8LqMu7RuqQXFXLBxh2CRx0dUbSx-3wE48,27
|
|
4
4
|
robotcode/robot/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
robotcode/robot/config/loader.py,sha256=HLbkZem8JiBuy9Q5naghZddAuMPOn8Tk6UBgN2T3Uyc,9073
|
|
6
|
-
robotcode/robot/config/model.py,sha256=
|
|
6
|
+
robotcode/robot/config/model.py,sha256=2dRKRDQV50_k1PmX-7uhqrPTX7Z6oJq4PcAG4pBDRi8,115165
|
|
7
7
|
robotcode/robot/config/utils.py,sha256=A8gD5IItxuC7AY5Olcc8tmsSbqCCi_vZPVoK3BqWt_8,4070
|
|
8
8
|
robotcode/robot/diagnostics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
robotcode/robot/diagnostics/analyzer_result.py,sha256=
|
|
9
|
+
robotcode/robot/diagnostics/analyzer_result.py,sha256=5pHW5Yf3C3rmaDvmM7tP0y_qwqLW3YHhkeVV-qDhxtg,1459
|
|
10
10
|
robotcode/robot/diagnostics/data_cache.py,sha256=Yi52oQTUBFgcPK0UF3lDAj4mRFqoD_VOtrAUyRWXnrQ,18200
|
|
11
11
|
robotcode/robot/diagnostics/diagnostic_rules.py,sha256=7b4vbGTEuxCef8AHTshV4XAJe75aR4-lJ5QEDyuZtcE,3558
|
|
12
12
|
robotcode/robot/diagnostics/diagnostics_modifier.py,sha256=MftxJlUEDsn9ZiOsBDApXC2N-4OjXHSZj1Y9PXXlSAg,11742
|
|
13
|
-
robotcode/robot/diagnostics/document_cache_helper.py,sha256=
|
|
14
|
-
robotcode/robot/diagnostics/entities.py,sha256=
|
|
13
|
+
robotcode/robot/diagnostics/document_cache_helper.py,sha256=llwyocHMKrSpuooXMuBalwnG6hlY6YgMTIPFq2tHFr8,35462
|
|
14
|
+
robotcode/robot/diagnostics/entities.py,sha256=vE5szceIL-ANqJPnAbqIebg3kLddE9OPhxwBL9iKP-M,11949
|
|
15
15
|
robotcode/robot/diagnostics/errors.py,sha256=lqHDWrMNy3ZhuEIVxeEhaU0ns8PB6pVTQBfaMqRQ8L4,2733
|
|
16
|
-
robotcode/robot/diagnostics/import_resolver.py,sha256=
|
|
17
|
-
robotcode/robot/diagnostics/imports_manager.py,sha256=
|
|
16
|
+
robotcode/robot/diagnostics/import_resolver.py,sha256=DZjUOD3uNxFAwsVUqrCTRhColjz9DRPetAvIb_Ccufs,32588
|
|
17
|
+
robotcode/robot/diagnostics/imports_manager.py,sha256=opemApuCLgPNsnqVuYc9MefqHt42g_7Ozqhrmu_osH0,90401
|
|
18
18
|
robotcode/robot/diagnostics/keyword_finder.py,sha256=nwTmjvhyu5H7HHzyk8gt-pny2K2xdH2qCNyKXwwXRnk,18998
|
|
19
|
-
robotcode/robot/diagnostics/library_doc.py,sha256=
|
|
19
|
+
robotcode/robot/diagnostics/library_doc.py,sha256=xJCFm1uMM2OFZqn6sJNfdjGZJ2WsQjGlRmlqDHv64uA,153571
|
|
20
20
|
robotcode/robot/diagnostics/model_helper.py,sha256=_ySXwerkK5xvjWa82T0DFy8jpxRzcaavTJiAHnk2lXQ,29740
|
|
21
|
-
robotcode/robot/diagnostics/namespace.py,sha256=
|
|
22
|
-
robotcode/robot/diagnostics/namespace_analyzer.py,sha256=
|
|
21
|
+
robotcode/robot/diagnostics/namespace.py,sha256=Rcmjk3V0vcY-DUkgjkRyuOdYXurKaTEqXJLc6efYrwY,35489
|
|
22
|
+
robotcode/robot/diagnostics/namespace_analyzer.py,sha256=dGAEbbznPJlFBKN4dLkMDU7w9xc0mYdmRARvRS3Zlwg,104331
|
|
23
23
|
robotcode/robot/diagnostics/project_index.py,sha256=Oqs7kV279PixVS6rGeigPKXGJYGqJdTDmDBjU7ODDRA,11006
|
|
24
24
|
robotcode/robot/diagnostics/scope_tree.py,sha256=UREHoFG7NWh6UTWxET1M5Jxz_XBjXaQJg1GUIYPctq0,8348
|
|
25
25
|
robotcode/robot/diagnostics/variable_scope.py,sha256=bge49DM-1FfQYIeqK2DtBG7JAMm25mazZ_zjh0DoMmg,3096
|
|
26
26
|
robotcode/robot/diagnostics/workspace_config.py,sha256=GlsR1VnUBHoLBvIZtUXkm1dBDFPT9qoiYZzwZlurmXM,3277
|
|
27
27
|
robotcode/robot/diagnostics/semantic_analyzer/__init__.py,sha256=ObbJv-S8LDgkWKZtTZjISBodXxyoLdEiOjVYp5aYbT4,1689
|
|
28
|
-
robotcode/robot/diagnostics/semantic_analyzer/analyzer.py,sha256=
|
|
28
|
+
robotcode/robot/diagnostics/semantic_analyzer/analyzer.py,sha256=CkROVL_B3QsSULXkHkRDTddLCR0sswVVxEKCQUYk_qs,206149
|
|
29
29
|
robotcode/robot/diagnostics/semantic_analyzer/enums.py,sha256=FIf3bHHqYY2etzpNrZTpZxGFsdtBE15lTssI1g8T2ro,8668
|
|
30
30
|
robotcode/robot/diagnostics/semantic_analyzer/json_dump.py,sha256=2Yq3CLAKF5IEqKeFDoIYn-Ize_1Ucgz82ORkx3wAguY,13836
|
|
31
31
|
robotcode/robot/diagnostics/semantic_analyzer/model.py,sha256=TTsrU_aDSC4JwbdypxSaGR79iCN071Y5VUMO8vGm5IE,20476
|
|
@@ -36,13 +36,13 @@ robotcode/robot/diagnostics/semantic_analyzer/variable_tokenizer.py,sha256=vBor3
|
|
|
36
36
|
robotcode/robot/utils/__init__.py,sha256=-A0YeHv7e7WK1SId7Er7Vu6frrDYWopUrcyFlyxLmPY,396
|
|
37
37
|
robotcode/robot/utils/ast.py,sha256=S9xyiJZee4Y5VcroNrGIIWp8f9sutb_1o4blAlCqzdg,11218
|
|
38
38
|
robotcode/robot/utils/markdown_docs.py,sha256=jY3jT919TicVRMhcYo03dR0NzhGq0kEnDolBSPc-lNI,29548
|
|
39
|
-
robotcode/robot/utils/markdownformatter.py,sha256=
|
|
39
|
+
robotcode/robot/utils/markdownformatter.py,sha256=hHilBaj1DzBV1tlNHyNfMvM3h92Six-15guhQy6edgU,14323
|
|
40
40
|
robotcode/robot/utils/match.py,sha256=KgA6o6TbnQpOT1eAzEjhl_-FZ2XO07xiV0L7IYRzyzE,1125
|
|
41
41
|
robotcode/robot/utils/robot_patching.py,sha256=jXHhnPvZx8hCQq4B_2uwX9j51Be_toWpUoTYYe8HjPo,1463
|
|
42
42
|
robotcode/robot/utils/robot_path.py,sha256=ObLP8PqamTZAz5oi0eDXE_dnyZwAPXp4TJYm6ugeENs,2185
|
|
43
43
|
robotcode/robot/utils/stubs.py,sha256=umugZYAyneFNgqRJBRMJPzm0u0B_TH8Sx_y-ykXnxpw,351
|
|
44
|
-
robotcode/robot/utils/variables.py,sha256=
|
|
44
|
+
robotcode/robot/utils/variables.py,sha256=9WnP3QqcHdq-zioJ_gJXJobw_2Gcto_rGPCUGmOH9YQ,9829
|
|
45
45
|
robotcode/robot/utils/visitor.py,sha256=nP3O0qh3YYuxR6S8wYJRBFfNwIVgsgohURBlrnFkRYQ,2299
|
|
46
|
-
robotcode_robot-2.
|
|
47
|
-
robotcode_robot-2.
|
|
48
|
-
robotcode_robot-2.
|
|
46
|
+
robotcode_robot-2.9.0.dev51.dist-info/METADATA,sha256=RziLuNl8hmcFuiRfMnuUYgOZm7OFM-E06dYl1ZNS6yE,2958
|
|
47
|
+
robotcode_robot-2.9.0.dev51.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
48
|
+
robotcode_robot-2.9.0.dev51.dist-info/RECORD,,
|