Skip to content

Commit bd7de6d

Browse files
committed
optimize loading of imports and collecting keywords
1 parent 8c433af commit bd7de6d

24 files changed

+1534
-273
lines changed

robotcode/language_server/robotframework/diagnostics/analyzer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
class Analyzer(AsyncVisitor):
3535
def __init__(self, model: ast.AST, namespace: Namespace) -> None:
36-
from robot.parsing.model.statements import TestTemplate, Template
36+
from robot.parsing.model.statements import Template, TestTemplate
3737

3838
self.model = model
3939
self._namespace = namespace

robotcode/language_server/robotframework/diagnostics/imports_manager.py

Lines changed: 122 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import ast
44
import asyncio
5+
import os
6+
import sys
57
import weakref
68
from abc import ABC, abstractmethod
79
from collections import OrderedDict
@@ -14,12 +16,17 @@
1416
Coroutine,
1517
Dict,
1618
List,
19+
Mapping,
1720
Optional,
1821
Tuple,
1922
cast,
2023
final,
2124
)
2225

26+
from robotcode.language_server.robotframework.utils.robot_path import find_file_ex
27+
28+
from ..utils.version import get_robot_version
29+
2330
from ....utils.async_tools import Lock, async_tasking_event, create_sub_task
2431
from ....utils.logging import LoggingDescriptor
2532
from ....utils.path import path_is_relative_to
@@ -38,6 +45,7 @@
3845

3946
from ..utils.process_pool import get_process_pool
4047
from .library_doc import (
48+
ROBOT_LIBRARY_PACKAGE,
4149
ArgumentSpec,
4250
CompleteResult,
4351
Error,
@@ -55,6 +63,8 @@
5563
get_library_doc,
5664
get_variables_doc,
5765
is_embedded_keyword,
66+
is_library_by_path,
67+
is_variables_by_path,
5868
resolve_variable,
5969
)
6070

@@ -453,13 +463,11 @@ async def get_libdoc(self) -> VariablesDoc:
453463
class ImportsManager:
454464
_logger = LoggingDescriptor()
455465

