|  | 
| 1 | 1 | import { inject, injectable } from 'inversify'; | 
| 2 | 2 | import * as path from 'path'; | 
| 3 | 3 | import { ConfigurationTarget, Disposable, Event, EventEmitter, Uri } from 'vscode'; | 
|  | 4 | +import '../../client/common/extensions'; | 
| 4 | 5 | import { IDocumentManager, IWorkspaceService } from '../common/application/types'; | 
| 5 | 6 | import { PythonSettings } from '../common/configSettings'; | 
| 6 | 7 | import { getArchitectureDisplayName } from '../common/platform/registry'; | 
| 7 | 8 | import { IFileSystem } from '../common/platform/types'; | 
| 8 | 9 | import { IPythonExecutionFactory } from '../common/process/types'; | 
| 9 | 10 | import { IConfigurationService, IDisposableRegistry, IPersistentStateFactory } from '../common/types'; | 
|  | 11 | +import { sleep } from '../common/utils/async'; | 
| 10 | 12 | import { IServiceContainer } from '../ioc/types'; | 
| 11 | 13 | import { IPythonPathUpdaterServiceManager } from './configuration/types'; | 
| 12 | 14 | import { | 
| @@ -126,44 +128,54 @@ export class InterpreterService implements Disposable, IInterpreterService { | 
| 126 | 128 |  } | 
| 127 | 129 |  } | 
| 128 | 130 | 
 | 
| 129 |  | - let fileHash = await this.fs.getFileHash(pythonPath).catch(() => ''); | 
| 130 |  | - fileHash = fileHash ? fileHash : ''; | 
|  | 131 | + const fileHash = await this.fs.getFileHash(pythonPath).catch(() => '') || ''; | 
| 131 | 132 |  const store = this.persistentStateFactory.createGlobalPersistentState<PythonInterpreter & { fileHash: string }>(`${pythonPath}.interpreter.details.v5`, undefined, EXPITY_DURATION); | 
| 132 | 133 |  if (store.value && fileHash && store.value.fileHash === fileHash) { | 
| 133 | 134 |  return store.value; | 
| 134 | 135 |  } | 
| 135 | 136 | 
 | 
