Skip to content

Commit

Permalink
Ensure Python environment defined in python.defaultInterpreterPath
Browse files Browse the repository at this point in the history
…is returned via discovery API (#22389)

Closes #22268

Even when it is not the active interpreter for any workspace, it's still
a known environment which users could consider selecting.
  • Loading branch information
Kartik Raj authored Oct 31, 2023
1 parent 7aefb21 commit 473de33
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ export class CustomVirtualEnvironmentLocator extends FSWatchingLocator {
}

protected async initResources(): Promise<void> {
this.disposables.push(onDidChangePythonSetting(VENVPATH_SETTING_KEY, () => this.emitter.fire({})));
this.disposables.push(onDidChangePythonSetting(VENVFOLDERS_SETTING_KEY, () => this.emitter.fire({})));
this.disposables.push(onDidChangePythonSetting(VENVPATH_SETTING_KEY, () => this.fire()));
this.disposables.push(onDidChangePythonSetting(VENVFOLDERS_SETTING_KEY, () => this.fire()));
}

// eslint-disable-next-line class-methods-use-this
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { PythonEnvKind } from '../../info';
import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator';
import { FSWatchingLocator } from './fsWatchingLocator';
import { getPythonSetting, onDidChangePythonSetting } from '../../../common/externalDependencies';
import '../../../../common/extensions';
import { traceVerbose } from '../../../../logging';
import { DEFAULT_INTERPRETER_SETTING } from '../../../../common/constants';

export const DEFAULT_INTERPRETER_PATH_SETTING_KEY = 'defaultInterpreterPath';

/**
* Finds and resolves custom virtual environments that users have provided.
*/
export class CustomWorkspaceLocator extends FSWatchingLocator {
public readonly providerId: string = 'custom-workspace-locator';

constructor(private readonly root: string) {
super(
() => [],
async () => PythonEnvKind.Unknown,
);
}

protected async initResources(): Promise<void> {
this.disposables.push(
onDidChangePythonSetting(DEFAULT_INTERPRETER_PATH_SETTING_KEY, () => this.fire(), this.root),
);
}

// eslint-disable-next-line class-methods-use-this
protected doIterEnvs(): IPythonEnvsIterator<BasicEnvInfo> {
const iterator = async function* (root: string) {
traceVerbose('Searching for custom workspace envs');
const filename = getPythonSetting<string>(DEFAULT_INTERPRETER_PATH_SETTING_KEY, root);
if (!filename || filename === DEFAULT_INTERPRETER_SETTING) {
// If the user has not set a custom interpreter, our job is done.
return;
}
yield { kind: PythonEnvKind.Unknown, executablePath: filename };
traceVerbose(`Finished searching for custom workspace envs`);
};
return iterator(this.root);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ export abstract class FSWatchingLocator extends LazyResourceBasedLocator {
watchableRoots.forEach((root) => this.startWatchers(root));
}

protected fire(args = {}): void {
this.emitter.fire({ ...args, providerId: this.providerId });
}

private startWatchers(root: string): void {
const opts = this.creationOptions;
if (isWatchingAFile(opts)) {
Expand Down
10 changes: 6 additions & 4 deletions src/client/pythonEnvironments/common/externalDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ export async function* getSubDirs(
* Returns the value for setting `python.<name>`.
* @param name The name of the setting.
*/
export function getPythonSetting<T>(name: string): T | undefined {
const settings = internalServiceContainer.get<IConfigurationService>(IConfigurationService).getSettings();
export function getPythonSetting<T>(name: string, root?: string): T | undefined {
const resource = root ? vscode.Uri.file(root) : undefined;
const settings = internalServiceContainer.get<IConfigurationService>(IConfigurationService).getSettings(resource);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (settings as any)[name];
}
Expand All @@ -186,9 +187,10 @@ export function getPythonSetting<T>(name: string): T | undefined {
* @param name The name of the setting.
* @param callback The listener function to be called when the setting changes.
*/
export function onDidChangePythonSetting(name: string, callback: () => void): IDisposable {
export function onDidChangePythonSetting(name: string, callback: () => void, root?: string): IDisposable {
return vscode.workspace.onDidChangeConfiguration((event: vscode.ConfigurationChangeEvent) => {
if (event.affectsConfiguration(`python.${name}`)) {
const scope = root ? vscode.Uri.file(root) : undefined;
if (event.affectsConfiguration(`python.${name}`, scope)) {
callback();
}
});
Expand Down
7 changes: 6 additions & 1 deletion src/client/pythonEnvironments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { EnvsCollectionService } from './base/locators/composite/envsCollectionS
import { IDisposable } from '../common/types';
import { traceError } from '../logging';
import { ActiveStateLocator } from './base/locators/lowLevel/activeStateLocator';
import { CustomWorkspaceLocator } from './base/locators/lowLevel/customWorkspaceLocator';

/**
* Set up the Python environments component (during extension activation).'
Expand Down Expand Up @@ -182,7 +183,11 @@ function watchRoots(args: WatchRootsArgs): IDisposable {

function createWorkspaceLocator(ext: ExtensionState): WorkspaceLocators {
const locators = new WorkspaceLocators(watchRoots, [
(root: vscode.Uri) => [new WorkspaceVirtualEnvironmentLocator(root.fsPath), new PoetryLocator(root.fsPath)],
(root: vscode.Uri) => [
new WorkspaceVirtualEnvironmentLocator(root.fsPath),
new PoetryLocator(root.fsPath),
new CustomWorkspaceLocator(root.fsPath),
],
// Add an ILocator factory func here for each kind of workspace-rooted locator.
]);
ext.disposables.push(locators);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ suite('CustomVirtualEnvironment Locator', () => {

test('onChanged fires if venvPath setting changes', async () => {
const events: PythonEnvsChangedEvent[] = [];
const expected: PythonEnvsChangedEvent[] = [{}];
const expected: PythonEnvsChangedEvent[] = [{ providerId: locator.providerId }];
locator.onChanged((e) => events.push(e));

await getEnvs(locator.iterEnvs());
Expand All @@ -228,7 +228,7 @@ suite('CustomVirtualEnvironment Locator', () => {

test('onChanged fires if venvFolders setting changes', async () => {
const events: PythonEnvsChangedEvent[] = [];
const expected: PythonEnvsChangedEvent[] = [{}];
const expected: PythonEnvsChangedEvent[] = [{ providerId: locator.providerId }];
locator.onChanged((e) => events.push(e));

await getEnvs(locator.iterEnvs());
Expand Down

0 comments on commit 473de33

Please sign in to comment.