Skip to content

Commit ba95e7c

Browse files
committed
cache finding libraries, recources, variables files
1 parent 336a498 commit ba95e7c

File tree

3 files changed

+86
-29
lines changed

3 files changed

+86
-29
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to the "robotcode" extension will be documented in this file
44

55
## [Unreleased]
66

7+
### added
8+
79
- Information about possible circular imports
810
- if one resource file imports another resource file and vice versa an information message is shown in source code and problems list
911

robotcode/language_server/robotframework/diagnostics/imports_manager.py

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
final,
2424
)
2525

26+
from ....utils.async_cache import AsyncSimpleCache
2627
from ....utils.async_tools import Lock, async_tasking_event, create_sub_task
2728
from ....utils.logging import LoggingDescriptor
2829
from ....utils.path import path_is_relative_to
@@ -471,6 +472,10 @@ def __init__(self, parent_protocol: RobotLanguageServerProtocol, folder: Uri, co
471472
self._python_path: Optional[List[str]] = None
472473
self._environment: Optional[Mapping[str, str]] = None
473474

475+
self._library_files_cache = AsyncSimpleCache()
476+
self._resource_files_cache = AsyncSimpleCache()
477+
self._variables_files_cache = AsyncSimpleCache()
478+
474479
@property
475480
def environment(self) -> Mapping[str, str]:
476481
if self._environment is None:
@@ -691,8 +696,10 @@ async def remove(k: _VariablesEntryKey, e: _VariablesEntry) -> None:
691696
except RuntimeError:
692697
pass
693698

694-
@_logger.call
695699
async def find_library(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str:
700+
return await self._library_files_cache.get(self._find_library, name, base_dir, variables)
701+
702+
async def _find_library(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str:
696703
from robot.libraries import STDLIBS
697704
from robot.variables.search import contains_variable
698705

@@ -722,8 +729,41 @@ async def find_library(self, name: str, base_dir: str, variables: Optional[Dict[
722729

723730
return result
724731

732+
async def find_resource(
733+
self, name: str, base_dir: str, file_type: str = "Resource", variables: Optional[Dict[str, Any]] = None
734+
) -> str:
735+
return await self._resource_files_cache.get(self.__find_resource, name, base_dir, file_type, variables)
736+
725737
@_logger.call
738+
async def __find_resource(
739+
self, name: str, base_dir: str, file_type: str = "Resource", variables: Optional[Dict[str, Any]] = None
740+
) -> str:
741+
from robot.variables.search import contains_variable
742+
743+
if contains_variable(name, "$@&%"):
744+
return await asyncio.wait_for(
745+
asyncio.get_running_loop().run_in_executor(
746+
self.process_pool,
747+
find_file,
748+
name,
749+
str(self.folder.to_path()),
750+
base_dir,
751+
self.config.python_path if self.config is not None else None,
752+
self.config.env if self.config is not None else None,
753+
self.config.variables if self.config is not None else None,
754+
variables,
755+
file_type,
756+
),
757+
FIND_FILE_TIME_OUT,
758+
)
759+
760+
return str(find_file_ex(name, base_dir, self.python_path, file_type))
761+
726762
async def find_variables(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str:
763+
return await self._variables_files_cache.get(self._find_variables, name, base_dir, variables)
764+
765+
@_logger.call
766+
async def _find_variables(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str:
727767
from robot.variables.search import contains_variable
728768

729769
if contains_variable(name, "$@&%"):
@@ -970,34 +1010,6 @@ async def _get_libdoc() -> VariablesDoc:
9701010

9711011
return await entry.get_libdoc()
9721012

973-
@_logger.call
974-
async def find_resource(
975-
self, name: str, base_dir: str, file_type: str = "Resource", variables: Optional[Dict[str, Any]] = None
976-
) -> str:
977-
from robot.variables.search import contains_variable
978-
979-
# if contains_variable(name, "$@&%"):
980-
# name = name.replace("%{CI_PROJECT_DIR}", self.environment["CI_PROJECT_DIR"])
981-
982-
if contains_variable(name, "$@&%"):
983-
return await asyncio.wait_for(
984-
asyncio.get_running_loop().run_in_executor(
985-
self.process_pool,
986-
find_file,
987-
name,
988-
str(self.folder.to_path()),
989-
base_dir,
990-
self.config.python_path if self.config is not None else None,
991-
self.config.env if self.config is not None else None,
992-
self.config.variables if self.config is not None else None,
993-
variables,
994-
file_type,
995-
),
996-
FIND_FILE_TIME_OUT,
997-
)
998-
999-
return str(find_file_ex(name, base_dir, self.python_path, file_type))
1000-
10011013
@_logger.call
10021014
async def _get_entry_for_resource_import(
10031015
self, name: str, base_dir: str, sentinel: Any = None, variables: Optional[Dict[str, Any]] = None

robotcode/utils/async_cache.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from typing import Any, Awaitable, Callable, Dict, List, Tuple, TypeVar, cast
2+
3+
from .async_tools import Lock
4+
5+
_T = TypeVar("_T")
6+
7+
8+
def _freeze(v: Any) -> Any:
9+
if isinstance(v, dict):
10+
return frozenset(v.items())
11+
return v
12+
13+
14+
class AsyncSimpleCache:
15+
def __init__(self, max_items: int = 128) -> None:
16+
self.max_items = max_items
17+
18+
self._cache: Dict[Tuple[Any, ...], Any] = {}
19+
self._order: List[Tuple[Any, ...]] = []
20+
self._lock = Lock()
21+
22+
async def get(self, func: Callable[..., Awaitable[_T]], *args: Any, **kwargs: Any) -> _T:
23+
key = self._make_key(*args, **kwargs)
24+
25+
async with self._lock:
26+
try:
27+
return cast(_T, self._cache[key])
28+
except KeyError:
29+
pass
30+
31+
res = await func(*args, **kwargs)
32+
33+
self._cache[key] = res
34+
self._order.insert(0, key)
35+
36+
if len(self._order) > self.max_items:
37+
del self._cache[self._order.pop()]
38+
39+
return res
40+
41+
@staticmethod
42+
def _make_key(*args: Any, **kwargs: Any) -> Tuple[Any, ...]:
43+
return (tuple(_freeze(v) for v in args), frozenset({k: _freeze(v) for k, v in kwargs.items()}))

0 commit comments

Comments
 (0)