From cfb213a106df30f06a0515aab5d3d475095f2ead Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 31 Aug 2021 13:24:53 -0700 Subject: [PATCH] Ensure workspace interpreters are discovered and watched --- src/client/common/utils/misc.ts | 9 +++++---- .../locators/composite/envsCollectionCache.ts | 2 ++ .../base/locators/lowLevel/fsWatchingLocator.ts | 11 +++++++++-- .../base/locators/lowLevel/poetryLocator.ts | 4 +++- .../lowLevel/workspaceVirtualEnvLocator.ts | 17 +++++++++++------ .../common/pythonBinariesWatcher.ts | 2 ++ 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/client/common/utils/misc.ts b/src/client/common/utils/misc.ts index 23d6db2b76d3..060edd904618 100644 --- a/src/client/common/utils/misc.ts +++ b/src/client/common/utils/misc.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. 'use strict'; import type { TextDocument, Uri } from 'vscode'; +import { arePathsSame, isParentPath } from '../../pythonEnvironments/common/externalDependencies'; import { InteractiveInputScheme, NotebookCellScheme } from '../constants'; import { InterpreterUri } from '../installer/types'; import { Resource } from '../types'; @@ -126,15 +127,15 @@ export function getURIFilter( while (candidate.path.endsWith('/')) { candidatePath = candidatePath.slice(0, -1); } - if (opts.checkExact && candidatePath === uriPath) { + if (opts.checkExact && arePathsSame(candidatePath, uriPath)) { return true; } - if (opts.checkParent && candidatePath.startsWith(uriRoot)) { + if (opts.checkParent && isParentPath(candidatePath, uriRoot)) { return true; } if (opts.checkChild) { - const candidateRoot = `{candidatePath}/`; - if (uriPath.startsWith(candidateRoot)) { + const candidateRoot = `${candidatePath}/`; + if (isParentPath(uriPath, candidateRoot)) { return true; } } diff --git a/src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts b/src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts index 897c536c1e74..c6d116792ea1 100644 --- a/src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts +++ b/src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { Event } from 'vscode'; +import { traceInfo } from '../../../../common/logger'; import { asyncFilter } from '../../../../common/utils/arrayUtils'; import { pathExists } from '../../../common/externalDependencies'; import { PythonEnvInfo } from '../../info'; @@ -112,6 +113,7 @@ export class PythonEnvInfoCache extends PythonEnvsWatcher { if (this.envs.length) { + traceInfo('Environments added to cache', JSON.stringify(this.envs)); await this.persistentStorage.store(this.envs); } } diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts index 6086a9584f00..39ff6f30f574 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/fsWatchingLocator.ts @@ -5,6 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { Uri } from 'vscode'; import { DiscoveryVariants } from '../../../../common/experiments/groups'; +import { traceVerbose } from '../../../../common/logger'; import { FileChangeType } from '../../../../common/platform/fileSystemWatcher'; import { sleep } from '../../../../common/utils/async'; import { logError } from '../../../../logging'; @@ -33,6 +34,7 @@ function checkDirWatchable(dirname: string): DirUnwatchableReason { try { names = fs.readdirSync(dirname); } catch (err) { + traceVerbose('haha', err); if (err.code === 'ENOENT') { // We treat a missing directory as watchable since it should // be watchable if created later. @@ -42,8 +44,10 @@ function checkDirWatchable(dirname: string): DirUnwatchableReason { } // The limit here is an educated guess. if (names.length > 200) { + traceVerbose('say what'); return 'too many files'; } + traceVerbose('yep'); return undefined; } @@ -92,12 +96,15 @@ export abstract class FSWatchingLocator extends LazyResourceB // Enable global watchers only if the experiment allows it. const enableGlobalWatchers = await inExperiment(DiscoveryVariants.discoverWithFileWatching); if (!enableGlobalWatchers) { + traceVerbose('Watcher disabled'); return; } } // Start the FS watchers. + traceVerbose('Getting roots'); let roots = await this.getRoots(); + traceVerbose('Found roots'); if (typeof roots === 'string') { roots = [roots]; } @@ -106,13 +113,12 @@ export abstract class FSWatchingLocator extends LazyResourceB // that might be watched due to a glob are not checked. const unwatchable = await checkDirWatchable(root); if (unwatchable) { - logError(`dir "${root}" is not watchable (${unwatchable})`); + logError(`Dir "${root}" is not watchable (${unwatchable})`); return undefined; } return root; }); const watchableRoots = (await Promise.all(promises)).filter((root) => !!root) as string[]; - watchableRoots.forEach((root) => this.startWatchers(root)); } @@ -147,6 +153,7 @@ export abstract class FSWatchingLocator extends LazyResourceB // The structure determines which globs are returned. this.opts.envStructure, ); + traceVerbose('Start watching root', root, 'for globs', JSON.stringify(globs)); const watchers = globs.map((g) => watchLocationForPythonBinaries(root, callback, g)); this.disposables.push(...watchers); } diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/poetryLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/poetryLocator.ts index 9382ab12cc68..395e3d6352a8 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/poetryLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/poetryLocator.ts @@ -8,7 +8,7 @@ import { traceError, traceVerbose } from '../../../../common/logger'; import { chain, iterable } from '../../../../common/utils/async'; import { PythonEnvKind } from '../../info'; import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator'; -import { FSWatchingLocator } from './fsWatchingLocator'; +import { FSWatcherKind, FSWatchingLocator } from './fsWatchingLocator'; import { getInterpreterPathFromDir } from '../../../common/commonUtils'; import { pathExists } from '../../../common/externalDependencies'; import { isPoetryEnvironment, localPoetryEnvDirName, Poetry } from '../../../common/environmentManagers/poetry'; @@ -65,6 +65,8 @@ export class PoetryLocator extends FSWatchingLocator { super( () => getRootVirtualEnvDir(root), async () => PythonEnvKind.Poetry, + undefined, + FSWatcherKind.Workspace, ); } diff --git a/src/client/pythonEnvironments/base/locators/lowLevel/workspaceVirtualEnvLocator.ts b/src/client/pythonEnvironments/base/locators/lowLevel/workspaceVirtualEnvLocator.ts index 0b0e60dd94d0..ebee7cbabd11 100644 --- a/src/client/pythonEnvironments/base/locators/lowLevel/workspaceVirtualEnvLocator.ts +++ b/src/client/pythonEnvironments/base/locators/lowLevel/workspaceVirtualEnvLocator.ts @@ -10,7 +10,7 @@ import { isPipenvEnvironment } from '../../../common/environmentManagers/pipenv' import { isVenvEnvironment, isVirtualenvEnvironment } from '../../../common/environmentManagers/simplevirtualenvs'; import { PythonEnvKind } from '../../info'; import { BasicEnvInfo, IPythonEnvsIterator } from '../../locator'; -import { FSWatchingLocator } from './fsWatchingLocator'; +import { FSWatcherKind, FSWatchingLocator } from './fsWatchingLocator'; import '../../../../common/extensions'; import { asyncFilter } from '../../../../common/utils/arrayUtils'; @@ -52,11 +52,16 @@ async function getVirtualEnvKind(interpreterPath: string): Promise { public constructor(private readonly root: string) { - super(() => getWorkspaceVirtualEnvDirs(this.root), getVirtualEnvKind, { - // Note detecting kind of virtual env depends on the file structure around the - // executable, so we need to wait before attempting to detect it. - delayOnCreated: 1000, - }); + super( + () => getWorkspaceVirtualEnvDirs(this.root), + getVirtualEnvKind, + { + // Note detecting kind of virtual env depends on the file structure around the + // executable, so we need to wait before attempting to detect it. + delayOnCreated: 1000, + }, + FSWatcherKind.Workspace, + ); } protected doIterEnvs(): IPythonEnvsIterator { diff --git a/src/client/pythonEnvironments/common/pythonBinariesWatcher.ts b/src/client/pythonEnvironments/common/pythonBinariesWatcher.ts index dc3ca86de559..364157b02fb0 100644 --- a/src/client/pythonEnvironments/common/pythonBinariesWatcher.ts +++ b/src/client/pythonEnvironments/common/pythonBinariesWatcher.ts @@ -5,6 +5,7 @@ import * as minimatch from 'minimatch'; import * as path from 'path'; +import { traceVerbose } from '../../common/logger'; import { FileChangeType, watchLocationForPattern } from '../../common/platform/fileSystemWatcher'; import { getOSType, OSType } from '../../common/utils/platform'; import { IDisposable } from '../../common/utils/resourceLifecycle'; @@ -26,6 +27,7 @@ export function watchLocationForPythonBinaries( const resolvedGlob = path.posix.normalize(executableGlob); const [baseGlob] = resolvedGlob.split('/').slice(-1); function callbackClosure(type: FileChangeType, e: string) { + traceVerbose('Received event', JSON.stringify(e), 'for baseglob', baseGlob); const isMatch = minimatch(path.basename(e), baseGlob, { nocase: getOSType() === OSType.Windows }); if (!isMatch) { // When deleting the file for some reason path to all directories leading up to python are reported