Skip to content

Commit

Permalink
Use spawn rather than exec to support paths with spaces (#539)
Browse files Browse the repository at this point in the history
## Summary

If the interpreter path contains a space, then the find script fails. We
need to escape the arguments that we pass to `exec`. But, it seems
easier to just use `spawn`, which doesn't require escaping.

Closes astral-sh/ruff#12394.

## Test Plan

Created a virtual environment `foo bar` in a repo; verified that the
script failed before but succeeds after this change.
  • Loading branch information
charliermarsh authored Jul 18, 2024
1 parent 2dd2871 commit e1777e1
Showing 1 changed file with 24 additions and 9 deletions.
33 changes: 24 additions & 9 deletions src/common/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {
import { updateServerKind, updateStatus } from "./status";
import { getProjectRoot } from "./utilities";
import { isVirtualWorkspace } from "./vscodeapi";
import { exec } from "child_process";
import { spawn } from "child_process";
import which = require("which");

export type IInitializationOptions = {
Expand All @@ -49,23 +49,40 @@ export type IInitializationOptions = {
/**
* Function to execute a command and return the stdout.
*/
function executeCommand(command: string): Promise<string> {
function executeCommand(cmd: string, args: string[] = []): Promise<string> {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, _) => {
if (error) {
reject(error);
const child = spawn(cmd, args);

let stdout = "";
let stderr = "";

child.stdout.on("data", (data) => {
stdout += data.toString();
});

child.stderr.on("data", (data) => {
stderr += data.toString();
});

child.on("close", (code) => {
if (code !== 0) {
reject(new Error(stderr));
} else {
resolve(stdout);
}
});

child.on("error", (error) => {
reject(error);
});
});
}

/**
* Get the version of the Ruff executable at the given path.
*/
async function getRuffVersion(executable: string): Promise<VersionInfo> {
const stdout = await executeCommand(`${executable} --version`);
const stdout = await executeCommand(executable, ["--version"]);
const version = stdout.trim().split(" ")[1];
const [major, minor, patch] = version.split(".").map((x) => parseInt(x, 10));
return { major, minor, patch };
Expand Down Expand Up @@ -114,9 +131,7 @@ async function findRuffBinaryPath(
// 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}`,
);
const stdout = await executeCommand(settings.interpreter[0], [FIND_RUFF_BINARY_SCRIPT_PATH]);
ruffBinaryPath = stdout.trim();
} catch (err) {
await vscode.window
Expand Down

0 comments on commit e1777e1

Please sign in to comment.