Skip to content

Commit 126af22

Browse files
committed
implement changes for RF 5.1 embedded keywords
1 parent 030329f commit 126af22

File tree

7 files changed

+79
-82
lines changed

7 files changed

+79
-82
lines changed

robotcode/language_server/robotframework/diagnostics/analyzer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ async def visit(self, node: ast.AST) -> None:
160160
)
161161

162162
if isinstance(node, Statement) and isinstance(node, KeywordCall) and node.keyword:
163-
kw_doc = await self.namespace.find_keyword(node.keyword)
163+
kw_doc = await self.finder.find_keyword(node.keyword)
164164
if kw_doc is not None and kw_doc.longname in ["BuiltIn.Comment"]:
165165
severity = DiagnosticSeverity.HINT
166166

robotcode/language_server/robotframework/diagnostics/imports_manager.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,6 @@ def __init__(self, parent_protocol: RobotLanguageServerProtocol, folder: Uri, co
470470
self.parent_protocol.documents.did_change.add(self.resource_document_changed)
471471
self._command_line_variables: Optional[List[VariableDefinition]] = None
472472

473-
# self.process_pool = get_process_pool()
474473
self._python_path: Optional[List[str]] = None
475474
self._environment: Optional[Mapping[str, str]] = None
476475

robotcode/language_server/robotframework/diagnostics/library_doc.py

Lines changed: 21 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from dataclasses import dataclass, field
1313
from enum import Enum
1414
from pathlib import Path
15-
from types import ModuleType
1615
from typing import (
1716
AbstractSet,
1817
Any,
@@ -23,7 +22,6 @@
2322
List,
2423
NamedTuple,
2524
Optional,
26-
Set,
2725
Tuple,
2826
Union,
2927
cast,
@@ -96,17 +94,22 @@ def is_embedded_keyword(name: str) -> bool:
9694
from robot.running.arguments.embedded import EmbeddedArguments
9795

9896
try:
99-
if EmbeddedArguments(name):
100-
return True
97+
if get_robot_version() >= (5, 1):
98+
if EmbeddedArguments.from_name(name):
99+
return True
100+
else:
101+
if EmbeddedArguments(name):
102+
return True
101103
except (VariableError, DataError):
102104
return True
103105

104106
return False
105107

106108

107109
class KeywordMatcher:
108-
def __init__(self, name: str) -> None:
110+
def __init__(self, name: str, can_have_embedded: bool = True) -> None:
109111
self.name = name
112+
self._can_have_embedded = can_have_embedded
110113
self._normalized_name: Optional[str] = None
111114
self._embedded_arguments: Any = None
112115

@@ -124,9 +127,15 @@ def embedded_arguments(self) -> Any:
124127
from robot.running.arguments.embedded import EmbeddedArguments
125128

126129
if self._embedded_arguments is None:
127-
try:
128-
self._embedded_arguments = EmbeddedArguments(self.name)
129-
except (VariableError, DataError):
130+
if self._can_have_embedded:
131+
try:
132+
if get_robot_version() >= (5, 1):
133+
self._embedded_arguments = EmbeddedArguments.from_name(self.name)
134+
else:
135+
self._embedded_arguments = EmbeddedArguments(self.name)
136+
except (VariableError, DataError):
137+
self._embedded_arguments = ()
138+
else:
130139
self._embedded_arguments = ()
131140

132141
return self._embedded_arguments
@@ -142,7 +151,10 @@ def __eq__(self, o: Any) -> bool:
142151
return False
143152

144153
if self.embedded_arguments:
145-
return self.embedded_arguments.name.match(o) is not None
154+
if get_robot_version() >= (5, 1):
155+
return self.embedded_arguments.match(o) is not None
156+
else:
157+
return self.embedded_arguments.name.match(o) is not None
146158

147159
return self.normalized_name == str(normalize(o))
148160

@@ -714,59 +726,13 @@ def update_python_path_and_env(
714726
) -> None:
715727
os.chdir(Path(working_dir))
716728

717-
# if pythonpath is not None:
718-
# for p in pythonpath:
719-
# absolute_path = str(Path(p).absolute())
720-
# if absolute_path not in sys.path:
721-
# sys.path.insert(0, absolute_path)
722-
723-
# if environment:
724-
# for k, v in environment.items():
725-
# os.environ[k] = v
726-
727-
728-
__PRELOADED_MODULES: Optional[Set[ModuleType]] = None
729-
730729

731730
def _update_env(
732731
working_dir: str = ".", pythonpath: Optional[List[str]] = None, environment: Optional[Dict[str, str]] = None
733732
) -> None:
734-
# import gc
735-
736-
# unload_preloaded_modules()
737-
738-
# file = Path(__file__).resolve()
739-
# top = file.parents[3]
740-
# for p in filter(lambda v: path_is_relative_to(v, top), sys.path.copy()):
741-
# sys.path.remove(p)
742-
743-
# importlib.invalidate_caches()
744-
745-
# gc.collect()
746-
747733
update_python_path_and_env(working_dir, pythonpath, environment)
748734

749735

750-
def unload_preloaded_modules() -> None:
751-
global __PRELOADED_MODULES
752-
753-
if __PRELOADED_MODULES is None:
754-
try:
755-
__import__("robot.libraries.BuiltIn")
756-
except ImportError:
757-
pass
758-
759-
__PRELOADED_MODULES = set(sys.modules.values())
760-
else:
761-
for m in (f for f in set(sys.modules.values()) - __PRELOADED_MODULES if not f.__name__.startswith("robot.")):
762-
try:
763-
importlib.reload(m)
764-
except (SystemExit, KeyboardInterrupt):
765-
raise
766-
except BaseException:
767-
pass
768-
769-
770736
def get_module_spec(module_name: str) -> Optional[ModuleSpec]:
771737
import importlib.util
772738

robotcode/language_server/robotframework/diagnostics/namespace.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,8 @@ async def get_libraries(self) -> OrderedDict[str, LibraryEntry]:
689689
async def get_libraries_matchers(self) -> Dict[KeywordMatcher, LibraryEntry]:
690690
if self._libraries_matchers is None:
691691
self._libraries_matchers = {
692-
KeywordMatcher(v.alias or v.name or v.import_name): v for v in (await self.get_libraries()).values()
692+
KeywordMatcher(v.alias or v.name or v.import_name, False): v
693+
for v in (await self.get_libraries()).values()
693694
}
694695
return self._libraries_matchers
695696

@@ -701,7 +702,8 @@ async def get_resources(self) -> OrderedDict[str, ResourceEntry]:
701702
async def get_resources_matchers(self) -> Dict[KeywordMatcher, ResourceEntry]:
702703
if self._resources_matchers is None:
703704
self._resources_matchers = {
704-
KeywordMatcher(v.alias or v.name or v.import_name): v for v in (await self.get_resources()).values()
705+
KeywordMatcher(v.alias or v.name or v.import_name, False): v
706+
for v in (await self.get_resources()).values()
705707
}
706708
return self._resources_matchers
707709

@@ -1528,11 +1530,11 @@ async def get_finder(self) -> KeywordFinder:
15281530
return self._finder
15291531

15301532
@_logger.call(condition=lambda self, name: self._finder is not None and name not in self._finder._cache)
1531-
async def find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]:
1533+
async def find_keyword(self, name: Optional[str], raise_keyword_error: bool = True) -> Optional[KeywordDoc]:
15321534
if self._finder is not None:
1533-
return await self._finder.find_keyword(name)
1535+
return await self._finder.find_keyword(name, raise_keyword_error)
15341536

1535-
return await (await self.get_finder()).find_keyword(name)
1537+
return await (await self.get_finder()).find_keyword(name, raise_keyword_error)
15361538

15371539

15381540
class DiagnosticsEntry(NamedTuple):
@@ -1556,7 +1558,7 @@ def __init__(self, namespace: Namespace) -> None:
15561558
def reset_diagnostics(self) -> None:
15571559
self.diagnostics = []
15581560

1559-
async def find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]:
1561+
async def find_keyword(self, name: Optional[str], raise_keyword_error: bool = False) -> Optional[KeywordDoc]:
15601562
try:
15611563
self.reset_diagnostics()
15621564

