@@ -71,14 +71,27 @@ export class UvPackageManager implements PackageManager, Disposable {
7171
7272 private readonly _onDidChangePackages = new EventEmitter < DidChangePackagesEventArgs > ( ) ;
7373 readonly onDidChangePackages : Event < DidChangePackagesEventArgs > = this . _onDidChangePackages . event ;
74+ private readonly _onDidMutatePackages = new EventEmitter < PythonEnvironment > ( ) ;
75+ readonly onDidMutatePackages = this . _onDidMutatePackages . event ;
7476 private readonly packages = new Map < string , Package [ ] > ( ) ;
77+ private readonly packageCacheRevisions = new Map < string , number > ( ) ;
7578
7679 constructor (
7780 private readonly api : PythonEnvironmentApi ,
7881 public readonly log : LogOutputChannel ,
7982 private readonly venv : BuiltInEnvironmentManager ,
8083 ) { }
8184
85+ /**
86+ * Evicts cached inventory and prevents older reads from restoring it.
87+ * @param environment Environment whose inventory may have changed.
88+ */
89+ invalidatePackages ( environment : PythonEnvironment ) : void {
90+ const id = environment . envId . id ;
91+ this . packages . delete ( id ) ;
92+ this . packageCacheRevisions . set ( id , ( this . packageCacheRevisions . get ( id ) ?? 0 ) + 1 ) ;
93+ }
94+
8295 readonly [ pythonToolSupport ] : PackageToolSupport = {
8396 manage : ( environment , options , token , scope ) =>
8497 this . manage ( environment , { ...options , runHeadless : true } , token , scope ) ,
@@ -144,33 +157,44 @@ export class UvPackageManager implements PackageManager, Disposable {
144157 throw new Error ( support . reason ) ;
145158 }
146159 const execute = async ( token ?: CancellationToken ) : Promise < void > => {
160+ const before = this . packages . get ( environment . envId . id ) ;
147161 try {
148162 const commandOptions = {
149163 cwd : scope ?. fsPath ,
150164 cancellationToken : token ,
151165 toolExecution : ! ! toolToken ,
152166 } ;
153- if ( toUninstall . length > 0 ) {
154- const command = new UvUninstallCommand ( await this . getCommandOptions ( environment , commandOptions ) ) ;
155- await command . execute ( {
156- packages : parsePackageSpecs ( toUninstall ) ,
157- cancellationToken : token ,
158- toolExecution : ! ! toolToken ,
159- } ) ;
160- }
161- if ( toInstall . length > 0 ) {
162- const command = new UvInstallCommand ( await this . getCommandOptions ( environment , commandOptions ) ) ;
163- await command . execute ( {
164- packages : parsePackageSpecs ( toInstall ) ,
165- upgrade : options . upgrade ,
166- cancellationToken : token ,
167- toolExecution : ! ! toolToken ,
168- } ) ;
167+ let mutationStarted = false ;
168+ try {
169+ if ( toUninstall . length > 0 ) {
170+ const command = new UvUninstallCommand ( await this . getCommandOptions ( environment , commandOptions ) ) ;
171+ mutationStarted = true ;
172+ await command . execute ( {
173+ packages : parsePackageSpecs ( toUninstall ) ,
174+ cancellationToken : token ,
175+ toolExecution : ! ! toolToken ,
176+ } ) ;
177+ }
178+ if ( toInstall . length > 0 ) {
179+ const command = new UvInstallCommand ( await this . getCommandOptions ( environment , commandOptions ) ) ;
180+ mutationStarted = true ;
181+ await command . execute ( {
182+ packages : parsePackageSpecs ( toInstall ) ,
183+ upgrade : options . upgrade ,
184+ cancellationToken : token ,
185+ toolExecution : ! ! toolToken ,
186+ } ) ;
187+ }
188+ } finally {
189+ if ( mutationStarted ) {
190+ this . invalidatePackages ( environment ) ;
191+ this . _onDidMutatePackages . fire ( environment ) ;
192+ }
169193 }
170194 await updatePackagesAndNotify (
171195 this ,
172196 environment ,
173- this . packages . get ( environment . envId . id ) ,
197+ before ,
174198 ( changes ) => this . _onDidChangePackages . fire ( { environment, manager : this , changes } ) ,
175199 ( ) => this . fetchPackages ( environment , ! options . runHeadless , toolToken , scope ) ,
176200 ! ! toolToken ,
@@ -212,11 +236,12 @@ export class UvPackageManager implements PackageManager, Disposable {
212236 await withProgress (
213237 { location : ProgressLocation . Window , title : l10n . t ( 'Refreshing packages' ) } ,
214238 async ( ) => {
239+ const revision = this . packageCacheRevisions . get ( environment . envId . id ) ;
215240 const packages = await updatePackagesAndNotify (
216241 this , environment , this . packages . get ( environment . envId . id ) ,
217242 ( changes ) => this . _onDidChangePackages . fire ( { environment, manager : this , changes } ) ,
218243 ) ;
219- if ( packages !== undefined ) {
244+ if ( packages !== undefined && revision === this . packageCacheRevisions . get ( environment . envId . id ) ) {
220245 this . packages . set ( environment . envId . id , packages ) ;
221246 }
222247 } ,
@@ -237,6 +262,7 @@ export class UvPackageManager implements PackageManager, Disposable {
237262 token ?: CancellationToken ,
238263 scope ?: Uri ,
239264 ) : Promise < Package [ ] | undefined > {
265+ const revision = this . packageCacheRevisions . get ( environment . envId . id ) ;
240266 try {
241267 throwIfCancelled ( token ) ;
242268 const command = new UvListCommand ( await this . getCommandOptions ( environment , {
@@ -247,7 +273,9 @@ export class UvPackageManager implements PackageManager, Disposable {
247273 ) ;
248274 throwIfCancelled ( token ) ;
249275 const packages = data . map ( ( pkg ) => this . api . createPackageItem ( pkg , environment , this ) ) ;
250- this . packages . set ( environment . envId . id , packages ) ;
276+ if ( revision === this . packageCacheRevisions . get ( environment . envId . id ) ) {
277+ this . packages . set ( environment . envId . id , packages ) ;
278+ }
251279 return packages ;
252280 } catch ( error ) {
253281 this . log . error ( 'Error refreshing packages' , error ) ;
@@ -294,6 +322,8 @@ export class UvPackageManager implements PackageManager, Disposable {
294322 /** Releases package-change listeners and cached packages. */
295323 dispose ( ) : void {
296324 this . _onDidChangePackages . dispose ( ) ;
325+ this . _onDidMutatePackages . dispose ( ) ;
297326 this . packages . clear ( ) ;
327+ this . packageCacheRevisions . clear ( ) ;
298328 }
299329}
0 commit comments