456-
def __init__(
457-
self, parent_protocol: RobotLanguageServerProtocol, folder: Uri, config: Optional[RobotConfig]
458-
) -> None:
466+
def __init__(self, parent_protocol: RobotLanguageServerProtocol, folder: Uri, config: RobotConfig) -> None:
459467
super().__init__()
460468
self.parent_protocol = parent_protocol
461469
self.folder = folder
462-
self.config = config
470+
self.config: RobotConfig = config
463471
self._libaries_lock = Lock()
464472
self._libaries: OrderedDict[_LibrariesEntryKey, _LibrariesEntry] = OrderedDict()
465473
self._resources_lock = Lock()
@@ -471,6 +479,34 @@ def __init__(
471479
self._command_line_variables: Optional[List[VariableDefinition]] = None
472480

473481
self.process_pool = get_process_pool()
482+
self._python_path: Optional[List[str]] = None
483+
self._environment: Optional[Mapping[str, str]] = None
484+
485+
@property
486+
def environment(self) -> Mapping[str, str]:
487+
if self._environment is None:
488+
self._environment = dict(os.environ)
489+
490+
self._environment.update(self.config.env)
491+
492+
return self._environment
493+
494+
@property
495+
def python_path(self) -> List[str]:
496+
if self._python_path is None:
497+
self._python_path = sys.path
498+
499+
file = Path(__file__).resolve()
500+
top = file.parents[3]
501+
for p in filter(lambda v: path_is_relative_to(v, top), sys.path.copy()):
502+
self._python_path.remove(p)
503+
504+
for p in self.config.python_path:
505+
absolute_path = str(Path(p).absolute())
506+
if absolute_path not in self._python_path:
507+
self._python_path.insert(0, absolute_path)
508+
509+
return self._python_path or []
474510

475511
@_logger.call
476512
def get_command_line_variables(self) -> List[VariableDefinition]:
@@ -621,37 +657,67 @@ async def remove(k: _VariablesEntryKey, e: _VariablesEntry) -> None:
621657

622658
@_logger.call
623659
async def find_library(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str:
624-
return await asyncio.wait_for(
625-
asyncio.get_running_loop().run_in_executor(
626-
self.process_pool,
627-
find_library,
628-
name,
629-
str(self.folder.to_path()),
630-
base_dir,
631-
self.config.python_path if self.config is not None else None,
632-
self.config.env if self.config is not None else None,
633-
self.config.variables if self.config is not None else None,
634-
variables,
635-
),
636-
FIND_FILE_TIME_OUT,
637-
)
660+
from robot.variables.search import contains_variable
661+
662+
from robot.libraries import STDLIBS
663+
664+
if contains_variable(name, "$@&%"):
665+
return await asyncio.wait_for(
666+
asyncio.get_running_loop().run_in_executor(
667+
self.process_pool,
668+
find_library,
669+
name,
670+
str(self.folder.to_path()),
671+
base_dir,
672+
self.config.python_path if self.config is not None else None,
673+
self.config.env if self.config is not None else None,
674+
self.config.variables if self.config is not None else None,
675+
variables,
676+
),
677+
FIND_FILE_TIME_OUT,
678+
)
679+
680+
if name in STDLIBS:
681+
result = ROBOT_LIBRARY_PACKAGE + "." + name
682+
else:
683+
result = name
684+
685+
if is_library_by_path(result):
686+
result = find_file_ex(result, base_dir, self.python_path, "Library")
687+
688+
return result
638689

639690
@_logger.call
640691
async def find_variables(self, name: str, base_dir: str, variables: Optional[Dict[str, Any]] = None) -> str:
641-
return await asyncio.wait_for(
642-
asyncio.get_running_loop().run_in_executor(
643-
self.process_pool,
644-
find_variables,
645-
name,
646-
str(self.folder.to_path()),
647-
base_dir,
648-
self.config.python_path if self.config is not None else None,
649-
self.config.env if self.config is not None else None,
650-
self.config.variables if self.config is not None else None,
651-
variables,
652-
),
653-
FIND_FILE_TIME_OUT,
654-
)
692+
from robot.variables.search import contains_variable
693+
694+
# if contains_variable(name, "$@&%"):
695+
# name = name.replace("%{CI_PROJECT_DIR}", self.environment["CI_PROJECT_DIR"])
696+
697+
if contains_variable(name, "$@&%"):
698+
return await asyncio.wait_for(
699+
asyncio.get_running_loop().run_in_executor(
700+
self.process_pool,
701+
find_variables,
702+
name,
703+
str(self.folder.to_path()),
704+
base_dir,
705+
self.config.python_path if self.config is not None else None,
706+
self.config.env if self.config is not None else None,
707+
self.config.variables if self.config is not None else None,
708+
variables,
709+
),
710+
FIND_FILE_TIME_OUT,
711+
)
712+
713+
if get_robot_version() >= (5, 0):
714+
715+
if is_variables_by_path(name):
716+
return str(find_file_ex(name, base_dir, self.python_path, "Library"))
717+
718+
return name
719+
720+
return str(find_file_ex(name, base_dir, self.python_path, "Library"))
655721

656722
@_logger.call
657723
async def get_libdoc_for_library_import(
@@ -662,7 +728,6 @@ async def get_libdoc_for_library_import(
662728
sentinel: Any = None,
663729
variables: Optional[Dict[str, Any]] = None,
664730
) -> LibraryDoc:
665-
666731
source = await self.find_library(
667732
name,
668733
base_dir,
@@ -872,32 +937,38 @@ async def _get_libdoc() -> VariablesDoc:
872937
return await entry.get_libdoc()
873938

874939
@_logger.call
875-
async def find_file(
940+
async def find_resource(
876941
self, name: str, base_dir: str, file_type: str = "Resource", variables: Optional[Dict[str, Any]] = None
877942
) -> str:
878-
result = await asyncio.wait_for(
879-
asyncio.get_running_loop().run_in_executor(
880-
self.process_pool,
881-
find_file,
882-
name,
883-
str(self.folder.to_path()),
884-
base_dir,
885-
self.config.python_path if self.config is not None else None,
886-
self.config.env if self.config is not None else None,
887-
self.config.variables if self.config is not None else None,
888-
variables,
889-
file_type,
890-
),
891-
FIND_FILE_TIME_OUT,
892-
)
943+
from robot.variables.search import contains_variable
893944

894-
return result
945+
# if contains_variable(name, "$@&%"):
946+
# name = name.replace("%{CI_PROJECT_DIR}", self.environment["CI_PROJECT_DIR"])
947+
948+
if contains_variable(name, "$@&%"):
949+
return await asyncio.wait_for(
950+
asyncio.get_running_loop().run_in_executor(
951+
self.process_pool,
952+
find_file,
953+
name,
954+
str(self.folder.to_path()),
955+
base_dir,
956+
self.config.python_path if self.config is not None else None,
957+
self.config.env if self.config is not None else None,
958+
self.config.variables if self.config is not None else None,
959+
variables,
960+
file_type,
961+
),
962+
FIND_FILE_TIME_OUT,
963+
)
964+
965+
return str(find_file_ex(name, base_dir, self.python_path, file_type))
895966

896967
@_logger.call
897968
async def _get_entry_for_resource_import(
898969
self, name: str, base_dir: str, sentinel: Any = None, variables: Optional[Dict[str, Any]] = None
899970
) -> _ResourcesEntry:
900-
source = await self.find_file(name, base_dir, variables=variables)
971+
source = await self.find_resource(name, base_dir, variables=variables)
901972

902973
async def _get_document() -> TextDocument:
903974
self._logger.debug(lambda: f"Load resource {name} from source {source}")

0 commit comments

Comments
 (0)