From cb11ff9f53cc60039c9b09630beec6d2c0a5b8b9 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Tue, 19 Dec 2023 11:44:59 +0100 Subject: [PATCH 01/12] [Rope] make cache generation non-blocking --- pylsp/plugins/rope_autoimport.py | 62 ++++++++++++++++++++++---------- test/plugins/test_autoimport.py | 4 +++ 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/pylsp/plugins/rope_autoimport.py b/pylsp/plugins/rope_autoimport.py index ebcc7070..053a3738 100644 --- a/pylsp/plugins/rope_autoimport.py +++ b/pylsp/plugins/rope_autoimport.py @@ -2,6 +2,7 @@ import logging from typing import Any, Dict, Generator, List, Optional, Set, Union +import threading import parso from jedi import Script @@ -319,20 +320,42 @@ def pylsp_code_actions( return code_actions -def _reload_cache( - config: Config, workspace: Workspace, files: Optional[List[Document]] = None -): - memory: bool = config.plugin_settings("rope_autoimport").get("memory", False) - rope_config = config.settings().get("rope", {}) - autoimport = workspace._rope_autoimport(rope_config, memory) - task_handle = PylspTaskHandle(workspace) - resources: Optional[List[Resource]] = ( - None - if files is None - else [document._rope_resource(rope_config) for document in files] - ) - autoimport.generate_cache(task_handle=task_handle, resources=resources) - autoimport.generate_modules_cache(task_handle=task_handle) +class AutoimportCache: + def __init__(self): + self.thread = None + + def reload_cache( + self, + config: Config, + workspace: Workspace, + files: Optional[List[Document]] = None, + ): + if self.thread and self.thread.is_alive(): + self.thread.join() + + memory: bool = config.plugin_settings("rope_autoimport").get("memory", False) + rope_config = config.settings().get("rope", {}) + autoimport = workspace._rope_autoimport(rope_config, memory) + task_handle = PylspTaskHandle(workspace) + resources: Optional[List[Resource]] = ( + None + if files is None + else [document._rope_resource(rope_config) for document in files] + ) + + self.thread = threading.Thread( + target=self.make_cache, args=(autoimport, task_handle, resources) + ) + self.thread.start() + + def make_cache( + self, + autoimport: AutoImport, + task_handle: PylspTaskHandle, + resources: Optional[List[Resource]], + ): + autoimport.generate_cache(task_handle=task_handle, resources=resources) + autoimport.generate_modules_cache(task_handle=task_handle) @hookimpl @@ -341,7 +364,7 @@ def pylsp_initialize(config: Config, workspace: Workspace): Generates the cache for local and global items. """ - _reload_cache(config, workspace) + cache.reload_cache(config, workspace) @hookimpl @@ -350,13 +373,13 @@ def pylsp_document_did_open(config: Config, workspace: Workspace): Generates the cache for local and global items. """ - _reload_cache(config, workspace) + cache.reload_cache(config, workspace) @hookimpl def pylsp_document_did_save(config: Config, workspace: Workspace, document: Document): """Update the names associated with this document.""" - _reload_cache(config, workspace, [document]) + cache.reload_cache(config, workspace, [document]) @hookimpl @@ -368,6 +391,9 @@ def pylsp_workspace_configuration_changed(config: Config, workspace: Workspace): Generates the cache for local and global items. """ if config.plugin_settings("rope_autoimport").get("enabled", False): - _reload_cache(config, workspace) + cache.reload_cache(config, workspace) else: log.debug("autoimport: Skipping cache reload.") + + +cache: AutoimportCache = AutoimportCache() diff --git a/test/plugins/test_autoimport.py b/test/plugins/test_autoimport.py index 9f02965b..ab68a994 100644 --- a/test/plugins/test_autoimport.py +++ b/test/plugins/test_autoimport.py @@ -15,6 +15,7 @@ from pylsp.plugins.rope_autoimport import ( _get_score, _should_insert, + cache, get_name_or_module, get_names, ) @@ -58,6 +59,7 @@ def autoimport_workspace(tmp_path_factory) -> Workspace: } ) pylsp_initialize(workspace._config, workspace) + wait_for_condition(lambda: not cache.thread.is_alive()) yield workspace workspace.close() @@ -293,6 +295,7 @@ def test_autoimport_code_actions_and_completions_for_notebook_document( } }, ) + from time import sleep with patch.object(server._endpoint, "notify") as mock_notify: # Expectations: @@ -312,6 +315,7 @@ def test_autoimport_code_actions_and_completions_for_notebook_document( ) assert rope_autoimport_settings.get("completions", {}).get("enabled", False) is True assert rope_autoimport_settings.get("memory", False) is True + wait_for_condition(lambda: not cache.thread.is_alive()) # 1. quick_fixes = server.code_actions("cell_1_uri", {}, make_context("os", 0, 0, 2)) From bf6ebdebf64ac66ec8c07f20a9310443f554d7af Mon Sep 17 00:00:00 2001 From: tkrabel Date: Tue, 19 Dec 2023 17:06:58 +0100 Subject: [PATCH 02/12] change unit test --- test/plugins/test_autoimport.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/plugins/test_autoimport.py b/test/plugins/test_autoimport.py index ab68a994..3c3681d6 100644 --- a/test/plugins/test_autoimport.py +++ b/test/plugins/test_autoimport.py @@ -295,8 +295,6 @@ def test_autoimport_code_actions_and_completions_for_notebook_document( } }, ) - from time import sleep - with patch.object(server._endpoint, "notify") as mock_notify: # Expectations: # 1. We receive an autoimport suggestion for "os" in the first cell because @@ -308,7 +306,9 @@ def test_autoimport_code_actions_and_completions_for_notebook_document( # 4. We receive an autoimport suggestion for "sys" because it's not already imported. # 5. If diagnostics doesn't contain "undefined name ...", we send empty quick fix suggestions. send_notebook_did_open(client, ["os", "import os\nos", "os", "sys"]) - wait_for_condition(lambda: mock_notify.call_count >= 3) + wait_for_condition(lambda: mock_notify.call_count >= 4) + # We received diagnostics messages for every cell + assert all('textDocument/publishDiagnostics' in c.args for c in mock_notify.call_args_list) rope_autoimport_settings = server.workspace._config.plugin_settings( "rope_autoimport" From 5a82344183a3ae09667ba53362e2e14ac405fe58 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Wed, 20 Dec 2023 12:00:27 +0100 Subject: [PATCH 03/12] wip --- pylsp/plugins/rope_autoimport.py | 39 +++++++++++++++++++------------- pylsp/python_lsp.py | 2 +- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/pylsp/plugins/rope_autoimport.py b/pylsp/plugins/rope_autoimport.py index 053a3738..21be2a7a 100644 --- a/pylsp/plugins/rope_autoimport.py +++ b/pylsp/plugins/rope_autoimport.py @@ -192,7 +192,7 @@ def pylsp_completions( not config.plugin_settings("rope_autoimport") .get("completions", {}) .get("enabled", True) - ): + ) or cache.is_blocked(): return [] line = document.lines[position["line"]] @@ -284,7 +284,7 @@ def pylsp_code_actions( not config.plugin_settings("rope_autoimport") .get("code_actions", {}) .get("enabled", True) - ): + ) or cache.is_blocked(): return [] log.debug(f"textDocument/codeAction: {document} {range} {context}") @@ -336,26 +336,31 @@ def reload_cache( memory: bool = config.plugin_settings("rope_autoimport").get("memory", False) rope_config = config.settings().get("rope", {}) autoimport = workspace._rope_autoimport(rope_config, memory) - task_handle = PylspTaskHandle(workspace) resources: Optional[List[Resource]] = ( None if files is None else [document._rope_resource(rope_config) for document in files] ) - self.thread = threading.Thread( - target=self.make_cache, args=(autoimport, task_handle, resources) + target=self._reload_cache, args=(workspace, autoimport, resources) ) self.thread.start() - def make_cache( + def _reload_cache( self, + workspace: Workspace, autoimport: AutoImport, - task_handle: PylspTaskHandle, - resources: Optional[List[Resource]], + resources: Optional[List[Resource]] = None, ): - autoimport.generate_cache(task_handle=task_handle, resources=resources) - autoimport.generate_modules_cache(task_handle=task_handle) + # NOTE: task_handle bombards FE with ~10k $/progress messages while it builds the cache: + # task_handle = PylspTaskHandle(workspace) + # autoimport.generate_cache(task_handle=task_handle, resources=resources) + # autoimport.generate_modules_cache(task_handle=task_handle) + autoimport.generate_cache(resources=resources) + autoimport.generate_modules_cache() + + def is_blocked(self): + return (cache.thread is None) or (cache.thread and cache.thread.is_alive()) @hookimpl @@ -367,13 +372,15 @@ def pylsp_initialize(config: Config, workspace: Workspace): cache.reload_cache(config, workspace) -@hookimpl -def pylsp_document_did_open(config: Config, workspace: Workspace): - """Initialize AutoImport. +# NOTE: I added this guy for notebook document support to make sure the cache is kept +# up to date when the user installed package in the notebook +# @hookimpl +# def pylsp_document_did_open(config: Config, workspace: Workspace): +# """Initialize AutoImport. - Generates the cache for local and global items. - """ - cache.reload_cache(config, workspace) +# Generates the cache for local and global items. +# """ +# cache.reload_cache(config, workspace) @hookimpl diff --git a/pylsp/python_lsp.py b/pylsp/python_lsp.py index a31e7612..700493e3 100644 --- a/pylsp/python_lsp.py +++ b/pylsp/python_lsp.py @@ -163,7 +163,6 @@ class PythonLSPServer(MethodDispatcher): """Implementation of the Microsoft VSCode Language Server Protocol https://github.com/Microsoft/language-server-protocol/blob/master/versions/protocol-1-x.md """ - # pylint: disable=too-many-public-methods,redefined-builtin def __init__( @@ -349,6 +348,7 @@ def m_initialize( workspace_config.update(self.config._settings) self.workspaces[uri] = Workspace(uri, self._endpoint, workspace_config) + self._dispatchers = self._hook("pylsp_dispatchers") self._hook("pylsp_initialize") From 45dde2f7001b3cbba17f4e5839f4b7ab8ca86624 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Wed, 20 Dec 2023 18:49:33 +0100 Subject: [PATCH 04/12] black --- pylsp/python_lsp.py | 2 +- test/plugins/test_autoimport.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pylsp/python_lsp.py b/pylsp/python_lsp.py index 700493e3..a31e7612 100644 --- a/pylsp/python_lsp.py +++ b/pylsp/python_lsp.py @@ -163,6 +163,7 @@ class PythonLSPServer(MethodDispatcher): """Implementation of the Microsoft VSCode Language Server Protocol https://github.com/Microsoft/language-server-protocol/blob/master/versions/protocol-1-x.md """ + # pylint: disable=too-many-public-methods,redefined-builtin def __init__( @@ -348,7 +349,6 @@ def m_initialize( workspace_config.update(self.config._settings) self.workspaces[uri] = Workspace(uri, self._endpoint, workspace_config) - self._dispatchers = self._hook("pylsp_dispatchers") self._hook("pylsp_initialize") diff --git a/test/plugins/test_autoimport.py b/test/plugins/test_autoimport.py index 3c3681d6..c9a40006 100644 --- a/test/plugins/test_autoimport.py +++ b/test/plugins/test_autoimport.py @@ -308,7 +308,10 @@ def test_autoimport_code_actions_and_completions_for_notebook_document( send_notebook_did_open(client, ["os", "import os\nos", "os", "sys"]) wait_for_condition(lambda: mock_notify.call_count >= 4) # We received diagnostics messages for every cell - assert all('textDocument/publishDiagnostics' in c.args for c in mock_notify.call_args_list) + assert all( + "textDocument/publishDiagnostics" in c.args + for c in mock_notify.call_args_list + ) rope_autoimport_settings = server.workspace._config.plugin_settings( "rope_autoimport" From 20f4a6fbf85ab83c51ba40e4db3005cfbf99a061 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Thu, 21 Dec 2023 17:59:44 +0100 Subject: [PATCH 05/12] wip --- pylsp/plugins/_rope_task_handle.py | 19 ++++++ pylsp/plugins/rope_autoimport.py | 97 ++++++++++++++---------------- 2 files changed, 65 insertions(+), 51 deletions(-) diff --git a/pylsp/plugins/_rope_task_handle.py b/pylsp/plugins/_rope_task_handle.py index ed93a058..c6eb7b17 100644 --- a/pylsp/plugins/_rope_task_handle.py +++ b/pylsp/plugins/_rope_task_handle.py @@ -1,6 +1,9 @@ from __future__ import annotations import logging +import time +from functools import wraps + from typing import Callable, ContextManager, List, Optional, Sequence from rope.base.taskhandle import BaseJobSet, BaseTaskHandle @@ -11,6 +14,21 @@ Report = Callable[[str, int], None] +def throttle(seconds=1): + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + if not hasattr(wrapper, "last_call"): + wrapper.last_call = 0 + if time.time() - wrapper.last_call >= seconds: + wrapper.last_call = time.time() + return func(*args, **kwargs) + + return wrapper + + return decorator + + class PylspJobSet(BaseJobSet): count: int = 0 done: int = 0 @@ -55,6 +73,7 @@ def increment(self) -> None: self.count += 1 self._report() + @throttle(0.5) def _report(self): percent = int(self.get_percent_done()) message = f"{self.job_name} {self.done}/{self.count}" diff --git a/pylsp/plugins/rope_autoimport.py b/pylsp/plugins/rope_autoimport.py index 21be2a7a..80681e6e 100644 --- a/pylsp/plugins/rope_autoimport.py +++ b/pylsp/plugins/rope_autoimport.py @@ -26,6 +26,46 @@ MAX_RESULTS_CODE_ACTIONS = 5 +class AutoimportCache: + def __init__(self): + self.thread = None + + def reload_cache( + self, + config: Config, + workspace: Workspace, + files: Optional[List[Document]] = None, + ): + if self.thread and self.thread.is_alive(): + return + + memory: bool = config.plugin_settings("rope_autoimport").get("memory", False) + rope_config = config.settings().get("rope", {}) + autoimport = workspace._rope_autoimport(rope_config, memory) + resources: Optional[List[Resource]] = ( + None + if files is None + else [document._rope_resource(rope_config) for document in files] + ) + self.thread = threading.Thread( + target=self._reload_cache, args=(workspace, autoimport, resources) + ) + self.thread.start() + + def _reload_cache( + self, + workspace: Workspace, + autoimport: AutoImport, + resources: Optional[List[Resource]] = None, + ): + task_handle = PylspTaskHandle(workspace) + autoimport.generate_cache(task_handle=task_handle, resources=resources) + autoimport.generate_modules_cache(task_handle=task_handle) + + def is_blocked(self): + return (cache.thread is None) or (cache.thread and cache.thread.is_alive()) + + @hookimpl def pylsp_settings() -> Dict[str, Dict[str, Dict[str, Any]]]: # Default rope_completion to disabled @@ -320,49 +360,6 @@ def pylsp_code_actions( return code_actions -class AutoimportCache: - def __init__(self): - self.thread = None - - def reload_cache( - self, - config: Config, - workspace: Workspace, - files: Optional[List[Document]] = None, - ): - if self.thread and self.thread.is_alive(): - self.thread.join() - - memory: bool = config.plugin_settings("rope_autoimport").get("memory", False) - rope_config = config.settings().get("rope", {}) - autoimport = workspace._rope_autoimport(rope_config, memory) - resources: Optional[List[Resource]] = ( - None - if files is None - else [document._rope_resource(rope_config) for document in files] - ) - self.thread = threading.Thread( - target=self._reload_cache, args=(workspace, autoimport, resources) - ) - self.thread.start() - - def _reload_cache( - self, - workspace: Workspace, - autoimport: AutoImport, - resources: Optional[List[Resource]] = None, - ): - # NOTE: task_handle bombards FE with ~10k $/progress messages while it builds the cache: - # task_handle = PylspTaskHandle(workspace) - # autoimport.generate_cache(task_handle=task_handle, resources=resources) - # autoimport.generate_modules_cache(task_handle=task_handle) - autoimport.generate_cache(resources=resources) - autoimport.generate_modules_cache() - - def is_blocked(self): - return (cache.thread is None) or (cache.thread and cache.thread.is_alive()) - - @hookimpl def pylsp_initialize(config: Config, workspace: Workspace): """Initialize AutoImport. @@ -372,15 +369,13 @@ def pylsp_initialize(config: Config, workspace: Workspace): cache.reload_cache(config, workspace) -# NOTE: I added this guy for notebook document support to make sure the cache is kept -# up to date when the user installed package in the notebook -# @hookimpl -# def pylsp_document_did_open(config: Config, workspace: Workspace): -# """Initialize AutoImport. +@hookimpl +def pylsp_document_did_open(config: Config, workspace: Workspace): + """Initialize AutoImport. -# Generates the cache for local and global items. -# """ -# cache.reload_cache(config, workspace) + Generates the cache for local and global items. + """ + cache.reload_cache(config, workspace) @hookimpl From 5ced8265bcac8580baf8bb6a41f8d995b2ced1cb Mon Sep 17 00:00:00 2001 From: tkrabel Date: Thu, 21 Dec 2023 18:11:34 +0100 Subject: [PATCH 06/12] fix pylint --- pylsp/plugins/_rope_task_handle.py | 2 +- pylsp/plugins/rope_autoimport.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pylsp/plugins/_rope_task_handle.py b/pylsp/plugins/_rope_task_handle.py index c6eb7b17..e31cf446 100644 --- a/pylsp/plugins/_rope_task_handle.py +++ b/pylsp/plugins/_rope_task_handle.py @@ -17,7 +17,7 @@ def throttle(seconds=1): def decorator(func): @wraps(func) - def wrapper(*args, **kwargs): + def wrapper(*args, **kwargs): # pylint: disable=inconsistent-return-statements if not hasattr(wrapper, "last_call"): wrapper.last_call = 0 if time.time() - wrapper.last_call >= seconds: diff --git a/pylsp/plugins/rope_autoimport.py b/pylsp/plugins/rope_autoimport.py index 80681e6e..30bf85b4 100644 --- a/pylsp/plugins/rope_autoimport.py +++ b/pylsp/plugins/rope_autoimport.py @@ -27,6 +27,8 @@ class AutoimportCache: + """Handles the cache creation.""" + def __init__(self): self.thread = None @@ -47,6 +49,8 @@ def reload_cache( if files is None else [document._rope_resource(rope_config) for document in files] ) + # Creating the cache may take 10-20s for a environment with 5k python modules. That's + # why we decided to move cache creation into its own thread. self.thread = threading.Thread( target=self._reload_cache, args=(workspace, autoimport, resources) ) From a2a52beab2dae345b4af51b113bfbe6456972161 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Thu, 21 Dec 2023 18:31:31 +0100 Subject: [PATCH 07/12] make unit test work on windows! --- pylsp/plugins/rope_autoimport.py | 21 +++++++++++++-------- test/plugins/test_autoimport.py | 3 +-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pylsp/plugins/rope_autoimport.py b/pylsp/plugins/rope_autoimport.py index 30bf85b4..f1e5ca70 100644 --- a/pylsp/plugins/rope_autoimport.py +++ b/pylsp/plugins/rope_autoimport.py @@ -37,8 +37,9 @@ def reload_cache( config: Config, workspace: Workspace, files: Optional[List[Document]] = None, + single_thread: Optional[bool] = False, ): - if self.thread and self.thread.is_alive(): + if self.is_blocked(): return memory: bool = config.plugin_settings("rope_autoimport").get("memory", False) @@ -49,12 +50,16 @@ def reload_cache( if files is None else [document._rope_resource(rope_config) for document in files] ) - # Creating the cache may take 10-20s for a environment with 5k python modules. That's - # why we decided to move cache creation into its own thread. - self.thread = threading.Thread( - target=self._reload_cache, args=(workspace, autoimport, resources) - ) - self.thread.start() + + if single_thread: + self._reload_cache(workspace, autoimport, resources) + else: + # Creating the cache may take 10-20s for a environment with 5k python modules. That's + # why we decided to move cache creation into its own thread. + self.thread = threading.Thread( + target=self._reload_cache, args=(workspace, autoimport, resources) + ) + self.thread.start() def _reload_cache( self, @@ -67,7 +72,7 @@ def _reload_cache( autoimport.generate_modules_cache(task_handle=task_handle) def is_blocked(self): - return (cache.thread is None) or (cache.thread and cache.thread.is_alive()) + return cache.thread and cache.thread.is_alive() @hookimpl diff --git a/test/plugins/test_autoimport.py b/test/plugins/test_autoimport.py index c9a40006..3dcec2b2 100644 --- a/test/plugins/test_autoimport.py +++ b/test/plugins/test_autoimport.py @@ -58,8 +58,7 @@ def autoimport_workspace(tmp_path_factory) -> Workspace: } } ) - pylsp_initialize(workspace._config, workspace) - wait_for_condition(lambda: not cache.thread.is_alive()) + cache.reload_cache(workspace._config, workspace, single_thread=True) yield workspace workspace.close() From 316ac23bee6b26f47f97fea0d8fa9a75f140103d Mon Sep 17 00:00:00 2001 From: tkrabel Date: Thu, 21 Dec 2023 18:34:14 +0100 Subject: [PATCH 08/12] fix pylint --- test/plugins/test_autoimport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/plugins/test_autoimport.py b/test/plugins/test_autoimport.py index 3dcec2b2..6d3eeb50 100644 --- a/test/plugins/test_autoimport.py +++ b/test/plugins/test_autoimport.py @@ -22,7 +22,6 @@ from pylsp.plugins.rope_autoimport import ( pylsp_completions as pylsp_autoimport_completions, ) -from pylsp.plugins.rope_autoimport import pylsp_initialize from pylsp.workspace import Workspace From 2f503ef3f63abe142b442d74c06fb33f87b23e85 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Fri, 22 Dec 2023 10:20:52 +0100 Subject: [PATCH 09/12] This one is for Murmel. Merry Xmas --- pylsp/_utils.py | 18 ++++++++++++++++++ pylsp/plugins/_rope_task_handle.py | 18 +----------------- pylsp/plugins/rope_autoimport.py | 2 +- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/pylsp/_utils.py b/pylsp/_utils.py index 9d393b92..5850a985 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -12,6 +12,8 @@ import docstring_to_markdown import jedi +import time +from functools import wraps JEDI_VERSION = jedi.__version__ @@ -55,6 +57,22 @@ def run(): return wrapper +def throttle(seconds=1): + """Throttles calls to a function evey `seconds` seconds.""" + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): # pylint: disable=inconsistent-return-statements + if not hasattr(wrapper, "last_call"): + wrapper.last_call = 0 + if time.time() - wrapper.last_call >= seconds: + wrapper.last_call = time.time() + return func(*args, **kwargs) + + return wrapper + + return decorator + + def find_parents(root, path, names): """Find files matching the given names relative to the given path. diff --git a/pylsp/plugins/_rope_task_handle.py b/pylsp/plugins/_rope_task_handle.py index e31cf446..841d6fee 100644 --- a/pylsp/plugins/_rope_task_handle.py +++ b/pylsp/plugins/_rope_task_handle.py @@ -1,34 +1,18 @@ from __future__ import annotations import logging -import time -from functools import wraps from typing import Callable, ContextManager, List, Optional, Sequence from rope.base.taskhandle import BaseJobSet, BaseTaskHandle from pylsp.workspace import Workspace +from pylsp._utils import throttle log = logging.getLogger(__name__) Report = Callable[[str, int], None] -def throttle(seconds=1): - def decorator(func): - @wraps(func) - def wrapper(*args, **kwargs): # pylint: disable=inconsistent-return-statements - if not hasattr(wrapper, "last_call"): - wrapper.last_call = 0 - if time.time() - wrapper.last_call >= seconds: - wrapper.last_call = time.time() - return func(*args, **kwargs) - - return wrapper - - return decorator - - class PylspJobSet(BaseJobSet): count: int = 0 done: int = 0 diff --git a/pylsp/plugins/rope_autoimport.py b/pylsp/plugins/rope_autoimport.py index f1e5ca70..b3626408 100644 --- a/pylsp/plugins/rope_autoimport.py +++ b/pylsp/plugins/rope_autoimport.py @@ -72,7 +72,7 @@ def _reload_cache( autoimport.generate_modules_cache(task_handle=task_handle) def is_blocked(self): - return cache.thread and cache.thread.is_alive() + return self.thread and self.thread.is_alive() @hookimpl From 84f3dd66a2797c3f6429023df03a29e698e83177 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Fri, 22 Dec 2023 10:36:28 +0100 Subject: [PATCH 10/12] fix pylint --- pylsp/_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylsp/_utils.py b/pylsp/_utils.py index 5850a985..0706e67e 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -10,10 +10,10 @@ import threading from typing import List, Optional -import docstring_to_markdown -import jedi import time from functools import wraps +import docstring_to_markdown +import jedi JEDI_VERSION = jedi.__version__ From 729eb5e52714e404b8e5bfe5b4bf80314ab666b3 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Fri, 22 Dec 2023 10:38:07 +0100 Subject: [PATCH 11/12] black --- pylsp/_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylsp/_utils.py b/pylsp/_utils.py index 0706e67e..7898ff54 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -59,6 +59,7 @@ def run(): def throttle(seconds=1): """Throttles calls to a function evey `seconds` seconds.""" + def decorator(func): @wraps(func) def wrapper(*args, **kwargs): # pylint: disable=inconsistent-return-statements From 4af136ced3c3d666f5d831b574db20c070680ee8 Mon Sep 17 00:00:00 2001 From: tkrabel Date: Fri, 22 Dec 2023 19:25:32 +0100 Subject: [PATCH 12/12] address comments --- pylsp/_utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pylsp/_utils.py b/pylsp/_utils.py index 7898ff54..f694a36a 100644 --- a/pylsp/_utils.py +++ b/pylsp/_utils.py @@ -8,10 +8,9 @@ import pathlib import re import threading +import time from typing import List, Optional -import time -from functools import wraps import docstring_to_markdown import jedi @@ -61,7 +60,7 @@ def throttle(seconds=1): """Throttles calls to a function evey `seconds` seconds.""" def decorator(func): - @wraps(func) + @functools.wraps(func) def wrapper(*args, **kwargs): # pylint: disable=inconsistent-return-statements if not hasattr(wrapper, "last_call"): wrapper.last_call = 0