astral-sh/ruff-vscode

VS Code setting ruff.interpreter does no longer accept relative paths

ni-co-la opened this issue · 16 comments

With the latest version of the VS Code extension (v2024.34.0 and v2024.32.0), relative paths for "ruff.interpreter" are no longer accepted (in .vscode/settings.json).

In our repository, this settings file is shared between for all developers and it points to the venv Python installation inside the repository.
For example:

    "ruff.interpreter": [
        ".venv/bin/python"
    ],

This was working just fine in previous versions and is still working fine with other extension like pylint and flake8.

Output:

2024-07-23 10:35:41.066 [error] Python version undefined.undefined is not supported.
2024-07-23 10:35:41.066 [error] Selected python path: undefined
2024-07-23 10:35:41.066 [error] Supported versions are 3.7 and above.

Setup:

  • Windows and Linux
  • Python: 3.12
  • Ruff: 0.5.4
  • VS Code extension: v2024.34.0
  • VS Code 1.91.1

Neither does the following setting work:

    "ruff.interpreter": [
        "${workspaceFolder}/.venv/bin/python"
    ],

Thanks, will fix!

I am surprised that "${workspaceFolder}/.venv/bin/python" doesn't work.

Can you tell me which language server are you using? Are you using the native server or ruff-lsp i.e., have you set the ruff.nativeServer setting?

I'm asking because there shouldn't be any change if you're using ruff-lsp. The meaning of the setting is only changed for the native server where it's only used to find out the ruff binary.

If it's the native server then I think it might be related to e1777e1 or 564a37a because earlier we would just pass the interpreter path as ServerOptions to create the LanguageClient in VS Code, as is still being done for ruff-lsp:

async function createLegacyServer(
settings: ISettings,
serverId: string,
serverName: string,
outputChannel: LogOutputChannel,
initializationOptions: IInitializationOptions,
): Promise<LanguageClient> {
const command = settings.interpreter[0];
const cwd = settings.cwd;
// Set debugger path needed for debugging python code.
const newEnv = { ...process.env };
const debuggerPath = await getDebuggerPath();
const isDebugScript = await fsapi.pathExists(DEBUG_SERVER_SCRIPT_PATH);
if (newEnv.USE_DEBUGPY && debuggerPath) {
newEnv.DEBUGPY_PATH = debuggerPath;
} else {
newEnv.USE_DEBUGPY = "False";
}
// Set notification type
newEnv.LS_SHOW_NOTIFICATION = settings.showNotifications;
const args =
newEnv.USE_DEBUGPY === "False" || !isDebugScript
? settings.interpreter.slice(1).concat([RUFF_LSP_SERVER_SCRIPT_PATH])
: settings.interpreter.slice(1).concat([DEBUG_SERVER_SCRIPT_PATH]);
traceInfo(`Server run command: ${[command, ...args].join(" ")}`);
const serverOptions: ServerOptions = {
command,
args,
options: { cwd, env: newEnv },
};
// Options to control the language client
const clientOptions: LanguageClientOptions = {
// Register the server for python documents
documentSelector: isVirtualWorkspace()
? [{ language: "python" }]
: [
{ scheme: "file", language: "python" },
{ scheme: "untitled", language: "python" },
{ scheme: "vscode-notebook", language: "python" },
{ scheme: "vscode-notebook-cell", language: "python" },
],
outputChannel: outputChannel,
traceOutputChannel: outputChannel,
revealOutputChannelOn: RevealOutputChannelOn.Never,
initializationOptions,
};
return new LanguageClient(serverId, serverName, serverOptions, clientOptions);
}

We have to expand settings.interpreter[0].

I am surprised that "${workspaceFolder}/.venv/bin/python" doesn't work.

Me too. I expected that this should not make a difference for ruff, if I enter the absolute path or if I use the ${workspaceFolder}.

Can you tell me which language server are you using? Are you using the native server or ruff-lsp i.e., have you set the ruff.nativeServer setting?

ruff.nativeServer is not set.

For the VS Code extension I have the following settings:

    "ruff.importStrategy": "fromEnvironment",
    "ruff.path": [
        "${workspaceFolder}/.venv/bin/ruff"
    ],
    "ruff.lint.args": [
        "--config=${workspaceFolder}/config/ruff.toml"
    ],
    "ruff.format.args": [
        "--config=${workspaceFolder}/config/ruff.toml"
    ],

