Skip to content

Commit f6b5b87

Browse files
committed
feat(analyzer): implemented better handling of imports of dynamic libraries
- show also errors on in dynamic library API like in `get_keyword_documentation` and `get_keyword_arguments`
1 parent ce787b2 commit f6b5b87

File tree

2 files changed

+114
-71
lines changed

2 files changed

+114
-71
lines changed

packages/robot/src/robotcode/robot/diagnostics/imports_manager.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,8 @@ def __init__(
577577
weakref.WeakKeyDictionary()
578578
)
579579

580+
self._process_pool_executor: Optional[ProcessPoolExecutor] = None
581+
580582
def __del__(self) -> None:
581583
try:
582584
if self._executor is not None:
@@ -894,12 +896,14 @@ def get_library_meta(
894896
)
895897

896898
if result is not None:
899+
# TODO: use IgnoreSpec instead of this
897900
ignore_arguments = any(
898901
(p.matches(result.name) if result.name is not None else False)
899902
or (p.matches(result.origin) if result.origin is not None else False)
900903
for p in self.ignore_arguments_for_library_patters
901904
)
902905

906+
# TODO: use IgnoreSpec instead of this
903907
if any(
904908
(p.matches(result.name) if result.name is not None else False)
905909
or (p.matches(result.origin) if result.origin is not None else False)
@@ -1173,7 +1177,7 @@ def _get_library_libdoc(
11731177
saved_meta = self.data_cache.read_cache_data(CacheSection.LIBRARY, meta_file, LibraryMetaData)
11741178
if saved_meta.has_errors:
11751179
self._logger.debug(
1176-
lambda: "Saved library spec for {name}{args!r} is not used "
1180+
lambda: f"Saved library spec for {name}{args!r} is not used "
11771181
"due to errors in meta data",
11781182
context_name="import",
11791183
)
@@ -1198,6 +1202,9 @@ def _get_library_libdoc(
11981202
self._logger.exception(e)
11991203

12001204
self._logger.debug(lambda: f"Load library in process {name}{args!r}", context_name="import")
1205+
# if self._process_pool_executor is None:
1206+
# self._process_pool_executor = ProcessPoolExecutor(max_workers=1, mp_context=mp.get_context("spawn"))
1207+
# executor = self._process_pool_executor
12011208
executor = ProcessPoolExecutor(max_workers=1, mp_context=mp.get_context("spawn"))
12021209
try:
12031210
try:

packages/robot/src/robotcode/robot/diagnostics/library_doc.py

Lines changed: 106 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,52 @@ def get_robot_library_html_doc_str(
17261726
return output.getvalue()
17271727

17281728

1729+
class _Logger(AbstractLogger):
1730+
def __init__(self) -> None:
1731+
super().__init__()
1732+
self.messages: List[Tuple[str, str, bool]] = []
1733+
1734+
def write(self, message: str, level: str, html: bool = False) -> None:
1735+
self.messages.append((message, level, html))
1736+
1737+
1738+
def _import_test_library(name: str) -> Union[Any, Tuple[Any, str]]:
1739+
with OutputCapturer(library_import=True):
1740+
importer = Importer("test library", LOGGER)
1741+
return importer.import_class_or_module(name, return_source=True)
1742+
1743+
1744+
def _get_test_library(
1745+
libcode: Any,
1746+
source: str,
1747+
name: str,
1748+
args: Optional[Tuple[Any, ...]] = None,
1749+
variables: Optional[Dict[str, Optional[Any]]] = None,
1750+
create_handlers: bool = True,
1751+
logger: Any = LOGGER,
1752+
) -> Any:
1753+
if get_robot_version() < (7, 0):
1754+
libclass = robot.running.testlibraries._get_lib_class(libcode)
1755+
lib = libclass(libcode, name, args or [], source, logger, variables)
1756+
if create_handlers:
1757+
lib.create_handlers()
1758+
else:
1759+
lib = robot.running.testlibraries.TestLibrary.from_code(
1760+
libcode,
1761+
name,
1762+
source=Path(source),
1763+
args=args or [],
1764+
variables=variables,
1765+
create_keywords=create_handlers,
1766+
logger=logger,
1767+
)
1768+
1769+
return lib
1770+
1771+
1772+
_T = TypeVar("_T")
1773+
1774+
17291775
def get_library_doc(
17301776
name: str,
17311777
args: Optional[Tuple[Any, ...]] = None,
@@ -1734,45 +1780,6 @@ def get_library_doc(
17341780
command_line_variables: Optional[Dict[str, Optional[Any]]] = None,
17351781
variables: Optional[Dict[str, Optional[Any]]] = None,
17361782
) -> LibraryDoc:
1737-
class Logger(AbstractLogger):
1738-
def __init__(self) -> None:
1739-
super().__init__()
1740-
self.messages: List[Tuple[str, str, bool]] = []
1741-
1742-
def write(self, message: str, level: str, html: bool = False) -> None:
1743-
self.messages.append((message, level, html))
1744-
1745-
def import_test_library(name: str) -> Union[Any, Tuple[Any, str]]:
1746-
with OutputCapturer(library_import=True):
1747-
importer = Importer("test library", LOGGER)
1748-
return importer.import_class_or_module(name, return_source=True)
1749-
1750-
def get_test_library(
1751-
libcode: Any,
1752-
source: str,
1753-
name: str,
1754-
args: Optional[Tuple[Any, ...]] = None,
1755-
variables: Optional[Dict[str, Optional[Any]]] = None,
1756-
create_handlers: bool = True,
1757-
logger: Any = LOGGER,
1758-
) -> Any:
1759-
if get_robot_version() < (7, 0):
1760-
libclass = robot.running.testlibraries._get_lib_class(libcode)
1761-
lib = libclass(libcode, name, args or [], source, logger, variables)
1762-
if create_handlers:
1763-
lib.create_handlers()
1764-
else:
1765-
lib = robot.running.testlibraries.TestLibrary.from_code(
1766-
libcode,
1767-
name,
1768-
source=Path(source),
1769-
args=args or [],
1770-
variables=variables,
1771-
create_keywords=create_handlers,
1772-
logger=logger,
1773-
)
1774-
1775-
return lib
17761783

17771784
with _std_capture() as std_capturer:
17781785
import_name, robot_variables = _find_library_internal(
@@ -1796,7 +1803,7 @@ def get_test_library(
17961803

17971804
source = None
17981805
try:
1799-
libcode, source = import_test_library(import_name)
1806+
libcode, source = _import_test_library(import_name)
18001807
except (SystemExit, KeyboardInterrupt):
18011808
raise
18021809
except BaseException as e:
@@ -1836,7 +1843,7 @@ def get_test_library(
18361843

18371844
lib = None
18381845
try:
1839-
lib = get_test_library(
1846+
lib = _get_test_library(
18401847
libcode,
18411848
source,
18421849
library_name,
@@ -1866,7 +1873,7 @@ def get_test_library(
18661873

18671874
if args:
18681875
try:
1869-
lib = get_test_library(libcode, source, library_name, (), create_handlers=False)
1876+
lib = _get_test_library(libcode, source, library_name, (), create_handlers=False)
18701877
if get_robot_version() < (7, 0):
18711878
_ = lib.get_instance()
18721879
else:
@@ -1881,6 +1888,10 @@ def get_test_library(
18811888
libdoc = LibraryDoc(
18821889
name=library_name,
18831890
source=real_source,
1891+
line_no=lib.lineno if lib is not None else -1,
1892+
version=str(lib.version) if lib is not None else "",
1893+
scope=str(lib.scope) if lib is not None else ROBOT_DEFAULT_SCOPE,
1894+
doc_format=(str(lib.doc_format) or ROBOT_DOC_FORMAT) if lib is not None else ROBOT_DOC_FORMAT,
18841895
module_spec=(
18851896
module_spec
18861897
if module_spec is not None
@@ -1889,16 +1900,33 @@ def get_test_library(
18891900
else None
18901901
),
18911902
python_path=sys.path,
1892-
line_no=lib.lineno if lib is not None else -1,
1893-
doc=str(lib.doc) if lib is not None else "",
1894-
version=str(lib.version) if lib is not None else "",
1895-
scope=str(lib.scope) if lib is not None else ROBOT_DEFAULT_SCOPE,
1896-
doc_format=(str(lib.doc_format) or ROBOT_DOC_FORMAT) if lib is not None else ROBOT_DOC_FORMAT,
18971903
member_name=module_spec.member_name if module_spec is not None else None,
18981904
)
18991905

19001906
if lib is not None:
19011907
try:
1908+
1909+
def _get(handler: Callable[[], _T]) -> Optional[_T]:
1910+
try:
1911+
return handler()
1912+
except (SystemExit, KeyboardInterrupt):
1913+
raise
1914+
except BaseException as e:
1915+
errors.append(
1916+
error_from_exception(
1917+
e,
1918+
source or module_spec.origin if module_spec is not None else None,
1919+
(
1920+
1
1921+
if source is not None or module_spec is not None and module_spec.origin is not None
1922+
else None
1923+
),
1924+
)
1925+
)
1926+
return None
1927+
1928+
libdoc.doc = _get(lambda: str(lib.doc) if lib is not None else "") or ""
1929+
19021930
if get_robot_version() < (7, 0):
19031931
libdoc.has_listener = lib.has_listener
19041932

@@ -1928,10 +1956,10 @@ def get_test_library(
19281956
keywords=[
19291957
KeywordDoc(
19301958
name=libdoc.name,
1931-
arguments=[ArgumentInfo.from_robot(a) for a in kw[0].args],
1932-
doc=kw[0].doc,
1933-
tags=list(kw[0].tags),
1934-
source=kw[0].source,
1959+
arguments=_get(lambda: [ArgumentInfo.from_robot(a) for a in kw[0].args]) or [],
1960+
doc=_get(lambda: kw[0].doc) or "",
1961+
tags=_get(lambda: list(kw[0].tags)) or [],
1962+
source=_get(lambda: kw[0].source) or "",
19351963
line_no=kw[0].lineno if kw[0].lineno is not None else -1,
19361964
col_offset=-1,
19371965
end_col_offset=-1,
@@ -1942,20 +1970,23 @@ def get_test_library(
19421970
longname=f"{libdoc.name}.{kw[0].name}",
19431971
doc_format=str(lib.doc_format) or ROBOT_DOC_FORMAT,
19441972
is_initializer=True,
1945-
arguments_spec=ArgumentSpec.from_robot_argument_spec(
1946-
kw[1].arguments if get_robot_version() < (7, 0) else kw[1].args
1973+
arguments_spec=_get(
1974+
lambda: ArgumentSpec.from_robot_argument_spec(
1975+
kw[1].arguments if get_robot_version() < (7, 0) else kw[1].args
1976+
)
19471977
),
19481978
)
19491979
for kw in init_keywords
19501980
]
19511981
)
19521982

1953-
logger = Logger()
1954-
lib.logger = logger
1983+
logger = _Logger()
19551984

19561985
if get_robot_version() < (7, 0):
1986+
lib.logger = logger
19571987
lib.create_handlers()
19581988
else:
1989+
lib._logger = logger
19591990
lib.create_keywords()
19601991

19611992
for m in logger.messages:
@@ -1988,10 +2019,10 @@ def get_args_to_process(libdoc_name: str, kw_name: str) -> Any:
19882019
keywords=[
19892020
KeywordDoc(
19902021
name=kw[0].name,
1991-
arguments=[ArgumentInfo.from_robot(a) for a in kw[0].args],
1992-
doc=kw[0].doc,
1993-
tags=list(kw[0].tags),
1994-
source=kw[0].source,
2022+
arguments=_get(lambda: [ArgumentInfo.from_robot(a) for a in kw[0].args]) or [],
2023+
doc=_get(lambda: kw[0].doc) or "",
2024+
tags=_get(lambda: list(kw[0].tags)) or [],
2025+
source=_get(lambda: kw[0].source) or "",
19952026
line_no=kw[0].lineno if kw[0].lineno is not None else -1,
19962027
col_offset=-1,
19972028
end_col_offset=-1,
@@ -2006,21 +2037,26 @@ def get_args_to_process(libdoc_name: str, kw_name: str) -> Any:
20062037
is_registered_run_keyword=RUN_KW_REGISTER.is_run_keyword(libdoc.name, kw[0].name),
20072038
args_to_process=get_args_to_process(libdoc.name, kw[0].name),
20082039
deprecated=kw[0].deprecated,
2009-
arguments_spec=(
2010-
ArgumentSpec.from_robot_argument_spec(
2011-
kw[1].arguments if get_robot_version() < (7, 0) else kw[1].args
2040+
arguments_spec=_get(
2041+
lambda: (
2042+
ArgumentSpec.from_robot_argument_spec(
2043+
kw[1].arguments if get_robot_version() < (7, 0) else kw[1].args
2044+
)
2045+
if not kw[1].is_error_handler
2046+
else None
20122047
)
2013-
if not kw[1].is_error_handler
2014-
else None
20152048
),
2016-
return_type=(
2017-
(
2018-
str(kw[1].args.return_type)
2019-
if kw[1].args.return_type is not None and kw[1].args.return_type is not type(None)
2049+
return_type=_get(
2050+
lambda: (
2051+
(
2052+
str(kw[1].args.return_type)
2053+
if kw[1].args.return_type is not None
2054+
and kw[1].args.return_type is not type(None)
2055+
else None
2056+
)
2057+
if get_robot_version() >= (7, 0)
20202058
else None
20212059
)
2022-
if get_robot_version() >= (7, 0)
2023-
else None
20242060
),
20252061
)
20262062
for kw in keyword_docs

0 commit comments

Comments
 (0)