robotcode-robot 0.75.0__py3-none-any.whl → 0.76.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -584,6 +584,9 @@ class Namespace:
584
584
  self._global_variables: Optional[List[VariableDefinition]] = None
585
585
  self._global_variables_lock = RLock(default_timeout=120, name="Namespace.global_variables")
586
586
 
587
+ self._suite_variables: Optional[Dict[str, Any]] = None
588
+ self._suite_variables_lock = RLock(default_timeout=120, name="Namespace.global_variables")
589
+
587
590
  self._diagnostics: List[Diagnostic] = []
588
591
  self._keyword_references: Dict[KeywordDoc, Set[Location]] = {}
589
592
  self._variable_references: Dict[VariableDefinition, Set[Location]] = {}
@@ -600,10 +603,10 @@ class Namespace:
600
603
 
601
604
  self._finder: Optional[KeywordFinder] = None
602
605
 
603
- self.imports_manager.imports_changed.add(self.imports_changed)
604
- self.imports_manager.libraries_changed.add(self.libraries_changed)
605
- self.imports_manager.resources_changed.add(self.resources_changed)
606
- self.imports_manager.variables_changed.add(self.variables_changed)
606
+ self.imports_manager.imports_changed.add(self._on_imports_changed)
607
+ self.imports_manager.libraries_changed.add(self._on_libraries_changed)
608
+ self.imports_manager.resources_changed.add(self._on_resources_changed)
609
+ self.imports_manager.variables_changed.add(self._on_variables_changed)
607
610
 
608
611
  self._in_initialize = False
609
612
 
@@ -618,6 +621,15 @@ class Namespace:
618
621
  @event
619
622
  def has_analysed(sender) -> None: ...
620
623
 
624
+ @event
625
+ def library_import_changed(sender) -> None: ...
626
+
627
+ @event
628
+ def resource_import_changed(sender) -> None: ...
629
+
630
+ @event
631
+ def variables_import_changed(sender) -> None: ...
632
+
621
633
  @property
622
634
  def document(self) -> Optional[TextDocument]:
623
635
  return self._document() if self._document is not None else None
@@ -629,12 +641,12 @@ class Namespace:
629
641
 
630
642
  return self._search_order
631
643
 
632
- def imports_changed(self, sender: Any, uri: DocumentUri) -> None:
644
+ def _on_imports_changed(self, sender: Any, uri: DocumentUri) -> None:
633
645
  # TODO: optimise this by checking our imports
634
646
  self.invalidate()
635
647
 
636
648
  @_logger.call
637
- def libraries_changed(self, sender: Any, libraries: List[LibraryDoc]) -> None:
649
+ def _on_libraries_changed(self, sender: Any, libraries: List[LibraryDoc]) -> None:
638
650
  if not self.initialized or self.invalid:
639
651
  return
640
652
 
@@ -649,7 +661,7 @@ class Namespace:
649
661
  self.invalidate()
650
662
 
651
663
  @_logger.call
652
- def resources_changed(self, sender: Any, resources: List[LibraryDoc]) -> None:
664
+ def _on_resources_changed(self, sender: Any, resources: List[LibraryDoc]) -> None:
653
665
  if not self.initialized or self.invalid:
654
666
  return
655
667
 
@@ -664,7 +676,7 @@ class Namespace:
664
676
  self.invalidate()
665
677
 
666
678
  @_logger.call
667
- def variables_changed(self, sender: Any, variables: List[LibraryDoc]) -> None:
679
+ def _on_variables_changed(self, sender: Any, variables: List[LibraryDoc]) -> None:
668
680
  if not self.initialized or self.invalid:
669
681
  return
670
682
 
@@ -684,12 +696,20 @@ class Namespace:
684
696
 
685
697
  def _invalidate(self) -> None:
686
698
  self._invalid = True
699
+ self.imports_manager.imports_changed.remove(self._on_imports_changed)
700
+ self.imports_manager.libraries_changed.remove(self._on_libraries_changed)
701
+ self.imports_manager.resources_changed.remove(self._on_resources_changed)
702
+ self.imports_manager.variables_changed.remove(self._on_variables_changed)
687
703
 
688
704
  @_logger.call
689
- def invalidate(self) -> None:
705
+ def invalidate(self) -> bool:
690
706
  with self._initialize_lock:
707
+ if self._invalid:
708
+ return False
709
+
691
710
  self._invalidate()
692
711
  self.has_invalidated(self)
