From 69c54b480dbfc64d397ecc4c6f943b4cf1d1af33 Mon Sep 17 00:00:00 2001 From: Jack Cherng Date: Mon, 9 Sep 2024 04:10:01 +0800 Subject: [PATCH] fix: libs in .venv should be preferred over in dev_environment When I am doing a py38 ST plugin, I found that the "types-requests" stub is never been used and the "requests" lib for ST is used... Signed-off-by: Jack Cherng --- plugin/client.py | 12 +++---- plugin/dev_environment/helpers.py | 3 ++ plugin/dev_environment/impl/blender.py | 2 +- plugin/dev_environment/impl/gdb.py | 2 +- plugin/dev_environment/impl/sublime_text.py | 2 +- plugin/dev_environment/interfaces.py | 36 +++++++++++++++++---- plugin/utils.py | 5 ++- plugin/virtual_env/venv_info.py | 26 +++++++++++++-- 8 files changed, 69 insertions(+), 19 deletions(-) diff --git a/plugin/client.py b/plugin/client.py index 09ec530..63fe27a 100644 --- a/plugin/client.py +++ b/plugin/client.py @@ -229,6 +229,12 @@ def update_venv_info( ) -> None: window_attr = cls.window_attrs[window] + def _update_simple_python_path() -> None: + window_attr.simple_python_executable = None + + if python_path := first_true(("py", "python3", "python"), pred=shutil.which): + window_attr.simple_python_executable = Path(python_path) + def _update_venv_info() -> None: window_attr.venv_info = None @@ -247,11 +253,5 @@ def _update_venv_info() -> None: window_attr.venv_info = venv_info return - def _update_simple_python_path() -> None: - window_attr.simple_python_executable = None - - if python_path := first_true(("py", "python3", "python"), pred=shutil.which): - window_attr.simple_python_executable = Path(python_path) - _update_simple_python_path() _update_venv_info() diff --git a/plugin/dev_environment/helpers.py b/plugin/dev_environment/helpers.py index c245409..3724c3c 100644 --- a/plugin/dev_environment/helpers.py +++ b/plugin/dev_environment/helpers.py @@ -5,6 +5,7 @@ from more_itertools import first_true +from ..virtual_env.venv_info import BaseVenvInfo from .impl import ( BlenderDevEnvironmentHandler, GdbDevEnvironmentHandler, @@ -27,11 +28,13 @@ def get_dev_environment_handler( *, server_dir: str | Path, workspace_folders: Sequence[str], + venv_info: BaseVenvInfo | None = None, ) -> BaseDevEnvironmentHandler | None: if handler_cls := find_dev_environment_handler_class(dev_environment): return handler_cls( server_dir=server_dir, workspace_folders=workspace_folders, + venv_info=venv_info, ) return None diff --git a/plugin/dev_environment/impl/blender.py b/plugin/dev_environment/impl/blender.py index c33b0b0..ad226f9 100644 --- a/plugin/dev_environment/impl/blender.py +++ b/plugin/dev_environment/impl/blender.py @@ -11,7 +11,7 @@ class BlenderDevEnvironmentHandler(BaseDevEnvironmentHandler): - def handle(self, *, settings: DottedDict) -> None: + def handle_(self, *, settings: DottedDict) -> None: self._inject_extra_paths(settings=settings, paths=self.find_paths(settings)) @classmethod diff --git a/plugin/dev_environment/impl/gdb.py b/plugin/dev_environment/impl/gdb.py index ea3faae..13eb9f3 100644 --- a/plugin/dev_environment/impl/gdb.py +++ b/plugin/dev_environment/impl/gdb.py @@ -11,7 +11,7 @@ class GdbDevEnvironmentHandler(BaseDevEnvironmentHandler): - def handle(self, *, settings: DottedDict) -> None: + def handle_(self, *, settings: DottedDict) -> None: self._inject_extra_paths(settings=settings, paths=self.find_paths(settings)) @classmethod diff --git a/plugin/dev_environment/impl/sublime_text.py b/plugin/dev_environment/impl/sublime_text.py index 0357059..7d3091a 100644 --- a/plugin/dev_environment/impl/sublime_text.py +++ b/plugin/dev_environment/impl/sublime_text.py @@ -18,7 +18,7 @@ class BaseSublimeTextDevEnvironmentHandler(BaseDevEnvironmentHandler, ABC): def python_version(self) -> tuple[int, int]: return (3, 3) - def handle(self, *, settings: DottedDict) -> None: + def handle_(self, *, settings: DottedDict) -> None: self._inject_extra_paths(settings=settings, paths=self.find_package_dependency_dirs()) def find_package_dependency_dirs(self) -> list[str]: diff --git a/plugin/dev_environment/interfaces.py b/plugin/dev_environment/interfaces.py index fbc88cf..a0ddb85 100644 --- a/plugin/dev_environment/interfaces.py +++ b/plugin/dev_environment/interfaces.py @@ -9,6 +9,7 @@ from ..constants import SERVER_SETTING_ANALYSIS_EXTRAPATHS, SERVER_SETTING_DEV_ENVIRONMENT from ..log import log_info from ..utils import camel_to_snake, remove_suffix +from ..virtual_env.venv_info import BaseVenvInfo class BaseDevEnvironmentHandler(ABC): @@ -17,11 +18,14 @@ def __init__( *, server_dir: str | Path, workspace_folders: Sequence[str], + venv_info: BaseVenvInfo | None = None, ) -> None: self.server_dir = Path(server_dir) """The language server directory.""" self.workspace_folders = workspace_folders """The workspace folders.""" + self.venv_info = venv_info + """The virtual environment information.""" @classmethod def name(cls) -> str: @@ -39,13 +43,31 @@ def can_support(cls, dev_environment: str) -> bool: """Check if this class support the given `dev_environment`.""" return cls.name() == dev_environment - @abstractmethod + @final def handle(self, *, settings: DottedDict) -> None: """Handle this environment.""" + self.handle_(settings=settings) + + if self.venv_info: + self._inject_extra_paths(settings=settings, paths=(self.venv_info.site_packages_dir,), prepend=True) + + @abstractmethod + def handle_(self, *, settings: DottedDict) -> None: + """Handle this environment. (subclass)""" - def _inject_extra_paths(self, *, settings: DottedDict, paths: Iterable[str | Path]) -> None: - """Appends the given `paths` to `XXX.analysis.extraPaths` setting.""" - extra_paths: list[str] = settings.get(SERVER_SETTING_ANALYSIS_EXTRAPATHS) or [] - extra_paths.extend(map(str, paths)) - log_info(f"Adding extra analysis paths: {paths}") - settings.set(SERVER_SETTING_ANALYSIS_EXTRAPATHS, extra_paths) + def _inject_extra_paths( + self, + *, + settings: DottedDict, + paths: Iterable[str | Path], + prepend: bool = False, + ) -> None: + """Injects the given `paths` to `XXX.analysis.extraPaths` setting.""" + current_paths: list[str] = settings.get(SERVER_SETTING_ANALYSIS_EXTRAPATHS) or [] + extra_paths = list(map(str, paths)) + if prepend: + next_paths = extra_paths + current_paths + else: + next_paths = current_paths + extra_paths + log_info(f"Adding extra analysis paths ({prepend = }): {paths}") + settings.set(SERVER_SETTING_ANALYSIS_EXTRAPATHS, next_paths) diff --git a/plugin/utils.py b/plugin/utils.py index 2b2e056..009e5b3 100644 --- a/plugin/utils.py +++ b/plugin/utils.py @@ -61,7 +61,10 @@ def get_default_startupinfo() -> Any: def run_shell_command( - command: str | Sequence[str], *, cwd: str | Path | None = None, shell: bool = True + command: str | Sequence[str], + *, + cwd: str | Path | None = None, + shell: bool = True, ) -> tuple[str, str, int] | None: try: proc = subprocess.Popen( diff --git a/plugin/virtual_env/venv_info.py b/plugin/virtual_env/venv_info.py index af68f56..2d5e10a 100644 --- a/plugin/virtual_env/venv_info.py +++ b/plugin/virtual_env/venv_info.py @@ -44,12 +44,34 @@ class BaseVenvInfo(ABC): meta: VenvInfoMeta = field(default_factory=VenvInfoMeta) """The metadata which is not related to venv.""" + @property + def bin_dir(self) -> Path: + """The path of the `bin` directory of the virtual environment.""" + if os.name == "nt": + return self.venv_dir / "Scripts" + return self.venv_dir / "bin" + + @property + def lib_dir(self) -> Path: + """The path of the `lib` directory of the virtual environment.""" + if os.name == "nt": + return self.venv_dir / "Lib" + return self.venv_dir / "lib" + + @property + def site_packages_dir(self) -> Path: + """The path of the `site-packages` directory of the virtual environment.""" + if os.name == "nt": + return self.lib_dir / "site-packages" + python_version = ".".join(self.python_version.split(".")[:2]) + return self.lib_dir / f"python{python_version}/site-packages" + @property def python_executable(self) -> Path: """The path of the Python executable of the virtual environment.""" if os.name == "nt": - return self.venv_dir / "Scripts/python.exe" - return self.venv_dir / "bin/python" + return self.bin_dir / "python.exe" + return self.bin_dir / "python" @abstractmethod def is_valid(self) -> bool: