Skip to content

Commit d65c331

Browse files
author
Kartik Raj
committed
Modify FSWatching Locator
1 parent 5925ab1 commit d65c331

File tree

3 files changed

+86
-44
lines changed

3 files changed

+86
-44
lines changed

src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
import { FileChangeType } from '../../../../common/platform/fileSystemWatcher';
5+
import { sleep } from '../../../../common/utils/async';
6+
import { watchLocationForPythonBinaries } from '../../../common/pythonBinariesWatcher';
7+
import { PythonEnvKind } from '../../info';
48
import { Locator } from '../../locator';
59

610
/**
@@ -10,5 +14,71 @@ import { Locator } from '../../locator';
1014
* Subclasses can call `this.emitter.fire()` * to emit events.
1115
*/
1216
export abstract class FSWatchingLocator extends Locator {
13-
public async abstract initialize(): Promise<void>;
17+
private initialized = false;
18+
19+
constructor(
20+
/**
21+
* Location(s) to watch for python binaries.
22+
*/
23+
private readonly getRoots: () => Promise<string[]> | string,
24+
/**
25+
* Returns the kind of environment specific to locator given the path to exectuable.
26+
*/
27+
private readonly getKind: (executable: string) => Promise<PythonEnvKind>,
28+
private readonly opts: {
29+
/**
30+
* Glob which represents basename of the executable to watch.
31+
*/
32+
executableBaseGlob?: string,
33+
/**
34+
* Time to wait before attempting to identify kind when detected that an environment has been created.
35+
*/
36+
delayOnCreated?: number, // milliseconds
37+
} = {},
38+
) {
39+
super();
40+
}
41+
42+
public async initialize(): Promise<void> {
43+
if (this.initialized) {
44+
return;
45+
}
46+
this.initialized = true;
47+
await this.startWatchers();
48+
}
49+
50+
public dispose(): void {
51+
super.dispose();
52+
this.initialized = false;
53+
}
54+
55+
private async startWatchers(): Promise<void> {
56+
let roots = await this.getRoots();
57+
if (typeof roots === 'string') {
58+
roots = [roots];
59+
}
60+
roots.map((root) => this.startWatcher(root));
61+
}
62+
63+
private startWatcher(root: string): void {
64+
this.disposables.push(
65+
watchLocationForPythonBinaries(
66+
root,
67+
async (type: FileChangeType, executable: string) => {
68+
if (type === FileChangeType.Created) {
69+
if (this.opts.delayOnCreated !== undefined) {
70+
// Note detecting kind of env depends on the file structure around the
71+
// executable, so we need to wait before attempting to detect it. However even
72+
// if the type detected is incorrect, it doesn't do any practical harm as kinds
73+
// in the same locator can be used in the same way (same activation commands etc.)
74+
await sleep(this.opts.delayOnCreated);
75+
}
76+
}
77+
const kind = await this.getKind(executable);
78+
this.emitter.fire({ type, kind });
79+
},
80+
this.opts.executableBaseGlob,
81+
),
82+
);
83+
}
1484
}

