Skip to content

Commit d0360b7

Browse files
committed
Delegate package locations to package manager
1 parent a024ce6 commit d0360b7

6 files changed

Lines changed: 85 additions & 44 deletions

File tree

api/src/main.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
FileChangeType,
99
LogOutputChannel,
1010
MarkdownString,
11+
RelativePattern,
1112
TaskExecution,
1213
Terminal,
1314
TerminalOptions,
@@ -689,6 +690,16 @@ export interface PackageManager {
689690
*/
690691
getPackages(environment: PythonEnvironment, options?: GetPackagesOptions): Promise<Package[] | undefined>;
691692

693+
/**
694+
* Returns additional filesystem patterns to watch for package install/uninstall changes.
695+
*
696+
* These patterns are appended to the default site-packages metadata locations.
697+
* Implement this for manager-specific locations (for example, conda-meta).
698+
*
699+
* @param environment - The Python environment whose package paths should be watched.
700+
* @returns Relative patterns to watch for package changes.
701+
*/
702+
getPackageWatchTargets?(environment: PythonEnvironment): RelativePattern[];
692703
/**
693704
* Event that is fired when packages change.
694705
*/

examples/sample1/src/api.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
FileChangeType,
88
LogOutputChannel,
99
MarkdownString,
10+
RelativePattern,
1011
TaskExecution,
1112
Terminal,
1213
TerminalOptions,
@@ -616,6 +617,17 @@ export interface PackageManager {
616617
*/
617618
getPackages(environment: PythonEnvironment, options?: GetPackagesOptions): Promise<Package[] | undefined>;
618619

620+
/**
621+
* Returns additional filesystem patterns to watch for package install/uninstall changes.
622+
*
623+
* These patterns are appended to the default site-packages metadata locations.
624+
* Implement this for manager-specific locations (for example, conda-meta).
625+
*
626+
* @param environment - The Python environment whose package paths should be watched.
627+
* @returns Relative patterns to watch for package changes.
628+
*/
629+
getPackageWatchTargets?(environment: PythonEnvironment): RelativePattern[];
630+
619631
/**
620632
* Event that is fired when packages change.
621633
*/

src/api.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
FileChangeType,
99
LogOutputChannel,
1010
MarkdownString,
11+
RelativePattern,
1112
TaskExecution,
1213
Terminal,
1314
TerminalOptions,
@@ -684,6 +685,17 @@ export interface PackageManager {
684685
*/
685686
getPackages(environment: PythonEnvironment, options?: GetPackagesOptions): Promise<Package[] | undefined>;
686687

688+
/**
689+
* Returns additional filesystem patterns to watch for package install/uninstall changes.
690+
*
691+
* These patterns are appended to the default site-packages metadata locations.
692+
* Implement this for manager-specific locations (for example, conda-meta).
693+
*
694+
* @param environment - The Python environment whose package paths should be watched.
695+
* @returns Relative patterns to watch for package changes.
696+
*/
697+
getPackageWatchTargets?(environment: PythonEnvironment): RelativePattern[];
698+
687699
/**
688700
* Event that is fired when packages change.
689701
*/

src/managers/common/packageWatcher.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,26 @@ import { createFileSystemWatcher } from '../../common/workspace.apis';
88
/**
99
* Derives the file system watch targets for a given Python environment.
1010
*
11-
* Targets include site-packages `.dist-info/METADATA` files (for pip installs/uninstalls)
12-
* and conda-meta JSON files (for conda installs/uninstalls).
11+
* Targets include site-packages `.dist-info/METADATA` files for pip-style installs.
1312
*
1413
* @param env - The Python environment to derive watch targets for.
1514
* @returns An array of RelativePattern objects, one per discoverable package location.
1615
* Empty if the environment has no `sysPrefix` or discoverable paths.
1716
*/
18-
function getWatchTargets(env: PythonEnvironment): RelativePattern[] {
17+
function getDefaultPackageWatchTargets(env: PythonEnvironment): RelativePattern[] {
1918
if (!env.sysPrefix) {
2019
return [];
2120
}
22-
23-
const targets: RelativePattern[] = [];
24-
if (process.platform === 'win32') {
25-
targets.push(new RelativePattern(path.join(env.sysPrefix, 'Lib'), 'site-packages/**/*.dist-info/METADATA'));
26-
} else {
27-
targets.push(
28-
new RelativePattern(path.join(env.sysPrefix, 'lib'), 'python*/site-packages/**/*.dist-info/METADATA'),
29-
);
30-
}
31-
targets.push(new RelativePattern(path.join(env.sysPrefix, 'conda-meta'), '**/*.json'));
32-
return targets;
21+
return process.platform === 'win32'
22+
? [new RelativePattern(path.join(env.sysPrefix, 'Lib'), 'site-packages/**/*.dist-info/METADATA')] // Windows
23+
: [new RelativePattern(path.join(env.sysPrefix, 'lib'), 'python*/site-packages/**/*.dist-info/METADATA')]; // Unix-like
3324
}
3425

3526
/**
3627
* Creates a file system watcher for package changes in a single environment.
3728
*
38-
* Monitors site-packages and conda-meta locations for install/uninstall operations
29+
* Monitors default site-packages locations and any manager-specific extra locations
30+
* for install/uninstall operations.
3931
* and triggers a debounced package refresh when changes are detected.
4032
*
4133
* @param env - The Python environment to watch.
@@ -49,14 +41,17 @@ export function watchPackageChangesForEnvironment(
4941
log: LogOutputChannel,
5042
): Disposable {
5143
// Watch targets
52-
const watchTargets = getWatchTargets(env);
44+
const watchTargets = [
45+
...getDefaultPackageWatchTargets(env),
46+
...(packageManager.getPackageWatchTargets?.(env) ?? []),
47+
];
5348
if (watchTargets.length === 0) {
5449
traceVerbose(log, `No watch targets for environment ${env.envId}`);
5550
return new Disposable(() => undefined);
5651
}
5752
// Debounced refresh function
5853
const debouncedRefresh = createSimpleDebounce(500, async () => {
59-
console.log(`Package change detected for environment ${env.envId}, refreshing packages...`);
54+
traceVerbose(log, `Package change detected for environment ${env.envId.id}, refreshing packages.`);
6055
packageManager.refresh(env).catch((ex) => {
6156
log.error(
6257
`Failed to refresh packages for environment ${env.envId}: ${ex instanceof Error ? ex.message : String(ex)}`,
@@ -68,9 +63,9 @@ export function watchPackageChangesForEnvironment(
6863
for (const target of watchTargets) {
6964
const watcher = createFileSystemWatcher(
7065
target,
71-
true, // create -> install
66+
false, // create -> install
7267
false, // change -> ignore
73-
true, // delete -> uninstall
68+
false, // delete -> uninstall
7469
);
7570
disposables.push(
7671
watcher,

src/managers/conda/condaPackageManager.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Pep440Version } from '@renovatebot/pep440';
22
import { explain as parse, rcompare } from '@renovatebot/pep440';
3+
import * as path from 'path';
34
import {
45
CancellationError,
56
Disposable,
@@ -8,6 +9,7 @@ import {
89
LogOutputChannel,
910
MarkdownString,
1011
ProgressLocation,
12+
RelativePattern,
1113
} from 'vscode';
1214
import {
1315
DidChangePackagesEventArgs,
@@ -197,6 +199,14 @@ export class CondaPackageManager implements PackageManager, Disposable {
197199
}
198200
}
199201

202+
getPackageWatchTargets(environment: PythonEnvironment): RelativePattern[] {
203+
if (!environment.sysPrefix) {
204+
return [];
205+
}
206+
207+
return [new RelativePattern(path.join(environment.sysPrefix, 'conda-meta'), '**/*.json')];
208+
}
209+
200210
dispose() {
201211
this._onDidChangePackages.dispose();
202212
this.packages.clear();

src/test/managers/common/packageWatcher.unit.test.ts

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,8 @@ suite('Package Watcher', () => {
104104
mockLogOutputChannel as LogOutputChannel,
105105
);
106106

107-
// Should create watchers for site-packages and conda-meta
108-
assert.strictEqual(
109-
createFileSystemWatcherStub.callCount,
110-
2,
111-
'Should create 2 watchers (site-packages + conda-meta)',
112-
);
107+
// Default should create watcher for site-packages metadata.
108+
assert.strictEqual(createFileSystemWatcherStub.callCount, 1, 'Should create 1 watcher (site-packages)');
113109
});
114110

115111
test('should create correct watch patterns on Windows', () => {
@@ -174,24 +170,35 @@ suite('Package Watcher', () => {
174170
}
175171
});
176172

177-
test('should watch conda-meta for conda packages', () => {
173+
test('should append package-manager-provided watch targets to defaults', () => {
178174
const mockWatcher = createMockWatcher();
179175
createFileSystemWatcherStub.returns(mockWatcher);
180176

181177
const env = createMockEnvironment({ sysPrefix: '/path/to/env' });
182178
const packageManager = createMockPackageManager();
179+
(packageManager as PackageManager).getPackageWatchTargets = () => [
180+
new RelativePattern('/path/to/env/conda-meta', '**/*.json'),
181+
];
183182

184183
watchPackageChangesForEnvironment(
185184
env,
186185
packageManager as PackageManager,
187186
mockLogOutputChannel as LogOutputChannel,
188187
);
189188

189+
assert.strictEqual(createFileSystemWatcherStub.callCount, 2, 'Should watch default and custom targets');
190+
191+
const firstCall = createFileSystemWatcherStub.getCall(0);
192+
const firstPattern = firstCall.args[0] as RelativePattern;
190193
const secondCall = createFileSystemWatcherStub.getCall(1);
191-
const pattern = secondCall.args[0] as RelativePattern;
194+
const secondPattern = secondCall.args[0] as RelativePattern;
192195

193-
assert.ok(pattern.baseUri.fsPath.includes('conda-meta'), 'Should watch conda-meta');
194-
assert.strictEqual(pattern.pattern, '**/*.json', 'Should watch JSON files in conda-meta');
196+
assert.ok(
197+
firstPattern.pattern.endsWith('site-packages/**/*.dist-info/METADATA'),
198+
'Should keep default site-packages watcher',
199+
);
200+
assert.ok(secondPattern.baseUri.fsPath.includes('conda-meta'), 'Should append conda-meta target');
201+
assert.strictEqual(secondPattern.pattern, '**/*.json', 'Should watch JSON files in conda-meta');
195202
});
196203

197204
test('should call packageManager.refresh on file create', () => {
@@ -207,12 +214,9 @@ suite('Package Watcher', () => {
207214
mockLogOutputChannel as LogOutputChannel,
208215
);
209216

210-
// Verify watchers are created which will have create event handlers
211-
assert.strictEqual(
212-
createFileSystemWatcherStub.callCount,
213-
2,
214-
'Should create watchers for site-packages and conda-meta',
215-
);
217+
// Verify watcher is created and create events are observed.
218+
assert.strictEqual(createFileSystemWatcherStub.callCount, 1, 'Should create watcher for site-packages');
219+
assert.strictEqual(createFileSystemWatcherStub.getCall(0).args[1], false, 'Should watch create events');
216220
});
217221

218222
test('should call packageManager.refresh on file delete', () => {
@@ -228,12 +232,9 @@ suite('Package Watcher', () => {
228232
mockLogOutputChannel as LogOutputChannel,
229233
);
230234

231-
// Verify watchers are created which will have delete event handlers
232-
assert.strictEqual(
233-
createFileSystemWatcherStub.callCount,
234-
2,
235-
'Should create watchers for site-packages and conda-meta',
236-
);
235+
// Verify watcher is created and delete events are observed.
236+
assert.strictEqual(createFileSystemWatcherStub.callCount, 1, 'Should create watcher for site-packages');
237+
assert.strictEqual(createFileSystemWatcherStub.getCall(0).args[3], false, 'Should watch delete events');
237238
});
238239

239240
test('should debounce multiple rapid file events', () => {
@@ -249,11 +250,11 @@ suite('Package Watcher', () => {
249250
mockLogOutputChannel as LogOutputChannel,
250251
);
251252

252-
// Verify watchers are created with event handlers for debouncing
253+
// Verify watcher is created with event handlers for debouncing.
253254
assert.strictEqual(
254255
createFileSystemWatcherStub.callCount,
255-
2,
256-
'Should create watchers with debounced event handlers',
256+
1,
257+
'Should create watcher with debounced event handlers',
257258
);
258259
});
259260

0 commit comments

Comments
 (0)