From def63f4608953392ecf6d86e0192a5d1526de5bb Mon Sep 17 00:00:00 2001 From: Tomoto Shimizu Washio Date: Sun, 8 Dec 2024 23:16:07 +0900 Subject: [PATCH 1/3] fix: catch RecursionError in initialization --- fortls/langserver.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fortls/langserver.py b/fortls/langserver.py index b6a1982e..d6aa6acb 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -1489,7 +1489,10 @@ def workspace_init(self): pool.close() pool.join() for path, result in results.items(): - result_obj = result.get() + try: + result_obj = result.get() + except: + result_obj = traceback.format_exc() if isinstance(result_obj, str): self.post_message( f"Initialization failed for file {path}: {result_obj}" From 800cc9c0392903c3ab2349d6c6940fe68efdd39e Mon Sep 17 00:00:00 2001 From: gnikit Date: Sat, 1 Mar 2025 22:33:47 +0000 Subject: [PATCH 2/3] feat: improve Exception handling multiprocessing.Pool does not retain the base exception that caused it to raise. As a result it is not possible under this setup to explicitly capture the RecursionError that is getting raised when Pool attempts to pickle the AST from the parser. --- fortls/langserver.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fortls/langserver.py b/fortls/langserver.py index d6aa6acb..c11f60c4 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -1491,8 +1491,12 @@ def workspace_init(self): for path, result in results.items(): try: result_obj = result.get() - except: - result_obj = traceback.format_exc() + except Exception as e: + result_obj = ( + "An exception has occured while initialising the workspace.\n" + f"Exception({(type(e))}): {e}\n" + + f"Traceback: {traceback.format_exc()}" + ) if isinstance(result_obj, str): self.post_message( f"Initialization failed for file {path}: {result_obj}" From f00b8ea5625334e96d34881b07884d2ee9c0d4f5 Mon Sep 17 00:00:00 2001 From: gnikit Date: Sat, 1 Mar 2025 22:34:48 +0000 Subject: [PATCH 3/3] test: add unit test that specifically checks the workspace_init exception handling mechanism --- test/test_server_init.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 test/test_server_init.py diff --git a/test/test_server_init.py b/test/test_server_init.py new file mode 100644 index 00000000..9573f498 --- /dev/null +++ b/test/test_server_init.py @@ -0,0 +1,32 @@ +import os +import tempfile + +import pytest +from setup_tests import Path, run_request, write_rpc_request + +from fortls.constants import Severity + + +@pytest.fixture() +def setup_tmp_file(): + levels = 2000 + fd, filename = tempfile.mkstemp(suffix=".f90") + try: + with os.fdopen(fd, "w") as tmp: + tmp.write( + "program nested_if\n" + + str("if (.true.) then\n" * levels) + + str("end if\n" * levels) + + "end program nested_if" + ) + yield filename + finally: + os.remove(filename) + + +def test_recursion_error_handling(setup_tmp_file): + root = Path(setup_tmp_file).parent + request_string = write_rpc_request(1, "initialize", {"rootPath": str(root)}) + errcode, results = run_request(request_string) + assert errcode == 0 + assert results[0]["type"] == Severity.error