robotcode-robot 0.75.0__py3-none-any.whl → 0.76.1__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.
@@ -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