src/client/pythonEnvironments/discovery/locators/services/globalVirtualEnvronmentLocator.ts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import { uniq } from 'lodash';
55
import * as path from 'path';
66
import { traceVerbose } from '../../../../common/logger';
7-
import { FileChangeType } from '../../../../common/platform/fileSystemWatcher';
8-
import { chain, iterable, sleep } from '../../../../common/utils/async';
7+
import { chain, iterable } from '../../../../common/utils/async';
98
import {
109
getEnvironmentVariable, getOSType, getUserHomeDir, OSType
1110
} from '../../../../common/utils/platform';
@@ -15,7 +14,6 @@ import { IPythonEnvsIterator } from '../../../base/locator';
1514
import { FSWatchingLocator } from '../../../base/locators/lowLevel/fsWatchingLocator';
1615
import { findInterpretersInDir } from '../../../common/commonUtils';
1716
import { getFileInfo, pathExists } from '../../../common/externalDependencies';
18-
import { watchLocationForPythonBinaries } from '../../../common/pythonBinariesWatcher';
1917
import { isPipenvEnvironment } from './pipEnvHelper';
2018
import {
2119
isVenvEnvironment,
@@ -89,12 +87,14 @@ export class GlobalVirtualEnvironmentLocator extends FSWatchingLocator {
8987
PythonEnvKind.Pipenv,
9088
];
9189

92-
public constructor(private readonly searchDepth?: number) {
93-
super();
94-
}
95-
96-
public async initialize(): Promise<void> {
97-
await this.startWatchers();
90+
constructor(private readonly searchDepth?: number) {
91+
super(getGlobalVirtualEnvDirs, getVirtualEnvKind, {
92+
// Note detecting kind of virtual env depends on the file structure around the
93+
// executable, so we need to wait before attempting to detect it. However even
94+
// if the type detected is incorrect, it doesn't do any practical harm as kinds
95+
// in this locator are used in the same way (same activation commands etc.)
96+
delayOnCreated: 1000,
97+
});
9898
}
9999

100100
public iterEnvs(): IPythonEnvsIterator {
@@ -171,21 +171,4 @@ export class GlobalVirtualEnvironmentLocator extends FSWatchingLocator {
171171
}
172172
return undefined;
173173
}
174-
175-
private async startWatchers(): Promise<void> {
176-
const dirs = await getGlobalVirtualEnvDirs();
177-
dirs.forEach(
178-
(d) => this.disposables.push(
179-
watchLocationForPythonBinaries(d, async (type: FileChangeType, executablePath: string) => {
180-
// Note detecting kind of virtual env depends on the file structure around the
181-
// executable, so we need to wait before attempting to detect it. However even
182-
// if the type detected is incorrect, it doesn't do any practical harm as kinds
183-
// in this locator are used in the same way (same activation commands etc.)
184-
await sleep(1000);
185-
const kind = await getVirtualEnvKind(executablePath);
186-
this.emitter.fire({ type, kind });
187-
}),
188-
),
189-
);
190-
}
191174
}

src/client/pythonEnvironments/discovery/locators/services/windowsStoreLocator.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@ import * as fsapi from 'fs-extra';
55
import * as minimatch from 'minimatch';
66
import * as path from 'path';
77
import { traceWarning } from '../../../../common/logger';
8-
import { FileChangeType } from '../../../../common/platform/fileSystemWatcher';
98
import { Architecture, getEnvironmentVariable } from '../../../../common/utils/platform';
109
import { PythonEnvInfo, PythonEnvKind } from '../../../base/info';
1110
import { buildEnvInfo } from '../../../base/info/env';
1211
import { getPythonVersionFromPath } from '../../../base/info/pythonVersion';
1312
import { IPythonEnvsIterator } from '../../../base/locator';
1413
import { FSWatchingLocator } from '../../../base/locators/lowLevel/fsWatchingLocator';
1514
import { getFileInfo } from '../../../common/externalDependencies';
16-
import { watchLocationForPythonBinaries } from '../../../common/pythonBinariesWatcher';
1715

1816
/**
1917
* Gets path to the Windows Apps directory.
@@ -143,8 +141,12 @@ export async function getWindowsStorePythonExes(): Promise<string[]> {
143141
export class WindowsStoreLocator extends FSWatchingLocator {
144142
private readonly kind: PythonEnvKind = PythonEnvKind.WindowsStore;
145143

146-
public async initialize(): Promise<void> {
147-
this.startWatcher();
144+
constructor() {
145+
super(
146+
getWindowsStoreAppsRoot,
147+
async () => this.kind,
148+
{ executableBaseGlob: pythonExeGlob },
149+
);
148150
}
149151

150152
public iterEnvs(): IPythonEnvsIterator {
@@ -176,17 +178,4 @@ export class WindowsStoreLocator extends FSWatchingLocator {
176178
}
177179
return undefined;
178180
}
179-
180-
private startWatcher(): void {
181-
const windowsAppsRoot = getWindowsStoreAppsRoot();
182-
this.disposables.push(
183-
watchLocationForPythonBinaries(
184-
windowsAppsRoot,
185-
(type: FileChangeType) => {
186-
this.emitter.fire({ type, kind: this.kind });
187-
},
188-
pythonExeGlob,
189-
),
190-
);
191-
}
192181
}

0 commit comments

Comments
 (0)