robotcode-robot 0.95.0__tar.gz → 0.95.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/PKG-INFO +2 -2
  2. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/pyproject.toml +1 -1
  3. robotcode_robot-0.95.1/src/robotcode/robot/__version__.py +1 -0
  4. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/entities.py +2 -2
  5. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/keyword_finder.py +18 -19
  6. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/library_doc.py +39 -53
  7. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/model_helper.py +3 -3
  8. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/utils/ast.py +0 -7
  9. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/utils/robot_path.py +2 -2
  10. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/utils/stubs.py +1 -19
  11. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/utils/visitor.py +0 -27
  12. robotcode_robot-0.95.0/src/robotcode/robot/__version__.py +0 -1
  13. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/.gitignore +0 -0
  14. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/LICENSE.txt +0 -0
  15. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/README.md +0 -0
  16. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/__init__.py +0 -0
  17. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/config/__init__.py +0 -0
  18. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/config/loader.py +0 -0
  19. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/config/model.py +0 -0
  20. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/config/utils.py +0 -0
  21. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/__init__.py +0 -0
  22. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/data_cache.py +0 -0
  23. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/diagnostics_modifier.py +0 -0
  24. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/document_cache_helper.py +0 -0
  25. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/errors.py +0 -0
  26. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/imports_manager.py +0 -0
  27. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/namespace.py +0 -0
  28. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/namespace_analyzer.py +0 -0
  29. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/diagnostics/workspace_config.py +0 -0
  30. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/py.typed +0 -0
  31. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/utils/__init__.py +0 -0
  32. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/utils/markdownformatter.py +0 -0
  33. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/utils/match.py +0 -0
  34. {robotcode_robot-0.95.0 → robotcode_robot-0.95.1}/src/robotcode/robot/utils/variables.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: robotcode-robot
3
- Version: 0.95.0
3
+ Version: 0.95.1
4
4
  Summary: Support classes for RobotCode for handling Robot Framework projects.
5
5
  Project-URL: Homepage, https://robotcode.io
6
6
  Project-URL: Donate, https://opencollective.com/robotcode
@@ -26,7 +26,7 @@ Classifier: Topic :: Utilities
26
26
  Classifier: Typing :: Typed
27
27
  Requires-Python: >=3.8
28
28
  Requires-Dist: platformdirs<4.2.0,>=3.2.0
29
- Requires-Dist: robotcode-core==0.95.0
29
+ Requires-Dist: robotcode-core==0.95.1
30
30
  Requires-Dist: robotframework>=4.1.0
31
31
  Requires-Dist: tomli>=1.1.0; python_version < '3.11'
32
32
  Description-Content-Type: text/markdown
@@ -29,7 +29,7 @@ dependencies = [
29
29
  "robotframework>=4.1.0",
30
30
  "tomli>=1.1.0; python_version < '3.11'",
31
31
  "platformdirs>=3.2.0,<4.2.0",
32
- "robotcode-core==0.95.0",
32
+ "robotcode-core==0.95.1",
33
33
  ]
34
34
  dynamic = ["version"]
35
35
 
@@ -0,0 +1 @@
1
+ __version__ = "0.95.1"
@@ -145,10 +145,10 @@ class VariableMatcher:
145
145
  self.normalized_name = str(normalize(self.base))
146
146
 
147
147
  def __eq__(self, o: object) -> bool:
148
- if isinstance(o, VariableMatcher):
148
+ if type(o) is VariableMatcher:
149
149
  return o.normalized_name == self.normalized_name
150
150
 
151
- if isinstance(o, str):
151
+ if type(o) is str:
152
152
  match = search_variable(o, "$@&%", ignore_errors=True)
153
153
  base = match.base
154
154
  if base is None:
@@ -58,8 +58,8 @@ class KeywordFinder:
58
58
  ] = {}
59
59
 
60
60
  self._all_keywords: Optional[List[LibraryEntry]] = None
61
- self._resource_keywords: Optional[List[ResourceEntry]] = None
62
- self._library_keywords: Optional[List[LibraryEntry]] = None
61
+ self._resource_imports: Optional[List[ResourceEntry]] = None
62
+ self._library_imports: Optional[List[LibraryEntry]] = None
63
63
 
64
64
  def reset_diagnostics(self) -> None:
65
65
  self.diagnostics = []
