diff --git a/src/managers/builtin/sysPythonManager.ts b/src/managers/builtin/sysPythonManager.ts index 31942303..f0d5661f 100644 --- a/src/managers/builtin/sysPythonManager.ts +++ b/src/managers/builtin/sysPythonManager.ts @@ -19,6 +19,7 @@ import { } from '../../api'; import { SysManagerStrings } from '../../common/localize'; import { createDeferred, Deferred } from '../../common/utils/deferred'; +import { normalizePath } from '../../common/utils/pathUtils'; import { NativePythonFinder } from '../common/nativePythonFinder'; import { getLatest } from '../common/utils'; import { @@ -134,7 +135,7 @@ export class SysPythonManager implements EnvironmentManager { } if (scope instanceof Uri) { - const env = this.fsPathToEnv.get(scope.fsPath); + const env = this.fsPathToEnv.get(normalizePath(scope.fsPath)); if (env) { return [env]; } @@ -171,10 +172,11 @@ export class SysPythonManager implements EnvironmentManager { return; } + const normalizedPwPath = normalizePath(pw.uri.fsPath); if (environment) { - this.fsPathToEnv.set(pw.uri.fsPath, environment); + this.fsPathToEnv.set(normalizedPwPath, environment); } else { - this.fsPathToEnv.delete(pw.uri.fsPath); + this.fsPathToEnv.delete(normalizedPwPath); } await setSystemEnvForWorkspace(pw.uri.fsPath, environment?.environmentPath.fsPath); } @@ -191,11 +193,12 @@ export class SysPythonManager implements EnvironmentManager { const before: Map = new Map(); projects.forEach((p) => { - before.set(p.uri.fsPath, this.fsPathToEnv.get(p.uri.fsPath)); + const normalizedPath = normalizePath(p.uri.fsPath); + before.set(p.uri.fsPath, this.fsPathToEnv.get(normalizedPath)); if (environment) { - this.fsPathToEnv.set(p.uri.fsPath, environment); + this.fsPathToEnv.set(normalizedPath, environment); } else { - this.fsPathToEnv.delete(p.uri.fsPath); + this.fsPathToEnv.delete(normalizedPath); } }); @@ -282,16 +285,20 @@ export class SysPythonManager implements EnvironmentManager { } private findEnvironmentByPath(fsPath: string): PythonEnvironment | undefined { - const normalized = path.normalize(fsPath); // /opt/homebrew/bin/python3.12 + const normalized = normalizePath(fsPath); return this.collection.find((e) => { - const n = path.normalize(e.environmentPath.fsPath); - return n === normalized || path.dirname(n) === normalized || path.dirname(path.dirname(n)) === normalized; + const n = normalizePath(e.environmentPath.fsPath); + return ( + n === normalized || + normalizePath(path.dirname(e.environmentPath.fsPath)) === normalized || + normalizePath(path.dirname(path.dirname(e.environmentPath.fsPath))) === normalized + ); }); } private fromEnvMap(uri: Uri): PythonEnvironment | undefined { // Find environment directly using the URI mapping - const env = this.fsPathToEnv.get(uri.fsPath); + const env = this.fsPathToEnv.get(normalizePath(uri.fsPath)); if (env) { return env; } @@ -299,7 +306,7 @@ export class SysPythonManager implements EnvironmentManager { // Find environment using the Python project for the Uri const project = this.api.getPythonProject(uri); if (project) { - return this.fsPathToEnv.get(project.uri.fsPath); + return this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); } return this.globalEnv; @@ -332,24 +339,26 @@ export class SysPythonManager implements EnvironmentManager { } // Try to find workspace environments - const paths = this.api.getPythonProjects().map((p) => p.uri.fsPath); + const projects = this.api.getPythonProjects(); - // Iterate over each path - for (const p of paths) { - const env = await getSystemEnvForWorkspace(p); + // Iterate over each project + for (const project of projects) { + const originalPath = project.uri.fsPath; + const normalizedPath = normalizePath(originalPath); + const env = await getSystemEnvForWorkspace(originalPath); if (env) { const found = this.findEnvironmentByPath(env); if (found) { - this.fsPathToEnv.set(p, found); + this.fsPathToEnv.set(normalizedPath, found); } else { // If not found, resolve the path. const resolved = await resolveSystemPythonEnvironmentPath(env, this.nativeFinder, this.api, this); if (resolved) { // If resolved add it to the collection. - this.fsPathToEnv.set(p, resolved); + this.fsPathToEnv.set(normalizedPath, resolved); this.collection.push(resolved); } else { this.log.error(`Failed to resolve python environment: ${env}`); diff --git a/src/managers/builtin/venvManager.ts b/src/managers/builtin/venvManager.ts index 27ed725b..9bf75add 100644 --- a/src/managers/builtin/venvManager.ts +++ b/src/managers/builtin/venvManager.ts @@ -291,15 +291,15 @@ export class VenvManager implements EnvironmentManager { } private updateCollection(environment: PythonEnvironment): void { - this.collection = this.collection.filter( - (e) => e.environmentPath.fsPath !== environment.environmentPath.fsPath, - ); + const envPath = normalizePath(environment.environmentPath.fsPath); + this.collection = this.collection.filter((e) => normalizePath(e.environmentPath.fsPath) !== envPath); } private updateFsPathToEnv(environment: PythonEnvironment): Uri[] { + const envPath = normalizePath(environment.environmentPath.fsPath); const changed: Uri[] = []; this.fsPathToEnv.forEach((env, uri) => { - if (env.environmentPath.fsPath === environment.environmentPath.fsPath) { + if (normalizePath(env.environmentPath.fsPath) === envPath) { this.fsPathToEnv.delete(uri); changed.push(Uri.file(uri)); } @@ -361,7 +361,7 @@ export class VenvManager implements EnvironmentManager { return []; } - const env = this.fsPathToEnv.get(scope.fsPath); + const env = this.fsPathToEnv.get(normalizePath(scope.fsPath)); return env ? [env] : []; } @@ -378,7 +378,7 @@ export class VenvManager implements EnvironmentManager { return this.globalEnv; } - let env = this.fsPathToEnv.get(project.uri.fsPath); + let env = this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); if (!env) { env = this.findEnvironmentByPath(project.uri.fsPath); } @@ -414,11 +414,12 @@ export class VenvManager implements EnvironmentManager { } } - const before = this.fsPathToEnv.get(pw.uri.fsPath); + const normalizedPwPath = normalizePath(pw.uri.fsPath); + const before = this.fsPathToEnv.get(normalizedPwPath); if (environment) { - this.fsPathToEnv.set(pw.uri.fsPath, environment); + this.fsPathToEnv.set(normalizedPwPath, environment); } else { - this.fsPathToEnv.delete(pw.uri.fsPath); + this.fsPathToEnv.delete(normalizedPwPath); } await setVenvForWorkspace(pw.uri.fsPath, environment?.environmentPath.fsPath); @@ -439,11 +440,12 @@ export class VenvManager implements EnvironmentManager { const before: Map = new Map(); projects.forEach((p) => { - before.set(p.uri.fsPath, this.fsPathToEnv.get(p.uri.fsPath)); + const normalizedPath = normalizePath(p.uri.fsPath); + before.set(p.uri.fsPath, this.fsPathToEnv.get(normalizedPath)); if (environment) { - this.fsPathToEnv.set(p.uri.fsPath, environment); + this.fsPathToEnv.set(normalizedPath, environment); } else { - this.fsPathToEnv.delete(p.uri.fsPath); + this.fsPathToEnv.delete(normalizedPath); } }); @@ -572,16 +574,17 @@ export class VenvManager implements EnvironmentManager { this.fsPathToEnv.clear(); const sorted = sortEnvironments(this.collection); - const projectPaths = this.api.getPythonProjects().map((p) => normalizePath(p.uri.fsPath)); + const projects = this.api.getPythonProjects(); const events: (() => void)[] = []; // Iterates through all workspace projects - for (const p of projectPaths) { - const env = await getVenvForWorkspace(p); + for (const project of projects) { + const originalPath = project.uri.fsPath; + const normalizedPath = normalizePath(originalPath); + const env = await getVenvForWorkspace(originalPath); if (env) { // from env path find PythonEnvironment object in the collection. let foundEnv = this.findEnvironmentByPath(env, sorted) ?? this.findEnvironmentByPath(env, globals); - const previousEnv = this.fsPathToEnv.get(p); - const pw = this.api.getPythonProject(Uri.file(p)); + const previousEnv = this.fsPathToEnv.get(normalizedPath); if (!foundEnv) { // attempt to resolve const resolved = await resolveVenvPythonEnvironmentPath( @@ -601,20 +604,20 @@ export class VenvManager implements EnvironmentManager { } } // Given found env, add it to the map and fire the event if needed. - this.fsPathToEnv.set(p, foundEnv); - if (pw && previousEnv?.envId.id !== foundEnv.envId.id) { + this.fsPathToEnv.set(normalizedPath, foundEnv); + if (previousEnv?.envId.id !== foundEnv.envId.id) { events.push(() => - this._onDidChangeEnvironment.fire({ uri: pw.uri, old: undefined, new: foundEnv }), + this._onDidChangeEnvironment.fire({ uri: project.uri, old: undefined, new: foundEnv }), ); } } else { // 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. const found = sorted.find((e) => { const t = this.api.getPythonProject(e.environmentPath)?.uri.fsPath; - return t && normalizePath(t) === p; + return t && normalizePath(t) === normalizedPath; }); if (found) { - this.fsPathToEnv.set(p, found); + this.fsPathToEnv.set(normalizedPath, found); } } } diff --git a/src/managers/common/utils.ts b/src/managers/common/utils.ts index f2dce1ae..8bc8a081 100644 --- a/src/managers/common/utils.ts +++ b/src/managers/common/utils.ts @@ -68,6 +68,13 @@ export function isGreater(a: string | undefined, b: string | undefined): boolean export function sortEnvironments(collection: PythonEnvironment[]): PythonEnvironment[] { return collection.sort((a, b) => { + // Environments with errors should be sorted to the end + if (a.error && !b.error) { + return 1; + } + if (!a.error && b.error) { + return -1; + } if (a.version !== b.version) { return isGreater(a.version, b.version) ? -1 : 1; } @@ -83,8 +90,12 @@ export function getLatest(collection: PythonEnvironment[]): PythonEnvironment | if (collection.length === 0) { return undefined; } - let latest = collection[0]; - for (const env of collection) { + // Filter out environments with errors first, then find latest + const nonErroredEnvs = collection.filter((e) => !e.error); + const candidates = nonErroredEnvs.length > 0 ? nonErroredEnvs : collection; + + let latest = candidates[0]; + for (const env of candidates) { if (isGreater(env.version, latest.version)) { latest = env; } diff --git a/src/managers/conda/condaEnvManager.ts b/src/managers/conda/condaEnvManager.ts index 45f31d9d..96be534a 100644 --- a/src/managers/conda/condaEnvManager.ts +++ b/src/managers/conda/condaEnvManager.ts @@ -22,6 +22,7 @@ import { import { CondaStrings } from '../../common/localize'; import { traceError } from '../../common/logging'; import { createDeferred, Deferred } from '../../common/utils/deferred'; +import { normalizePath } from '../../common/utils/pathUtils'; import { showErrorMessage, withProgress } from '../../common/window.apis'; import { NativePythonFinder } from '../common/nativePythonFinder'; import { CondaSourcingStatus } from './condaSourcingUtils'; @@ -261,13 +262,13 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { async get(scope: GetEnvironmentScope): Promise { await this.initialize(); if (scope instanceof Uri) { - let env = this.fsPathToEnv.get(scope.fsPath); + let env = this.fsPathToEnv.get(normalizePath(scope.fsPath)); if (env) { return env; } const project = this.api.getPythonProject(scope); if (project) { - env = this.fsPathToEnv.get(project.uri.fsPath); + env = this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); if (env) { return env; } @@ -288,10 +289,11 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { const folder = this.api.getPythonProject(scope); const fsPath = folder?.uri?.fsPath ?? scope.fsPath; if (fsPath) { + const normalizedFsPath = normalizePath(fsPath); if (checkedEnv) { - this.fsPathToEnv.set(fsPath, checkedEnv); + this.fsPathToEnv.set(normalizedFsPath, checkedEnv); } else { - this.fsPathToEnv.delete(fsPath); + this.fsPathToEnv.delete(normalizedFsPath); } await setCondaForWorkspace(fsPath, checkedEnv?.environmentPath.fsPath); } @@ -307,11 +309,12 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { const before: Map = new Map(); projects.forEach((p) => { - before.set(p.uri.fsPath, this.fsPathToEnv.get(p.uri.fsPath)); + const normalizedPath = normalizePath(p.uri.fsPath); + before.set(p.uri.fsPath, this.fsPathToEnv.get(normalizedPath)); if (checkedEnv) { - this.fsPathToEnv.set(p.uri.fsPath, checkedEnv); + this.fsPathToEnv.set(normalizedPath, checkedEnv); } else { - this.fsPathToEnv.delete(p.uri.fsPath); + this.fsPathToEnv.delete(normalizedPath); } }); @@ -405,22 +408,24 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { }); // Try to find workspace environments - const paths = this.api.getPythonProjects().map((p) => p.uri.fsPath); - for (const p of paths) { - const env = await getCondaForWorkspace(p); + const projects = this.api.getPythonProjects(); + for (const project of projects) { + const originalPath = project.uri.fsPath; + const normalizedPath = normalizePath(originalPath); + const env = await getCondaForWorkspace(originalPath); if (env) { const found = this.findEnvironmentByPath(env); if (found) { - this.fsPathToEnv.set(p, found); + this.fsPathToEnv.set(normalizedPath, found); } else { // If not found, resolve the conda path. Could be portable conda. const resolved = await resolveCondaPath(env, this.nativeFinder, this.api, this.log, this); if (resolved) { // If resolved add it to the collection - this.fsPathToEnv.set(p, resolved); + this.fsPathToEnv.set(normalizedPath, resolved); this.collection.push(resolved); } else { this.log.error(`Failed to resolve conda environment: ${env}`); @@ -430,16 +435,16 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { // If there is not an environment already assigned by user to this project // then see if there is one in the collection if (pathSorted.length === 1) { - this.fsPathToEnv.set(p, pathSorted[0]); + this.fsPathToEnv.set(normalizedPath, pathSorted[0]); } else { // If there is more than one environment then we need to check if the project // is a subfolder of one of the environments const found = pathSorted.find((e) => { const t = this.api.getPythonProject(e.environmentPath)?.uri.fsPath; - return t && path.normalize(t) === p; + return t && normalizePath(t) === normalizedPath; }); if (found) { - this.fsPathToEnv.set(p, found); + this.fsPathToEnv.set(normalizedPath, found); } } } @@ -448,7 +453,7 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { private fromEnvMap(uri: Uri): PythonEnvironment | undefined { // Find environment directly using the URI mapping - const env = this.fsPathToEnv.get(uri.fsPath); + const env = this.fsPathToEnv.get(normalizePath(uri.fsPath)); if (env) { return env; } @@ -456,7 +461,7 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { // Find environment using the Python project for the Uri const project = this.api.getPythonProject(uri); if (project) { - return this.fsPathToEnv.get(project.uri.fsPath); + return this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); } return undefined; @@ -470,10 +475,14 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { } private findEnvironmentByPath(fsPath: string): PythonEnvironment | undefined { - const normalized = path.normalize(fsPath); + const normalized = normalizePath(fsPath); return this.collection.find((e) => { - const n = path.normalize(e.environmentPath.fsPath); - return n === normalized || path.dirname(n) === normalized || path.dirname(path.dirname(n)) === normalized; + const n = normalizePath(e.environmentPath.fsPath); + return ( + n === normalized || + normalizePath(path.dirname(e.environmentPath.fsPath)) === normalized || + normalizePath(path.dirname(path.dirname(e.environmentPath.fsPath))) === normalized + ); }); } diff --git a/src/managers/pipenv/pipenvManager.ts b/src/managers/pipenv/pipenvManager.ts index 81a3b5cf..32c99a9c 100644 --- a/src/managers/pipenv/pipenvManager.ts +++ b/src/managers/pipenv/pipenvManager.ts @@ -16,6 +16,7 @@ import { } from '../../api'; import { PipenvStrings } from '../../common/localize'; import { createDeferred, Deferred } from '../../common/utils/deferred'; +import { normalizePath } from '../../common/utils/pathUtils'; import { withProgress } from '../../common/window.apis'; import { NativePythonFinder } from '../common/nativePythonFinder'; import { @@ -101,7 +102,7 @@ export class PipenvManager implements EnvironmentManager { if (envPath) { const env = this.findEnvironmentByPath(envPath); if (env) { - this.fsPathToEnv.set(project.uri.fsPath, env); + this.fsPathToEnv.set(normalizePath(project.uri.fsPath), env); } } } @@ -114,8 +115,11 @@ export class PipenvManager implements EnvironmentManager { } private findEnvironmentByPath(fsPath: string): PythonEnvironment | undefined { + const normalized = normalizePath(fsPath); return this.collection.find( - (env) => env.environmentPath.fsPath === fsPath || env.execInfo?.run.executable === fsPath, + (env) => + normalizePath(env.environmentPath.fsPath) === normalized || + (env.execInfo?.run.executable && normalizePath(env.execInfo.run.executable) === normalized), ); } @@ -171,7 +175,7 @@ export class PipenvManager implements EnvironmentManager { if (scope instanceof Uri) { const project = this.api.getPythonProject(scope); if (project) { - const env = this.fsPathToEnv.get(project.uri.fsPath); + const env = this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); return env ? [env] : []; } } @@ -199,11 +203,12 @@ export class PipenvManager implements EnvironmentManager { return; } - const before = this.fsPathToEnv.get(project.uri.fsPath); + const normalizedPath = normalizePath(project.uri.fsPath); + const before = this.fsPathToEnv.get(normalizedPath); if (environment) { - this.fsPathToEnv.set(project.uri.fsPath, environment); + this.fsPathToEnv.set(normalizedPath, environment); } else { - this.fsPathToEnv.delete(project.uri.fsPath); + this.fsPathToEnv.delete(normalizedPath); } await setPipenvForWorkspace(project.uri.fsPath, environment?.environmentPath.fsPath); @@ -226,11 +231,12 @@ export class PipenvManager implements EnvironmentManager { const before: Map = new Map(); projects.forEach((p) => { - before.set(p.uri.fsPath, this.fsPathToEnv.get(p.uri.fsPath)); + const normalizedPath = normalizePath(p.uri.fsPath); + before.set(p.uri.fsPath, this.fsPathToEnv.get(normalizedPath)); if (environment) { - this.fsPathToEnv.set(p.uri.fsPath, environment); + this.fsPathToEnv.set(normalizedPath, environment); } else { - this.fsPathToEnv.delete(p.uri.fsPath); + this.fsPathToEnv.delete(normalizedPath); } }); @@ -258,7 +264,7 @@ export class PipenvManager implements EnvironmentManager { if (scope instanceof Uri) { const project = this.api.getPythonProject(scope); if (project) { - return this.fsPathToEnv.get(project.uri.fsPath); + return this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); } } diff --git a/src/managers/poetry/poetryManager.ts b/src/managers/poetry/poetryManager.ts index dc2a3e6d..25b12cf9 100644 --- a/src/managers/poetry/poetryManager.ts +++ b/src/managers/poetry/poetryManager.ts @@ -18,6 +18,7 @@ import { import { PoetryStrings } from '../../common/localize'; import { traceError, traceInfo } from '../../common/logging'; import { createDeferred, Deferred } from '../../common/utils/deferred'; +import { normalizePath } from '../../common/utils/pathUtils'; import { withProgress } from '../../common/window.apis'; import { NativePythonFinder } from '../common/nativePythonFinder'; import { getLatest } from '../common/utils'; @@ -145,13 +146,13 @@ export class PoetryManager implements EnvironmentManager, Disposable { async get(scope: GetEnvironmentScope): Promise { await this.initialize(); if (scope instanceof Uri) { - let env = this.fsPathToEnv.get(scope.fsPath); + let env = this.fsPathToEnv.get(normalizePath(scope.fsPath)); if (env) { return env; } const project = this.api.getPythonProject(scope); if (project) { - env = this.fsPathToEnv.get(project.uri.fsPath); + env = this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); if (env) { return env; } @@ -168,10 +169,11 @@ export class PoetryManager implements EnvironmentManager, Disposable { const folder = this.api.getPythonProject(scope); const fsPath = folder?.uri?.fsPath ?? scope.fsPath; if (fsPath) { + const normalizedFsPath = normalizePath(fsPath); if (environment) { - this.fsPathToEnv.set(fsPath, environment); + this.fsPathToEnv.set(normalizedFsPath, environment); } else { - this.fsPathToEnv.delete(fsPath); + this.fsPathToEnv.delete(normalizedFsPath); } await setPoetryForWorkspace(fsPath, environment?.environmentPath?.fsPath); } @@ -187,11 +189,12 @@ export class PoetryManager implements EnvironmentManager, Disposable { const before: Map = new Map(); projects.forEach((p) => { - before.set(p.uri.fsPath, this.fsPathToEnv.get(p.uri.fsPath)); + const normalizedPath = normalizePath(p.uri.fsPath); + before.set(p.uri.fsPath, this.fsPathToEnv.get(normalizedPath)); if (environment) { - this.fsPathToEnv.set(p.uri.fsPath, environment); + this.fsPathToEnv.set(normalizedPath, environment); } else { - this.fsPathToEnv.delete(p.uri.fsPath); + this.fsPathToEnv.delete(normalizedPath); } }); @@ -271,22 +274,24 @@ export class PoetryManager implements EnvironmentManager, Disposable { }); // Try to find workspace environments - const paths = this.api.getPythonProjects().map((p) => p.uri.fsPath); - for (const p of paths) { - const env = await getPoetryForWorkspace(p); + const projects = this.api.getPythonProjects(); + for (const project of projects) { + const originalPath = project.uri.fsPath; + const normalizedPath = normalizePath(originalPath); + const env = await getPoetryForWorkspace(originalPath); if (env) { const found = this.findEnvironmentByPath(env); if (found) { - this.fsPathToEnv.set(p, found); + this.fsPathToEnv.set(normalizedPath, found); } else { // If not found, resolve the poetry path const resolved = await resolvePoetryPath(env, this.nativeFinder, this.api, this); if (resolved) { // If resolved add it to the collection - this.fsPathToEnv.set(p, resolved); + this.fsPathToEnv.set(normalizedPath, resolved); this.collection.push(resolved); } else { traceError(`Failed to resolve poetry environment: ${env}`); @@ -296,16 +301,16 @@ export class PoetryManager implements EnvironmentManager, Disposable { // If there is not an environment already assigned by user to this project // then see if there is one in the collection if (pathSorted.length === 1) { - this.fsPathToEnv.set(p, pathSorted[0]); + this.fsPathToEnv.set(normalizedPath, pathSorted[0]); } else { // If there is more than one environment then we need to check if the project // is a subfolder of one of the environments const found = pathSorted.find((e) => { const t = this.api.getPythonProject(e.environmentPath)?.uri.fsPath; - return t && path.normalize(t) === p; + return t && normalizePath(t) === normalizedPath; }); if (found) { - this.fsPathToEnv.set(p, found); + this.fsPathToEnv.set(normalizedPath, found); } } } @@ -314,7 +319,7 @@ export class PoetryManager implements EnvironmentManager, Disposable { private fromEnvMap(uri: Uri): PythonEnvironment | undefined { // Find environment directly using the URI mapping - const env = this.fsPathToEnv.get(uri.fsPath); + const env = this.fsPathToEnv.get(normalizePath(uri.fsPath)); if (env) { return env; } @@ -322,17 +327,21 @@ export class PoetryManager implements EnvironmentManager, Disposable { // Find environment using the Python project for the Uri const project = this.api.getPythonProject(uri); if (project) { - return this.fsPathToEnv.get(project.uri.fsPath); + return this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); } return undefined; } private findEnvironmentByPath(fsPath: string): PythonEnvironment | undefined { - const normalized = path.normalize(fsPath); + const normalized = normalizePath(fsPath); return this.collection.find((e) => { - const n = path.normalize(e.environmentPath.fsPath); - return n === normalized || path.dirname(n) === normalized || path.dirname(path.dirname(n)) === normalized; + const n = normalizePath(e.environmentPath.fsPath); + return ( + n === normalized || + normalizePath(path.dirname(e.environmentPath.fsPath)) === normalized || + normalizePath(path.dirname(path.dirname(e.environmentPath.fsPath))) === normalized + ); }); } } diff --git a/src/managers/pyenv/pyenvManager.ts b/src/managers/pyenv/pyenvManager.ts index 4eaacd64..31f9d360 100644 --- a/src/managers/pyenv/pyenvManager.ts +++ b/src/managers/pyenv/pyenvManager.ts @@ -18,6 +18,7 @@ import { import { PyenvStrings } from '../../common/localize'; import { traceError, traceInfo } from '../../common/logging'; import { createDeferred, Deferred } from '../../common/utils/deferred'; +import { normalizePath } from '../../common/utils/pathUtils'; import { withProgress } from '../../common/window.apis'; import { NativePythonFinder } from '../common/nativePythonFinder'; import { getLatest } from '../common/utils'; @@ -143,13 +144,13 @@ export class PyEnvManager implements EnvironmentManager, Disposable { async get(scope: GetEnvironmentScope): Promise { await this.initialize(); if (scope instanceof Uri) { - let env = this.fsPathToEnv.get(scope.fsPath); + let env = this.fsPathToEnv.get(normalizePath(scope.fsPath)); if (env) { return env; } const project = this.api.getPythonProject(scope); if (project) { - env = this.fsPathToEnv.get(project.uri.fsPath); + env = this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); if (env) { return env; } @@ -165,10 +166,11 @@ export class PyEnvManager implements EnvironmentManager, Disposable { const folder = this.api.getPythonProject(scope); const fsPath = folder?.uri?.fsPath ?? scope.fsPath; if (fsPath) { + const normalizedFsPath = normalizePath(fsPath); if (environment) { - this.fsPathToEnv.set(fsPath, environment); + this.fsPathToEnv.set(normalizedFsPath, environment); } else { - this.fsPathToEnv.delete(fsPath); + this.fsPathToEnv.delete(normalizedFsPath); } await setPyenvForWorkspace(fsPath, environment?.environmentPath?.fsPath); } @@ -184,11 +186,12 @@ export class PyEnvManager implements EnvironmentManager, Disposable { const before: Map = new Map(); projects.forEach((p) => { - before.set(p.uri.fsPath, this.fsPathToEnv.get(p.uri.fsPath)); + const normalizedPath = normalizePath(p.uri.fsPath); + before.set(p.uri.fsPath, this.fsPathToEnv.get(normalizedPath)); if (environment) { - this.fsPathToEnv.set(p.uri.fsPath, environment); + this.fsPathToEnv.set(normalizedPath, environment); } else { - this.fsPathToEnv.delete(p.uri.fsPath); + this.fsPathToEnv.delete(normalizedPath); } }); @@ -268,22 +271,24 @@ export class PyEnvManager implements EnvironmentManager, Disposable { }); // Try to find workspace environments - const paths = this.api.getPythonProjects().map((p) => p.uri.fsPath); - for (const p of paths) { - const env = await getPyenvForWorkspace(p); + const projects = this.api.getPythonProjects(); + for (const project of projects) { + const originalPath = project.uri.fsPath; + const normalizedPath = normalizePath(originalPath); + const env = await getPyenvForWorkspace(originalPath); if (env) { const found = this.findEnvironmentByPath(env); if (found) { - this.fsPathToEnv.set(p, found); + this.fsPathToEnv.set(normalizedPath, found); } else { // If not found, resolve the pyenv path. Could be portable pyenv. const resolved = await resolvePyenvPath(env, this.nativeFinder, this.api, this); if (resolved) { // If resolved add it to the collection - this.fsPathToEnv.set(p, resolved); + this.fsPathToEnv.set(normalizedPath, resolved); this.collection.push(resolved); } else { traceError(`Failed to resolve pyenv environment: ${env}`); @@ -293,16 +298,16 @@ export class PyEnvManager implements EnvironmentManager, Disposable { // If there is not an environment already assigned by user to this project // then see if there is one in the collection if (pathSorted.length === 1) { - this.fsPathToEnv.set(p, pathSorted[0]); + this.fsPathToEnv.set(normalizedPath, pathSorted[0]); } else { // If there is more than one environment then we need to check if the project // is a subfolder of one of the environments const found = pathSorted.find((e) => { const t = this.api.getPythonProject(e.environmentPath)?.uri.fsPath; - return t && path.normalize(t) === p; + return t && normalizePath(t) === normalizedPath; }); if (found) { - this.fsPathToEnv.set(p, found); + this.fsPathToEnv.set(normalizedPath, found); } } } @@ -311,7 +316,7 @@ export class PyEnvManager implements EnvironmentManager, Disposable { private fromEnvMap(uri: Uri): PythonEnvironment | undefined { // Find environment directly using the URI mapping - const env = this.fsPathToEnv.get(uri.fsPath); + const env = this.fsPathToEnv.get(normalizePath(uri.fsPath)); if (env) { return env; } @@ -319,17 +324,21 @@ export class PyEnvManager implements EnvironmentManager, Disposable { // Find environment using the Python project for the Uri const project = this.api.getPythonProject(uri); if (project) { - return this.fsPathToEnv.get(project.uri.fsPath); + return this.fsPathToEnv.get(normalizePath(project.uri.fsPath)); } return undefined; } private findEnvironmentByPath(fsPath: string): PythonEnvironment | undefined { - const normalized = path.normalize(fsPath); + const normalized = normalizePath(fsPath); return this.collection.find((e) => { - const n = path.normalize(e.environmentPath.fsPath); - return n === normalized || path.dirname(n) === normalized || path.dirname(path.dirname(n)) === normalized; + const n = normalizePath(e.environmentPath.fsPath); + return ( + n === normalized || + normalizePath(path.dirname(e.environmentPath.fsPath)) === normalized || + normalizePath(path.dirname(path.dirname(e.environmentPath.fsPath))) === normalized + ); }); } }