Skip to content

Improve Python environment selection with auto-discovery - #16

Open
rmcd-mscb wants to merge 5 commits into
mainfrom
feature/issue-15-python-env-selection
Open

Improve Python environment selection with auto-discovery#16
rmcd-mscb wants to merge 5 commits into
mainfrom
feature/issue-15-python-env-selection

Conversation

@rmcd-mscb

Copy link
Copy Markdown
Owner

Summary

  • Integrate with VS Code Python extension to use the already-selected interpreter
  • Auto-discover conda environments, workspace virtual environments (.venv, venv, env), and system Python
  • Replace file browser with QuickPick dropdown showing all discovered environments
  • Add status bar item showing current Python environment (click to change)
  • Validate dependencies before accepting environment selection
  • Remove redundant activationEvents from package.json

Changes

New Files

  • src/python/environmentDiscovery.ts - Python environment detection and discovery module

Modified Files

  • src/extension.ts - QuickPick selector, status bar item, config listener
  • README.md - Updated Python configuration documentation
  • CHANGELOG.md - Added feature entries
  • CONTRIBUTING.md - Updated project structure
  • package.json - Removed redundant activationEvents

Test Plan

  • Verify status bar shows current Python environment
  • Click status bar to open environment selector
  • Verify VS Code Python extension interpreter appears first (if installed)
  • Verify conda environments are discovered
  • Verify workspace venvs are discovered
  • Test "Browse..." option opens file picker
  • Test "Enter path..." option allows manual input
  • Verify dependency validation warns when xarray/netCDF4 missing

Closes #15

🤖 Generated with Claude Code

rmcdco and others added 3 commits February 4, 2026 12:14
- Add VS Code Python extension integration to use already-selected interpreter
- Auto-discover conda environments, workspace venvs, and system Python
- Replace file browser with QuickPick dropdown showing all discovered environments
- Add status bar item showing current Python environment (click to change)
- Validate dependencies before accepting environment selection
- Update documentation (README, CHANGELOG, CONTRIBUTING)

Closes #15

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove explicit activationEvents from package.json (VS Code auto-generates from contributes)
- Move CLAUDE.md to .claude/ directory
- Update package-lock.json from WSL npm install

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add npm run vsce:package to create .vsix file
- Add npm run vsce:install to package and install locally
- Add @vscode/vsce as dev dependency
- Update CONTRIBUTING.md with new workflow documentation
- Add netcdf-viewer.vsix to .gitignore

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add onStartupFinished activation event so status bar shows immediately
- Change status bar icon to beaker with "NC:" prefix for clarity
- Fix environment name display to show conda env name instead of "bin"
- Skip "bin" and "Scripts" folders when extracting environment name

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request significantly improves the Python environment selection experience by implementing automatic discovery of Python installations and integrating with the VS Code Python extension. The changes replace the manual file browser approach with an intelligent QuickPick dropdown that shows discovered environments with friendly names.

Changes:

  • Added automatic Python environment discovery (VS Code Python extension, conda, workspace venvs, system Python)
  • Implemented QuickPick interface for environment selection with validation
  • Added status bar indicator showing current Python environment

Reviewed changes

Copilot reviewed 7 out of 9 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
src/python/environmentDiscovery.ts New module implementing Python environment discovery logic for various sources
src/extension.ts Integrated QuickPick selector, status bar item, and configuration listener for Python environment
package.json Removed redundant activationEvents, added vsce scripts and dependency
README.md Updated documentation to describe automatic environment detection and new selection UI
CONTRIBUTING.md Added new module to project structure documentation and new packaging commands
CHANGELOG.md Documented new features and improvements
.gitignore Added build artifact exclusion
CLAUDE.md Removed (appears to be internal documentation)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +31 to +47
// Try the newer API first (Python extension 2023.4+)
const execDetails = pythonExt.exports?.settings?.getExecutionDetails?.(
vscode.workspace.workspaceFolders?.[0]?.uri
);
if (execDetails?.execCommand?.[0]) {
const pythonPath = execDetails.execCommand[0];
const version = await getPythonVersion(pythonPath);
return {
name: `VS Code Python${version ? ` (${version})` : ''}`,
path: pythonPath,
source: 'vscode-python',
version,
};
}