@@ -1566,13 +1568,21 @@ async def find_keyword(self, name: Optional[str]) -> Optional[KeywordDoc]:
15661568
self.diagnostics = cached[1]
15671569
return cached[0]
15681570
else:
1569-
result = await self._find_keyword(name)
1570-
if result is None:
1571-
self.diagnostics.append(
1572-
DiagnosticsEntry(
1573-
f"No keyword with name {repr(name)} found.", DiagnosticSeverity.ERROR, "KeywordError"
1571+
try:
1572+
result = await self._find_keyword(name)
1573+
if result is None:
1574+
self.diagnostics.append(
1575+
DiagnosticsEntry(
1576+
f"No keyword with name {repr(name)} found.", DiagnosticSeverity.ERROR, "KeywordError"
1577+
)
15741578
)
1575-
)
1579+
except KeywordError as e:
1580+
if raise_keyword_error:
1581+
raise
1582+
1583+
result = None
1584+
self.diagnostics.append(DiagnosticsEntry(str(e), DiagnosticSeverity.ERROR, "KeywordError"))
1585+
15761586
self._cache[name] = (result, self.diagnostics)
15771587

15781588
return result

robotcode/language_server/robotframework/parts/codelens.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,7 @@ async def resolve(self, sender: Any, code_lens: CodeLens) -> Optional[CodeLens]:
111111
name = code_lens.data["name"]
112112
line = code_lens.data["line"]
113113

114-
if (
115-
self.parent.diagnostics.workspace_loaded_event.is_set()
116-
and self.parent.diagnostics.in_get_workspace_diagnostics.is_set()
117-
):
114+
if self.parent.diagnostics.workspace_loaded_event.is_set():
118115
kw_doc = await self.get_keyword_definition_at_line(namespace, name, line)
119116

120117
if kw_doc is not None and not kw_doc.is_error_handler:

robotcode/utils/async_cache.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ async def get(self, func: Callable[..., Awaitable[_T]], *args: Any, **kwargs: An
4848
entry.data = await func(*args, **kwargs)
4949
entry.has_data = True
5050

51-
async with self.lock:
52-
if self._order is not None and self.max_items is not None:
51+
if self._order is not None and self.max_items is not None:
52+
async with self.lock:
5353
self._order.insert(0, key)
5454

5555
if len(self._order) > self.max_items:

robotcode/utils/async_tools.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ def check_canceled_sync() -> bool:
436436
return True
437437

438438

439-
__threadpool_executor = ThreadPoolExecutor(thread_name_prefix="sub_asyncio")
439+
# __threadpool_executor = ThreadPoolExecutor(thread_name_prefix="sub_asyncio")
440440

441441

442442
def run_in_thread(func: Callable[..., _T], /, *args: Any, **kwargs: Any) -> asyncio.Future[_T]:
@@ -447,10 +447,20 @@ def run_in_thread(func: Callable[..., _T], /, *args: Any, **kwargs: Any) -> asyn
447447
ctx = contextvars.copy_context()
448448
func_call = functools.partial(ctx.run, func, *args, **kwargs)
449449

450-
return cast(
451-
"asyncio.Future[_T]",
452-
loop.run_in_executor(__threadpool_executor, cast(Callable[..., _T], func_call)),
453-
)
450+
# return cast(
451+
# "asyncio.Future[_T]",
452+
# # loop.run_in_executor(__threadpool_executor, cast(Callable[..., _T], func_call)),
453+
# loop.run_in_executor(None, cast(Callable[..., _T], func_call)),
454+
# )
455+
456+
executor = ThreadPoolExecutor(max_workers=1, thread_name_prefix="sub_asyncio")
457+
try:
458+
return cast(
459+
"asyncio.Future[_T]",
460+
loop.run_in_executor(executor, cast(Callable[..., _T], func_call)),
461+
)
462+
finally:
463+
executor.shutdown(wait=False)
454464

455465

456466
def run_coroutine_in_thread(
@@ -525,10 +535,16 @@ def done(task: asyncio.Future[_T]) -> None:
525535

526536
@contextlib.asynccontextmanager
527537
async def async_lock(lock: threading.RLock) -> AsyncGenerator[None, None]:
538+
import time
539+
540+
start_time = time.monotonic()
528541
locked = lock.acquire(blocking=False)
529542
while not locked:
543+
if time.monotonic() - start_time >= 10:
544+
raise RuntimeError("Timeout waiting for lock")
545+
530546
await asyncio.sleep(0.001)
531-
locked = lock.acquire(False)
547+
locked = lock.acquire(blocking=False)
532548
try:
533549
yield
534550
finally:
@@ -878,7 +894,16 @@ def create_sub_task(
878894
ct = get_current_future_info()
879895

880896
if loop is not None:
881-
result = loop.create_task(coro, name=name)
897+
if loop == asyncio.get_running_loop():
898+
result = loop.create_task(coro, name=name)
899+
else:
900+
901+
async def s(
902+
lo: asyncio.AbstractEventLoop, c: Coroutine[Any, Any, _T], n: Optional[str]
903+
) -> asyncio.Task[_T]:
904+
return create_sub_task(c, name=n, loop=lo)
905+
906+
return asyncio.run_coroutine_threadsafe(s(loop, coro, name), loop=loop).result()
882907
else:
883908
result = asyncio.create_task(coro, name=name)
884909

0 commit comments

Comments
 (0)