2
2
// Licensed under the MIT License.
3
3
4
4
import { injectable } from 'inversify' ;
5
+ import * as vscode from 'vscode' ;
6
+ import { createDeferred } from '../common/utils/async' ;
7
+ import { Architecture } from '../common/utils/platform' ;
8
+ import { getVersionString , parseVersion } from '../common/utils/version' ;
5
9
import {
6
10
CONDA_ENV_FILE_SERVICE ,
7
11
CONDA_ENV_SERVICE ,
8
12
CURRENT_PATH_SERVICE ,
9
13
GLOBAL_VIRTUAL_ENV_SERVICE ,
14
+ IComponentAdapter ,
10
15
ICondaService ,
11
16
IInterpreterLocatorHelper ,
12
17
IInterpreterLocatorProgressService ,
@@ -23,10 +28,13 @@ import {
23
28
} from '../interpreter/contracts' ;
24
29
import { IPipEnvServiceHelper , IPythonInPathCommandProvider } from '../interpreter/locators/types' ;
25
30
import { IServiceContainer , IServiceManager } from '../ioc/types' ;
31
+ import { PythonEnvInfo , PythonEnvKind , PythonReleaseLevel } from './base/info' ;
32
+ import { ILocator , PythonLocatorQuery } from './base/locator' ;
26
33
import { initializeExternalDependencies } from './common/externalDependencies' ;
27
34
import { PythonInterpreterLocatorService } from './discovery/locators' ;
28
35
import { InterpreterLocatorHelper } from './discovery/locators/helpers' ;
29
36
import { InterpreterLocatorProgressService } from './discovery/locators/progressService' ;
37
+ import { CondaEnvironmentInfo } from './discovery/locators/services/conda' ;
30
38
import { CondaEnvFileService } from './discovery/locators/services/condaEnvFileService' ;
31
39
import { CondaEnvService } from './discovery/locators/services/condaEnvService' ;
32
40
import { CondaService } from './discovery/locators/services/condaService' ;
@@ -48,31 +56,285 @@ import {
48
56
WorkspaceVirtualEnvService ,
49
57
} from './discovery/locators/services/workspaceVirtualEnvService' ;
50
58
import { WorkspaceVirtualEnvWatcherService } from './discovery/locators/services/workspaceVirtualEnvWatcherService' ;
59
+ import { EnvironmentType , PythonEnvironment } from './info' ;
51
60
import { EnvironmentInfoService , IEnvironmentInfoService } from './info/environmentInfoService' ;
52
61
53
- import { PythonEnvironments } from '.' ;
62
+ const convertedKinds = new Map ( Object . entries ( {
63
+ [ PythonEnvKind . System ] : EnvironmentType . System ,
64
+ [ PythonEnvKind . MacDefault ] : EnvironmentType . System ,
65
+ [ PythonEnvKind . WindowsStore ] : EnvironmentType . WindowsStore ,
66
+ [ PythonEnvKind . Pyenv ] : EnvironmentType . Pyenv ,
67
+ [ PythonEnvKind . Conda ] : EnvironmentType . Conda ,
68
+ [ PythonEnvKind . CondaBase ] : EnvironmentType . Conda ,
69
+ [ PythonEnvKind . VirtualEnv ] : EnvironmentType . VirtualEnv ,
70
+ [ PythonEnvKind . Pipenv ] : EnvironmentType . Pipenv ,
71
+ [ PythonEnvKind . Venv ] : EnvironmentType . Venv ,
72
+ } ) ) ;
54
73
55
- export const IComponentAdapter = Symbol ( 'IComponentAdapter' ) ;
56
- export interface IComponentAdapter {
57
- // We will fill in the API separately.
74
+ function convertEnvInfo ( info : PythonEnvInfo ) : PythonEnvironment {
75
+ const {
76
+ name,
77
+ location,
78
+ executable,
79
+ arch,
80
+ kind,
81
+ searchLocation,
82
+ version,
83
+ distro,
84
+ } = info ;
85
+ const { filename, sysPrefix } = executable ;
86
+ const env : PythonEnvironment = {
87
+ sysPrefix,
88
+ envType : EnvironmentType . Unknown ,
89
+ envName : name ,
90
+ envPath : location ,
91
+ path : filename ,
92
+ architecture : arch ,
93
+ } ;
94
+
95
+ const envType = convertedKinds . get ( kind ) ;
96
+ if ( envType !== undefined ) {
97
+ env . envType = envType ;
98
+ }
99
+ // Otherwise it stays Unknown.
100
+
101
+ if ( searchLocation !== undefined ) {
102
+ if ( kind === PythonEnvKind . Pipenv ) {
103
+ env . pipEnvWorkspaceFolder = searchLocation . fsPath ;
104
+ }
105
+ }
106
+
107
+ if ( version !== undefined ) {
108
+ const { release, sysVersion } = version ;
109
+ const { level, serial } = release ;
110
+ const releaseStr = level === PythonReleaseLevel . Final
111
+ ? 'final'
112
+ : `${ level } ${ serial } ` ;
113
+ const versionStr = `${ getVersionString ( version ) } -${ releaseStr } ` ;
114
+ env . version = parseVersion ( versionStr ) ;
115
+ env . sysVersion = sysVersion ;
116
+ }
117
+
118
+ if ( distro !== undefined && distro . org !== '' ) {
119
+ env . companyDisplayName = distro . org ;
120
+ }
121
+ // We do not worry about using distro.defaultDisplayName
122
+ // or info.defaultDisplayName.
123
+
124
+ return env ;
125
+ }
126
+
127
+ function buildEmptyEnvInfo ( ) : PythonEnvInfo {
128
+ return {
129
+ id : '' ,
130
+ kind : PythonEnvKind . Unknown ,
131
+ executable : {
132
+ filename : '' ,
133
+ sysPrefix : '' ,
134
+ ctime : - 1 ,
135
+ mtime : - 1 ,
136
+ } ,
137
+ name : '' ,
138
+ location : '' ,
139
+ version : {
140
+ major : - 1 ,
141
+ minor : - 1 ,
142
+ micro : - 1 ,
143
+ release : {
144
+ level : PythonReleaseLevel . Final ,
145
+ serial : 0 ,
146
+ } ,
147
+ } ,
148
+ arch : Architecture . Unknown ,
149
+ distro : {
150
+ org : '' ,
151
+ } ,
152
+ } ;
58
153
}
59
154
155
+ interface IPythonEnvironments extends ILocator { }
156
+
60
157
@injectable ( )
61
158
class ComponentAdapter implements IComponentAdapter {
62
159
constructor (
63
160
// The adapter only wraps one thing: the component API.
64
- private readonly api : PythonEnvironments
65
- ) {
66
- // For the moment we use this placeholder to exercise the property.
67
- if ( this . api . onChanged ) {
68
- this . api . onChanged ( ( _event ) => {
69
- // do nothing
161
+ private readonly api : IPythonEnvironments ,
162
+ // For now we effectively disable the component.
163
+ private readonly enabled = false ,
164
+ ) { }
165
+
166
+ // IInterpreterHelper
167
+
168
+ // A result of `undefined` means "Fall back to the old code!"
169
+ public async getInterpreterInformation ( pythonPath : string ) : Promise < undefined | Partial < PythonEnvironment > > {
170
+ if ( ! this . enabled ) {
171
+ return undefined ;
172
+ }
173
+ const env = await this . api . resolveEnv ( pythonPath ) ;
174
+ if ( env === undefined ) {
175
+ return undefined ;
176
+ }
177
+ return convertEnvInfo ( env ) ;
178
+ }
179
+
180
+ // A result of `undefined` means "Fall back to the old code!"
181
+ public async isMacDefaultPythonPath ( pythonPath : string ) : Promise < boolean | undefined > {
182
+ if ( ! this . enabled ) {
183
+ return undefined ;
184
+ }
185
+ const env = await this . api . resolveEnv ( pythonPath ) ;
186
+ if ( env === undefined ) {
187
+ return undefined ;
188
+ }
189
+ return env . kind === PythonEnvKind . MacDefault ;
190
+ }
191
+
192
+ // IInterpreterService
193
+
194
+ // A result of `undefined` means "Fall back to the old code!"
195
+ public get hasInterpreters ( ) : Promise < boolean | undefined > {
196
+ if ( ! this . enabled ) {
197
+ return Promise . resolve ( undefined ) ;
198
+ }
199
+ const iterator = this . api . iterEnvs ( ) ;
200
+ return iterator . next ( ) . then ( ( res ) => ! res . done ) ;
201
+ }
202
+
203
+ // We use the same getInterpreters() here as for IInterpreterLocatorService.
204
+
205
+ // A result of `undefined` means "Fall back to the old code!"
206
+ public async getInterpreterDetails (
207
+ pythonPath : string ,
208
+ resource ?: vscode . Uri ,
209
+ ) : Promise < undefined | PythonEnvironment > {
210
+ if ( ! this . enabled ) {
211
+ return undefined ;
212
+ }
213
+ const info = buildEmptyEnvInfo ( ) ;
214
+ info . executable . filename = pythonPath ;
215
+ if ( resource !== undefined ) {
216
+ const wsFolder = vscode . workspace . getWorkspaceFolder ( resource ) ;
217
+ if ( wsFolder !== undefined ) {
218
+ info . searchLocation = wsFolder . uri ;
219
+ }
220
+ }
221
+ const env = await this . api . resolveEnv ( info ) ;
222
+ if ( env === undefined ) {
223
+ return undefined ;
224
+ }
225
+ return convertEnvInfo ( env ) ;
226
+ }
227
+
228
+ // ICondaService
229
+
230
+ // A result of `undefined` means "Fall back to the old code!"
231
+ public async isCondaEnvironment ( interpreterPath : string ) : Promise < boolean | undefined > {
232
+ if ( ! this . enabled ) {
233
+ return undefined ;
234
+ }
235
+ const env = await this . api . resolveEnv ( interpreterPath ) ;
236
+ if ( env === undefined ) {
237
+ return undefined ;
238
+ }
239
+ return env . kind === PythonEnvKind . Conda ;
240
+ }
241
+
242
+ // A result of `undefined` means "Fall back to the old code!"
243
+ public async getCondaEnvironment ( interpreterPath : string ) : Promise < CondaEnvironmentInfo | undefined > {
244
+ if ( ! this . enabled ) {
245
+ return undefined ;
246
+ }
247
+ const env = await this . api . resolveEnv ( interpreterPath ) ;
248
+ if ( env === undefined ) {
249
+ return undefined ;
250
+ }
251
+ if ( env . kind !== PythonEnvKind . Conda ) {
252
+ return undefined ;
253
+ }
254
+ if ( env . name !== '' ) {
255
+ return { name : env . name , path : '' } ;
256
+ }
257
+ // else
258
+ return { name : '' , path : env . location } ;
259
+ }
260
+
261
+ // IWindowsStoreInterpreter
262
+
263
+ // A result of `undefined` means "Fall back to the old code!"
264
+ public async isWindowsStoreInterpreter ( pythonPath : string ) : Promise < boolean | undefined > {
265
+ if ( ! this . enabled ) {
266
+ return undefined ;
267
+ }
268
+ const env = await this . api . resolveEnv ( pythonPath ) ;
269
+ if ( env === undefined ) {
270
+ return undefined ;
271
+ }
272
+ return env . kind === PythonEnvKind . WindowsStore ;
273
+ }
274
+
275
+ // IInterpreterLocatorService
276
+
277
+ // A result of `undefined` means "Fall back to the old code!"
278
+ public async getInterpreters (
279
+ resource ?: vscode . Uri ,
280
+ // Currently we have no plans to support GetInterpreterLocatorOptions:
281
+ // {
282
+ // ignoreCache?: boolean
283
+ // onSuggestion?: boolean;
284
+ // }
285
+ ) : Promise < PythonEnvironment [ ] | undefined > {
286
+ if ( ! this . enabled ) {
287
+ return undefined ;
288
+ }
289
+ const query : PythonLocatorQuery = { } ;
290
+ if ( resource !== undefined ) {
291
+ const wsFolder = vscode . workspace . getWorkspaceFolder ( resource ) ;
292
+ if ( wsFolder !== undefined ) {
293
+ query . searchLocations = [ wsFolder . uri ] ;
294
+ }
295
+ }
296
+
297
+ const deferred = createDeferred < PythonEnvironment [ ] > ( ) ;
298
+ const envs : PythonEnvironment [ ] = [ ] ;
299
+ const executableToLegacy : Record < string , PythonEnvironment > = { } ;
300
+ const iterator = this . api . iterEnvs ( query ) ;
301
+
302
+ if ( iterator . onUpdated !== undefined ) {
303
+ iterator . onUpdated ( ( event ) => {
304
+ if ( event === null ) {
305
+ deferred . resolve ( envs ) ;
306
+ } else {
307
+ // Replace the old one.
308
+ const old = executableToLegacy [ event . old . executable . filename ] ;
309
+ if ( old !== undefined ) {
310
+ const index = envs . indexOf ( old ) ;
311
+ if ( index !== - 1 ) {
312
+ envs [ index ] = convertEnvInfo ( event . new ) ;
313
+ }
314
+ }
315
+ }
70
316
} ) ;
317
+ } else {
318
+ deferred . resolve ( envs ) ;
71
319
}
320
+
321
+ let res = await iterator . next ( ) ;
322
+ while ( ! res . done ) {
323
+ const env = convertEnvInfo ( res . value ) ;
324
+ envs . push ( env ) ;
325
+ executableToLegacy [ env . path ] = env ;
326
+ res = await iterator . next ( ) ; // eslint-disable-line no-await-in-loop
327
+ }
328
+
329
+ return deferred . promise ;
72
330
}
73
331
}
74
332
75
- export function registerForIOC ( serviceManager : IServiceManager , serviceContainer : IServiceContainer , api : PythonEnvironments ) : void {
333
+ export function registerForIOC (
334
+ serviceManager : IServiceManager ,
335
+ serviceContainer : IServiceContainer ,
336
+ api : IPythonEnvironments ,
337
+ ) : void {
76
338
const adapter = new ComponentAdapter ( api ) ;
77
339
serviceManager . addSingletonInstance < IComponentAdapter > ( IComponentAdapter , adapter ) ;
78
340
0 commit comments