Skip to content

Commit

Permalink
Additional conda telemetry (microsoft#23790)
Browse files Browse the repository at this point in the history
  • Loading branch information
DonJayamanne authored and eleanorjboyd committed Jul 30, 2024
1 parent 72260f4 commit 56a795d
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,17 @@ export interface NativeEnvManagerInfo {
version?: string;
}

export type NativeCondaInfo = {
canSpawnConda: boolean;
condaRcs: string[];
envDirs: string[];
};

export interface NativeGlobalPythonFinder extends Disposable {
resolve(executable: string): Promise<NativeEnvInfo>;
refresh(): AsyncIterable<NativeEnvInfo>;
categoryToKind(category?: string): PythonEnvKind;
getCondaInfo(): Promise<NativeCondaInfo>;
}

interface NativeLog {
Expand Down Expand Up @@ -361,6 +368,10 @@ class NativeGlobalPythonFinderImpl extends DisposableBase implements NativeGloba
this.outputChannel.error('Refresh error', ex);
}
}

async getCondaInfo(): Promise<NativeCondaInfo> {
return this.connection.sendRequest<NativeCondaInfo>('condaInfo');
}
}

type ConfigurationOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,18 +333,30 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
let condaInfoEnvsInvalid = 0;
let condaInfoEnvsDuplicate = 0;
let condaInfoEnvsInvalidPrefix = 0;
let condaInfoEnvsDirs: undefined | number;
let condaInfoEnvsDirs = 0;
let envsDirs: string[] = [];
let condaRcs: number | undefined;
let condaRootPrefixFoundInInfoNotInNative: undefined | boolean;
let condaDefaultPrefixFoundInInfoNotInNative: undefined | boolean;
try {
const conda = await Conda.getConda();
const info = await conda?.getInfo();
canSpawnConda = true;
canSpawnConda = !!info;
condaInfoEnvs = info?.envs?.length;
// eslint-disable-next-line camelcase
condaInfoEnvsDirs = info?.envs_dirs?.length;
// eslint-disable-next-line camelcase
envsDirs = info?.envs_dirs || [];

const condaRcFiles = new Set<string>();
await Promise.all(
// eslint-disable-next-line camelcase
[info?.rc_path, info?.user_rc_path, info?.sys_rc_path, ...(info?.config_files || [])].map(
async (rc) => {
if (rc && (await pathExists(rc))) {
condaRcFiles.add(rc);
}
},
),
).catch(noop);
condaRcs = condaRcFiles.size;
const duplicate = new Set<string>();
Promise.all(
(info?.envs || []).map(async (e) => {
Expand All @@ -361,16 +373,54 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
}
}),
);
Promise.all(
envsDirs.map(async (e) => {
if (await pathExists(e)) {
condaInfoEnvsDirs += 1;
}
}),
);
nativeEnvs
.filter((e) => this.nativeFinder.categoryToKind(e.kind) === PythonEnvKind.Conda)
.forEach((e) => {
if (e.prefix && envsDirs.some((d) => e.prefix && e.prefix.startsWith(d))) {
missingEnvironments.nativeCondaEnvsInEnvDir += 1;
}
});

// Check if we have found the conda env that matches the `root_prefix` in the conda info.
// eslint-disable-next-line camelcase
const rootPrefix = (info?.root_prefix || '').toLowerCase();
if (rootPrefix) {
// Check if we have a conda env that matches this prefix.
if (
envs.some(
(e) => e.executable.sysPrefix.toLowerCase() === rootPrefix && e.kind === PythonEnvKind.Conda,
)
) {
condaRootPrefixFoundInInfoNotInNative = nativeEnvs.some(
(e) => e.prefix?.toLowerCase() === rootPrefix.toLowerCase(),
);
}
}
// eslint-disable-next-line camelcase
const defaultPrefix = (info?.default_prefix || '').toLowerCase();
if (rootPrefix) {
// Check if we have a conda env that matches this prefix.
if (
envs.some(
(e) => e.executable.sysPrefix.toLowerCase() === defaultPrefix && e.kind === PythonEnvKind.Conda,
)
) {
condaDefaultPrefixFoundInInfoNotInNative = nativeEnvs.some(
(e) => e.prefix?.toLowerCase() === defaultPrefix.toLowerCase(),
);
}
}
} catch (ex) {
canSpawnConda = false;
}
const nativeCondaInfoPromise = this.nativeFinder.getCondaInfo();
const prefixesSeenAlready = new Set<string>();
await Promise.all(
envs.map(async (env) => {
Expand Down Expand Up @@ -480,6 +530,22 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
}),
).catch((ex) => traceError('Failed to send telemetry for missing environments', ex));

const nativeCondaInfo = await nativeCondaInfoPromise.catch((ex) =>
traceError(`Failed to get conda info from native locator`, ex),
);

type CondaTelemetry = {
nativeCanSpawnConda?: boolean;
nativeCondaInfoEnvsDirs?: number;
nativeCondaRcs?: number;
};

