Skip to content

Commit

Permalink
perf(analyzer): cache embedded arguments and some more little perf tw…
Browse files Browse the repository at this point in the history
…eaks
  • Loading branch information
d-biehl committed Oct 24, 2024
1 parent 77ce8f1 commit 3603ff6
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 50 deletions.
24 changes: 11 additions & 13 deletions packages/robot/src/robotcode/robot/diagnostics/keyword_finder.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import functools
import re
from itertools import chain
from typing import TYPE_CHECKING, Dict, Iterable, Iterator, List, NamedTuple, Optional, Sequence, Tuple
Expand Down Expand Up @@ -59,7 +60,6 @@ def __init__(self, namespace: "Namespace", library_doc: LibraryDoc) -> None:
self._all_keywords: Optional[List[LibraryEntry]] = None
self._resource_keywords: Optional[List[ResourceEntry]] = None
self._library_keywords: Optional[List[LibraryEntry]] = None
self._bdd_prefix_regexp: Optional["re.Pattern[str]"] = None

def reset_diagnostics(self) -> None:
self.diagnostics = []
Expand Down Expand Up @@ -459,20 +459,18 @@ def _create_custom_and_standard_keyword_conflict_warning_message(
f"or '{'' if standard[0] is None else standard[0].alias or standard[0].name}.{standard[1].name}'."
)

@property
@functools.cached_property
def bdd_prefix_regexp(self) -> "re.Pattern[str]":
if not self._bdd_prefix_regexp:
prefixes = (
"|".join(
self.namespace.languages.bdd_prefixes
if self.namespace.languages is not None
else ["given", "when", "then", "and", "but"]
)
.replace(" ", r"\s")
.lower()
prefixes = (
"|".join(
self.namespace.languages.bdd_prefixes
if self.namespace.languages is not None
else ["given", "when", "then", "and", "but"]
)
self._bdd_prefix_regexp = re.compile(rf"({prefixes})\s", re.IGNORECASE)
return self._bdd_prefix_regexp
.replace(" ", r"\s")
.lower()
)
return re.compile(rf"({prefixes})\s", re.IGNORECASE)

def _get_bdd_style_keyword(self, name: str) -> Optional[KeywordDoc]:
match = self.bdd_prefix_regexp.match(name)
Expand Down
53 changes: 34 additions & 19 deletions packages/robot/src/robotcode/robot/diagnostics/library_doc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import ast
import functools
import hashlib
import importlib
import importlib.util
Expand Down Expand Up @@ -197,14 +198,29 @@ def convert_from_rest(text: str) -> str:
return text


if get_robot_version() >= (6, 0):

@functools.lru_cache(maxsize=None)
def _get_embedded_arguments(name: str) -> Any:
try:
return EmbeddedArguments.from_name(name)
except (VariableError, DataError):
return ()

else:

@functools.lru_cache(maxsize=None)
def _get_embedded_arguments(name: str) -> Any:
try:
return EmbeddedArguments(name)
except (VariableError, DataError):
return ()


def is_embedded_keyword(name: str) -> bool:
try:
if get_robot_version() >= (6, 0):
if EmbeddedArguments.from_name(name):
return True
else:
if EmbeddedArguments(name):
return True
if _get_embedded_arguments(name):
return True
except (VariableError, DataError):
return True

Expand Down Expand Up @@ -235,18 +251,22 @@ def normalized_name(self) -> str:
def embedded_arguments(self) -> Any:
if self._embedded_arguments is None:
if self._can_have_embedded:
try:
if get_robot_version() >= (6, 0):
self._embedded_arguments = EmbeddedArguments.from_name(self.name)
else:
self._embedded_arguments = EmbeddedArguments(self.name)
except (VariableError, DataError):
self._embedded_arguments = ()
self._embedded_arguments = _get_embedded_arguments(self.name)
else:
self._embedded_arguments = ()

return self._embedded_arguments

if get_robot_version() >= (6, 0):

def __match_embedded(self, name: str) -> bool:
return self.embedded_arguments.match(name) is not None

else:

def __match_embedded(self, name: str) -> bool:
return self.embedded_arguments.name.match(name) is not None

def __eq__(self, o: object) -> bool:
if cached_isinstance(o, KeywordMatcher):
if self._is_namespace != o._is_namespace:
Expand All @@ -261,10 +281,7 @@ def __eq__(self, o: object) -> bool:
return False

if self.embedded_arguments:
if get_robot_version() >= (6, 0):
return self.embedded_arguments.match(o) is not None

return self.embedded_arguments.name.match(o) is not None
return self.__match_embedded(o)

return self.normalized_name == str(normalize_namespace(o) if self._is_namespace else normalize(o))

Expand Down Expand Up @@ -935,8 +952,6 @@ def __getitem__(self, key: str) -> KeywordDoc:
)

