Skip to content

Commit a4374fe

Browse files
authored
Create a new API to retrieve interpreter details with the ability to cache the details (microsoft#1567)
Fixes microsoft#1569
1 parent f18a5ee commit a4374fe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+631
-256
lines changed

news/3 Code Health/1569.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Create a new API to retrieve interpreter details with the ability to cache the details.

pythonFiles/interpreterInfo.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import json
5+
import sys
6+
7+
obj = {}
8+
obj["versionInfo"] = sys.version_info[:4]
9+
obj["sysPrefix"] = sys.prefix
10+
obj["version"] = sys.version
11+
obj["is64Bit"] = sys.maxsize > 2**32
12+
13+
print(json.dumps(obj))

src/client/activation/interpreterDataService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class InterpreterDataService {
3333

3434
public async getInterpreterData(resource?: Uri): Promise<InterpreterData | undefined> {
3535
const executionFactory = this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory);
36-
const execService = await executionFactory.create(resource);
36+
const execService = await executionFactory.create({ resource });
3737

3838
const interpreterPath = await execService.getExecutablePath();
3939
if (interpreterPath.length === 0) {

src/client/common/installer/channelManager.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

44
import { inject, injectable } from 'inversify';
5-
import { QuickPickItem, Uri } from 'vscode';
5+
import { Uri } from 'vscode';
66
import { IInterpreterService, InterpreterType } from '../../interpreter/contracts';
77
import { IServiceContainer } from '../../ioc/types';
88
import { IApplicationShell } from '../application/types';
@@ -34,9 +34,9 @@ export class InstallationChannelManager implements IInstallationChannelManager {
3434
label: `Install using ${installer.displayName}`,
3535
description: '',
3636
installer
37-
} as QuickPickItem & { installer: IModuleInstaller };
37+
};
3838
});
39-
const selection = await appShell.showQuickPick(options, { matchOnDescription: true, matchOnDetail: true, placeHolder });
39+
const selection = await appShell.showQuickPick<typeof options[0]>(options, { matchOnDescription: true, matchOnDetail: true, placeHolder });
4040
return selection ? selection.installer : undefined;
4141
}
4242

src/client/common/installer/pipInstaller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class PipInstaller extends ModuleInstaller implements IModuleInstaller {
3838
}
3939
private isPipAvailable(resource?: Uri): Promise<boolean> {
4040
const pythonExecutionFactory = this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory);
41-
return pythonExecutionFactory.create(resource)
41+
return pythonExecutionFactory.create({ resource })
4242
.then(proc => proc.isModuleInstalled('pip'))
4343
.catch(() => false);
4444
}