| 136 | 137 |  const fs = this.serviceContainer.get<IFileSystem>(IFileSystem); | 
| 137 |  | - const interpreters = await this.getInterpreters(resource); | 
| 138 |  | - let interpreterInfo = interpreters.find(i => fs.arePathsSame(i.path, pythonPath)); | 
| 139 |  | - if (!interpreterInfo) { | 
| 140 |  | - const interpreterHelper = this.serviceContainer.get<IInterpreterHelper>(IInterpreterHelper); | 
| 141 |  | - const virtualEnvManager = this.serviceContainer.get<IVirtualEnvironmentManager>(IVirtualEnvironmentManager); | 
| 142 |  | - const [info, type] = await Promise.all([ | 
| 143 |  | - interpreterHelper.getInterpreterInformation(pythonPath), | 
| 144 |  | - virtualEnvManager.getEnvironmentType(pythonPath) | 
| 145 |  | - ]); | 
| 146 |  | - if (!info) { | 
| 147 |  | - return; | 
|  | 138 | + | 
|  | 139 | + // Don't want for all interpreters are collected. | 
|  | 140 | + // Try to collect the infromation manually, that's faster. | 
|  | 141 | + // Get from which ever comes first. | 
|  | 142 | + const option1 = (async () => { | 
|  | 143 | + const result = this.collectInterpreterDetails(pythonPath, resource); | 
|  | 144 | + await sleep(1000); // let the other option complete within 1s if possible. | 
|  | 145 | + return result; | 
|  | 146 | + })(); | 
|  | 147 | + | 
|  | 148 | + // This is the preferred approach, hence the delay in option 1. | 
|  | 149 | + const option2 = (async () => { | 
|  | 150 | + const interpreters = await this.getInterpreters(resource); | 
|  | 151 | + const found = interpreters.find(i => fs.arePathsSame(i.path, pythonPath)); | 
|  | 152 | + if (found) { | 
|  | 153 | + // Cache the interpreter info, only if we get the data from interpretr list. | 
|  | 154 | + // tslint:disable-next-line:no-any | 
|  | 155 | + (found as any).__store = true; | 
|  | 156 | + return found; | 
| 148 | 157 |  } | 
| 149 |  | - const details: Partial<PythonInterpreter> = { | 
| 150 |  | - ...(info as PythonInterpreter), | 
| 151 |  | - path: pythonPath, | 
| 152 |  | - type: type | 
| 153 |  | - }; | 
|  | 158 | + // Use option1 as a fallback. | 
|  | 159 | + // tslint:disable-next-line:no-any | 
|  | 160 | + return option1 as any as PythonInterpreter; | 
|  | 161 | + })(); | 
| 154 | 162 | 
 | 
| 155 |  | - const envName = type === InterpreterType.Unknown ? undefined : await virtualEnvManager.getEnvironmentName(pythonPath, resource); | 
| 156 |  | - interpreterInfo = { | 
| 157 |  | - ...(details as PythonInterpreter), | 
| 158 |  | - envName | 
| 159 |  | - }; | 
| 160 |  | - interpreterInfo.displayName = await this.getDisplayName(interpreterInfo, resource); | 
| 161 |  | - } | 
|  | 163 | + const interpreterInfo = await Promise.race([option2, option1]) as PythonInterpreter; | 
| 162 | 164 | 
 | 
| 163 |  | - await store.updateValue({ ...interpreterInfo, path: pythonPath, fileHash }); | 
|  | 165 | + // tslint:disable-next-line:no-any | 
|  | 166 | + if (interpreterInfo && (interpreterInfo as any).__store) { | 
|  | 167 | + await store.updateValue({ ...interpreterInfo, path: pythonPath, fileHash }); | 
|  | 168 | + } else { | 
|  | 169 | + // If we got information from option1, then when option2 finishes cache it for later use (ignoring erors); | 
|  | 170 | + option2.then(info => { | 
|  | 171 | + // tslint:disable-next-line:no-any | 
|  | 172 | + if (info && (info as any).__store) { | 
|  | 173 | + return store.updateValue({ ...info, path: pythonPath, fileHash }); | 
|  | 174 | + } | 
|  | 175 | + }).ignoreErrors(); | 
|  | 176 | + } | 
| 164 | 177 |  return interpreterInfo; | 
| 165 | 178 |  } | 
| 166 |  | - | 
| 167 | 179 |  /** | 
| 168 | 180 |  * Gets the display name of an interpreter. | 
| 169 | 181 |  * The format is `Python <Version> <bitness> (<env name>: <env type>)` | 
| @@ -243,4 +255,28 @@ export class InterpreterService implements Disposable, IInterpreterService { | 
| 243 | 255 |  interpreterDisplay.refresh() | 
| 244 | 256 |  .catch(ex => console.error('Python Extension: display.refresh', ex)); | 
| 245 | 257 |  } | 
|  | 258 | + private async collectInterpreterDetails(pythonPath: string, resource: Uri | undefined) { | 
|  | 259 | + const interpreterHelper = this.serviceContainer.get<IInterpreterHelper>(IInterpreterHelper); | 
|  | 260 | + const virtualEnvManager = this.serviceContainer.get<IVirtualEnvironmentManager>(IVirtualEnvironmentManager); | 
|  | 261 | + const [info, type] = await Promise.all([ | 
|  | 262 | + interpreterHelper.getInterpreterInformation(pythonPath), | 
|  | 263 | + virtualEnvManager.getEnvironmentType(pythonPath) | 
|  | 264 | + ]); | 
|  | 265 | + if (!info) { | 
|  | 266 | + return; | 
|  | 267 | + } | 
|  | 268 | + const details: Partial<PythonInterpreter> = { | 
|  | 269 | + ...(info as PythonInterpreter), | 
|  | 270 | + path: pythonPath, | 
|  | 271 | + type: type | 
|  | 272 | + }; | 
|  | 273 | + | 
|  | 274 | + const envName = type === InterpreterType.Unknown ? undefined : await virtualEnvManager.getEnvironmentName(pythonPath, resource); | 
|  | 275 | + const pthonInfo = { | 
|  | 276 | + ...(details as PythonInterpreter), | 
|  | 277 | + envName | 
|  | 278 | + }; | 
|  | 279 | + pthonInfo.displayName = await this.getDisplayName(pthonInfo, resource); | 
|  | 280 | + return pthonInfo; | 
|  | 281 | + } | 
| 246 | 282 | } | 
0 commit comments