11import { inject , injectable } from 'inversify' ;
2+ import * as md5 from 'md5' ;
23import * as path from 'path' ;
34import { Disposable , Event , EventEmitter , Uri } from 'vscode' ;
45import '../../client/common/extensions' ;
56import { IDocumentManager , IWorkspaceService } from '../common/application/types' ;
67import { getArchitectureDisplayName } from '../common/platform/registry' ;
78import { IFileSystem } from '../common/platform/types' ;
89import { IPythonExecutionFactory } from '../common/process/types' ;
9- import { IConfigurationService , IDisposableRegistry , IPersistentStateFactory } from '../common/types' ;
10+ import { IConfigurationService , IDisposableRegistry , IPersistentState , IPersistentStateFactory } from '../common/types' ;
1011import { sleep } from '../common/utils/async' ;
1112import { IServiceContainer } from '../ioc/types' ;
1213import { captureTelemetry } from '../telemetry' ;
@@ -26,6 +27,7 @@ export class InterpreterService implements Disposable, IInterpreterService {
2627 private readonly persistentStateFactory : IPersistentStateFactory ;
2728 private readonly configService : IConfigurationService ;
2829 private readonly didChangeInterpreterEmitter = new EventEmitter < void > ( ) ;
30+ private readonly didChangeInterpreterInformation = new EventEmitter < PythonInterpreter > ( ) ;
2931 private pythonPathSetting : string = '' ;
3032
3133 constructor ( @inject ( IServiceContainer ) private serviceContainer : IServiceContainer ) {
@@ -63,19 +65,30 @@ export class InterpreterService implements Disposable, IInterpreterService {
6365 const interpreters = await this . locator . getInterpreters ( resource ) ;
6466 await Promise . all ( interpreters
6567 . filter ( item => ! item . displayName )
66- . map ( async item => item . displayName = await this . getDisplayName ( item , resource ) ) ) ;
68+ . map ( async item => {
69+ item . displayName = await this . getDisplayName ( item , resource ) ;
70+ // Always keep information up to date with latest details.
71+ if ( ! item . cachedEntry ) {
72+ this . updateCachedInterpreterInformation ( item ) . ignoreErrors ( ) ;
73+ }
74+ } ) ) ;
6775 return interpreters ;
6876 }
6977
7078 public dispose ( ) : void {
7179 this . locator . dispose ( ) ;
7280 this . didChangeInterpreterEmitter . dispose ( ) ;
81+ this . didChangeInterpreterInformation . dispose ( ) ;
7382 }
7483
7584 public get onDidChangeInterpreter ( ) : Event < void > {
7685 return this . didChangeInterpreterEmitter . event ;
7786 }
7887
88+ public get onDidChangeInterpreterInformation ( ) : Event < PythonInterpreter > {
89+ return this . didChangeInterpreterInformation . event ;
90+ }
91+
7992 public async getActiveInterpreter ( resource ?: Uri ) : Promise < PythonInterpreter | undefined > {
8093 const pythonExecutionFactory = this . serviceContainer . get < IPythonExecutionFactory > ( IPythonExecutionFactory ) ;
8194 const pythonExecutionService = await pythonExecutionFactory . create ( { resource } ) ;
@@ -99,10 +112,9 @@ export class InterpreterService implements Disposable, IInterpreterService {
99112 }
100113 }
101114
102- const fileHash = await this . fs . getFileHash ( pythonPath ) . catch ( ( ) => '' ) || '' ;
103- const store = this . persistentStateFactory . createGlobalPersistentState < PythonInterpreter & { fileHash : string } > ( `${ pythonPath } .interpreter.details.v5` , undefined , EXPITY_DURATION ) ;
104- if ( store . value && fileHash && store . value . fileHash === fileHash ) {
105- return store . value ;
115+ const store = await this . getInterpreterCache ( pythonPath ) ;
116+ if ( store . value && store . value . info ) {
117+ return store . value . info ;
106118 }
107119
108120 const fs = this . serviceContainer . get < IFileSystem > ( IFileSystem ) ;
@@ -135,13 +147,13 @@ export class InterpreterService implements Disposable, IInterpreterService {
135147
136148 // tslint:disable-next-line:no-any
137149 if ( interpreterInfo && ( interpreterInfo as any ) . __store ) {
138- await store . updateValue ( { ... interpreterInfo , path : pythonPath , fileHash } ) ;
150+ await this . updateCachedInterpreterInformation ( interpreterInfo ) ;
139151 } else {
140152 // If we got information from option1, then when option2 finishes cache it for later use (ignoring erors);
141- option2 . then ( info => {
153+ option2 . then ( async info => {
142154 // tslint:disable-next-line:no-any
143155 if ( info && ( info as any ) . __store ) {
144- return store . updateValue ( { ... info , path : pythonPath , fileHash } ) ;
156+ await this . updateCachedInterpreterInformation ( info ) ;
145157 }
146158 } ) . ignoreErrors ( ) ;
147159 }
@@ -157,8 +169,9 @@ export class InterpreterService implements Disposable, IInterpreterService {
157169 */
158170 public async getDisplayName ( info : Partial < PythonInterpreter > , resource ?: Uri ) : Promise < string > {
159171 const fileHash = ( info . path ? await this . fs . getFileHash ( info . path ) . catch ( ( ) => '' ) : '' ) || '' ;
160- const store = this . persistentStateFactory . createGlobalPersistentState < { fileHash : string ; displayName : string } > ( `${ info . path } ${ fileHash } .interpreter.displayName.v5` , undefined , EXPITY_DURATION ) ;
161- if ( store . value && store . value . fileHash === fileHash && store . value . displayName ) {
172+ const interpreterHash = `${ fileHash } -${ md5 ( JSON . stringify ( info ) ) } ` ;
173+ const store = this . persistentStateFactory . createGlobalPersistentState < { hash : string ; displayName : string } > ( `${ info . path } ${ interpreterHash } .interpreter.displayName.v5` , undefined , EXPITY_DURATION ) ;
174+ if ( store . value && store . value . hash === interpreterHash && store . value . displayName ) {
162175 return store . value . displayName ;
163176 }
164177 const displayNameParts : string [ ] = [ 'Python' ] ;
@@ -194,11 +207,24 @@ export class InterpreterService implements Disposable, IInterpreterService {
194207
195208 // If dealing with cached entry, then do not store the display name in cache.
196209 if ( ! info . cachedEntry ) {
197- await store . updateValue ( { displayName, fileHash } ) ;
210+ await store . updateValue ( { displayName, hash : interpreterHash } ) ;
198211 }
199212
200213 return displayName ;
201214 }
215+ protected async getInterpreterCache ( pythonPath : string ) : Promise < IPersistentState < { fileHash : string ; info ?: PythonInterpreter } > > {
216+ const fileHash = ( pythonPath ? await this . fs . getFileHash ( pythonPath ) . catch ( ( ) => '' ) : '' ) || '' ;
217+ const store = this . persistentStateFactory . createGlobalPersistentState < { fileHash : string ; info ?: PythonInterpreter } > ( `${ pythonPath } .interpreter.Details.v6` , undefined , EXPITY_DURATION ) ;
218+ if ( ! store . value || store . value . fileHash !== fileHash ) {
219+ await store . updateValue ( { fileHash } ) ;
220+ }
221+ return store ;
222+ }
223+ protected async updateCachedInterpreterInformation ( info : PythonInterpreter ) : Promise < void > {
224+ this . didChangeInterpreterInformation . fire ( info ) ;
225+ const state = await this . getInterpreterCache ( info . path ) ;
226+ await state . updateValue ( { fileHash : state . value . fileHash , info } ) ;
227+ }
202228 private onConfigChanged = ( ) => {
203229 // Check if we actually changed our python path
204230 const pySettings = this . configService . getSettings ( ) ;
0 commit comments