src/client/common/installer/productInstaller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ abstract class BaseInstaller {
7474

7575
const isModule = typeof moduleName === 'string' && moduleName.length > 0 && path.basename(executableName) === executableName;
7676
if (isModule) {
77-
const pythonProcess = await this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory).create(resource);
77+
const pythonProcess = await this.serviceContainer.get<IPythonExecutionFactory>(IPythonExecutionFactory).create({ resource });
7878
return pythonProcess.isModuleInstalled(executableName);
7979
} else {
8080
const process = await this.serviceContainer.get<IProcessServiceFactory>(IProcessServiceFactory).create(resource);

src/client/common/persistentState.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,38 @@ import { Memento } from 'vscode';
88
import { GLOBAL_MEMENTO, IMemento, IPersistentState, IPersistentStateFactory, WORKSPACE_MEMENTO } from './types';
99

1010
class PersistentState<T> implements IPersistentState<T>{
11-
constructor(private storage: Memento, private key: string, private defaultValue: T) { }
11+
constructor(private storage: Memento, private key: string, private defaultValue?: T, private expiryDurationMs?: number) { }
1212

1313
public get value(): T {
14-
return this.storage.get<T>(this.key, this.defaultValue);
14+
if (this.expiryDurationMs) {
15+
const cachedData = this.storage.get<{ data?: T; expiry?: number }>(this.key, { data: this.defaultValue! });
16+
if (!cachedData || !cachedData.expiry || cachedData.expiry < Date.now()) {
17+
return this.defaultValue!;
18+
} else {
19+
return cachedData.data!;
20+
}
21+
} else {
22+
return this.storage.get<T>(this.key, this.defaultValue!);
23+
}
1524
}
1625

1726
public async updateValue(newValue: T): Promise<void> {
18-
await this.storage.update(this.key, newValue);
27+
if (this.expiryDurationMs) {
28+
await this.storage.update(this.key, { data: newValue, expiry: Date.now() + this.expiryDurationMs });
29+
} else {
30+
await this.storage.update(this.key, newValue);
31+
}
1932
}
2033
}
2134

2235
@injectable()
2336
export class PersistentStateFactory implements IPersistentStateFactory {
24-
constructor( @inject(IMemento) @named(GLOBAL_MEMENTO) private globalState: Memento,
37+
constructor(@inject(IMemento) @named(GLOBAL_MEMENTO) private globalState: Memento,
2538
@inject(IMemento) @named(WORKSPACE_MEMENTO) private workspaceState: Memento) { }
26-
public createGlobalPersistentState<T>(key: string, defaultValue: T): IPersistentState<T> {
27-
return new PersistentState<T>(this.globalState, key, defaultValue);
39+
public createGlobalPersistentState<T>(key: string, defaultValue?: T, expiryDurationMs?: number): IPersistentState<T> {
40+
return new PersistentState<T>(this.globalState, key, defaultValue, expiryDurationMs);
2841
}
29-
public createWorkspacePersistentState<T>(key: string, defaultValue: T): IPersistentState<T> {
30-
return new PersistentState<T>(this.workspaceState, key, defaultValue);
42+
public createWorkspacePersistentState<T>(key: string, defaultValue?: T, expiryDurationMs?: number): IPersistentState<T> {
43+
return new PersistentState<T>(this.workspaceState, key, defaultValue, expiryDurationMs);
3144
}
3245
}

src/client/common/platform/fileSystem.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33
'use strict';
44

5+
import { createHash } from 'crypto';
56
import * as fs from 'fs-extra';
67
import { inject, injectable } from 'inversify';
78
import * as path from 'path';
@@ -117,4 +118,16 @@ export class FileSystem implements IFileSystem {
117118
fs.unlink(filename, err => err ? deferred.reject(err) : deferred.resolve());
118119
return deferred.promise;
119120
}
121+
public getFileHash(filePath: string): Promise<string | undefined> {
122+
return new Promise<string | undefined>(resolve => {
123+
fs.lstat(filePath, (err, stats) => {
124+
if (err) {
125+
resolve();
126+
} else {
127+
const actual = createHash('sha512').update(`${stats.ctimeMs}-${stats.mtimeMs}`).digest('hex');
128+
resolve(actual);
129+
}
130+
});
131+
});
132+
}
120133
}

src/client/common/platform/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,5 @@ export interface IFileSystem {
4646
getRealPath(path: string): Promise<string>;
4747
copyFile(src: string, dest: string): Promise<void>;
4848
deleteFile(filename: string): Promise<void>;
49+
getFileHash(filePath: string): Promise<string | undefined>;
4950
}

src/client/common/process/pythonExecutionFactory.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@
44
import { inject, injectable } from 'inversify';
55
import { Uri } from 'vscode';
66
import { IServiceContainer } from '../../ioc/types';
7+
import { IConfigurationService } from '../types';
78
import { PythonExecutionService } from './pythonProcess';
8-
import { IProcessServiceFactory, IPythonExecutionFactory, IPythonExecutionService } from './types';
9+
import { ExecutionFactoryCreationOptions, IProcessServiceFactory, IPythonExecutionFactory, IPythonExecutionService } from './types';
910

1011
@injectable()
1112
export class PythonExecutionFactory implements IPythonExecutionFactory {
13+
private readonly configService: IConfigurationService;
1214
private processServiceFactory: IProcessServiceFactory;
1315
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
1416
this.processServiceFactory = serviceContainer.get<IProcessServiceFactory>(IProcessServiceFactory);
17+
this.configService = serviceContainer.get<IConfigurationService>(IConfigurationService);
1518
}
16-
public async create(resource?: Uri): Promise<IPythonExecutionService> {
17-
const processService = await this.processServiceFactory.create(resource);
18-
return new PythonExecutionService(this.serviceContainer, processService, resource);
19+
public async create(options: ExecutionFactoryCreationOptions): Promise<IPythonExecutionService> {
20+
const pythonPath = options.pythonPath ? options.pythonPath : this.configService.getSettings(options.resource).pythonPath;
21+
const processService = await this.processServiceFactory.create(options.resource);
22+
return new PythonExecutionService(this.serviceContainer, processService, pythonPath);
1923
}
2024
}

0 commit comments

Comments
 (0)