Skip to content

Commit abfb8c3

Browse files
author
Pavel Minaev
authored
Re-implement conda tool discovery in a separate uncoupled component. (microsoft#14745)
1 parent 0124c32 commit abfb8c3

8 files changed

Lines changed: 747 additions & 139 deletions

File tree

src/client/pythonEnvironments/common/externalDependencies.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import * as fsapi from 'fs-extra';
55
import * as path from 'path';
6-
import { ExecutionResult, IProcessServiceFactory } from '../../common/process/types';
6+
import { ExecutionResult, IProcessServiceFactory, SpawnOptions } from '../../common/process/types';
77
import { IPersistentStateFactory } from '../../common/types';
88
import { chain, iterable } from '../../common/utils/async';
99
import { getOSType, OSType } from '../../common/utils/platform';
@@ -23,6 +23,11 @@ export async function shellExecute(command: string, timeout: number): Promise<Ex
2323
return proc.shellExec(command, { timeout });
2424
}
2525

26+
export async function exec(file: string, args: string[], options: SpawnOptions = {}): Promise<ExecutionResult<string>> {
27+
const proc = await getProcessFactory().create();
28+
return proc.exec(file, args, options);
29+
}
30+
2631
export function pathExists(absPath: string): Promise<boolean> {
2732
return fsapi.pathExists(absPath);
2833
}
@@ -67,7 +72,7 @@ export function getGlobalPersistentStore<T>(key: string): IPersistentStore<T> {
6772
};
6873
}
6974

