diff --git a/src/multilspy/language_server.py b/src/multilspy/language_server.py index 665d39c..8ccc0d6 100644 --- a/src/multilspy/language_server.py +++ b/src/multilspy/language_server.py @@ -27,7 +27,7 @@ from .multilspy_exceptions import MultilspyException from .multilspy_utils import PathUtils, FileUtils, TextUtils from pathlib import PurePath -from typing import AsyncIterator, Iterator, List, Dict, Union, Tuple +from typing import AsyncIterator, Iterator, List, Dict, Optional, Union, Tuple from .type_helpers import ensure_all_methods_implemented @@ -656,14 +656,15 @@ class SyncLanguageServer: It is used to communicate with Language Servers of different programming languages. """ - def __init__(self, language_server: LanguageServer) -> None: + def __init__(self, language_server: LanguageServer, timeout: Optional[int] = None): self.language_server = language_server self.loop = None self.loop_thread = None + self.timeout = timeout @classmethod def create( - cls, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str + cls, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str, timeout: Optional[int] = None ) -> "SyncLanguageServer": """ Creates a language specific LanguageServer instance based on the given configuration, and appropriate settings for the programming language. @@ -676,7 +677,7 @@ def create( :return SyncLanguageServer: A language specific LanguageServer instance. """ - return SyncLanguageServer(LanguageServer.create(config, logger, repository_root_path)) + return SyncLanguageServer(LanguageServer.create(config, logger, repository_root_path), timeout=timeout) @contextmanager def open_file(self, relative_file_path: str) -> Iterator[None]: @@ -751,7 +752,7 @@ def request_definition(self, file_path: str, line: int, column: int) -> List[mul """ result = asyncio.run_coroutine_threadsafe( self.language_server.request_definition(file_path, line, column), self.loop - ).result() + ).result(timeout=self.timeout) return result def request_references(self, file_path: str, line: int, column: int) -> List[multilspy_types.Location]: @@ -767,7 +768,7 @@ def request_references(self, file_path: str, line: int, column: int) -> List[mul """ result = asyncio.run_coroutine_threadsafe( self.language_server.request_references(file_path, line, column), self.loop - ).result() + ).result(timeout=self.timeout) return result def request_completions( @@ -786,7 +787,7 @@ def request_completions( result = asyncio.run_coroutine_threadsafe( self.language_server.request_completions(relative_file_path, line, column, allow_incomplete), self.loop, - ).result() + ).result(timeout=self.timeout) return result def request_document_symbols(self, relative_file_path: str) -> Tuple[List[multilspy_types.UnifiedSymbolInformation], Union[List[multilspy_types.TreeRepr], None]]: @@ -800,7 +801,7 @@ def request_document_symbols(self, relative_file_path: str) -> Tuple[List[multil """ result = asyncio.run_coroutine_threadsafe( self.language_server.request_document_symbols(relative_file_path), self.loop - ).result() + ).result(timeout=self.timeout) return result def request_hover(self, relative_file_path: str, line: int, column: int) -> Union[multilspy_types.Hover, None]: @@ -816,5 +817,5 @@ def request_hover(self, relative_file_path: str, line: int, column: int) -> Unio """ result = asyncio.run_coroutine_threadsafe( self.language_server.request_hover(relative_file_path, line, column), self.loop - ).result() + ).result(timeout=self.timeout) return result diff --git a/tests/multilspy/test_sync_multilspy_timeout.py b/tests/multilspy/test_sync_multilspy_timeout.py new file mode 100644 index 0000000..3b7dd4e --- /dev/null +++ b/tests/multilspy/test_sync_multilspy_timeout.py @@ -0,0 +1,34 @@ +""" +This file contains tests for running the Python Language Server: jedi-language-server +""" + +import pytest +from multilspy import SyncLanguageServer +from multilspy.multilspy_config import Language +from tests.test_utils import create_test_context +from pathlib import PurePath +import time + +def test_multilspy_timeout() -> None: + """ + Test timeout error in multilspy + """ + code_language = Language.PYTHON + params = { + "code_language": code_language, + "repo_url": "https://github.com/psf/black/", + "repo_commit": "f3b50e466969f9142393ec32a4b2a383ffbe5f23" + } + with create_test_context(params) as context: + lsp = SyncLanguageServer.create(context.config, context.logger, context.source_directory, timeout=1) + + # Mock the request_definition method to simulate a long running process + async def request_definition(*args, **kwargs): + time.sleep(5) + return [] + + lsp.language_server.request_definition = request_definition + + with lsp.start_server(): + with pytest.raises(TimeoutError): + lsp.request_definition(str(PurePath("src/black/mode.py")), 163, 4)