From fbb04674f30f21a0a11f7aff1d40b0fe461150d8 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Thu, 4 Jul 2024 09:19:30 +0530 Subject: [PATCH] Keep Python script limited to `sysconfig` usage (#511) ## Summary This PR makes progress towards #476 and #479 by making the following changes: * Update the `ruff_server.py` script to limit itself to just getting the Ruff binary path using `sysconfig` * Use the `which` dependency and move the logic of locating the Ruff binary from `PATH` to TypeScript * Add log messages similar to `ruff-lsp` ## Test Plan ### 1 ```json { "ruff.nativeServer": true } ``` Or ```json { "ruff.nativeServer": true, "ruff.importStrategy": "fromEnvironment" } ``` Logs: When the `ruff` binary is installed in the current virtual environment: ``` 2024-07-03 15:28:35.674 [info] Using the Ruff binary: /Users/dhruv/work/astral/ruff-vscode/.venv/bin/ruff 2024-07-03 15:28:35.675 [info] Server run command: /Users/dhruv/work/astral/ruff-vscode/.venv/bin/ruff server --preview ``` If there's no `ruff` binary in the current virtual environment: ``` 2024-07-03 15:25:19.605 [info] Using the Ruff binary: /Users/dhruv/.local/bin/ruff 2024-07-03 15:25:19.606 [info] Server run command: /Users/dhruv/.local/bin/ruff server --preview ``` If the `ruff` binary is only available via `PATH`: ``` 2024-07-03 15:32:12.172 [info] Using environment executable: /opt/homebrew/bin/ruff 2024-07-03 15:32:12.173 [info] Server run command: /opt/homebrew/bin/ruff server --preview ``` And, if there's no `ruff` binary anywhere on the system, use the bundled binary: ``` 2024-07-03 15:29:47.948 [info] Falling back to bundled executable: /Users/dhruv/work/astral/ruff-vscode/bundled/libs/bin/ruff 2024-07-03 15:29:47.948 [info] Server run command: /Users/dhruv/work/astral/ruff-vscode/bundled/libs/bin/ruff server --preview ``` ### 2 ```json { "ruff.nativeServer": true, "ruff.importStrategy": "useBundled" } ``` Logs: ``` 2024-07-03 15:26:28.767 [info] Using bundled executable: /Users/dhruv/work/astral/ruff-vscode/bundled/libs/bin/ruff 2024-07-03 15:26:28.767 [info] Server run command: /Users/dhruv/work/astral/ruff-vscode/bundled/libs/bin/ruff server --preview ``` ### 3 ```json { "ruff.nativeServer": true, "ruff.path": ["/Users/dhruv/work/astral/ruff/target/debug/ruff"] } ``` Logs: ``` 2024-07-03 15:27:42.342 [info] Using 'path' setting: /Users/dhruv/work/astral/ruff/target/debug/ruff 2024-07-03 15:27:42.342 [info] Server run command: /Users/dhruv/work/astral/ruff/target/debug/ruff server --preview ``` ### 4 ```json { "ruff.nativeServer": true, "ruff.interpreter": [ "/Users/dhruv/work/astral/ruff-lsp/.venv/bin/python" ] } ``` Logs: ``` 2024-07-03 15:35:23.578 [info] Using the Ruff binary: /Users/dhruv/work/astral/ruff-lsp/.venv/bin/ruff 2024-07-03 15:35:23.578 [info] Server run command: /Users/dhruv/work/astral/ruff-lsp/.venv/bin/ruff server --preview ``` ### 5 What if the script failed? I modified the script path in TypeScript code and we get the following logs: ``` 2024-07-03 15:42:41.035 [error] Error while trying to find the Ruff binary: Error: Command failed: /Users/dhruv/work/astral/ruff-lsp/.venv/bin/python /Users/dhruv/work/astral/ruff-vscode/bundled/tool/find_ruff_binary_pat.py /Users/dhruv/work/astral/ruff-lsp/.venv/bin/python: can't open file '/Users/dhruv/work/astral/ruff-vscode/bundled/tool/find_ruff_binary_pat.py': [Errno 2] No such file or directory 2024-07-03 15:42:41.037 [info] Using environment executable: /Users/dhruv/.local/bin/ruff 2024-07-03 15:42:41.040 [info] Server run command: /Users/dhruv/.local/bin/ruff server --preview ``` --- bundled/tool/find_ruff_binary_path.py | 35 ++++++++ bundled/tool/ruff_server.py | 92 -------------------- package-lock.json | 98 ++++++++++++++++----- package.json | 4 +- src/common/constants.ts | 11 ++- src/common/server.ts | 121 +++++++++++++++++++------- 6 files changed, 211 insertions(+), 150 deletions(-) create mode 100644 bundled/tool/find_ruff_binary_path.py delete mode 100644 bundled/tool/ruff_server.py diff --git a/bundled/tool/find_ruff_binary_path.py b/bundled/tool/find_ruff_binary_path.py new file mode 100644 index 0000000..aaca26d --- /dev/null +++ b/bundled/tool/find_ruff_binary_path.py @@ -0,0 +1,35 @@ +import os +import sys +import sysconfig +from pathlib import Path +from typing import Optional + +RUFF_EXE = "ruff.exe" if sys.platform == "win32" else "ruff" + + +def find_ruff_binary_path() -> Optional[Path]: + """Return the ruff binary path if it exists, `None` otherwise.""" + bin_path = Path(sysconfig.get_path("scripts")) / RUFF_EXE + if bin_path.is_file(): + return bin_path + + if sys.version_info >= (3, 10): + user_scheme = sysconfig.get_preferred_scheme("user") + elif os.name == "nt": + user_scheme = "nt_user" + elif sys.platform == "darwin" and sys._framework: + user_scheme = "osx_framework_user" + else: + user_scheme = "posix_user" + + scripts_path = Path(sysconfig.get_path("scripts", scheme=user_scheme)) / RUFF_EXE + if scripts_path.is_file(): + return scripts_path + + return None + + +if __name__ == "__main__": + ruff_binary_path = find_ruff_binary_path() + if ruff_binary_path: + print(os.fsdecode(str(ruff_binary_path)), flush=True) diff --git a/bundled/tool/ruff_server.py b/bundled/tool/ruff_server.py deleted file mode 100644 index 1126199..0000000 --- a/bundled/tool/ruff_server.py +++ /dev/null @@ -1,92 +0,0 @@ -import os -import shutil -import site -import subprocess -import sys -import sysconfig -from pathlib import Path - -RUFF_EXE = "ruff.exe" if sys.platform == "win32" else "ruff" - -BUNDLE_DIR = Path(__file__).parent.parent - - -def update_sys_path(path_to_add: str) -> None: - """Add given path to `sys.path`.""" - if os.path.isdir(path_to_add): - # The `site` module adds the directory at the end, if not yet present; we want - # it to be at the beginning, so that it takes precedence over any other - # installed versions. - sys.path.insert(0, path_to_add) - - # Allow development versions of libraries to be imported. - site.addsitedir(path_to_add) - - -# This is separate from the 'main' entrypoint because we need -# to update the system path _before_ importing `pacakging` -if __name__ == "__main__": - # Ensure that we can import bundled libraries like `packaging` - update_sys_path(os.fspath(BUNDLE_DIR / "libs")) - - -from packaging.specifiers import SpecifierSet # noqa: E402 -from packaging.version import Version # noqa: E402 - -# This is subject to change in the future -RUFF_VERSION_REQUIREMENT = SpecifierSet(">=0.3.5") - - -def executable_version(executable: str) -> Version: - """Return the version of the executable at the given path.""" - output = subprocess.check_output([executable, "--version"]).decode().strip() - version = output.replace("ruff ", "") - return Version(version) - - -def check_compatibility( - executable: str, - requirement: SpecifierSet, -) -> None: - """Check the executable for compatibility against various version specifiers.""" - version = executable_version(executable) - if not requirement.contains(version, prereleases=True): - message = f"Ruff {requirement} required, but found {version} at {executable}" - raise RuntimeError(message) - - -def find_ruff_bin(fallback: Path) -> Path: - """Return the ruff binary path.""" - bin_path = Path(sysconfig.get_path("scripts")) / RUFF_EXE - if bin_path.is_file(): - return bin_path - - if sys.version_info >= (3, 10): - user_scheme = sysconfig.get_preferred_scheme("user") - elif os.name == "nt": - user_scheme = "nt_user" - elif sys.platform == "darwin" and sys._framework: - user_scheme = "osx_framework_user" - else: - user_scheme = "posix_user" - - scripts_path = Path(sysconfig.get_path("scripts", scheme=user_scheme)) / RUFF_EXE - if scripts_path.is_file(): - return scripts_path - - which_path = shutil.which("ruff") - if which_path: - return Path(which_path) - - return fallback - - -if __name__ == "__main__": - ruff = os.fsdecode( - find_ruff_bin( - Path(BUNDLE_DIR / "libs" / "bin" / RUFF_EXE), - ), - ) - check_compatibility(ruff, RUFF_VERSION_REQUIREMENT) - completed_process = subprocess.run([ruff, *sys.argv[1:]], check=False) - sys.exit(completed_process.returncode) diff --git a/package-lock.json b/package-lock.json index fb03e9b..e855849 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,13 +11,15 @@ "dependencies": { "fs-extra": "^11.1.1", "ovsx": "^0.7.1", - "vscode-languageclient": "^8.1.0" + "vscode-languageclient": "^8.1.0", + "which": "^4.0.0" }, "devDependencies": { "@types/fs-extra": "^11.0.1", "@types/glob": "^8.1.0", "@types/node": "^20.3.1", "@types/vscode": "1.75.0", + "@types/which": "^3.0.4", "@typescript-eslint/eslint-plugin": "^5.59.2", "@typescript-eslint/parser": "^5.59.2", "@vscode/vsce": "^2.19.0", @@ -356,6 +358,13 @@ "integrity": "sha512-SAr0PoOhJS6FUq5LjNr8C/StBKALZwDVm3+U4pjF/3iYkt3GioJOPV/oB1Sf1l7lROe4TgrMyL5N1yaEgTWycw==", "dev": true }, + "node_modules/@types/which": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", + "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.59.11", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz", @@ -1303,6 +1312,29 @@ "node": ">= 8" } }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -2369,10 +2401,13 @@ } }, "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } }, "node_modules/isobject": { "version": "3.0.1", @@ -4321,18 +4356,18 @@ } }, "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "node-which": "bin/node-which" + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/wildcard": { @@ -4765,6 +4800,12 @@ "integrity": "sha512-SAr0PoOhJS6FUq5LjNr8C/StBKALZwDVm3+U4pjF/3iYkt3GioJOPV/oB1Sf1l7lROe4TgrMyL5N1yaEgTWycw==", "dev": true }, + "@types/which": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", + "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "5.59.11", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz", @@ -5440,6 +5481,23 @@ "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "dependencies": { + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "css-select": { @@ -6204,10 +6262,9 @@ } }, "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" }, "isobject": { "version": "3.0.1", @@ -7607,12 +7664,11 @@ "dev": true }, "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "requires": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" } }, "wildcard": { diff --git a/package.json b/package.json index 7ed95d1..13d1181 100644 --- a/package.json +++ b/package.json @@ -386,13 +386,15 @@ "dependencies": { "fs-extra": "^11.1.1", "ovsx": "^0.7.1", - "vscode-languageclient": "^8.1.0" + "vscode-languageclient": "^8.1.0", + "which": "^4.0.0" }, "devDependencies": { "@types/fs-extra": "^11.0.1", "@types/glob": "^8.1.0", "@types/node": "^20.3.1", "@types/vscode": "1.75.0", + "@types/which": "^3.0.4", "@typescript-eslint/eslint-plugin": "^5.59.2", "@typescript-eslint/parser": "^5.59.2", "@vscode/vsce": "^2.19.0", diff --git a/src/common/constants.ts b/src/common/constants.ts index 579ef92..179c446 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -44,16 +44,15 @@ export const DEBUG_SERVER_SCRIPT_PATH = path.join( ); /** - * Path to the Python script that starts the native language server with an - * appropriate `ruff` executable. + * Path to the Python script that tries to find the Ruff binary path. * - * This should only be used as a fallback if the user has not specified either - * the `path` setting or is not using the bundled import strategy. + * This should only be used as a fallback if there is no valid `ruff` binary in + * the user's `path` setting or the import strategy isn't `useBundled`. */ -export const NATIVE_SERVER_SCRIPT_PATH = path.join( +export const FIND_RUFF_BINARY_SCRIPT_PATH = path.join( BUNDLED_PYTHON_SCRIPTS_DIR, "tool", - "ruff_server.py", + "find_ruff_binary_path.py", ); /** diff --git a/src/common/server.ts b/src/common/server.ts index 29f0663..bced5c8 100644 --- a/src/common/server.ts +++ b/src/common/server.ts @@ -1,8 +1,8 @@ import * as fsapi from "fs-extra"; +import * as vscode from "vscode"; import { Disposable, l10n, LanguageStatusSeverity, LogOutputChannel } from "vscode"; import { State } from "vscode-languageclient"; import { - Executable, LanguageClient, LanguageClientOptions, RevealOutputChannelOn, @@ -14,7 +14,8 @@ import { RUFF_SERVER_REQUIRED_ARGS, RUFF_SERVER_SUBCOMMAND, RUFF_LSP_SERVER_SCRIPT_PATH, - NATIVE_SERVER_SCRIPT_PATH, + FIND_RUFF_BINARY_SCRIPT_PATH, + RUFF_BINARY_NAME, } from "./constants"; import { traceError, traceInfo, traceVerbose } from "./log/logging"; import { getDebuggerPath } from "./python"; @@ -27,23 +28,54 @@ import { import { updateStatus } from "./status"; import { getProjectRoot } from "./utilities"; import { isVirtualWorkspace } from "./vscodeapi"; +import { exec } from "child_process"; +import which = require("which"); -export type IInitOptions = { +export type IInitializationOptions = { settings: ISettings[]; globalSettings: ISettings; }; -function findNativeServerExecutable(settings: ISettings): Executable { +/** + * Function to execute a command and return the stdout. + */ +function executeCommand(command: string): Promise { + return new Promise((resolve, reject) => { + exec(command, (error, stdout, _) => { + if (error) { + reject(error); + } else { + resolve(stdout); + } + }); + }); +} + +/** + * Finds the Ruff binary path and returns it. + * + * The strategy is as follows: + * 1. If the 'path' setting is set, check each path in order. The first valid + * path is returned. + * 2. If the 'importStrategy' setting is 'useBundled', return the bundled + * executable path. + * 3. Execute a Python script that tries to locate the binary. This uses either + * the user-provided interpreter or the interpreter provided by the Python + * extension. + * 4. If the Python script doesn't return a path, check the global environment + * which checks the PATH environment variable. + * 5. If all else fails, return the bundled executable path. + */ +async function findRuffBinaryPath( + settings: ISettings, + outputChannel: LogOutputChannel, +): Promise { // 'path' setting takes priority over everything. if (settings.path.length > 0) { for (const path of settings.path) { - if (fsapi.existsSync(path)) { + if (await fsapi.pathExists(path)) { traceInfo(`Using 'path' setting: ${path}`); - return { - command: path, - args: [RUFF_SERVER_SUBCOMMAND, ...RUFF_SERVER_REQUIRED_ARGS], - options: { cwd: settings.cwd, env: process.env }, - }; + return path; } } traceInfo(`Could not find executable in 'path': ${settings.path.join(", ")}`); @@ -51,20 +83,46 @@ function findNativeServerExecutable(settings: ISettings): Executable { if (settings.importStrategy === "useBundled") { traceInfo(`Using bundled executable: ${BUNDLED_RUFF_EXECUTABLE}`); - return { - command: BUNDLED_RUFF_EXECUTABLE, - args: [RUFF_SERVER_SUBCOMMAND, ...RUFF_SERVER_REQUIRED_ARGS], - options: { cwd: settings.cwd, env: process.env }, - }; + return BUNDLED_RUFF_EXECUTABLE; } - // Otherwise, we'll call a Python script that tries to locate a binary, - // falling back to the bundled version if no local executable is found. - return { - command: settings.interpreter[0], - args: [NATIVE_SERVER_SCRIPT_PATH, RUFF_SERVER_SUBCOMMAND, ...RUFF_SERVER_REQUIRED_ARGS], - options: { cwd: settings.cwd, env: process.env }, - }; + // Otherwise, we'll call a Python script that tries to locate a binary. + let ruffBinaryPath: string | undefined; + try { + const stdout = await executeCommand( + `${settings.interpreter[0]} ${FIND_RUFF_BINARY_SCRIPT_PATH}`, + ); + ruffBinaryPath = stdout.trim(); + } catch (err) { + await vscode.window + .showErrorMessage( + "Unexpected error while trying to find the Ruff binary. See the logs for more details.", + "Show Logs", + ) + .then((selection) => { + if (selection) { + outputChannel.show(); + } + }); + traceError(`Error while trying to find the Ruff binary: ${err}`); + } + + if (ruffBinaryPath && ruffBinaryPath.length > 0) { + // First choice: the executable found by the script. + traceInfo(`Using the Ruff binary: ${ruffBinaryPath}`); + return ruffBinaryPath; + } + + // Second choice: the executable in the global environment. + const environmentPath = await which(RUFF_BINARY_NAME, { nothrow: true }); + if (environmentPath) { + traceInfo(`Using environment executable: ${environmentPath}`); + return environmentPath; + } + + // Third choice: bundled executable. + traceInfo(`Falling back to bundled executable: ${BUNDLED_RUFF_EXECUTABLE}`); + return BUNDLED_RUFF_EXECUTABLE; } async function createNativeServer( @@ -72,14 +130,17 @@ async function createNativeServer( serverId: string, serverName: string, outputChannel: LogOutputChannel, - initializationOptions: IInitOptions, + initializationOptions: IInitializationOptions, ): Promise { - let serverOptions = findNativeServerExecutable(settings); - if (serverOptions.args) { - traceInfo(`Server run command: ${[serverOptions.command, ...serverOptions.args].join(" ")}`); - } else { - traceInfo(`Server run command: ${serverOptions.command}`); - } + const ruffBinaryPath = await findRuffBinaryPath(settings, outputChannel); + const ruffServerArgs = [RUFF_SERVER_SUBCOMMAND, ...RUFF_SERVER_REQUIRED_ARGS]; + traceInfo(`Server run command: ${[ruffBinaryPath, ...ruffServerArgs].join(" ")}`); + + let serverOptions = { + command: ruffBinaryPath, + args: ruffServerArgs, + options: { cwd: settings.cwd, env: process.env }, + }; const clientOptions = { // Register the server for python documents @@ -105,7 +166,7 @@ async function createServer( serverId: string, serverName: string, outputChannel: LogOutputChannel, - initializationOptions: IInitOptions, + initializationOptions: IInitializationOptions, ): Promise { const command = settings.interpreter[0]; const cwd = settings.cwd;