@@ -13,9 +13,13 @@ import {
1313 PipAvailableVersionsTextCommand ,
1414 UvAvailableVersionsCommand ,
1515} from '../../../managers/builtin/commands/availableVersions' ;
16+ import { createPipOrUvCommand } from '../../../managers/builtin/commands/factory' ;
1617import { PipInstallCommand , UvInstallCommand } from '../../../managers/builtin/commands/install' ;
1718import { PipListCommand , UvListCommand } from '../../../managers/builtin/commands/list' ;
18- import { PipListDirectNamesCommand , UvListDirectNamesCommand } from '../../../managers/builtin/commands/listDirectNames' ;
19+ import {
20+ PipListDirectNamesCommand ,
21+ UvListDirectNamesCommand ,
22+ } from '../../../managers/builtin/commands/listDirectNames' ;
1923import { PipUninstallCommand , UvUninstallCommand } from '../../../managers/builtin/commands/uninstall' ;
2024import { PipVersionCommand , UvVersionCommand } from '../../../managers/builtin/commands/version' ;
2125import * as helpers from '../../../managers/builtin/helpers' ;
@@ -28,6 +32,7 @@ suite('Pip and UV command parsing', () => {
2832 let mockLog : LogOutputChannel ;
2933 let runPythonStub : sinon . SinonStub ;
3034 let runUvStub : sinon . SinonStub ;
35+ let shouldUseUvStub : sinon . SinonStub ;
3136
3237 setup ( ( ) => {
3338 log = createMockLogOutputChannel ( ) ;
@@ -37,6 +42,7 @@ suite('Pip and UV command parsing', () => {
3742 } as unknown as ReturnType < typeof workspaceApis . getConfiguration > ) ;
3843 runPythonStub = sinon . stub ( helpers , 'runPython' ) . resolves ( '' ) ;
3944 runUvStub = sinon . stub ( helpers , 'runUV' ) . resolves ( '' ) ;
45+ shouldUseUvStub = sinon . stub ( helpers , 'shouldUseUv' ) . resolves ( false ) ;
4046 } ) ;
4147
4248 teardown ( ( ) => {
@@ -259,7 +265,10 @@ suite('Pip and UV command parsing', () => {
259265 '--python-version' ,
260266 '3.13.1' ,
261267 ] ) ;
262- assert . deepStrictEqual ( result . map ( ( version ) => version . public ) , [ '1.0.0' ] ) ;
268+ assert . deepStrictEqual (
269+ result . map ( ( version ) => version . public ) ,
270+ [ '1.0.0' ] ,
271+ ) ;
263272 } ) ;
264273
265274 test ( 'PipAvailableVersionsTextCommand parses text output for Pip 21.2 through 25.0' , async ( ) => {
@@ -282,7 +291,10 @@ suite('Pip and UV command parsing', () => {
282291 '--python-version' ,
283292 '3.13.1' ,
284293 ] ) ;
285- assert . deepStrictEqual ( result . map ( ( version ) => version . public ) , [ '1.0.0' ] ) ;
294+ assert . deepStrictEqual (
295+ result . map ( ( version ) => version . public ) ,
296+ [ '1.0.0' ] ,
297+ ) ;
286298 } ) ;
287299
288300 test ( 'UvAvailableVersionsCommand rejects output surrounding JSON' , async ( ) => {
@@ -319,7 +331,10 @@ suite('Pip and UV command parsing', () => {
319331 const result = await command . execute ( ) ;
320332
321333 assert . deepStrictEqual ( runUvStub . firstCall . args [ 0 ] , [ 'pip' , 'list' , '--format=json' , '--python' , 'python' ] ) ;
322- assert . deepStrictEqual ( result . map ( ( pkg ) => pkg . name ) , [ 'package' ] ) ;
334+ assert . deepStrictEqual (
335+ result . map ( ( pkg ) => pkg . name ) ,
336+ [ 'package' ] ,
337+ ) ;
323338 } ) ;
324339
325340 test ( 'direct-package commands normalize names and ignore UV dependencies' , async ( ) => {
@@ -349,4 +364,72 @@ suite('Pip and UV command parsing', () => {
349364 assert . strictEqual ( pipVersion ?. public , '24.0' ) ;
350365 assert . strictEqual ( uvVersion ?. public , '0.4.20' ) ;
351366 } ) ;
367+
368+ test ( 'UV package commands target the environment directory rather than the resolved interpreter' , async ( ) => {
369+ const baseInterpreter = path . join ( path . sep , 'uv' , 'python' , 'cpython-3.13' , 'bin' , 'python' ) ;
370+ const environmentPath = path . join ( path . sep , 'virtualenvs' , 'pipenv-project' ) ;
371+ shouldUseUvStub . resolves ( true ) ;
372+ const options = { pythonExecutable : baseInterpreter , log : mockLog } ;
373+
374+ const install : PipInstallCommand | UvInstallCommand = await createPipOrUvCommand (
375+ options ,
376+ environmentPath ,
377+ PipInstallCommand ,
378+ UvInstallCommand ,
379+ ) ;
380+ await install . execute ( { packages : [ { packageName : 'requests' } ] } ) ;
381+
382+ const uninstall : PipUninstallCommand | UvUninstallCommand = await createPipOrUvCommand (
383+ options ,
384+ environmentPath ,
385+ PipUninstallCommand ,
386+ UvUninstallCommand ,
387+ ) ;
388+ await uninstall . execute ( { packages : [ { packageName : 'requests' } ] } ) ;
389+
390+ const list : PipListCommand | UvListCommand = await createPipOrUvCommand (
391+ options ,
392+ environmentPath ,
393+ PipListCommand ,
394+ UvListCommand ,
395+ ) ;
396+ runUvStub . resolves ( '[]' ) ;
397+ await list . execute ( ) ;
398+
399+ const directNames : PipListDirectNamesCommand | UvListDirectNamesCommand = await createPipOrUvCommand (
400+ options ,
401+ environmentPath ,
402+ PipListDirectNamesCommand ,
403+ UvListDirectNamesCommand ,
404+ ) ;
405+ runUvStub . resolves ( '' ) ;
406+ await directNames . execute ( ) ;
407+
408+ assert . strictEqual ( runPythonStub . callCount , 0 ) ;
409+ assert . strictEqual ( runUvStub . callCount , 4 ) ;
410+ for ( const call of runUvStub . getCalls ( ) ) {
411+ const args = call . args [ 0 ] as string [ ] ;
412+ const pythonIndex = args . indexOf ( '--python' ) ;
413+ assert . notStrictEqual ( pythonIndex , - 1 ) ;
414+ assert . strictEqual ( args [ pythonIndex + 1 ] , environmentPath ) ;
415+ assert . ok ( ! args . includes ( baseInterpreter ) ) ;
416+ }
417+ } ) ;
418+
419+ test ( 'Pip package commands continue using the environment interpreter' , async ( ) => {
420+ const pythonExecutable = path . join ( path . sep , 'virtualenvs' , 'project' , 'bin' , 'python' ) ;
421+ const environmentPath = path . join ( path . sep , 'virtualenvs' , 'project' ) ;
422+ shouldUseUvStub . resolves ( false ) ;
423+
424+ const install : PipInstallCommand | UvInstallCommand = await createPipOrUvCommand (
425+ { pythonExecutable, log : mockLog } ,
426+ environmentPath ,
427+ PipInstallCommand ,
428+ UvInstallCommand ,
429+ ) ;
430+ await install . execute ( { packages : [ { packageName : 'requests' } ] } ) ;
431+
432+ assert . strictEqual ( runPythonStub . firstCall . args [ 0 ] , pythonExecutable ) ;
433+ assert . ok ( runUvStub . notCalled ) ;
434+ } ) ;
352435} ) ;
0 commit comments