-
Notifications
You must be signed in to change notification settings - Fork 47
Add site-packages watchers #1597
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
edvilme
wants to merge
11
commits into
main
Choose a base branch
from
packages-sync
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
19e554f
Add site-packages watchers
edvilme 1d6f085
Only refresh current environment
edvilme f20378e
Linting and comments
edvilme 06677ef
Add tests
edvilme a024ce6
Fix linting
edvilme d0360b7
Delegate package locations to package manager
edvilme b0301d5
Remove unnecessary async
edvilme 276ad8d
Add setting to conditionally enable package watchers
edvilme 8c77ee8
Fix unit tests: stub getConfiguration for packageWatchers setting
edvilme 1220cdd
Merge branch 'main' into packages-sync
edvilme 5939dd1
Address PR review comments on package watchers
edvilme File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| import * as path from 'path'; | ||
| import { Disposable, LogOutputChannel, RelativePattern } from 'vscode'; | ||
| import { EnvironmentManager, PackageManager, PythonEnvironment } from '../../api'; | ||
| import { createSimpleDebounce } from '../../common/utils/debounce'; | ||
| import { createFileSystemWatcher, getConfiguration } from '../../common/workspace.apis'; | ||
|
|
||
| /** | ||
| * Derives the file system watch targets for a given Python environment. | ||
| * | ||
| * Targets include site-packages `.dist-info/METADATA` files for pip-style installs. | ||
| * | ||
| * @param env - The Python environment to derive watch targets for. | ||
| * @returns An array of RelativePattern objects, one per discoverable package location. | ||
| * Empty if the environment has no `sysPrefix` or discoverable paths. | ||
| */ | ||
| function getDefaultPackageWatchTargets(env: PythonEnvironment): RelativePattern[] { | ||
| if (!env.sysPrefix) { | ||
| return []; | ||
| } | ||
| return process.platform === 'win32' | ||
| ? [new RelativePattern(path.join(env.sysPrefix, 'Lib'), 'site-packages/**/*.dist-info/METADATA')] // Windows | ||
| : [new RelativePattern(path.join(env.sysPrefix, 'lib'), 'python*/site-packages/**/*.dist-info/METADATA')]; // Unix-like | ||
| } | ||
|
|
||
| /** | ||
| * Creates a file system watcher for package changes in a single environment. | ||
| * | ||
| * Monitors default site-packages locations and any manager-specific extra locations | ||
| * for install/uninstall operations. | ||
| * and triggers a debounced package refresh when changes are detected. | ||
| * | ||
| * @param env - The Python environment to watch. | ||
| * @param packageManager - The package manager to call refresh on when changes occur. | ||
| * @param log - Logger for diagnostic messages. | ||
| * @returns A disposable that removes the watcher when disposed. | ||
| */ | ||
| export function watchPackageChangesForEnvironment( | ||
| env: PythonEnvironment, | ||
| packageManager: PackageManager, | ||
| log: LogOutputChannel, | ||
| ): Disposable { | ||
| // Watch targets | ||
| const watchTargets = [ | ||
| ...getDefaultPackageWatchTargets(env), | ||
| ...(packageManager.getPackageWatchTargets?.(env) ?? []), | ||
| ]; | ||
| if (watchTargets.length === 0) { | ||
| log.debug(`No watch targets for environment ${env.envId.id}`); | ||
| return new Disposable(() => undefined); | ||
| } | ||
| // Debounced refresh function | ||
| const debouncedRefresh = createSimpleDebounce(500, async () => { | ||
| log.debug(`Package change detected for environment ${env.envId.id}, refreshing packages.`); | ||
| packageManager.refresh(env).catch((ex) => { | ||
| log.error( | ||
| `Failed to refresh packages for environment ${env.envId.id}: ${ex instanceof Error ? ex.message : String(ex)}`, | ||
| ); | ||
| }); | ||
| }); | ||
| // Create watchers | ||
| const disposables: Disposable[] = [debouncedRefresh]; | ||
| const trigger = debouncedRefresh.trigger.bind(debouncedRefresh); | ||
| for (const target of watchTargets) { | ||
| const watcher = createFileSystemWatcher( | ||
| target, | ||
| false, // create -> install | ||
| true, // change -> ignore | ||
| false, // delete -> uninstall | ||
| ); | ||
| disposables.push( | ||
| watcher, | ||
| watcher.onDidCreate(trigger), | ||
| watcher.onDidDelete(trigger), | ||
| ); | ||
| } | ||
|
|
||
| return new Disposable(() => disposables.forEach((d) => d.dispose())); | ||
| } | ||
|
|
||
| /** | ||
| * Registers automatic file system watchers for the active environment managed by a given manager. | ||
| * | ||
| * Creates per-environment watchers that are attached when the active environment changes | ||
| * and detached when it changes to a different environment. Ensures package changes | ||
| * (installs/uninstalls) in the active environment are detected and trigger a refresh. | ||
| * | ||
| * @param envManager - The environment manager whose active environment should be watched. | ||
| * @param packageManager - The package manager to call refresh on when changes occur. | ||
| * @param log - Logger for diagnostic and error messages. | ||
| * @returns A disposable that removes all watchers and subscriptions when disposed. | ||
| */ | ||
| export function registerPackageWatcherForManager( | ||
| envManager: EnvironmentManager, | ||
| packageManager: PackageManager, | ||
| log: LogOutputChannel, | ||
| ): Disposable { | ||
| const packageWatchersEnabled = getConfiguration('python-envs').get<boolean>('packageWatchers', true); | ||
| if (!packageWatchersEnabled) { | ||
| return new Disposable(() => undefined); | ||
| } | ||
|
|
||
| // One watcher per environment id. | ||
| const watchers = new Map<string, Disposable>(); | ||
|
|
||
| const addWatcher = (env: PythonEnvironment): void => { | ||
| if (!watchers.has(env.envId.id)) { | ||
| watchers.set(env.envId.id, watchPackageChangesForEnvironment(env, packageManager, log)); | ||
| } | ||
| }; | ||
|
|
||
| const removeWatcher = (envId: string): void => { | ||
| watchers.get(envId)?.dispose(); | ||
| watchers.delete(envId); | ||
| }; | ||
|
|
||
| const envChangeDisposable = envManager.onDidChangeEnvironment?.((changes) => { | ||
| if (changes.new) { | ||
| addWatcher(changes.new); | ||
| } | ||
| if (changes.old && changes.old.envId.id !== changes.new?.envId.id) { | ||
| removeWatcher(changes.old.envId.id); | ||
| } | ||
| }); | ||
|
edvilme marked this conversation as resolved.
|
||
|
|
||
| return new Disposable(() => { | ||
| envChangeDisposable?.dispose(); | ||
| watchers.forEach((watcher) => watcher.dispose()); | ||
| watchers.clear(); | ||
| }); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.