Skip to content

Commit 695bea3

Browse files
committed
add separate function
1 parent 19b990d commit 695bea3

File tree

7 files changed

+349
-200
lines changed

7 files changed

+349
-200
lines changed

src/managers/builtin/sysPythonManager.ts

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import {
1818
SetEnvironmentScope,
1919
} from '../../api';
2020
import { SysManagerStrings } from '../../common/localize';
21-
import { traceError, traceWarn } from '../../common/logging';
2221
import { createDeferred, Deferred } from '../../common/utils/deferred';
2322
import { normalizePath } from '../../common/utils/pathUtils';
23+
import { tryFastPathGet } from '../common/fastPath';
2424
import { NativePythonFinder } from '../common/nativePythonFinder';
2525
import { getLatest } from '../common/utils';
2626
import {
@@ -146,41 +146,20 @@ export class SysPythonManager implements EnvironmentManager {
146146
}
147147

148148
async get(scope: GetEnvironmentScope): Promise<PythonEnvironment | undefined> {
149-
// Fast path: if init hasn't completed yet and we have a persisted env, resolve it
150-
// directly without waiting for full discovery
151-
if ((!this._initialized || !this._initialized.completed) && scope instanceof Uri) {
152-
const project = this.api.getPythonProject(scope);
153-
const fsPath = project?.uri.fsPath ?? scope.fsPath;
154-
const persistedPath = await getSystemEnvForWorkspace(fsPath);
155-
156-
if (persistedPath) {
157-
try {
158-
const resolved = await resolveSystemPythonEnvironmentPath(
159-
persistedPath,
160-
this.nativeFinder,
161-
this.api,
162-
this,
163-
);
164-
if (resolved) {
165-
// Ensure full init is running in background (may already be in progress)
166-
if (!this._initialized) {
167-
this._initialized = createDeferred();
168-
this.internalRefresh(false, SysManagerStrings.sysManagerDiscovering).then(
169-
() => this._initialized!.resolve(),
170-
(err) => {
171-
traceError(`[system] Background initialization failed: ${err}`);
172-
this._initialized!.resolve();
173-
},
174-
);
175-
}
176-
return resolved;
177-
}
178-
} catch (err) {
179-
traceWarn(
180-
`[system] Fast path resolve failed for '${persistedPath}', falling back to full init: ${err}`,
181-
);
182-
}
149+
const fastResult = await tryFastPathGet({
150+
initialized: this._initialized,
151+
scope,
152+
label: 'system',
153+
getProjectFsPath: (s) => this.api.getPythonProject(s)?.uri.fsPath ?? s.fsPath,
154+
getPersistedPath: (fsPath) => getSystemEnvForWorkspace(fsPath),
155+
resolve: (p) => resolveSystemPythonEnvironmentPath(p, this.nativeFinder, this.api, this),
156+
startBackgroundInit: () => this.internalRefresh(false, SysManagerStrings.sysManagerDiscovering),
157+
});
158+
if (fastResult) {
159+
if (fastResult.newDeferred) {
160+
this._initialized = fastResult.newDeferred;
183161
}
162+
return fastResult.env;
184163
}
185164

186165
await this.initialize();

src/managers/builtin/venvManager.ts

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { createDeferred, Deferred } from '../../common/utils/deferred';
3535
import { normalizePath } from '../../common/utils/pathUtils';
3636
import { showErrorMessage, showInformationMessage, withProgress } from '../../common/window.apis';
3737
import { findParentIfFile } from '../../features/envCommands';
38+
import { tryFastPathGet } from '../common/fastPath';
3839
import { NativePythonFinder } from '../common/nativePythonFinder';
3940
import { getLatest, shortVersion, sortEnvironments } from '../common/utils';
4041
import { promptInstallPythonViaUv } from './uvPythonInstaller';
@@ -366,42 +367,20 @@ export class VenvManager implements EnvironmentManager {
366367
}
367368

368369
async get(scope: GetEnvironmentScope): Promise<PythonEnvironment | undefined> {
369-
// Fast path: if init hasn't completed yet and we have a persisted env, resolve it
370-
// directly without waiting for full discovery
371-
if ((!this._initialized || !this._initialized.completed) && scope instanceof Uri) {
372-
const project = this.api.getPythonProject(scope);
373-
const fsPath = project?.uri.fsPath ?? scope.fsPath;
374-
const persistedPath = await getVenvForWorkspace(fsPath);
375-
376-
if (persistedPath) {
377-
try {
378-
const resolved = await resolveVenvPythonEnvironmentPath(
379-
persistedPath,
380-
this.nativeFinder,
381-
this.api,
382-
this,
383-
this.baseManager,
384-
);
385-
if (resolved) {
386-
// Ensure full init is running in background (may already be in progress)
387-
if (!this._initialized) {
388-
this._initialized = createDeferred();
389-
this.internalRefresh(undefined, false, VenvManagerStrings.venvInitialize).then(
390-
() => this._initialized!.resolve(),
391-
(err) => {
392-
traceError(`[venv] Background initialization failed: ${err}`);
393-
this._initialized!.resolve();
394-
},
395-
);
396-
}
397-
return resolved;
398-
}
399-
} catch (err) {
400-
traceWarn(
401-
`[venv] Fast path resolve failed for '${persistedPath}', falling back to full init: ${err}`,
402-
);
403-
}
370+
const fastResult = await tryFastPathGet({
371+
initialized: this._initialized,
372+
scope,
373+
label: 'venv',
374+
getProjectFsPath: (s) => this.api.getPythonProject(s)?.uri.fsPath ?? s.fsPath,
375+
getPersistedPath: (fsPath) => getVenvForWorkspace(fsPath),
376+
resolve: (p) => resolveVenvPythonEnvironmentPath(p, this.nativeFinder, this.api, this, this.baseManager),
377+
startBackgroundInit: () => this.internalRefresh(undefined, false, VenvManagerStrings.venvInitialize),
378+
});
379+
if (fastResult) {
380+
if (fastResult.newDeferred) {
381+
this._initialized = fastResult.newDeferred;
404382
}
383+
return fastResult.env;
405384
}
406385

407386
await this.initialize();

src/managers/common/fastPath.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { Uri } from 'vscode';
5+
import { GetEnvironmentScope, PythonEnvironment } from '../../api';
6+
import { traceError, traceWarn } from '../../common/logging';
7+
import { createDeferred, Deferred } from '../../common/utils/deferred';
8+
9+
/**
10+
* Options for the fast-path resolution in manager.get().
11+
*/
12+
export interface FastPathOptions {
13+
/** The current _initialized deferred (may be undefined if init hasn't started). */
14+
initialized: Deferred<void> | undefined;
15+
/** The scope passed to get(). */
16+
scope: GetEnvironmentScope;
17+
/** Label for log messages, e.g. 'venv', 'conda'. */
18+
label: string;
19+
/** Gets the project fsPath for a given Uri scope. */
20+
getProjectFsPath: (scope: Uri) => string;
21+
/** Reads the persisted env path for a workspace fsPath. */
22+
getPersistedPath: (workspaceFsPath: string) => Promise<string | undefined>;
23+
/** Resolves a persisted path to a full PythonEnvironment. */
24+
resolve: (persistedPath: string) => Promise<PythonEnvironment | undefined>;
25+
/** Starts background initialization (full discovery). Returns a promise that completes when init is done. */
26+
startBackgroundInit: () => Promise<void> | Thenable<void>;
27+
}
28+
29+
/**
30+
* Result from a successful fast-path resolution.
31+
*/
32+
export interface FastPathResult {
33+
/** The resolved environment. */
34+
env: PythonEnvironment;
35+
/** A new deferred if one was created (caller must assign to _initialized). */
36+
newDeferred?: Deferred<void>;
37+
}
38+
39+
/**
40+
* Attempts fast-path resolution for manager.get(): if full initialization hasn't completed yet
41+
* and there's a persisted environment for the workspace, resolve it directly via nativeFinder
42+
* instead of waiting for full discovery.
43+
*
44+
* Returns the resolved environment (with an optional new deferred) if successful, or undefined
45+
* to fall through to the normal init path.
46+
*/
47+
export async function tryFastPathGet(opts: FastPathOptions): Promise<FastPathResult | undefined> {
48+
if ((!opts.initialized || !opts.initialized.completed) && opts.scope instanceof Uri) {
49+
const fsPath = opts.getProjectFsPath(opts.scope);
50+
const persistedPath = await opts.getPersistedPath(fsPath);
51+
52+
if (persistedPath) {
53+
try {
54+
const resolved = await opts.resolve(persistedPath);
55+
if (resolved) {
56+
let newDeferred: Deferred<void> | undefined;
57+
// Ensure full init is running in background (may already be in progress)
58+
if (!opts.initialized) {
59+
newDeferred = createDeferred();
60+
const deferred = newDeferred;
61+
Promise.resolve(opts.startBackgroundInit()).then(
62+
() => deferred.resolve(),
63+
(err) => {
64+
traceError(`[${opts.label}] Background initialization failed: ${err}`);
65+
deferred.resolve();
66+
},
67+
);
68+
}
69+
return { env: resolved, newDeferred };
70+
}
71+
} catch (err) {
72+
traceWarn(
73+
`[${opts.label}] Fast path resolve failed for '${persistedPath}', falling back to full init: ${err}`,
74+
);
75+
}
76+
}
77+
}
78+
return undefined;
79+
}

src/managers/conda/condaEnvManager.ts

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ import {
2020
SetEnvironmentScope,
2121
} from '../../api';
2222
import { CondaStrings } from '../../common/localize';
23-
import { traceError, traceWarn } from '../../common/logging';
23+
import { traceError } from '../../common/logging';
2424
import { createDeferred, Deferred } from '../../common/utils/deferred';
2525
import { normalizePath } from '../../common/utils/pathUtils';
2626
import { showErrorMessage, showInformationMessage, withProgress } from '../../common/window.apis';
27+
import { tryFastPathGet } from '../common/fastPath';
2728
import { NativePythonFinder } from '../common/nativePythonFinder';
2829
import { CondaSourcingStatus } from './condaSourcingUtils';
2930
import {
@@ -260,54 +261,30 @@ export class CondaEnvManager implements EnvironmentManager, Disposable {
260261
}
261262
}
262263
async get(scope: GetEnvironmentScope): Promise<PythonEnvironment | undefined> {
263-
// Fast path: if init hasn't completed yet and we have a persisted env, resolve it
264-
// directly without waiting for full discovery
265-
if ((!this._initialized || !this._initialized.completed) && scope instanceof Uri) {
266-
const project = this.api.getPythonProject(scope);
267-
const fsPath = project?.uri.fsPath ?? scope.fsPath;
268-
const persistedPath = await getCondaForWorkspace(fsPath);
269-
270-
if (persistedPath) {
271-
try {
272-
const resolved = await resolveCondaPath(persistedPath, this.nativeFinder, this.api, this.log, this);
273-
if (resolved) {
274-
// Ensure full init is running in background (may already be in progress)
275-
if (!this._initialized) {
276-
this._initialized = createDeferred();
277-
withProgress(
278-
{ location: ProgressLocation.Window, title: CondaStrings.condaDiscovering },
279-
async () => {
280-
this.collection = await refreshCondaEnvs(
281-
false,
282-
this.nativeFinder,
283-
this.api,
284-
this.log,
285-
this,
286-
);
287-
await this.loadEnvMap();
288-
this._onDidChangeEnvironments.fire(
289-
this.collection.map((e) => ({
290-
environment: e,
291-
kind: EnvironmentChangeKind.add,
292-
})),
293-
);
294-
},
295-
).then(
296-
() => this._initialized!.resolve(),
297-
(err) => {
298-
traceError(`[conda] Background initialization failed: ${err}`);
299-
this._initialized!.resolve();
300-
},
301-
);
302-
}
303-
return resolved;
304-
}
305-
} catch (err) {
306-
traceWarn(
307-
`[conda] Fast path resolve failed for '${persistedPath}', falling back to full init: ${err}`,
264+
const fastResult = await tryFastPathGet({
265+
initialized: this._initialized,
266+
scope,
267+
label: 'conda',
268+
getProjectFsPath: (s) => this.api.getPythonProject(s)?.uri.fsPath ?? s.fsPath,
269+
getPersistedPath: (fsPath) => getCondaForWorkspace(fsPath),
270+
resolve: (p) => resolveCondaPath(p, this.nativeFinder, this.api, this.log, this),
271+
startBackgroundInit: () =>
272+
withProgress({ location: ProgressLocation.Window, title: CondaStrings.condaDiscovering }, async () => {
273+
this.collection = await refreshCondaEnvs(false, this.nativeFinder, this.api, this.log, this);
274+
await this.loadEnvMap();
275+
this._onDidChangeEnvironments.fire(
276+
this.collection.map((e) => ({
277+
environment: e,
278+
kind: EnvironmentChangeKind.add,
279+
})),
308280
);
309-
}
281+
}),
282+
});
283+
if (fastResult) {
284+
if (fastResult.newDeferred) {
285+
this._initialized = fastResult.newDeferred;
310286
}
287+
return fastResult.env;
311288
}
312289

313290
await this.initialize();

src/managers/pipenv/pipenvManager.ts

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import {
1515
SetEnvironmentScope,
1616
} from '../../api';
1717
import { PipenvStrings } from '../../common/localize';
18-
import { traceError, traceWarn } from '../../common/logging';
1918
import { createDeferred, Deferred } from '../../common/utils/deferred';
2019
import { normalizePath } from '../../common/utils/pathUtils';
2120
import { withProgress } from '../../common/window.apis';
21+
import { tryFastPathGet } from '../common/fastPath';
2222
import { NativePythonFinder } from '../common/nativePythonFinder';
2323
import {
2424
clearPipenvCache,
@@ -256,48 +256,33 @@ export class PipenvManager implements EnvironmentManager {
256256
}
257257

258258
async get(scope: GetEnvironmentScope): Promise<PythonEnvironment | undefined> {
259-
// Fast path: if init hasn't completed yet and we have a persisted env, resolve it
260-
// directly without waiting for full discovery
261-
if ((!this._initialized || !this._initialized.completed) && scope instanceof Uri) {
262-
const project = this.api.getPythonProject(scope);
263-
const fsPath = project?.uri.fsPath ?? scope.fsPath;
264-
const persistedPath = await getPipenvForWorkspace(fsPath);
265-
266-
if (persistedPath) {
267-
try {
268-
const resolved = await resolvePipenvPath(persistedPath, this.nativeFinder, this.api, this);
269-
if (resolved) {
270-
// Ensure full init is running in background (may already be in progress)
271-
if (!this._initialized) {
272-
this._initialized = createDeferred();
273-
withProgress(
274-
{ location: ProgressLocation.Window, title: PipenvStrings.pipenvDiscovering },
275-
async () => {
276-
this.collection = await refreshPipenv(false, this.nativeFinder, this.api, this);
277-
await this.loadEnvMap();
278-
this._onDidChangeEnvironments.fire(
279-
this.collection.map((e) => ({
280-
environment: e,
281-
kind: EnvironmentChangeKind.add,
282-
})),
283-
);
284-
},
285-
).then(
286-
() => this._initialized!.resolve(),
287-
(err) => {
288-
traceError(`[pipenv] Background initialization failed: ${err}`);
289-
this._initialized!.resolve();
290-
},
291-
);
292-
}
293-
return resolved;
294-
}
295-
} catch (err) {
296-
traceWarn(
297-
`[pipenv] Fast path resolve failed for '${persistedPath}', falling back to full init: ${err}`,
298-
);
299-
}
259+
const fastResult = await tryFastPathGet({
260+
initialized: this._initialized,
261+
scope,
262+
label: 'pipenv',
263+
getProjectFsPath: (s) => this.api.getPythonProject(s)?.uri.fsPath ?? s.fsPath,
264+
getPersistedPath: (fsPath) => getPipenvForWorkspace(fsPath),
265+
resolve: (p) => resolvePipenvPath(p, this.nativeFinder, this.api, this),
266+
startBackgroundInit: () =>
267+
withProgress(
268+
{ location: ProgressLocation.Window, title: PipenvStrings.pipenvDiscovering },
269+
async () => {
270+
this.collection = await refreshPipenv(false, this.nativeFinder, this.api, this);
271+
await this.loadEnvMap();
272+
this._onDidChangeEnvironments.fire(
273+
this.collection.map((e) => ({
274+
environment: e,
275+
kind: EnvironmentChangeKind.add,
276+
})),
277+
);
278+
},
279+
),
280+
});
281+
if (fastResult) {
282+
if (fastResult.newDeferred) {
283+
this._initialized = fastResult.newDeferred;
300284
}
285+
return fastResult.env;
301286
}
302287

303288
await this.initialize();

0 commit comments

Comments
 (0)