Skip to content

Commit 89cce49

Browse files
authored
Watch package changes through PackageWatcher instead of TerminaPacakageWatcher (#1698)
This pull request refactors and improves the package change detection and watcher system for Python environments. It consolidates the logic for watching package changes across all environment managers into a single, manager-agnostic watcher registration, and removes redundant or manager-specific watcher code. The new system ensures that package changes are detected and handled consistently, regardless of environment type or package manager. Fixes #1683 **Core improvements to package watcher system:** * A new `registerPackageWatchers` function is introduced in `packageWatcher.ts`, which registers package watchers for all active environments, sharing watchers when environments are active in multiple scopes and cleaning up watchers when no longer needed. This replaces the old per-manager watcher registration. ([src/managers/common/packageWatcher.tsL2-R11](diffhunk://#diff-db307f917634197b286344124fab7702aa1c37601df0c09bceba8b6d1cd76cd3L2-R11), F89a1842L51R51) * The watcher registration is now called once during extension activation, rather than per environment manager, ensuring all active environments are covered. [[1]](diffhunk://#diff-04bba6a35cad1c794cbbe677678a51de13441b7a6ee8592b7b50be1f05c6f626R673-R674) [[2]](diffhunk://#diff-04bba6a35cad1c794cbbe677678a51de13441b7a6ee8592b7b50be1f05c6f626L684-L688) * The watcher logic is updated to better handle watcher sharing and disposal, and to use improved glob patterns for `.dist-info` directories, catching more package changes. (F89a1842L18R18, F89a1842L43R43) **Cleanup and removal of legacy code:** * The old `terminalPackageWatcher.ts` and its related imports and registration are removed, as its functionality is now covered by the new watcher system. [[1]](diffhunk://#diff-04bba6a35cad1c794cbbe677678a51de13441b7a6ee8592b7b50be1f05c6f626L89) [[2]](diffhunk://#diff-04bba6a35cad1c794cbbe677678a51de13441b7a6ee8592b7b50be1f05c6f626L684-L688) [[3]](diffhunk://#diff-9b9f96660a5ea04265c5ed7f91dba539d24dc06469c0feed8e3af541aa7d1321L1-L111) * Manager-specific watcher registration code is removed from `builtin/main.ts` and `poetry/main.ts`, further consolidating watcher logic. [[1]](diffhunk://#diff-450a068335039323c00b342cbfbff615ecf1cda7bd9d73aaed6b8e13d2d9d7f5L7) [[2]](diffhunk://#diff-450a068335039323c00b342cbfbff615ecf1cda7bd9d73aaed6b8e13d2d9d7f5L44-L48) [[3]](diffhunk://#diff-27854fc91d92524ea1d6cecfec07dcb12f64204246b5d45097156ddc3a1867ecL7) [[4]](diffhunk://#diff-27854fc91d92524ea1d6cecfec07dcb12f64204246b5d45097156ddc3a1867ecL28) **API and test updates:** * The `InternalPackageManager` class now exposes a `getPackageWatchTargets` method to allow custom watch targets per package manager. * Tests are updated to use the new watcher registration API. [[1]](diffhunk://#diff-960d3ebbd27add9587b7da36c8ce1af3836525d17909b0e8f8fb01cf293f7ac3L7-R10) [[2]](diffhunk://#diff-960d3ebbd27add9587b7da36c8ce1af3836525d17909b0e8f8fb01cf293f7ac3L87-L95) These changes improve maintainability, reliability, and coverage of package change detection across all Python environments managed by the extension. --------- Copilot-Session: a7de2667-ab70-47de-a8fd-5b00953e14f7
1 parent 772f34f commit 89cce49

13 files changed

Lines changed: 505 additions & 1057 deletions

File tree

docs/startup-flow.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ All three trigger `initialize()` lazily (once-only, guarded by `_initialized` de
9191
---
9292

9393
POST-INIT:
94-
1. register terminal package watcher
94+
1. register package watchers for workspace-selected and terminal-activated environments
9595
2. register settings change listener (`registerInterpreterSettingsChangeListener`) — re-runs priority chain if settings change
9696
3. initialize terminal manager
9797
4. send telemetry (manager selection, project structure, discovery summary)

src/extension.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,26 +86,26 @@ import { cleanupStartupScripts } from './features/terminal/shellStartupSetupHand
8686
import { TerminalActivationImpl } from './features/terminal/terminalActivationState';
8787
import { TerminalEnvVarInjector } from './features/terminal/terminalEnvVarInjector';
8888
import { TerminalManager, TerminalManagerImpl } from './features/terminal/terminalManager';
89-
import { registerTerminalPackageWatcher } from './features/terminal/terminalPackageWatcher';
9089
import { getEnvironmentForTerminal } from './features/terminal/utils';
9190
import { openSearchSettings } from './features/views/envManagerSearch';
9291
import { EnvManagerView } from './features/views/envManagersView';
9392
import { ProjectView } from './features/views/projectView';
9493
import { PythonStatusBarImpl } from './features/views/pythonStatusBar';
9594
import { updateViewsAndStatus } from './features/views/revealHandler';
9695
import { TemporaryStateManager } from './features/views/temporaryStateManager';
97-
import { ProjectItem, PythonEnvTreeItem } from './features/views/treeViewItems';
96+
import { PythonEnvTreeItem } from './features/views/treeViewItems';
9897
import { collectEnvironmentInfo, getEnvManagerAndPackageManagerConfigLevels, runPetInTerminalImpl } from './helpers';
9998
import { EnvironmentManagers, ProjectCreators, PythonProjectManager } from './internal.api';
100-
import { registerSystemPythonFeatures } from './managers/builtin/main';
10199
import { registerInlineScriptFeatures } from './managers/builtin/inlineScript/main';
100+
import { registerSystemPythonFeatures } from './managers/builtin/main';
102101
import { SysPythonManager } from './managers/builtin/sysPythonManager';
103102
import {
104103
createNativePythonFinder,
105104
getNativePythonToolsPathAndSource,
106105
getNativePythonToolsVersion,
107106
NativePythonFinder,
108107
} from './managers/common/nativePythonFinder';
108+
import { registerPackageWatchers } from './managers/common/packageWatcher';
109109
import { IDisposable } from './managers/common/types';
110110
import { registerCondaFeatures } from './managers/conda/main';
111111
import { registerPipenvFeatures } from './managers/pipenv/main';
@@ -364,17 +364,7 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
364364
});
365365
}),
366366
commands.registerCommand('python-envs.removePythonProject', async (item) => {
367-
// Clear environment association before removing project
368-
if (item instanceof ProjectItem) {
369-
const uri = item.project.uri;
370-
const manager = envManagers.getEnvironmentManager(uri);
371-
if (manager) {
372-
manager.set(uri, undefined);
373-
} else {
374-
traceError(`No environment manager found for ${uri.fsPath}`);
375-
}
376-
}
377-
await removePythonProject(item, projectManager);
367+
await removePythonProject(item, projectManager, envManagers);
378368
}),
379369
commands.registerCommand('python-envs.clearCache', async () => {
380370
await clearPersistentState();
@@ -672,6 +662,8 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
672662
safeRegister('shellStartupVars', shellStartupVarsMgr.initialize()),
673663
]);
674664