const condaTelemetry: CondaTelemetry = {};
if (nativeCondaInfo) {
condaTelemetry.nativeCanSpawnConda = nativeCondaInfo.canSpawnConda;
condaTelemetry.nativeCondaInfoEnvsDirs = new Set(nativeCondaInfo.envDirs).size;
condaTelemetry.nativeCondaRcs = new Set(nativeCondaInfo.condaRcs).size;
}
const environmentsWithoutPython = envs.filter(
(e) => getEnvPath(e.executable.filename, e.location).pathType === 'envFolderPath',
).length;
Expand Down Expand Up @@ -513,7 +579,7 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
if (e.executable.sysPrefix && !(await pathExists(e.executable.sysPrefix))) {
missingEnvironments.prefixNotExistsCondaEnvs += 1;
}
if (e.executable.filename && (await isCondaEnvironment(e.executable.filename))) {
if (e.executable.filename && !(await isCondaEnvironment(e.executable.filename))) {
missingEnvironments.invalidCondaEnvs += 1;
}
}),
Expand Down Expand Up @@ -568,10 +634,13 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection

// Intent is to capture time taken for discovery of all envs to complete the first time.
sendTelemetryEvent(EventName.PYTHON_INTERPRETER_DISCOVERY, elapsedTime, {
telVer: 2,
telVer: 3,
condaRcs,
condaInfoEnvsInvalid,
condaInfoEnvsDuplicate,
condaInfoEnvsInvalidPrefix,
condaRootPrefixFoundInInfoNotInNative,
condaDefaultPrefixFoundInInfoNotInNative,
nativeDuration,
workspaceFolderCount: (workspace.workspaceFolders || []).length,
interpreters: this.cache.getAllEnvs().length,
Expand Down Expand Up @@ -610,6 +679,7 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
nativeVirtualEnvEnvs,
nativeVirtualEnvWrapperEnvs,
nativeGlobal,
...condaTelemetry,
...missingEnvironments,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export type CondaInfo = {
root_prefix?: string; // eslint-disable-line camelcase
conda_version?: string; // eslint-disable-line camelcase
conda_shlvl?: number; // eslint-disable-line camelcase
config_files?: string[]; // eslint-disable-line camelcase
rc_path?: string; // eslint-disable-line camelcase
sys_rc_path?: string; // eslint-disable-line camelcase
user_rc_path?: string; // eslint-disable-line camelcase
};

type CondaEnvInfo = {
Expand Down
32 changes: 32 additions & 0 deletions src/client/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,9 @@ export interface IEventNamePropertyMapping {
"envsNotFound" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"condaInfoEnvs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"condaInfoEnvsDirs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"nativeCondaInfoEnvsDirs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"condaRcs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"nativeCondaRcs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"condaEnvsInEnvDir" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"nativeCondaEnvsInEnvDir" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"invalidCondaEnvs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
Expand All @@ -1161,6 +1164,9 @@ export interface IEventNamePropertyMapping {
"environmentsWithoutPython" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "donjayamanne" },
"usingNativeLocator" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "donjayamanne" },
"canSpawnConda" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "donjayamanne" },
"nativeCanSpawnConda" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"condaRootPrefixFoundInInfoNotInNative" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"condaDefaultPrefixFoundInInfoNotInNative" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true , "owner": "donjayamanne"},
"activeStateEnvs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "donjayamanne" },
"condaEnvs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "donjayamanne" },
"customEnvs" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "donjayamanne" },
Expand Down Expand Up @@ -1249,6 +1255,18 @@ export interface IEventNamePropertyMapping {
* The number of the envs_dirs returned by `conda info`
*/
condaInfoEnvsDirs?: number;
/**
* The number of the envs_dirs returned by native locator.
*/
nativeCondaInfoEnvsDirs?: number;
/**
* The number of the conda rc files found using conda info
*/
condaRcs?: number;
/**
* The number of the conda rc files found using native locator.
*/
nativeCondaRcs?: number;
/**
* The number of conda interpreters that are in the one of the global conda env locations.
* Global conda envs locations are returned by `conda info` in the `envs_dirs` setting.
Expand All @@ -1259,6 +1277,16 @@ export interface IEventNamePropertyMapping {
* Global conda envs locations are returned by `conda info` in the `envs_dirs` setting.
*/
nativeCondaEnvsInEnvDir?: number;
/**
* A conda env found that matches the root_prefix returned by `conda info`
* However a corresponding conda env not found by native locator.
*/
condaRootPrefixFoundInInfoNotInNative?: boolean;
/**
* A conda env found that matches the root_prefix returned by `conda info`
* However a corresponding conda env not found by native locator.
*/
condaDefaultPrefixFoundInInfoNotInNative?: boolean;
/**
* The number of conda interpreters without the `conda-meta` directory.
*/
Expand All @@ -1275,6 +1303,10 @@ export interface IEventNamePropertyMapping {
* Conda exe can be spawned.
*/
canSpawnConda?: boolean;
/**
* Conda exe can be spawned by native locator.
*/
nativeCanSpawnConda?: boolean;
/**
* The number of the interpreters not found in disc.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ import { OSType, getOSType } from '../../../../common';
import * as nativeFinder from '../../../../../client/pythonEnvironments/base/locators/common/nativePythonFinder';

class MockNativePythonFinder implements nativeFinder.NativeGlobalPythonFinder {
getCondaInfo(): Promise<nativeFinder.NativeCondaInfo> {
throw new Error('Method not implemented.');
}

categoryToKind(_category: string): PythonEnvKind {
throw new Error('Method not implemented.');
}
Expand Down

0 comments on commit 56a795d

Please sign in to comment.