forked from DonJayamanne/vscode-python-manager
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathenvironmentInfoService.ts
More file actions
164 lines (148 loc) · 6.4 KB
/
Copy pathenvironmentInfoService.ts
File metadata and controls
164 lines (148 loc) · 6.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { IDisposableRegistry } from '../../../common/types';
import { createDeferred, Deferred } from '../../../common/utils/async';
import { createRunningWorkerPool, IWorkerPool, QueuePosition } from '../../../common/utils/workerPool';
import { getInterpreterInfo, InterpreterInformation } from './interpreter';
import { buildPythonExecInfo } from '../../exec';
import { traceError, traceInfo } from '../../../logging';
import { Conda, CONDA_RUN_TIMEOUT, isCondaEnvironment } from '../../common/environmentManagers/conda';
import { PythonEnvInfo, PythonEnvKind } from '.';
import { normCasePath } from '../../common/externalDependencies';
export enum EnvironmentInfoServiceQueuePriority {
Default,
High,
}
export interface IEnvironmentInfoService {
getEnvironmentInfo(
env: PythonEnvInfo,
priority?: EnvironmentInfoServiceQueuePriority,
): Promise<InterpreterInformation | undefined>;
}
async function buildEnvironmentInfo(env: PythonEnvInfo): Promise<InterpreterInformation | undefined> {
const python = [env.executable.filename];
const interpreterInfo = await getInterpreterInfo(buildPythonExecInfo(python, undefined, env.executable.filename));
return interpreterInfo;
}
async function buildEnvironmentInfoUsingCondaRun(env: PythonEnvInfo): Promise<InterpreterInformation | undefined> {
const conda = await Conda.getConda();
const condaEnv = await conda?.getCondaEnvironment(env.executable.filename);
if (!condaEnv) {
return undefined;
}
const python = await conda?.getRunPythonArgs(condaEnv);
if (!python) {
return undefined;
}
const interpreterInfo = await getInterpreterInfo(
buildPythonExecInfo(python, undefined, env.executable.filename),
CONDA_RUN_TIMEOUT,
);
return interpreterInfo;
}
class EnvironmentInfoService implements IEnvironmentInfoService {
// Caching environment here in-memory. This is so that we don't have to run this on the same
// path again and again in a given session. This information will likely not change in a given
// session. There are definitely cases where this will change. But a simple reload should address
// those.
private readonly cache: Map<string, Deferred<InterpreterInformation>> = new Map<
string,
Deferred<InterpreterInformation>
>();
private workerPool?: IWorkerPool<PythonEnvInfo, InterpreterInformation | undefined>;
private condaRunWorkerPool?: IWorkerPool<PythonEnvInfo, InterpreterInformation | undefined>;
public dispose(): void {
if (this.workerPool !== undefined) {
this.workerPool.stop();
this.workerPool = undefined;
}
if (this.condaRunWorkerPool !== undefined) {
this.condaRunWorkerPool.stop();
this.condaRunWorkerPool = undefined;
}
}
public async getEnvironmentInfo(
env: PythonEnvInfo,
priority?: EnvironmentInfoServiceQueuePriority,
): Promise<InterpreterInformation | undefined> {
const interpreterPath = env.executable.filename;
const result = this.cache.get(normCasePath(interpreterPath));
if (result !== undefined) {
// Another call for this environment has already been made, return its result.
return result.promise;
}
const deferred = createDeferred<InterpreterInformation>();
this.cache.set(normCasePath(interpreterPath), deferred);
this._getEnvironmentInfo(env, priority)
.then((r) => {
deferred.resolve(r);
})
.catch((ex) => {
deferred.reject(ex);
});
return deferred.promise;
}
public async _getEnvironmentInfo(
env: PythonEnvInfo,
priority?: EnvironmentInfoServiceQueuePriority,
): Promise<InterpreterInformation | undefined> {
if (this.workerPool === undefined) {
this.workerPool = createRunningWorkerPool<PythonEnvInfo, InterpreterInformation | undefined>(
buildEnvironmentInfo,
);
}
let reason: unknown;
let r = await addToQueue(this.workerPool, env, priority).catch((err) => {
reason = err;
return undefined;
});
if (r === undefined) {
// Even though env kind is not conda, it can still be a conda environment
// as complete env info may not be available at this time.
const isCondaEnv = env.kind === PythonEnvKind.Conda || (await isCondaEnvironment(env.executable.filename));
if (isCondaEnv) {
traceInfo(
`Validating ${env.executable.filename} normally failed with error, falling back to using conda run: (${reason})`,
);
if (this.condaRunWorkerPool === undefined) {
// Create a separate queue for validation using conda, so getting environment info for
// other types of environment aren't blocked on conda.
this.condaRunWorkerPool = createRunningWorkerPool<
PythonEnvInfo,
InterpreterInformation | undefined
>(buildEnvironmentInfoUsingCondaRun);
}
r = await addToQueue(this.condaRunWorkerPool, env, priority).catch((err) => {
traceError(err);
return undefined;
});
} else if (reason) {
traceError(reason);
}
}
return r;
}
}
function addToQueue(
workerPool: IWorkerPool<PythonEnvInfo, InterpreterInformation | undefined>,
env: PythonEnvInfo,
priority: EnvironmentInfoServiceQueuePriority | undefined,
) {
return priority === EnvironmentInfoServiceQueuePriority.High
? workerPool.addToQueue(env, QueuePosition.Front)
: workerPool.addToQueue(env, QueuePosition.Back);
}
let envInfoService: IEnvironmentInfoService | undefined;
export function getEnvironmentInfoService(disposables?: IDisposableRegistry): IEnvironmentInfoService {
if (envInfoService === undefined) {
const service = new EnvironmentInfoService();
disposables?.push({
dispose: () => {
service.dispose();
envInfoService = undefined;
},
});
envInfoService = service;
}
return envInfoService;
}