70-
export async function getFileInfo(filePath: string): Promise<{ctime:number, mtime:number}> {
75+
export async function getFileInfo(filePath: string): Promise<{ ctime: number, mtime: number }> {
7176
try {
7277
const data = await fsapi.lstat(filePath);
7378
return {
@@ -81,7 +86,7 @@ export async function getFileInfo(filePath: string): Promise<{ctime:number, mtim
8186
}
8287
}
8388

84-
export async function resolveSymbolicLink(filepath:string): Promise<string> {
89+
export async function resolveSymbolicLink(filepath: string): Promise<string> {
8590
const stats = await fsapi.lstat(filepath);
8691
if (stats.isSymbolicLink()) {
8792
const link = await fsapi.readlink(filepath);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import {
5+
HKCU, HKLM, Options, REG_SZ, Registry, RegistryItem,
6+
} from 'winreg';
7+
import { createDeferred } from '../../common/utils/async';
8+
9+
10+
export { HKCU, HKLM, REG_SZ, Options };
11+
12+
13+
export interface IRegistryKey {
14+
hive: string;
15+
arch: string;
16+
key: string;
17+
parentKey?: IRegistryKey;
18+
}
19+
20+
export interface IRegistryValue {
21+
hive: string;
22+
arch: string;
23+
key: string;
24+
name: string;
25+
type: string;
26+
value: string;
27+
}
28+
29+
export async function readRegistryValues(options: Options): Promise<IRegistryValue[]> {
30+
// tslint:disable-next-line:no-require-imports
31+
const WinReg = require('winreg');
32+
const regKey = new WinReg(options);
33+
const deferred = createDeferred<RegistryItem[]>();
34+
regKey.values((err: Error, res: RegistryItem[]) => {
35+
if (err) {
36+
deferred.reject(err);
37+
}
38+
deferred.resolve(res);
39+
});
40+
return deferred.promise;
41+
}
42+
43+
export async function readRegistryKeys(options: Options): Promise<IRegistryKey[]> {
44+
// tslint:disable-next-line:no-require-imports
45+
const WinReg = require('winreg');
46+
const regKey = new WinReg(options);
47+
const deferred = createDeferred<Registry[]>();
48+
regKey.keys((err: Error, res: Registry[]) => {
49+
if (err) {
50+
deferred.reject(err);
51+
}
52+
deferred.resolve(res);
53+
});
54+
return deferred.promise;
55+
}
Lines changed: 37 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
import { uniqBy } from 'lodash';
45
import * as path from 'path';
5-
import {
6-
Options, REG_SZ, Registry, RegistryItem,
7-
} from 'winreg';
8-
import { traceVerbose } from '../../common/logger';
9-
import { createDeferred } from '../../common/utils/async';
6+
import { traceError, traceVerbose } from '../../common/logger';
7+
import { HKCU, HKLM, IRegistryKey, IRegistryValue, readRegistryKeys, readRegistryValues, REG_SZ } from './windowsRegistry';
108

119
// tslint:disable-next-line: no-single-line-block-comment
1210
/* eslint-disable global-require */
@@ -16,7 +14,7 @@ import { createDeferred } from '../../common/utils/async';
1614
* @param {string} interpreterPath : Path to python interpreter.
1715
* @returns {boolean} : Returns true if the path matches pattern for windows python executable.
1816
*/
19-
export function isWindowsPythonExe(interpreterPath:string): boolean {
17+
export function isWindowsPythonExe(interpreterPath: string): boolean {
2018
/**
2119
* This Reg-ex matches following file names:
2220
* python.exe
@@ -29,69 +27,25 @@ export function isWindowsPythonExe(interpreterPath:string): boolean {
2927
return windowsPythonExes.test(path.basename(interpreterPath));
3028
}
3129

32-
export interface IRegistryKey{
33-
hive:string;
34-
arch:string;
35-
key:string;
36-
parentKey?:IRegistryKey;
37-
}
38-
39-
export interface IRegistryValue{
40-
hive:string;
41-
arch:string;
42-
key:string;
43-
name:string;
44-
type:string;
45-
value:string;
46-
}
47-
48-
export async function readRegistryValues(options: Options): Promise<IRegistryValue[]> {
49-
// tslint:disable-next-line:no-require-imports
50-
const WinReg = require('winreg');
51-
const regKey = new WinReg(options);
52-
const deferred = createDeferred<RegistryItem[]>();
53-
regKey.values((err:Error, res:RegistryItem[]) => {
54-
if (err) {
55-
deferred.reject(err);
56-
}
57-
deferred.resolve(res);
58-
});
59-
return deferred.promise;
60-
}
61-
62-
export async function readRegistryKeys(options: Options): Promise<IRegistryKey[]> {
63-
// tslint:disable-next-line:no-require-imports
64-
const WinReg = require('winreg');
65-
const regKey = new WinReg(options);
66-
const deferred = createDeferred<Registry[]>();
67-
regKey.keys((err:Error, res:Registry[]) => {
68-
if (err) {
69-
deferred.reject(err);
70-
}
71-
deferred.resolve(res);
72-
});
73-
return deferred.promise;
74-
}
75-
76-
export interface IRegistryInterpreterData{
30+
export interface IRegistryInterpreterData {
7731
interpreterPath: string;
7832
versionStr?: string;
79-
sysVersionStr?:string;
33+
sysVersionStr?: string;
8034
bitnessStr?: string;
8135
displayName?: string;
8236
distroOrgName?: string;
8337
}
8438

8539
async function getInterpreterDataFromKey(
86-
{ arch, hive, key }:IRegistryKey,
87-
distroOrgName:string,
40+
{ arch, hive, key }: IRegistryKey,
41+
distroOrgName: string,
8842
): Promise<IRegistryInterpreterData | undefined> {
89-
const result:IRegistryInterpreterData = {
43+
const result: IRegistryInterpreterData = {
9044
interpreterPath: '',
9145
distroOrgName,
9246
};
9347

94-
const values:IRegistryValue[] = await readRegistryValues({ arch, hive, key });
48+
const values: IRegistryValue[] = await readRegistryValues({ arch, hive, key });
9549
for (const value of values) {
9650
switch (value.name) {
9751
case 'SysArchitecture':
@@ -111,10 +65,10 @@ async function getInterpreterDataFromKey(
11165
}
11266
}
11367

114-
const subKeys:IRegistryKey[] = await readRegistryKeys({ arch, hive, key });
68+
const subKeys: IRegistryKey[] = await readRegistryKeys({ arch, hive, key });
11569
const subKey = subKeys.map((s) => s.key).find((s) => s.endsWith('InstallPath'));
11670
if (subKey) {
117-
const subKeyValues:IRegistryValue[] = await readRegistryValues({ arch, hive, key: subKey });
71+
const subKeyValues: IRegistryValue[] = await readRegistryValues({ arch, hive, key: subKey });
11872
const value = subKeyValues.find((v) => v.name === 'ExecutablePath');
11973
if (value) {
12074
result.interpreterPath = value.value;
@@ -131,12 +85,34 @@ async function getInterpreterDataFromKey(
13185
}
13286

13387
export async function getInterpreterDataFromRegistry(
134-
arch:string,
135-
hive:string,
136-
key:string,
88+
arch: string,
89+
hive: string,
90+
key: string,
13791
): Promise<IRegistryInterpreterData[]> {
13892
const subKeys = await readRegistryKeys({ arch, hive, key });
13993
const distroOrgName = key.substr(key.lastIndexOf('\\') + 1);
14094
const allData = await Promise.all(subKeys.map((subKey) => getInterpreterDataFromKey(subKey, distroOrgName)));
14195
return (allData.filter((data) => data !== undefined) || []) as IRegistryInterpreterData[];
14296
}
97+
98+
export async function getRegistryInterpreters(): Promise<IRegistryInterpreterData[]> {
99+
let registryData: IRegistryInterpreterData[] = [];
100+
101+
for (const arch of ['x64', 'x86']) {
102+
for (const hive of [HKLM, HKCU]) {
103+
const root = '\\SOFTWARE\\Python';
104+
let keys: string[] = [];
105+
try {
106+
keys = (await readRegistryKeys({ arch, hive, key: root })).map((k) => k.key);
107+
} catch (ex) {
108+
traceError(`Failed to access Registry: ${arch}\\${hive}\\${root}`, ex);
109+
}
110+
111+
for (const key of keys) {
112+
registryData = registryData.concat(await getInterpreterDataFromRegistry(arch, hive, key));
113+
}
114+
}
115+
}
116+
117+
return uniqBy(registryData, (r: IRegistryInterpreterData) => r.interpreterPath);
118+
}

0 commit comments

Comments
 (0)