Skip to content

Commit 5a2439a

Browse files
authored
fixes recursive venv assignment and incorrect multiroot result (#558)
fixes #491
1 parent ee77dbd commit 5a2439a

File tree

1 file changed

+44
-34
lines changed

1 file changed

+44
-34
lines changed

src/managers/builtin/venvManager.ts

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ export class VenvManager implements EnvironmentManager {
103103
}
104104
}
105105

106+
/**
107+
* Returns configuration for quick create in the workspace root, undefined if no suitable Python 3 version is found.
108+
*/
106109
quickCreateConfig(): QuickCreateConfig | undefined {
107110
if (!this.globalEnv || !this.globalEnv.version.startsWith('3.')) {
108111
return undefined;
109112
}
110-
111113
return {
112114
description: l10n.t('Create a virtual environment in workspace root'),
113115
detail: l10n.t(
@@ -214,6 +216,9 @@ export class VenvManager implements EnvironmentManager {
214216
}
215217
}
216218

219+
/**
220+
* Removes the specified Python environment, updates internal collections, and fires change events as needed.
221+
*/
217222
async remove(environment: PythonEnvironment): Promise<void> {
218223
try {
219224
this.skipWatcherRefresh = true;
@@ -467,6 +472,9 @@ export class VenvManager implements EnvironmentManager {
467472
await this.loadGlobalEnv(globals);
468473
}
469474

475+
/**
476+
* Loads and sets the global Python environment from the provided list, resolving if necessary. O(g) where g = globals.length
477+
*/
470478
private async loadGlobalEnv(globals: PythonEnvironment[]) {
471479
this.globalEnv = undefined;
472480

@@ -499,30 +507,28 @@ export class VenvManager implements EnvironmentManager {
499507
}
500508
}
501509

510+
/**
511+
* Loads and maps Python environments to their corresponding project paths in the workspace. about O(p × e) where p = projects.len and e = environments.len
512+
*/
502513
private async loadEnvMap() {
503514
const globals = await this.baseManager.getEnvironments('global');
504515
await this.loadGlobalEnv(globals);
505516

506517
this.fsPathToEnv.clear();
507518

508519
const sorted = sortEnvironments(this.collection);
509-
const paths = this.api.getPythonProjects().map((p) => path.normalize(p.uri.fsPath));
520+
const projectPaths = this.api.getPythonProjects().map((p) => path.normalize(p.uri.fsPath));
510521
const events: (() => void)[] = [];
511-
for (const p of paths) {
522+
// Iterates through all workspace projects
523+
for (const p of projectPaths) {
512524
const env = await getVenvForWorkspace(p);
513-
514525
if (env) {
515-
const found = this.findEnvironmentByPath(env, sorted) ?? this.findEnvironmentByPath(env, globals);
516-
const previous = this.fsPathToEnv.get(p);
526+
// from env path find PythonEnvironment object in the collection.
527+
let foundEnv = this.findEnvironmentByPath(env, sorted) ?? this.findEnvironmentByPath(env, globals);
528+
const previousEnv = this.fsPathToEnv.get(p);
517529
const pw = this.api.getPythonProject(Uri.file(p));
518-
if (found) {
519-
this.fsPathToEnv.set(p, found);
520-
if (pw && previous?.envId.id !== found.envId.id) {
521-
events.push(() =>
522-
this._onDidChangeEnvironment.fire({ uri: pw.uri, old: undefined, new: found }),
523-
);
524-
}
525-
} else {
530+
if (!foundEnv) {
531+
// attempt to resolve
526532
const resolved = await resolveVenvPythonEnvironmentPath(
527533
env,
528534
this.nativeFinder,
@@ -531,39 +537,39 @@ export class VenvManager implements EnvironmentManager {
531537
this.baseManager,
532538
);
533539
if (resolved) {
534-
// If resolved add it to the collection
535-
this.fsPathToEnv.set(p, resolved);
540+
// If resolved; add it to the venvManager collection
536541
this.addEnvironment(resolved, false);
537-
if (pw && previous?.envId.id !== resolved.envId.id) {
538-
events.push(() =>
539-
this._onDidChangeEnvironment.fire({ uri: pw.uri, old: undefined, new: resolved }),
540-
);
541-
}
542+
foundEnv = resolved;
542543
} else {
543544
this.log.error(`Failed to resolve python environment: ${env}`);
545+
return;
544546
}
545547
}
548+
// Given found env, add it to the map and fire the event if needed.
549+
this.fsPathToEnv.set(p, foundEnv);
550+
if (pw && previousEnv?.envId.id !== foundEnv.envId.id) {
551+
events.push(() =>
552+
this._onDidChangeEnvironment.fire({ uri: pw.uri, old: undefined, new: foundEnv }),
553+
);
554+
}
546555
} else {
547-
// There is NO selected venv, then try and choose the venv that is in the workspace.
548-
if (sorted.length === 1) {
549-
this.fsPathToEnv.set(p, sorted[0]);
550-
} else {
551-
// These are sorted by version and by path length. The assumption is that the user would want to pick
552-
// latest version and the one that is closest to the workspace.
553-
const found = sorted.find((e) => {
554-
const t = this.api.getPythonProject(e.environmentPath)?.uri.fsPath;
555-
return t && path.normalize(t) === p;
556-
});
557-
if (found) {
558-
this.fsPathToEnv.set(p, found);
559-
}
556+
// Search through all known environments (e) and check if any are associated with the current project path. If so, add that environment and path in the map.
557+
const found = sorted.find((e) => {
558+
const t = this.api.getPythonProject(e.environmentPath)?.uri.fsPath;
559+
return t && path.normalize(t) === p;
560+
});
561+
if (found) {
562+
this.fsPathToEnv.set(p, found);
560563
}
561564
}
562565
}
563566

564567
events.forEach((e) => e());
565568
}
566569

570+
/**
571+
* Finds a PythonEnvironment in the given collection (or all environments) that matches the provided file system path. O(e) where e = environments.len
572+
*/
567573
private findEnvironmentByPath(fsPath: string, collection?: PythonEnvironment[]): PythonEnvironment | undefined {
568574
const normalized = path.normalize(fsPath);
569575
const envs = collection ?? this.collection;
@@ -573,6 +579,10 @@ export class VenvManager implements EnvironmentManager {
573579
});
574580
}
575581

582+
/**
583+
* Returns all Python projects associated with the given environment.
584+
* O(p), where p is project.len
585+
*/
576586
public getProjectsByEnvironment(environment: PythonEnvironment): PythonProject[] {
577587
const projects: PythonProject[] = [];
578588
this.fsPathToEnv.forEach((env, fsPath) => {

0 commit comments

Comments
 (0)