I think I see at least the non-expansion issue.

@dhruvmanila - Do you want to take it? getInterpreterFromSetting just calls config.get<string[]>("interpreter"), but we need to run resolveVariables on it like we do in getWorkspaceSettings.

Yeah, I can take it on.

Thanks. We probably need to expand relative paths too before passing to execFile.

I think there might be a bigger issue here. We do resolve the variables but only when it's a workspace setting:

interpreter: resolveVariables(interpreter, workspace),

And, we don't resolve any variables for the global settings:

export async function getGlobalSettings(namespace: string): Promise<ISettings> {
const config = getConfiguration(namespace);
return {
nativeServer: getGlobalValue<NativeServer>(config, "nativeServer", "auto"),
cwd: process.cwd(),
workspace: process.cwd(),
path: getGlobalValue<string[]>(config, "path", []),
ignoreStandardLibrary: getGlobalValue<boolean>(config, "ignoreStandardLibrary", true),
interpreter: [],
configuration: getGlobalValue<string | null>(config, "configuration", null),
importStrategy: getGlobalValue<ImportStrategy>(config, "importStrategy", "fromEnvironment"),
codeAction: getGlobalValue<CodeAction>(config, "codeAction", {}),
lint: {
enable: getPreferredGlobalSetting<boolean>("lint.enable", "enable", config) ?? true,
run: getPreferredGlobalSetting<Run>("lint.run", "run", config) ?? "onType",
args: getPreferredGlobalSetting<string[]>("lint.args", "args", config) ?? [],
preview: getOptionalGlobalValue<boolean>(config, "lint.preview"),
select: getOptionalGlobalValue<string[]>(config, "lint.select"),
extendSelect: getOptionalGlobalValue<string[]>(config, "lint.extendSelect"),
ignore: getOptionalGlobalValue<string[]>(config, "lint.ignore"),
},
format: {
args: getGlobalValue<string[]>(config, "format.args", []),
preview: getOptionalGlobalValue<boolean>(config, "format.preview"),
},
enable: getGlobalValue<boolean>(config, "enable", true),
organizeImports: getGlobalValue<boolean>(config, "organizeImports", true),
fixAll: getGlobalValue<boolean>(config, "fixAll", true),
showNotifications: getGlobalValue<string>(config, "showNotifications", "off"),
exclude: getOptionalGlobalValue<string[]>(config, "exclude"),
lineLength: getOptionalGlobalValue<number>(config, "lineLength"),
configurationPreference: getGlobalValue<ConfigPreference>(
config,
"configurationPreference",
"editorFirst",
),
showSyntaxErrors: getGlobalValue<boolean>(config, "showSyntaxErrors", true),
logLevel: getOptionalGlobalValue<LogLevel>(config, "logLevel"),
logFile: getOptionalGlobalValue<string>(config, "logFile"),
};
}

Which, I guess, makes sense because it's not tied to any specific workspace.

Black doesn't seem to be resolving the variables from global settings either:

https://github.com/microsoft/vscode-black-formatter/blob/458ed8f03f03c6e021361b7dcf4a8c7ab7c3d5fb/src/common/settings.ts#L149-L171

Ok, I can reproduce this, let me check what's actually going on.

I found the problem, it's bug I introduced in 4b03f7c

Hi @ni-co-la, can you try out the pre-release version which contains the bug fix. You can go to the Ruff extension page and click on "Switch to Pre-Release Version":

Screenshot 2024-07-24 at 10 28 16

I just tested it with the pre-release version and it is working fine with "ruff.interpreter": [ "${workspaceFolder}/.venv/bin/python" ].

"ruff.interpreter": [ "./.venv/bin/python" ] is still not working, which was working in a previous version.

Which is fine for me, as it is more explicit to use the ${workspaceFolder}.

Thanks a lot for the fix.

Thanks for testing it!

"ruff.interpreter": [ "./.venv/bin/python" ] is still not working, which was working in a previous version.

Yes, I explicitly decided not to support this. It wasn't working before either, and you'll probably get the same log messages as in the PR description for earlier versions. It's just that a recent change (e665ec7) made it explicitly visible by removing an unintended fall back mechanism.

I'll make a stable release later today.