def __contains__(self, _x: object) -> bool:
if not isinstance(_x, KeywordMatcher):
_x = KeywordMatcher(str(_x))
return any(k == _x for k in self._matchers.keys())

def __len__(self) -> int:
Expand Down
6 changes: 3 additions & 3 deletions packages/robot/src/robotcode/robot/diagnostics/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,7 @@ def get_namespaces(self) -> Dict[KeywordMatcher, List[LibraryEntry]]:
self._namespaces[KeywordMatcher(v.alias or v.name or v.import_name, is_namespace=True)].append(v)
for v in (self.get_resources()).values():
self._namespaces[KeywordMatcher(v.alias or v.name or v.import_name, is_namespace=True)].append(v)

return self._namespaces

def get_resources(self) -> Dict[str, ResourceEntry]:
Expand Down Expand Up @@ -1793,11 +1794,10 @@ def iter_all_keywords(self) -> Iterator[KeywordDoc]:

libdoc = self.get_library_doc()

for doc in itertools.chain(
yield from itertools.chain(
self.get_imported_keywords(),
libdoc.keywords if libdoc is not None else [],
):
yield doc
)

@_logger.call
def get_keywords(self) -> List[KeywordDoc]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,20 @@ def _append_diagnostics(
)
)

KEYWORDS_WITH_EXPRESSIONS = [
"BuiltIn.Evaluate",
"BuiltIn.Should Be True",
"BuiltIn.Should Not Be True",
"BuiltIn.Skip If",
"BuiltIn.Continue For Loop If",
"BuiltIn.Exit For Loop If",
"BuiltIn.Return From Keyword If",
"BuiltIn.Run Keyword And Return If",
"BuiltIn.Pass Execution If",
"BuiltIn.Run Keyword If",
"BuiltIn.Run Keyword Unless",
]

def _analyze_keyword_call(
self,
node: ast.AST,
Expand Down Expand Up @@ -708,19 +722,7 @@ def _analyze_keyword_call(
)

if result is not None:
if result.longname in [
"BuiltIn.Evaluate",
"BuiltIn.Should Be True",
"BuiltIn.Should Not Be True",
"BuiltIn.Skip If",
"BuiltIn.Continue For Loop If",
"BuiltIn.Exit For Loop If",
"BuiltIn.Return From Keyword If",
"BuiltIn.Run Keyword And Return If",
"BuiltIn.Pass Execution If",
"BuiltIn.Run Keyword If",
"BuiltIn.Run Keyword Unless",
]:
if result.longname in self.KEYWORDS_WITH_EXPRESSIONS:
tokens = argument_tokens
if tokens and (token := tokens[0]):
self._analyze_token_expression_variables(token)
Expand Down
4 changes: 2 additions & 2 deletions packages/robot/src/robotcode/robot/utils/match.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
_transform_table = str.maketrans("", "", "_ ")


@lru_cache(maxsize=5000)
@lru_cache(maxsize=None)
def normalize(text: str) -> str:
# return text.lower().replace("_", "").replace(" ", "")
return text.casefold().translate(_transform_table)


@lru_cache(maxsize=5000)
@lru_cache(maxsize=None)
def normalize_namespace(text: str) -> str:
return text.lower().replace(" ", "")

Expand Down

0 comments on commit 3603ff6

Please sign in to comment.