665+
context.subscriptions.push(registerPackageWatchers(envManagers, terminalActivation, outputChannel));
666+
675667
failureStage = 'envSelection';
676668
stageWatch.reset();
677669
await applyInitialEnvironmentSelection(
@@ -683,11 +675,6 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
683675
globalScopeDeferredRef,
684676
);
685677

686-
// Register manager-agnostic terminal watcher for package-modifying commands
687-
failureStage = 'terminalWatcher';
688-
stageWatch.reset();
689-
registerTerminalPackageWatcher(api, terminalActivation, outputChannel, context.subscriptions);
690-
691678
// Register listener for interpreter settings changes for interpreter re-selection
692679
failureStage = 'settingsListener';
693680
stageWatch.reset();

src/features/envCommands.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,12 @@ export async function addPythonProjectCommand(
652652
}
653653
}
654654

655-
export async function removePythonProject(item: ProjectItem, wm: PythonProjectManager): Promise<void> {
655+
export async function removePythonProject(
656+
item: ProjectItem,
657+
wm: PythonProjectManager,
658+
em: EnvironmentManagers,
659+
): Promise<void> {
660+
await em.setEnvironment(item.project.uri, undefined);
656661
await removePythonProjectSetting([{ project: item.project }]);
657662
wm.remove(item.project);
658663
}

src/features/terminal/terminalPackageWatcher.ts

Lines changed: 0 additions & 111 deletions
This file was deleted.

src/internal.api.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Pep440Version } from '@renovatebot/pep440';
2-
import { CancellationError, Disposable, Event, LogOutputChannel, MarkdownString, Uri } from 'vscode';
2+
import { CancellationError, Disposable, Event, LogOutputChannel, MarkdownString, RelativePattern, Uri } from 'vscode';
33
import {
44
CreateEnvironmentOptions,
55
CreateEnvironmentScope,
@@ -380,6 +380,10 @@ export class InternalPackageManager implements PackageManager {
380380
return this.manager.getPackages(environment, options);
381381
}
382382

383+
getPackageWatchTargets(environment: PythonEnvironment): RelativePattern[] {
384+
return this.manager.getPackageWatchTargets?.(environment) ?? [];
385+
}
386+
383387
onDidChangePackages(handler: (e: DidChangePackagesEventArgs) => void): Disposable {
384388
return this.manager.onDidChangePackages ? this.manager.onDidChangePackages(handler) : new Disposable(() => {});
385389
}

src/managers/builtin/main.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { createSimpleDebounce } from '../../common/utils/debounce';
44
import { createFileSystemWatcher, onDidDeleteFiles } from '../../common/workspace.apis';
55
import { getPythonApi } from '../../features/pythonApi';
66
import { NativePythonFinder } from '../common/nativePythonFinder';
7-
import { registerPackageWatcherForManager } from '../common/packageWatcher';
87
import { PipPackageManager } from './pipPackageManager';
98
import { SysPythonManager } from './sysPythonManager';
109
import { VenvManager } from './venvManager';
@@ -41,9 +40,4 @@ export async function registerSystemPythonFeatures(
4140
venvDebouncedRefresh.trigger();
4241
}),
4342
);
44-
45-
disposables.push(
46-
registerPackageWatcherForManager(envManager, pkgManager, log),
47-
registerPackageWatcherForManager(venvManager, pkgManager, log),
48-
);
4943
}

src/managers/builtin/pipPackageManager.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,11 @@ export class PipPackageManager implements PackageManager, Disposable {
127127
async getPackages(environment: PythonEnvironment, options?: GetPackagesOptions): Promise<Package[] | undefined> {
128128
if (options?.skipCache || !this.packages.has(environment.envId.id)) {
129129
const data = await refreshPipPackages(environment, this.log);
130-
const packages = (data ?? []).map((pkg) => this.api.createPackageItem(pkg, environment, this));
130+
if (data === undefined) {
131+
return this.packages.get(environment.envId.id);
132+
}
133+
134+
const packages = data.map((pkg) => this.api.createPackageItem(pkg, environment, this));
131135
this.packages.set(environment.envId.id, packages);
132136
return packages;
133137
}

0 commit comments

Comments
 (0)