@@ -312,23 +312,23 @@ class KeywordFinder:
312
312
  other: Tuple[Optional[LibraryEntry], KeywordDoc],
313
313
  ) -> bool:
314
314
  return (
315
- other[1].matcher.embedded_arguments.match(candidate[1].name) is not None
315
+ other[1].matcher.embedded_arguments is not None
316
+ and candidate[1].matcher.embedded_arguments is not None
317
+ and other[1].matcher.embedded_arguments.match(candidate[1].name) is not None
316
318
  and candidate[1].matcher.embedded_arguments.match(other[1].name) is None
317
319
  )
318
320
 
319
321
  def _get_keyword_from_resource_files(self, name: str) -> Optional[KeywordDoc]:
320
- if self._resource_keywords is None:
321
- self._resource_keywords = list(chain(self.namespace._resources.values()))
322
+ if self._resource_imports is None:
323
+ self._resource_imports = list(chain(self.namespace._resources.values()))
322
324
 
323
325
  if get_robot_version() >= (6, 0):
324
- found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = []
325
- for v in self._resource_keywords:
326
- r = v.library_doc.keywords.get_all(name)
327
- if r:
328
- found.extend([(v, k) for k in r])
326
+ found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
327
+ (v, k) for v in self._resource_imports for k in v.library_doc.keywords.iter_all(name)
328
+ ]
329
329
  else:
330
330
  found = []
331
- for k in self._resource_keywords:
331
+ for k in self._resource_imports:
332
332
  s = k.library_doc.keywords.get(name, None)
333
333
  if s is not None:
334
334
  found.append((k, s))
@@ -373,19 +373,18 @@ class KeywordFinder:
373
373
  return entries
374
374
 
375
375
  def _get_keyword_from_libraries(self, name: str) -> Optional[KeywordDoc]:
376
- if self._library_keywords is None:
377
- self._library_keywords = list(chain(self.namespace._libraries.values()))
376
+ if self._library_imports is None:
377
+ self._library_imports = list(chain(self.namespace._libraries.values()))
378
378
 
379
379
  if get_robot_version() >= (6, 0):
380
- found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = []
381
- for v in self._library_keywords:
382
- r = v.library_doc.keywords.get_all(name)
383
- if r:
384
- found.extend([(v, k) for k in r])
380
+ found: List[Tuple[Optional[LibraryEntry], KeywordDoc]] = [
381
+ (v, k) for v in self._library_imports for k in v.library_doc.keywords.iter_all(name)
382
+ ]
383
+
385
384
  else:
386
385
  found = []
387
386
 
388
- for k in self._library_keywords:
387
+ for k in self._library_imports:
389
388
  s = k.library_doc.keywords.get(name, None)
390
389
  if s is not None:
391
390
  found.append((k, s))
@@ -76,12 +76,12 @@ from robotcode.robot.utils import get_robot_version
76
76
  from robotcode.robot.utils.ast import (
77
77
  cached_isinstance,
78
78
  get_variable_token,
79
+ iter_nodes,
79
80
  range_from_token,
80
81
  strip_variable_token,
81
82
  )
82
83
  from robotcode.robot.utils.markdownformatter import MarkDownFormatter
83
84
  from robotcode.robot.utils.match import normalize, normalize_namespace
84
- from robotcode.robot.utils.stubs import HasError, HasErrors
85
85
 
86
86
  from ..utils.variables import contains_variable
87
87
 
@@ -239,7 +239,11 @@ class KeywordMatcher:
239
239
  self._can_have_embedded = can_have_embedded and not is_namespace
240
240
  self._is_namespace = is_namespace
241
241
  self._normalized_name: Optional[str] = None
242
- self._embedded_arguments: Any = None
242
+
243
+ self.embedded_arguments: Optional[EmbeddedArguments] = (
244
+ _get_embedded_arguments(self.name) or None if self._can_have_embedded else None
245
+ )
246
+ self._match_cache: Dict[str, bool] = {}
243
247
 
244
248
  @property
245
249
  def normalized_name(self) -> str:
@@ -248,28 +252,18 @@ class KeywordMatcher:
248
252
 
249
253
  return self._normalized_name
250
254
 
