Skip to content

Commit

Permalink
Feature/vscode pyenv (#803)
Browse files Browse the repository at this point in the history
* Use subdirectory environments when present

- When running or debugging a task, use any discovered environments in subdirectories when present.

- Introduce setting to disable this (on by default).

* Update extension version

---------

Co-authored-by: jjallaire-aisi <[email protected]>
  • Loading branch information
dragonstyle and jjallaire-aisi authored Nov 4, 2024
1 parent 7fb578b commit b0517d7
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 7 deletions.
5 changes: 5 additions & 0 deletions docs/vscode.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ The activity bar has four panels:

- **Task** provides a way to tweak the CLI arguments passed to `inspect eval` when it is run from the user interface.

## Python Environments

When running and debugging Inspect evaluations, the Inspect extension will attempt to use python environments that it discovers in the task subfolder and its parent folders (all the way to the workspace root). It will use the first environment that it discovers, otherwise it will use the python interpreter configured for the workspace. Note that since the extension will use the sub-environments, Inspect must be installed in any of the environments to be used.

You can control this behavior with the `Use Subdirectory Environments`. If you disable this setting, the globally configured interpreter will always be used when running or debugging evaluations, even when environments are present in subdirectories.

## Troubleshooting

Expand Down
4 changes: 4 additions & 0 deletions tools/vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.3.40

- When running and debuggin Inspect evals, the extension will by default use any environments present in the task folder or parent folders (up to the workspace). To always use the workspace environment, change the 'Use Subdirectory Environments` setting.

## 0.3.39

- Fix an issue that would cause view to be unable to display when using VSCode extension with Inpsect version 0.3.42
Expand Down
8 changes: 7 additions & 1 deletion tools/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"author": {
"name": "UK AI Safety Institute"
},
"version": "0.3.39",
"version": "0.3.40",
"license": "MIT",
"homepage": "https://inspect.ai-safety-institute.org.uk/",
"repository": {
Expand Down Expand Up @@ -242,6 +242,12 @@
"default": true,
"description": "Limit evaluation to one sample when debugging.",
"order": 5
},
"inspect_ai.useSubdirectoryEnvironments": {
"type": "boolean",
"default": true,
"description": "Run and debug inspect commands using subdirectory environments when present.",
"order": 2
}
}
},
Expand Down
58 changes: 58 additions & 0 deletions tools/vscode/src/core/python/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as fs from "fs";
import * as os from "os";
import { existsSync } from "node:fs";
import path, { join } from "path";
import { AbsolutePath, toAbsolutePath } from "../path";


export function findEnvPythonPath(startDir: AbsolutePath, baseDir: AbsolutePath): AbsolutePath | null {
let currentDir = startDir;
while (currentDir.path !== baseDir.path) {

// Look for a pythong environment
const pythonPath = findEnvPython(currentDir);
if (pythonPath) {
return toAbsolutePath(pythonPath);
}

// Move to the parent directory
currentDir = currentDir.dirname();
}

// No Python environment found
return null;
}

// Helper function to search for Python environment in a given directory
function findEnvPython(directory: AbsolutePath): string | null {
const items = fs.readdirSync(directory.path);

// Filter only directories and check if any is an environment directory
const envDir = items
.map((item) => path.join(directory.path, item))
.filter((filePath) => fs.statSync(filePath).isDirectory())
.find(isEnvDir);

if (envDir) {
return getPythonPath(envDir);
}

return null;
}

function getPythonPath(dir: string): string | null {
const pythonSuffixes = os.platform() === "win32" ? ["Scripts/python.exe", "python.exe"] : ["bin/python3", "bin/python"];
for (const suffix of pythonSuffixes) {
const fullPath = path.join(dir, suffix);
if (fs.existsSync(fullPath)) {
return fullPath;
}
}

return null;
}

function isEnvDir(dir: string) {
return existsSync(join(dir, "pyvenv.cfg")) ||
existsSync(join(dir, "conda-meta"));
}
3 changes: 2 additions & 1 deletion tools/vscode/src/core/python/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

export * from './code';
export * from './exec';
export * from './interpreter';
export * from './interpreter';
export * from "./env";
29 changes: 24 additions & 5 deletions tools/vscode/src/providers/inspect/inspect-eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { inspectBinPath } from "../../inspect/props";
import { activeWorkspaceFolder } from "../../core/workspace";
import { findOpenPort } from "../../core/port";
import { log } from "../../core/log";
import { findEnvPythonPath } from "../../core/python";

export async function activateEvalManager(
stateManager: WorkspaceStateManager,
Expand Down Expand Up @@ -113,6 +114,10 @@ export class InspectEvalManager {
});
}

// Find the python environment
const useSubdirectoryEnvironments = workspace.getConfiguration("inspect_ai").get("useSubdirectoryEnvironments");
const pythonPath = useSubdirectoryEnvironments ? findEnvPythonPath(file.dirname(), activeWorkspacePath()) : undefined;

// If we're debugging, launch using the debugger
if (debug) {
// Handle debugging
Expand All @@ -136,16 +141,17 @@ export class InspectEvalManager {
args,
workspaceDir.path,
debugPort,
env
env,
pythonPath ? pythonPath : undefined
);
} else {
// Run the command
runEvalCmd(args, workspaceDir.path);
runEvalCmd(args, workspaceDir.path, pythonPath ? pythonPath : undefined);
}
}
}

const runEvalCmd = (args: string[], cwd: string) => {
const runEvalCmd = (args: string[], cwd: string, python?: AbsolutePath) => {
// See if there a non-busy terminal that we can re-use
const name = "Inspect Eval";
let terminal = window.terminals.find((t) => {
Expand All @@ -156,15 +162,27 @@ const runEvalCmd = (args: string[], cwd: string) => {
}
terminal.show();
terminal.sendText(`cd ${cwd}`);
terminal.sendText(["inspect", ...args].join(" "));

const cmd = [];
if (python) {
cmd.push(`${python.path}`);
cmd.push("-m");
cmd.push("inspect_ai");
} else {
cmd.push("inspect");
}
cmd.push(...args);

terminal.sendText(cmd.join(" "));
};

const runDebugger = async (
program: string,
args: string[],
cwd: string,
port: number,
env?: Record<string, string>
env?: Record<string, string>,
pythonPath?: AbsolutePath
) => {
const name = "Inspect Eval";
const debugConfiguration: DebugConfiguration = {
Expand All @@ -178,6 +196,7 @@ const runDebugger = async (
port,
env,
justMyCode: false,
pythonPath: pythonPath?.path
};
await debug.startDebugging(activeWorkspaceFolder(), debugConfiguration);
};

0 comments on commit b0517d7

Please sign in to comment.