// Fallback to older API
const pythonPath = pythonExt.exports?.settings?.getExecutionDetails?.()?.execCommand?.[0];

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The VS Code Python extension API is accessed without proper type definitions, using optional chaining throughout. While this handles the case where the API doesn't exist, it's fragile. The comment mentions "Python extension 2023.4+" but there's no documentation of what happens with newer versions. Consider documenting the expected API structure or using the official Python extension API types if available.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +10
/**
* Represents a discovered Python environment
*/
export interface PythonEnvironment {
name: string;

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new Python environment discovery functionality lacks test coverage. Given that the existing test file has comprehensive tests for other features like NetCDFTreeProvider, consider adding tests for the environment discovery functions, especially for edge cases like missing conda, invalid paths, and the VS Code Python extension integration.

Copilot uses AI. Check for mistakes.
const workspaceFolders = vscode.workspace.workspaceFolders;

if (!workspaceFolders) {
return environments;

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The venv directory list includes '.env' which is commonly used for environment variable files (e.g., by dotenv), not virtual environments. This could lead to false positives. Consider removing '.env' from the list unless there's a specific use case for it.

Suggested change
return environments;
const venvDirs = ['.venv', 'venv', 'env'];

Copilot uses AI. Check for mistakes.
Comment on lines +248 to +250
}

// Check if it's a venv (look for common venv folder names in the path)

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the getEnvironmentDisplayName function, the check for venv indicators uses path.sep + indicator + path.sep, which won't match if the venv directory is at the end of the path (which it typically is, e.g., /path/to/.venv/bin/python). The current code would fail to identify this as a venv. Consider checking if the path contains the indicator followed by path separators leading to 'bin' or 'Scripts'.

Suggested change
}
// Check if it's a venv (look for common venv folder names in the path)
const pathSegments = pythonPath.split(path.sep);
for (const indicator of venvIndicators) {
const index = pathSegments.lastIndexOf(indicator);
if (index !== -1) {
const nextSegment = pathSegments[index + 1];
// Accept if the indicator is the last segment, or is followed by bin/Scripts
if (!nextSegment || nextSegment === 'bin' || nextSegment === 'Scripts') {
return `venv: ${indicator}`;
}

Copilot uses AI. Check for mistakes.
Comment thread src/extension.ts
Comment on lines +160 to +165
validateInput: (value) => {
if (!value.trim()) {
return 'Please enter a path';
}
return undefined;
},

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation input function only checks if the value is empty, but doesn't validate that the path exists or is executable. A user could enter an invalid path that passes validation but fails later. Consider adding basic path validation such as checking if the file exists and is executable, or at least checking if it looks like a valid path.

Copilot uses AI. Check for mistakes.
/**
* Gets the Python version for a given interpreter path
*/
export async function getPythonVersion(pythonPath: string): Promise<string | undefined> {

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Python version regex pattern /Python (\d+\.\d+\.\d+)/ expects a three-part version number (e.g., "3.9.7"), but some Python installations may output version strings in different formats (e.g., "Python 3.9.7+" for modified builds, or "Python 3.12.0rc1" for release candidates). Consider making the regex more flexible to handle these variations.

Suggested change
export async function getPythonVersion(pythonPath: string): Promise<string | undefined> {
const match = output.match(/Python (\d+(?:\.\d+){1,2})(?:\D|$)/);

Copilot uses AI. Check for mistakes.
Comment thread src/python/environmentDiscovery.ts Outdated
export interface PythonEnvironment {
name: string;
path: string;
source: 'vscode-python' | 'conda' | 'venv' | 'system' | 'manual';

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PythonEnvironment interface includes a 'manual' source type, but this source is never actually used in the code. Environments selected manually via browse or text input don't create a PythonEnvironment object with source='manual'. Consider either using this source type when manual selection occurs, or removing it from the interface to avoid confusion.

Suggested change
source: 'vscode-python' | 'conda' | 'venv' | 'system' | 'manual';
source: 'vscode-python' | 'conda' | 'venv' | 'system';

Copilot uses AI. Check for mistakes.

/**
* Helper function to run a command and return stdout
*/

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout of 10000ms (10 seconds) for command execution may be too short for slow systems or environments with many conda environments. Consider increasing this timeout or making it configurable, especially for the conda discovery which might take longer to enumerate all environments.

Suggested change
*/
const envTimeout = process.env.PYTHON_ENV_DISCOVERY_TIMEOUT_MS;
let timeout = 30000; // Default to 30 seconds to better support slow systems or many environments
if (envTimeout) {
const parsed = Number(envTimeout);
if (!Number.isNaN(parsed) && parsed > 0) {
timeout = parsed;
}
}
execFile(command, args, { timeout }, (error, stdout, stderr) => {

Copilot uses AI. Check for mistakes.
Comment on lines +120 to +128
? path.join(venvPath, 'Scripts', 'python.exe')
: path.join(venvPath, 'bin', 'python');

if (fs.existsSync(pythonPath)) {
const version = await getPythonVersion(pythonPath);
environments.push({
name: `venv: ${venvDir}${version ? ` (${version})` : ''}`,
path: pythonPath,
source: 'venv',

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fs.existsSync calls are synchronous and will block the event loop. Since this code discovers multiple environments, it could perform many synchronous filesystem checks. Consider using the asynchronous fs.promises.access() or fs.promises.stat() instead to avoid blocking, especially when checking multiple venv directories across multiple workspace folders.

Suggested change
? path.join(venvPath, 'Scripts', 'python.exe')
: path.join(venvPath, 'bin', 'python');
if (fs.existsSync(pythonPath)) {
const version = await getPythonVersion(pythonPath);
environments.push({
name: `venv: ${venvDir}${version ? ` (${version})` : ''}`,
path: pythonPath,
source: 'venv',
try {
await fs.promises.access(pythonPath);
} catch {
// If the Python executable does not exist or is not accessible, skip this venv
continue;
}
const version = await getPythonVersion(pythonPath);
environments.push({
name: `venv: ${venvDir}${version ? ` (${version})` : ''}`,
path: pythonPath,
source: 'venv',
version,
});

Copilot uses AI. Check for mistakes.
Comment thread src/extension.ts
Comment on lines +113 to +132
const items: (vscode.QuickPickItem & { env?: PythonEnvironment })[] = environments.map((env) => ({
label: env.name,
description: env.path,
detail: env.source === 'vscode-python' ? '$(star) Recommended - from VS Code Python extension' : undefined,
env,
}));

// Add option to browse manually
items.push({
label: '$(folder) Browse...',
description: 'Select Python executable manually',
alwaysShow: true,
});

// Add option to enter path manually
items.push({
label: '$(edit) Enter path...',
description: 'Type a custom Python path',
alwaysShow: true,
});

Copilot AI Feb 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The QuickPick items for manual selection use icon codes like $(folder) and $(edit), but there's no consistent icon used for the discovered environments. Consider adding icons to the discovered environment items as well (e.g., $(symbol-misc) for VS Code Python, $(package) for conda, $(folder-library) for venv) to improve visual consistency and make the list easier to scan.

Copilot uses AI. Check for mistakes.
- Remove unused 'manual' source type from PythonEnvironment interface
- Remove '.env' from venvDirs (commonly used for env variable files)
- Use async fs.promises.access instead of sync fs.existsSync
- Use fs.realpathSync for symlink resolution in duplicate detection
- Make Python version regex more flexible for rc/+ versions
- Increase command timeout from 10s to 30s for slow systems
- Include stderr in error messages for better debugging
- Fix venv indicator matching to verify bin/Scripts structure
- Add debouncing to status bar updates on config changes
- Add icons to discovered environment QuickPick items
- Show helpful placeholder when no environments found

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Python environment selection with VS Code Python extension integration

3 participants