712
+ return True
693
713
 
694
714
  @_logger.call
695
715
  def get_diagnostics(self) -> List[Diagnostic]:
@@ -796,7 +816,7 @@ class Namespace:
796
816
 
797
817
  imports = self.get_imports()
798
818
 
799
- variables = self.get_resolvable_variables()
819
+ variables = self.get_suite_variables()
800
820
 
801
821
  self._import_default_libraries(variables)
802
822
  self._import_imports(
@@ -861,8 +881,9 @@ class Namespace:
861
881
  return self.imports_manager.get_command_line_variables()
862
882
 
863
883
  def _reset_global_variables(self) -> None:
864
- with self._global_variables_lock:
884
+ with self._global_variables_lock, self._suite_variables_lock:
865
885
  self._global_variables = None
886
+ self._suite_variables = None
866
887
 
867
888
  def get_global_variables(self) -> List[VariableDefinition]:
868
889
  with self._global_variables_lock:
@@ -887,13 +908,16 @@ class Namespace:
887
908
  ) -> Iterator[Tuple[VariableMatcher, VariableDefinition]]:
888
909
  yielded: Dict[VariableMatcher, VariableDefinition] = {}
889
910
 
890
- test_or_keyword_nodes = list(
891
- itertools.dropwhile(
892
- lambda v: not isinstance(v, (TestCase, Keyword)),
893
- nodes if nodes else [],
911
+ test_or_keyword = None
912
+
913
+ if nodes:
914
+ test_or_keyword_nodes = list(
915
+ itertools.dropwhile(
916
+ lambda v: not isinstance(v, (TestCase, Keyword)),
917
+ nodes if nodes else [],
918
+ )
894
919
  )
895
- )
896
- test_or_keyword = test_or_keyword_nodes[0] if test_or_keyword_nodes else None
920
+ test_or_keyword = test_or_keyword_nodes[0] if test_or_keyword_nodes else None
897
921
 
898
922
  for var in chain(
899
923
  *[
@@ -921,6 +945,31 @@ class Namespace:
921
945
 
922
946
  yield var.matcher, var
923
947
 
948
+ def get_suite_variables(
949
+ self,
950
+ nodes: Optional[List[ast.AST]] = None,
951
+ position: Optional[Position] = None,
952
+ ) -> Dict[str, Any]:
953
+ if nodes:
954
+ return {
955
+ v.name: v.value
956
+ for k, v in self.yield_variables(nodes, position, skip_commandline_variables=True)
957
+ if v.has_value
958
+ }
959
+ with self._suite_variables_lock:
960
+ vars = {}
961
+
962
+ def check_var(var: VariableDefinition) -> bool:
963
+ if var.matcher in vars:
964
+ return False
965
+ vars[var.matcher] = var
966
+
967
+ return var.has_value
968
+
969
+ self._suite_variables = {v.name: v.value for v in filter(check_var, self.get_global_variables())}
970
+
971
+ return self._suite_variables
972
+
924
973
  def get_resolvable_variables(
925
974
  self,
926
975
  nodes: Optional[List[ast.AST]] = None,
@@ -981,215 +1030,228 @@ class Namespace:
981
1030
 
982
1031
  return None
983
1032
 
984
- def _import_imports(
1033
+ def _import(
985
1034
  self,
986
- imports: Iterable[Import],
1035
+ value: Import,
1036
+ variables: Optional[Dict[str, Any]],
987
1037
  base_dir: str,
988
1038
  *,
989
1039
  top_level: bool = False,
990
- variables: Optional[Dict[str, Any]] = None,
991
1040
  source: Optional[str] = None,
992
1041
  parent_import: Optional[Import] = None,
993
- ) -> None:
994
- def _import(
995
- value: Import, variables: Optional[Dict[str, Any]] = None
996
- ) -> Tuple[Optional[LibraryEntry], Optional[Dict[str, Any]]]:
997
- result: Optional[LibraryEntry] = None
998
- try:
999
- if isinstance(value, LibraryImport):
1000
- if value.name is None:
1001
- raise NameSpaceError("Library setting requires value.")
1042
+ ) -> Optional[LibraryEntry]:
1043
+ result: Optional[LibraryEntry] = None
1044
+ try:
1045
+ if isinstance(value, LibraryImport):
1046
+ if value.name is None:
1047
+ raise NameSpaceError("Library setting requires value.")
1048
+
1049
+ result = self._get_library_entry(
1050
+ value.name,
1051
+ value.args,
1052
+ value.alias,
1053
+ base_dir,
1054
+ sentinel=self,
1055
+ variables=variables,
1056
+ )
1057
+ result.import_range = value.range
1058
+ result.import_source = value.source
1059
+ result.alias_range = value.alias_range
1060
+
1061
+ self._import_entries[value] = result
1062
+
1063
+ if (
1064
+ top_level
1065
+ and result.library_doc.errors is None
1066
+ and (len(result.library_doc.keywords) == 0 and not bool(result.library_doc.has_listener))
1067
+ ):
1068
+ self.append_diagnostics(
1069
+ range=value.range,
1070
+ message=f"Imported library '{value.name}' contains no keywords.",
1071
+ severity=DiagnosticSeverity.WARNING,
1072
+ source=DIAGNOSTICS_SOURCE_NAME,
1073
+ code=Error.LIBRARY_CONTAINS_NO_KEYWORDS,
1074
+ )
1075
+ elif isinstance(value, ResourceImport):
1076
+ if value.name is None:
1077
+ raise NameSpaceError("Resource setting requires value.")
1002
1078
 
1003
- result = self._get_library_entry(
1079
+ source = self.imports_manager.find_resource(value.name, base_dir, variables=variables)
1080
+
1081
+ if self.source == source:
1082
+ if parent_import:
1083
+ self.append_diagnostics(
1084
+ range=parent_import.range,
1085
+ message="Possible circular import.",
1086
+ severity=DiagnosticSeverity.INFORMATION,
1087
+ source=DIAGNOSTICS_SOURCE_NAME,
1088
+ related_information=(
1089
+ [
1090
+ DiagnosticRelatedInformation(
1091
+ location=Location(
1092
+ str(Uri.from_path(value.source)),
1093
+ value.range,
1094
+ ),
1095
+ message=f"'{Path(self.source).name}' is also imported here.",
1096
+ )
1097
+ ]
1098
+ if value.source
1099
+ else None
1100
+ ),
1101
+ code=Error.POSSIBLE_CIRCULAR_IMPORT,
1102
+ )
1103
+ else:
1104
+ result = self._get_resource_entry(
1004
1105
  value.name,
1005
- value.args,
1006
- value.alias,
1007
1106
  base_dir,
1008
- sentinel=self,
1009
1107
  variables=variables,
1010
1108
  )
1011
1109
  result.import_range = value.range
1012
1110
  result.import_source = value.source
1013
- result.alias_range = value.alias_range
1014
1111
 
1015
1112
  self._import_entries[value] = result
1016
1113
 
1017
- if (
1018
- top_level
1019
- and result.library_doc.errors is None
1020
- and (len(result.library_doc.keywords) == 0 and not bool(result.library_doc.has_listener))
1114
+ if top_level and (
1115
+ not result.library_doc.errors
1116
+ and top_level
1117
+ and not result.imports
1118
+ and not result.variables
1119
+ and not result.library_doc.keywords
1021
1120
  ):
1022
1121
  self.append_diagnostics(
1023
1122
  range=value.range,
1024
- message=f"Imported library '{value.name}' contains no keywords.",
1123
+ message=f"Imported resource file '{value.name}' is empty.",
1025
1124
  severity=DiagnosticSeverity.WARNING,
1026
1125
  source=DIAGNOSTICS_SOURCE_NAME,
1027
- code=Error.LIBRARY_CONTAINS_NO_KEYWORDS,
1028
- )
1029
- elif isinstance(value, ResourceImport):
1030
- if value.name is None:
1031
- raise NameSpaceError("Resource setting requires value.")
1032
-
1033
- source = self.imports_manager.find_resource(value.name, base_dir, variables=variables)
1034
-
1035
- if self.source == source:
1036
- if parent_import:
1037
- self.append_diagnostics(
1038
- range=parent_import.range,
1039
- message="Possible circular import.",
1040
- severity=DiagnosticSeverity.INFORMATION,
1041
- source=DIAGNOSTICS_SOURCE_NAME,
1042
- related_information=(
1043
- [
1044
- DiagnosticRelatedInformation(
1045
- location=Location(
1046
- str(Uri.from_path(value.source)),
1047
- value.range,
1048
- ),
1049
- message=f"'{Path(self.source).name}' is also imported here.",
1050
- )
1051
- ]
1052
- if value.source
1053
- else None
1054
- ),
1055
- code=Error.POSSIBLE_CIRCULAR_IMPORT,
1056
- )
1057
- else:
1058
- result = self._get_resource_entry(
1059
- value.name,
1060
- base_dir,
1061
- variables=variables,
1126
+ code=Error.RESOURCE_EMPTY,
1062
1127
  )
1063
- result.import_range = value.range
1064
- result.import_source = value.source
1065
-
1066
- self._import_entries[value] = result
1067
- if result.variables:
1068
- variables = None
1069
-
1070
- if top_level and (
1071
- not result.library_doc.errors
1072
- and top_level
1073
- and not result.imports
1074
- and not result.variables
1075
- and not result.library_doc.keywords
1076
- ):
1077
- self.append_diagnostics(
1078
- range=value.range,
1079
- message=f"Imported resource file '{value.name}' is empty.",
1080
- severity=DiagnosticSeverity.WARNING,
1081
- source=DIAGNOSTICS_SOURCE_NAME,
1082
- code=Error.RESOURCE_EMPTY,
1083
- )
1084
1128
 
1085
- elif isinstance(value, VariablesImport):
1086
- if value.name is None:
1087
- raise NameSpaceError("Variables setting requires value.")
1129
+ elif isinstance(value, VariablesImport):
1130
+ if value.name is None:
1131
+ raise NameSpaceError("Variables setting requires value.")
1088
1132
 
1089
- result = self._get_variables_entry(
1090
- value.name,
1091
- value.args,
1092
- base_dir,
1093
- variables=variables,
1094
- )
1133
+ result = self._get_variables_entry(
1134
+ value.name,
1135
+ value.args,
1136
+ base_dir,
1137
+ variables=variables,
1138
+ )
1095
1139
 
1096
- result.import_range = value.range
1097
- result.import_source = value.source
1140
+ result.import_range = value.range
1141
+ result.import_source = value.source
1098
1142
 
1099
- self._import_entries[value] = result
1100
- variables = None
1101
- else:
1102
- raise DiagnosticsError("Unknown import type.")
1143
+ self._import_entries[value] = result
1144
+ else:
1145
+ raise DiagnosticsError("Unknown import type.")
1103
1146
 
1104
- if top_level and result is not None:
1105
- if result.library_doc.source is not None and result.library_doc.errors:
1106
- if any(err.source and Path(err.source).is_absolute() for err in result.library_doc.errors):
1107
- self.append_diagnostics(
1108
- range=value.range,
1109
- message="Import definition contains errors.",
1110
- severity=DiagnosticSeverity.ERROR,
1111
- source=DIAGNOSTICS_SOURCE_NAME,
1112
- related_information=[
1113
- DiagnosticRelatedInformation(
1114
- location=Location(
1115
- uri=str(Uri.from_path(err.source)),
1116
- range=Range(
1117
- start=Position(
1118
- line=(
1119
- err.line_no - 1
1120
- if err.line_no is not None
1121
- else max(
1122
- result.library_doc.line_no,
1123
- 0,
1124
- )
1125
- ),
1126
- character=0,
1147
+ if top_level and result is not None:
1148
+ if result.library_doc.source is not None and result.library_doc.errors:
1149
+ if any(err.source and Path(err.source).is_absolute() for err in result.library_doc.errors):
1150
+ self.append_diagnostics(
1151
+ range=value.range,
1152
+ message="Import definition contains errors.",
1153
+ severity=DiagnosticSeverity.ERROR,
1154
+ source=DIAGNOSTICS_SOURCE_NAME,
1155
+ related_information=[
1156
+ DiagnosticRelatedInformation(
1157
+ location=Location(
1158
+ uri=str(Uri.from_path(err.source)),
1159
+ range=Range(
1160
+ start=Position(
1161
+ line=(
1162
+ err.line_no - 1
1163
+ if err.line_no is not None
1164
+ else max(
1165
+ result.library_doc.line_no,
1166
+ 0,
1167
+ )
1127
1168
  ),
1128
- end=Position(
1129
- line=(
1130
- err.line_no - 1
1131
- if err.line_no is not None
1132
- else max(
1133
- result.library_doc.line_no,
1134
- 0,
1135
- )
1136
- ),
1137
- character=0,
1169
+ character=0,
1170
+ ),
1171
+ end=Position(
1172
+ line=(
1173
+ err.line_no - 1
1174
+ if err.line_no is not None
1175
+ else max(
1176
+ result.library_doc.line_no,
1177
+ 0,
1178
+ )
1138
1179
  ),
1180
+ character=0,
1139
1181
  ),
1140
1182
  ),
1141
- message=err.message,
1142
- )
1143
- for err in result.library_doc.errors
1144
- if err.source is not None
1145
- ],
1146
- code=Error.IMPORT_CONTAINS_ERRORS,
1147
- )
1148
- for err in filter(
1149
- lambda e: e.source is None or not Path(e.source).is_absolute(),
1150
- result.library_doc.errors,
1151
- ):
1152
- self.append_diagnostics(
1153
- range=value.range,
1154
- message=err.message,
1155
- severity=DiagnosticSeverity.ERROR,
1156
- source=DIAGNOSTICS_SOURCE_NAME,
1157
- code=err.type_name,
1158
- )
1159
- elif result.library_doc.errors is not None:
1160
- for err in result.library_doc.errors:
1161
- self.append_diagnostics(
1162
- range=value.range,
1163
- message=err.message,
1164
- severity=DiagnosticSeverity.ERROR,
1165
- source=DIAGNOSTICS_SOURCE_NAME,
1166
- code=err.type_name,
1167
- )
1183
+ ),
1184
+ message=err.message,
1185
+ )
1186
+ for err in result.library_doc.errors
1187
+ if err.source is not None
1188
+ ],
1189
+ code=Error.IMPORT_CONTAINS_ERRORS,
1190
+ )
1191
+ for err in filter(
1192
+ lambda e: e.source is None or not Path(e.source).is_absolute(),
1193
+ result.library_doc.errors,
1194
+ ):
1195
+ self.append_diagnostics(
1196
+ range=value.range,
1197
+ message=err.message,
1198
+ severity=DiagnosticSeverity.ERROR,
1199
+ source=DIAGNOSTICS_SOURCE_NAME,
1200
+ code=err.type_name,
1201
+ )
1202
+ elif result.library_doc.errors is not None:
1203
+ for err in result.library_doc.errors:
1204
+ self.append_diagnostics(
1205
+ range=value.range,
1206
+ message=err.message,
1207
+ severity=DiagnosticSeverity.ERROR,
1208
+ source=DIAGNOSTICS_SOURCE_NAME,
1209
+ code=err.type_name,
1210
+ )
1168
1211
 
1169
- except (SystemExit, KeyboardInterrupt):
1170
- raise
1171
- except BaseException as e:
1172
- if top_level:
1173
- self.append_diagnostics(
1174
- range=value.range,
1175
- message=str(e),
1176
- severity=DiagnosticSeverity.ERROR,
1177
- source=DIAGNOSTICS_SOURCE_NAME,
1178
- code=type(e).__qualname__,
1179
- )
1180
- finally:
1181
- self._reset_global_variables()
1212
+ except (SystemExit, KeyboardInterrupt):
1213
+ raise
1214
+ except BaseException as e:
1215
+ if top_level:
1216
+ self.append_diagnostics(
1217
+ range=value.range,
1218
+ message=str(e),
1219
+ severity=DiagnosticSeverity.ERROR,
1220
+ source=DIAGNOSTICS_SOURCE_NAME,
1221
+ code=type(e).__qualname__,
1222
+ )
1223
+ finally:
1224
+ self._reset_global_variables()
1182
1225
 
1183
- return result, variables
1226
+ return result
1227
+
1228
+ def _import_imports(
1229
+ self,
1230
+ imports: Iterable[Import],
1231
+ base_dir: str,
1232
+ *,
1233
+ top_level: bool = False,
1234
+ variables: Optional[Dict[str, Any]] = None,
1235
+ source: Optional[str] = None,
1236
+ parent_import: Optional[Import] = None,
1237
+ depth: int = 0,
1238
+ ) -> Optional[Dict[str, Any]]:
1184
1239
 
1185
1240
  current_time = time.monotonic()
1186
- self._logger.debug(lambda: f"start imports for {self.document if top_level else source}")
1241
+ self._logger.debug(lambda: f"{' '*depth}start imports for {self.document if top_level else source}")
1187
1242
  try:
1188
1243
  for imp in imports:
1189
1244
  if variables is None:
1190
- variables = self.get_resolvable_variables()
1245
+ variables = self.get_suite_variables()
1191
1246
 
1192
- entry, variables = _import(imp, variables=variables)
1247
+ entry = self._import(
1248
+ imp,
1249
+ variables=variables,
1250
+ base_dir=base_dir,
1251
+ top_level=top_level,
1252
+ source=source,
1253
+ parent_import=parent_import,
1254
+ )
1193
1255
 
1194
1256
  if entry is not None:
1195
1257
  if isinstance(entry, ResourceEntry):
@@ -1201,14 +1263,18 @@ class Namespace:
1201
1263
 
1202
1264
  if already_imported_resources is None and entry.library_doc.source != self.source:
1203
1265
  self._resources[entry.import_name] = entry
1266
+ if entry.variables:
1267
+ variables = self.get_suite_variables()
1268
+
1204
1269
  try:
1205
- self._import_imports(
1270
+ variables = self._import_imports(
1206
1271
  entry.imports,
1207
1272
  str(Path(entry.library_doc.source).parent),
1208
1273
  top_level=False,
1209
1274
  variables=variables,
1210
1275
  source=entry.library_doc.source,
1211
1276
  parent_import=imp if top_level else parent_import,
1277
+ depth=depth + 1,
1212
1278
  )
1213
1279
  except (SystemExit, KeyboardInterrupt):
1214
1280
  raise
@@ -1221,56 +1287,59 @@ class Namespace:
1221
1287
  source=DIAGNOSTICS_SOURCE_NAME,
1222
1288
  code=type(e).__qualname__,
1223
1289
  )
1224
- else:
1225
- if top_level:
1226
- if entry.library_doc.source == self.source:
1227
- self.append_diagnostics(
1228
- range=entry.import_range,
1229
- message="Recursive resource import.",
1230
- severity=DiagnosticSeverity.INFORMATION,
1231
- source=DIAGNOSTICS_SOURCE_NAME,
1232
- code=Error.RECURSIVE_IMPORT,
1233
- )
1234
- elif (
1235
- already_imported_resources is not None
1236
- and already_imported_resources.library_doc.source
1237
- ):
1238
- self.append_diagnostics(
1239
- range=entry.import_range,
1240
- message=f"Resource {entry} already imported.",
1241
- severity=DiagnosticSeverity.INFORMATION,
1242
- source=DIAGNOSTICS_SOURCE_NAME,
1243
- related_information=(
1244
- [
1245
- DiagnosticRelatedInformation(
1246
- location=Location(
1247
- uri=str(
1248
- Uri.from_path(already_imported_resources.import_source)
1249
- ),
1250
- range=already_imported_resources.import_range,
1251
- ),
1252
- message="",
1253
- )
1254
- ]
1255
- if already_imported_resources.import_source
1256
- else None
1257
- ),
1258
- code=Error.RESOURCE_ALREADY_IMPORTED,
1259
- )
1290
+ elif top_level:
1291
+ if entry.library_doc.source == self.source:
1292
+ self.append_diagnostics(
1293
+ range=entry.import_range,
1294
+ message="Recursive resource import.",
1295
+ severity=DiagnosticSeverity.INFORMATION,
1296
+ source=DIAGNOSTICS_SOURCE_NAME,
1297
+ code=Error.RECURSIVE_IMPORT,
1298
+ )
1299
+ elif (
1300
+ already_imported_resources is not None and already_imported_resources.library_doc.source
1301
+ ):
1302
+ self.append_diagnostics(
1303
+ range=entry.import_range,
1304
+ message=f"Resource {entry} already imported.",
1305
+ severity=DiagnosticSeverity.INFORMATION,
1306
+ source=DIAGNOSTICS_SOURCE_NAME,
1307
+ related_information=(
1308
+ [
1309
+ DiagnosticRelatedInformation(
1310
+ location=Location(
1311
+ uri=str(Uri.from_path(already_imported_resources.import_source)),
1312
+ range=already_imported_resources.import_range,
1313
+ ),
1314
+ message="",
1315
+ )
1316
+ ]
1317
+ if already_imported_resources.import_source
1318
+ else None
1319
+ ),
1320
+ code=Error.RESOURCE_ALREADY_IMPORTED,
1321
+ )
1260
1322
 
1261
1323
  elif isinstance(entry, VariablesEntry):
1262
- already_imported_variables = [
1263
- e
1264
- for e in self._variables.values()
1265
- if e.library_doc.source == entry.library_doc.source
1266
- and e.alias == entry.alias
1267
- and e.args == entry.args
1268
- ]
1324
+ already_imported_variables = next(
1325
+ (
1326
+ e
1327
+ for e in self._variables.values()
1328
+ if e.library_doc.source == entry.library_doc.source
1329
+ and e.alias == entry.alias
1330
+ and e.args == entry.args
1331
+ ),
1332
+ None,
1333
+ )
1269
1334
  if (
1270
- top_level
1271
- and already_imported_variables
1272
- and already_imported_variables[0].library_doc.source
1335
+ already_imported_variables is None
1336
+ and entry.library_doc is not None
1337
+ and entry.library_doc.source_or_origin
1273
1338
  ):
1339
+ self._variables[entry.library_doc.source_or_origin] = entry
1340
+ if entry.variables:
1341
+ variables = self.get_suite_variables()
1342
+ elif top_level and already_imported_variables and already_imported_variables.library_doc.source:
1274
1343
  self.append_diagnostics(
1275
1344
  range=entry.import_range,
1276
1345
  message=f'Variables "{entry}" already imported.',
@@ -1280,21 +1349,18 @@ class Namespace:
1280
1349
  [
1281
1350
  DiagnosticRelatedInformation(
1282
1351
  location=Location(
1283
- uri=str(Uri.from_path(already_imported_variables[0].import_source)),
1284
- range=already_imported_variables[0].import_range,
1352
+ uri=str(Uri.from_path(already_imported_variables.import_source)),
1353
+ range=already_imported_variables.import_range,
1285
1354
  ),
1286
1355
  message="",
1287
1356
  )
1288
1357
  ]
1289
- if already_imported_variables[0].import_source
1358
+ if already_imported_variables.import_source
1290
1359
  else None
1291
1360
  ),
1292
1361
  code=Error.VARIABLES_ALREADY_IMPORTED,
1293
1362
  )
1294
1363
 
1295
- if entry.library_doc is not None and entry.library_doc.source_or_origin:
1296
- self._variables[entry.library_doc.source_or_origin] = entry
1297
-
1298
1364
  elif isinstance(entry, LibraryEntry):
1299
1365
  if top_level and entry.name == BUILTIN_LIBRARY_NAME and entry.alias is None:
1300
1366
  self.append_diagnostics(
@@ -1320,15 +1386,23 @@ class Namespace:
1320
1386
  )
1321
1387
  continue
1322
1388
 
1323
- already_imported_library = [
1324
- e
1325
- for e in self._libraries.values()
1326
- if e.library_doc.source == entry.library_doc.source
1327
- and e.library_doc.member_name == entry.library_doc.member_name
1328
- and e.alias == entry.alias
1329
- and e.args == entry.args
1330
- ]
1331
- if top_level and already_imported_library and already_imported_library[0].library_doc.source:
1389
+ already_imported_library = next(
1390
+ (
1391
+ e
1392
+ for e in self._libraries.values()
1393
+ if e.library_doc.source == entry.library_doc.source
1394
+ and e.library_doc.member_name == entry.library_doc.member_name
1395
+ and e.alias == entry.alias
1396
+ and e.args == entry.args
1397
+ ),
1398
+ None,
1399
+ )
1400
+ if (
1401
+ already_imported_library is None
1402
+ and (entry.alias or entry.name or entry.import_name) not in self._libraries
1403
+ ):
1404
+ self._libraries[entry.alias or entry.name or entry.import_name] = entry
1405
+ elif top_level and already_imported_library and already_imported_library.library_doc.source:
1332
1406
  self.append_diagnostics(
1333
1407
  range=entry.import_range,
1334
1408
  message=f'Library "{entry}" already imported.',
@@ -1338,26 +1412,26 @@ class Namespace:
1338
1412
  [
1339
1413
  DiagnosticRelatedInformation(
1340
1414
  location=Location(
1341
- uri=str(Uri.from_path(already_imported_library[0].import_source)),
1342
- range=already_imported_library[0].import_range,
1415
+ uri=str(Uri.from_path(already_imported_library.import_source)),
1416
+ range=already_imported_library.import_range,
1343
1417
  ),
1344
1418
  message="",
1345
1419
  )
1346
1420
  ]
1347
- if already_imported_library[0].import_source
1421
+ if already_imported_library.import_source
1348
1422
  else None
1349
1423
  ),
1350
1424
  code=Error.LIBRARY_ALREADY_IMPORTED,
1351
1425
  )
1352
1426
 
1353
- if (entry.alias or entry.name or entry.import_name) not in self._libraries:
1354
- self._libraries[entry.alias or entry.name or entry.import_name] = entry
1355
1427
  finally:
1356
1428
  self._logger.debug(
1357
- lambda: "end import imports for "
1429
+ lambda: f"{' '*depth}end imports for "
1358
1430
  f"{self.document if top_level else source} in {time.monotonic() - current_time}s"
1359
1431
  )
1360
1432
 
1433
+ return variables
1434
+
1361
1435
  def _import_default_libraries(self, variables: Optional[Dict[str, Any]] = None) -> None:
1362
1436
  def _import_lib(library: str, variables: Optional[Dict[str, Any]] = None) -> Optional[LibraryEntry]:
1363
1437
  try:
@@ -1383,8 +1457,11 @@ class Namespace:
1383
1457
 
1384
1458
  self._logger.debug(lambda: f"start import default libraries for document {self.document}")
1385
1459
  try:
1460
+ if variables is None:
1461
+ variables = self.get_suite_variables()
1462
+
1386
1463
  for library in DEFAULT_LIBRARIES:
1387
- e = _import_lib(library, variables or self.get_resolvable_variables())
1464
+ e = _import_lib(library, variables)
1388
1465
  if e is not None:
1389
1466
  self._libraries[e.alias or e.name or e.import_name] = e
1390
1467
  finally:
@@ -1402,12 +1479,15 @@ class Namespace:
1402
1479
  sentinel: Any = None,
1403
1480
  variables: Optional[Dict[str, Any]] = None,
1404
1481
  ) -> LibraryEntry:
1482
+ if variables is None:
1483
+ variables = self.get_suite_variables()
1484
+
1405
1485
  library_doc = self.imports_manager.get_libdoc_for_library_import(
1406
1486
  name,
1407
1487
  args,
1408
1488
  base_dir=base_dir,
1409
1489
  sentinel=None if is_default_library else sentinel,
1410
- variables=variables or self.get_resolvable_variables(),
1490
+ variables=variables,
1411
1491
  )
1412
1492
 
1413
1493
  return LibraryEntry(
@@ -1441,14 +1521,11 @@ class Namespace:
1441
1521
  *,
1442
1522
  variables: Optional[Dict[str, Any]] = None,
1443
1523
  ) -> ResourceEntry:
1444
- (
1445
- namespace,
1446
- library_doc,
1447
- ) = self.imports_manager.get_namespace_and_libdoc_for_resource_import(
1448
- name,
1449
- base_dir,
1450
- sentinel=self,
1451
- variables=variables or self.get_resolvable_variables(),
1524
+ if variables is None:
1525
+ variables = self.get_suite_variables()
1526
+
1527
+ (namespace, library_doc) = self.imports_manager.get_namespace_and_libdoc_for_resource_import(
1528
+ name, base_dir, sentinel=self, variables=variables
1452
1529
  )
1453
1530
 
1454
1531
  return ResourceEntry(
@@ -1481,12 +1558,15 @@ class Namespace:
1481
1558
  *,
1482
1559
  variables: Optional[Dict[str, Any]] = None,
1483
1560
  ) -> VariablesEntry:
1561
+ if variables is None:
1562
+ variables = self.get_suite_variables()
1563
+
1484
1564
  library_doc = self.imports_manager.get_libdoc_for_variables_import(
1485
1565
  name,
1486
1566
  args,
1487
1567
  base_dir=base_dir,
1488
1568
  sentinel=self,
1489
- variables=variables or self.get_resolvable_variables(),
1569
+ variables=variables,
1490
1570
  )
1491
1571
 
1492
1572
  return VariablesEntry(
@@ -1592,6 +1672,10 @@ class Namespace:
1592
1672
  )
1593
1673
  )
1594
1674
 
1675
+ def is_analyzed(self) -> bool:
1676
+ with self._analyze_lock:
1677
+ return self._analyzed
1678
+
1595
1679
  @_logger.call(condition=lambda self: not self._analyzed)
1596
1680
  def analyze(self) -> None:
1597
1681
  import time
@@ -1602,6 +1686,8 @@ class Namespace:
1602
1686
  if not self._analyzed:
1603
1687
  canceled = False
1604
1688
 
1689
+ self.ensure_initialized()
1690
+
1605
1691
  self._logger.debug(lambda: f"start analyze {self.document}")
1606
1692
  start_time = time.monotonic()
1607
1693