251
- @property
252
- def embedded_arguments(self) -> Any:
253
- if self._embedded_arguments is None:
254
- if self._can_have_embedded:
255
- self._embedded_arguments = _get_embedded_arguments(self.name)
256
- else:
257
- self._embedded_arguments = ()
258
-
259
- return self._embedded_arguments
260
-
261
255
  if get_robot_version() >= (6, 0):
262
256
 
263
257
  def __match_embedded(self, name: str) -> bool:
264
- return self.embedded_arguments.match(name) is not None
258
+ return self.embedded_arguments is not None and self.embedded_arguments.match(name) is not None
265
259
 
266
260
  else:
267
261
 
268
262
  def __match_embedded(self, name: str) -> bool:
269
- return self.embedded_arguments.name.match(name) is not None
263
+ return self.embedded_arguments is not None and self.embedded_arguments.name.match(name) is not None
270
264
 
271
265
  def __eq__(self, o: object) -> bool:
272
- if cached_isinstance(o, KeywordMatcher):
266
+ if type(o) is KeywordMatcher:
273
267
  if self._is_namespace != o._is_namespace:
274
268
  return False
275
269
 
@@ -278,7 +272,7 @@ class KeywordMatcher:
278
272
 
279
273
  o = o.name
280
274
 
281
- if not cached_isinstance(o, str):
275
+ if type(o) is not str:
282
276
  return False
283
277
 
284
278
  if self.embedded_arguments:
@@ -667,13 +661,11 @@ class KeywordDoc(SourceEntity):
667
661
  def __str__(self) -> str:
668
662
  return f"{self.name}({', '.join(str(arg) for arg in self.arguments)})"
669
663
 
670
- @property
664
+ @functools.cached_property
671
665
  def matcher(self) -> KeywordMatcher:
672
- if not hasattr(self, "__matcher"):
673
- self.__matcher = KeywordMatcher(self.name)
674
- return self.__matcher
666
+ return KeywordMatcher(self.name)
675
667
 
676
- @property
668
+ @functools.cached_property
677
669
  def is_deprecated(self) -> bool:
678
670
  return self.deprecated or DEPRECATED_PATTERN.match(self.doc) is not None
679
671
 
@@ -685,13 +677,13 @@ class KeywordDoc(SourceEntity):
685
677
  def is_library_keyword(self) -> bool:
686
678
  return self.libtype == "LIBRARY"
687
679
 
688
- @property
680
+ @functools.cached_property
689
681
  def deprecated_message(self) -> str:
690
682
  if (m := DEPRECATED_PATTERN.match(self.doc)) is not None:
691
683
  return m.group("message").strip()
692
684
  return ""
693
685
 
694
- @property
686
+ @functools.cached_property
695
687
  def name_range(self) -> Range:
696
688
  if self.name_token is not None:
697
689
  return range_from_token(self.name_token)
@@ -709,7 +701,7 @@ class KeywordDoc(SourceEntity):
709
701
 
710
702
  return "robot:private" in self.normalized_tags()
711
703
 
712
- @property
704
+ @functools.cached_property
713
705
  def range(self) -> Range:
714
706
  if self.name_token is not None:
715
707
  return range_from_token(self.name_token)
@@ -820,7 +812,7 @@ class KeywordDoc(SourceEntity):
820
812
 
821
813
  return result
822
814
 
823
- @property
815
+ @functools.cached_property
824
816
  def signature(self) -> str:
