22// Licensed under the MIT License.
33
44import { 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' ;
59import {
610 CONDA_ENV_FILE_SERVICE ,
711 CONDA_ENV_SERVICE ,
812 CURRENT_PATH_SERVICE ,
913 GLOBAL_VIRTUAL_ENV_SERVICE ,
14+ IComponentAdapter ,
1015 ICondaService ,
1116 IInterpreterLocatorHelper ,
1217 IInterpreterLocatorProgressService ,
@@ -23,10 +28,13 @@ import {
2328} from '../interpreter/contracts' ;
2429import { IPipEnvServiceHelper , IPythonInPathCommandProvider } from '../interpreter/locators/types' ;
2530import { IServiceContainer , IServiceManager } from '../ioc/types' ;
31+ import { PythonEnvInfo , PythonEnvKind , PythonReleaseLevel } from './base/info' ;
32+ import { ILocator , PythonLocatorQuery } from './base/locator' ;
2633import { initializeExternalDependencies } from './common/externalDependencies' ;
2734import { PythonInterpreterLocatorService } from './discovery/locators' ;
2835import { InterpreterLocatorHelper } from './discovery/locators/helpers' ;
2936import { InterpreterLocatorProgressService } from './discovery/locators/progressService' ;
37+ import { CondaEnvironmentInfo } from './discovery/locators/services/conda' ;
3038import { CondaEnvFileService } from './discovery/locators/services/condaEnvFileService' ;
3139import { CondaEnvService } from './discovery/locators/services/condaEnvService' ;
3240import { CondaService } from './discovery/locators/services/condaService' ;
@@ -48,31 +56,285 @@ import {
4856 WorkspaceVirtualEnvService ,
4957} from './discovery/locators/services/workspaceVirtualEnvService' ;
5058import { WorkspaceVirtualEnvWatcherService } from './discovery/locators/services/workspaceVirtualEnvWatcherService' ;
59+ import { EnvironmentType , PythonEnvironment } from './info' ;
5160import { EnvironmentInfoService , IEnvironmentInfoService } from './info/environmentInfoService' ;
5261
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+ } ) ) ;
5473
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+ } ;
58153}
59154
155+ interface IPythonEnvironments extends ILocator { }
156+
60157@injectable ( )
61158class ComponentAdapter implements IComponentAdapter {
62159 constructor (
63160 // 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+ }
70316 } ) ;
317+ } else {
318+ deferred . resolve ( envs ) ;
71319 }
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 ;
72330 }
73331}
74332
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 {
76338 const adapter = new ComponentAdapter ( api ) ;
77339 serviceManager . addSingletonInstance < IComponentAdapter > ( IComponentAdapter , adapter ) ;
78340
0 commit comments