825
817
  return (
826
818
  f'({self.type}) "{self.name}": ('
@@ -919,19 +911,13 @@ class KeywordStore:
919
911
  source_type: Optional[str] = None
920
912
  keywords: List[KeywordDoc] = field(default_factory=list)
921
913
 
922
- @property
923
- def _matchers(self) -> Dict[KeywordMatcher, KeywordDoc]:
924
- if not hasattr(self, "__matchers"):
925
- self.__matchers = {v.matcher: v for v in self.keywords}
926
- return self.__matchers
927
-
928
914
  def __getitem__(self, key: str) -> KeywordDoc:
929
- items = [(k, v) for k, v in self._matchers.items() if k == key]
915
+ items = [v for v in self.keywords if v.matcher == key]
930
916
 
931
917
  if not items:
932
918
  raise KeyError
933
919
  if len(items) == 1:
934
- return items[0][1]
920
+ return items[0]
935
921
 
936
922
  if self.source and self.source_type:
937
923
  file_info = ""
@@ -946,14 +932,14 @@ class KeywordStore:
946
932
  else:
947
933
  file_info = "File"
948
934
  error = [f"{file_info} contains multiple keywords matching name '{key}':"]
949
- names = sorted(k.name for k, v in items)
935
+ names = sorted(v.name for v in items)
950
936
  raise KeywordError(
951
937
  "\n ".join(error + names),
952
- multiple_keywords=[v for _, v in items],
938
+ multiple_keywords=[v for v in items],
953
939
  )
954
940
 
955
941
  def __contains__(self, _x: object) -> bool:
956
- return any(k == _x for k in self._matchers.keys())
942
+ return any(v.matcher == _x for v in self.keywords)
957
943
 
958
944
  def __len__(self) -> int:
959
945
  return len(self.keywords)
@@ -983,7 +969,7 @@ class KeywordStore:
983
969
  return list(self.iter_all(key))
984
970
 
985
971
  def iter_all(self, key: str) -> Iterable[KeywordDoc]:
986
- yield from (v for k, v in self._matchers.items() if k == key)
972
+ yield from (v for v in self.keywords if v.matcher == key)
987
973
 
988
974
 
989
975
  @dataclass
@@ -2716,15 +2702,16 @@ def get_model_doc(
2716
2702
  append_model_errors: bool = True,
2717
2703
  ) -> LibraryDoc:
2718
2704
  errors: List[Error] = []
2719
- keyword_name_nodes: List[KeywordName] = []
2720
- keywords_nodes: List[Keyword] = []
2721
- for node in ast.walk(model):
2722
- if isinstance(node, Keyword):
2723
- keywords_nodes.append(node)
2724
- if isinstance(node, KeywordName):
2725
- keyword_name_nodes.append(node)
2726
-
2727
- error = node.error if isinstance(node, HasError) else None
2705
+ keyword_name_nodes: Dict[int, KeywordName] = {}
2706
+ keywords_nodes: Dict[int, Keyword] = {}
2707
+ for node in iter_nodes(model):
2708
+ if cached_isinstance(node, Keyword):
2709
+ node.lineno
2710
+ keywords_nodes[node.lineno] = node
2711
+ if cached_isinstance(node, KeywordName):
2712
+ keyword_name_nodes[node.lineno] = node
2713
+
2714
+ error = getattr(node, "error", None)
2728
2715
  if error is not None:
2729
2716
  errors.append(
2730
2717
  Error(
@@ -2735,7 +2722,7 @@ def get_model_doc(
2735
2722
  )
2736
2723
  )
2737
2724
  if append_model_errors:
2738
- node_errors = node.errors if isinstance(node, HasErrors) else None
2725
+ node_errors = getattr(node, "errors", None)
2739
2726
  if node_errors is not None:
2740
2727
  for e in node_errors:
2741
2728
  errors.append(
@@ -2748,16 +2735,15 @@ def get_model_doc(
2748
2735
  )
2749
2736
 
2750
2737
  def get_keyword_name_token_from_line(line: int) -> Optional[Token]:
2751
- for keyword_name in keyword_name_nodes:
2752
- if keyword_name.lineno == line:
2753
- return cast(Token, keyword_name.get_token(RobotToken.KEYWORD_NAME))
2754
-
2755
- return None
2738
+ keyword_name = keyword_name_nodes.get(line, None)
2739
+ if keyword_name is None:
2740
+ return None
2741
+ return cast(Token, keyword_name.get_token(RobotToken.KEYWORD_NAME))
2756
2742
 
2757
2743
  def get_argument_definitions_from_line(
2758
2744
  line: int,
2759
2745
  ) -> List[ArgumentDefinition]:
2760
- keyword_node = next((k for k in keywords_nodes if k.lineno == line), None)
2746
+ keyword_node = keywords_nodes.get(line, None)
2761
2747
  if keyword_node is None:
2762
2748
  return []
2763
2749
 
@@ -665,12 +665,12 @@ class ModelHelper:
665
665
 
666
666
  @classmethod
667
667
  def get_keyword_definition_at_token(cls, library_doc: LibraryDoc, token: Token) -> Optional[KeywordDoc]:
668
- return cls.get_keyword_definition_at_line(library_doc, token.value, token.lineno)
668
+ return cls.get_keyword_definition_at_line(library_doc, token.lineno)
669
669
 
670
670
  @classmethod
671
- def get_keyword_definition_at_line(cls, library_doc: LibraryDoc, value: str, line: int) -> Optional[KeywordDoc]:
671
+ def get_keyword_definition_at_line(cls, library_doc: LibraryDoc, line: int) -> Optional[KeywordDoc]:
672
672
  return next(
673
- (k for k in library_doc.keywords.iter_all(value) if k.line_no == line),
673
+ (k for k in library_doc.keywords.keywords if k.line_no == line),
674
674
  None,
675
675
  )
676
676
 
@@ -38,13 +38,6 @@ def cached_isinstance(obj: Any, *expected_types: Type[_T]) -> TypeGuard[Union[_T
38
38
  return False
39
39
 
40
40
 
41
- # def cached_isinstance(obj: Any, *expected_types: type) -> bool:
42
- # try:
43
- # return isinstance(obj, expected_types)
44
- # except TypeError:
45
- # return False
46
-
47
-
48
41
  def iter_nodes(node: ast.AST, descendants: bool = True) -> Iterator[ast.AST]:
49
42
  for _field, value in ast.iter_fields(node):
50
43
  if cached_isinstance(value, list):
@@ -6,7 +6,7 @@ from typing import Optional, Union
6
6
 
7
7
  def find_file_ex(
8
8
  path: Union[Path, "PathLike[str]", str],
9
- basedir: Union[Path, PathLike[str], str] = ".",
9
+ basedir: Union[Path, "PathLike[str]", str] = ".",
10
10
  file_type: Optional[str] = None,
11
11
  ) -> str:
12
12
  from robot.errors import DataError
@@ -33,7 +33,7 @@ def find_file_ex(
33
33
 
34
34
  def find_file(
35
35
  path: Union[Path, "PathLike[str]", str],
36
- basedir: Union[Path, PathLike[str], str] = ".",
36
+ basedir: Union[Path, "PathLike[str]", str] = ".",
37
37
  file_type: Optional[str] = None,
38
38
  ) -> str:
39
39
  return find_file_ex(path, basedir, file_type)
@@ -1,22 +1,4 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any, Dict, Iterator, List, Optional, Protocol, Set, runtime_checkable
4
-
5
-
6
- @runtime_checkable
7
- class HasError(Protocol):
8
- error: Optional[str]
9
-
10
-
11
- @runtime_checkable
12
- class HasErrors(Protocol):
13
- errors: Optional[List[str]]
14
-
15
-
16
- @runtime_checkable
17
- class HeaderAndBodyBlock(Protocol):
18
- header: Any
19
- body: List[Any]
1
+ from typing import Any, Dict, Iterator, List, Protocol, Set, runtime_checkable
20
2
 
21
3
 
22
4
  @runtime_checkable
@@ -2,7 +2,6 @@ import ast
2
2
  from abc import ABC
3
3
  from typing import (
4
4
  Any,
5
- AsyncIterator,
6
5
  Callable,
7
6
  Dict,
8
7
  Iterator,
@@ -37,32 +36,6 @@ def iter_field_values(node: ast.AST) -> Iterator[Any]:
37
36
  pass
38
37
 
39
38
 
40
- def iter_child_nodes(node: ast.AST) -> Iterator[ast.AST]:
41
- for _name, field in iter_fields(node):
42
- if isinstance(field, ast.AST):
43
- yield field
44
- elif isinstance(field, list):
45
- for item in field:
46
- if isinstance(item, ast.AST):
47
- yield item
48
-
49
-
50
- async def iter_nodes(node: ast.AST) -> AsyncIterator[ast.AST]:
51
- for _name, value in iter_fields(node):
52
- if isinstance(value, list):
53
- for item in value:
54
- if isinstance(item, ast.AST):
55
- yield item
56
- async for n in iter_nodes(item):
57
- yield n
58
-
59
- elif isinstance(value, ast.AST):
60
- yield value
61
-
62
- async for n in iter_nodes(value):
63
- yield n
64
-
65
-
66
39
  class VisitorFinder(ABC):
67
40
  __cls_finder_cache__: Dict[Type[Any], Optional[Callable[..., Any]]]
68
41
 
@@ -1 +0,0 @@
1
- __version__ = "0.95.0"