@@ -312,6 +312,17 @@ impl Uv {
312
312
}
313
313
}
314
314
315
+ /// Ensures a venv is exists or is created at the given path.
316
+ /// Returns a UvWithVenv that can be used to run commands in the venv.
317
+ pub fn venv_read_only ( & self , venv_dir : & Path ) -> Result < UvWithReadOnlyVenv , Error > {
318
+ if venv_dir. is_dir ( ) {
319
+ Ok ( UvWithReadOnlyVenv :: new ( self . clone ( ) , venv_dir) )
320
+ } else {
321
+ Err ( anyhow ! ( "Virtualenv not found" ) )
322
+ . with_context ( || format ! ( "path: `{}`" , venv_dir. display( ) ) )
323
+ }
324
+ }
325
+
315
326
/// Get uv binary path
316
327
///
317
328
/// Warning: Always use self.cmd() when at all possible
@@ -406,13 +417,82 @@ impl Uv {
406
417
}
407
418
}
408
419
409
- // Represents a venv generated and managed by uv
420
+ /// Represents uv with any venv
421
+ ///
422
+ /// Methods on this type are not allowed to create or modify the underlying virtualenv
423
+ pub struct UvWithReadOnlyVenv {
424
+ uv : Uv ,
425
+ venv_path : PathBuf ,
426
+ }
427
+
428
+ /// Represents a venv generated and managed by uv
410
429
pub struct UvWithWritableVenv {
411
430
uv : Uv ,
412
431
venv_path : PathBuf ,
413
432
py_version : PythonVersion ,
414
433
}
415
434
435
+ pub trait UvWithVenvCommon {
436
+ fn cmd ( & self ) -> Command ;
437
+ fn venv_path ( & self ) -> & Path ;
438
+
439
+ /// Returns a new command with the uv binary as the command to run.
440
+ /// The command will have the correct proxy settings and verbosity level based on CommandOutput.
441
+ /// The command will also have the VIRTUAL_ENV environment variable set to the venv path.
442
+ fn venv_cmd ( & self ) -> Command {
443
+ let mut cmd = self . cmd ( ) ;
444
+ cmd. env ( "VIRTUAL_ENV" , self . venv_path ( ) ) ;
445
+ cmd
446
+ }
447
+
448
+ /// Freezes the venv.
449
+ fn freeze ( & self ) -> Result < ( ) , Error > {
450
+ let status = self
451
+ . venv_cmd ( )
452
+ . arg ( "pip" )
453
+ . arg ( "freeze" )
454
+ . status ( )
455
+ . with_context ( || format ! ( "unable to freeze venv at {}" , self . venv_path( ) . display( ) ) ) ?;
456
+
457
+ if !status. success ( ) {
458
+ return Err ( anyhow ! (
459
+ "Failed to freeze venv at {}. uv exited with status: {}" ,
460
+ self . venv_path( ) . display( ) ,
461
+ status
462
+ ) ) ;
463
+ }
464
+
465
+ Ok ( ( ) )
466
+ }
467
+ }
468
+
469
+ impl UvWithVenvCommon for UvWithReadOnlyVenv {
470
+ fn cmd ( & self ) -> Command {
471
+ self . uv . cmd ( )
472
+ }
473
+ fn venv_path ( & self ) -> & Path {
474
+ & self . venv_path
475
+ }
476
+ }
477
+
478
+ impl UvWithVenvCommon for UvWithWritableVenv {
479
+ fn cmd ( & self ) -> Command {
480
+ self . uv . cmd ( )
481
+ }
482
+ fn venv_path ( & self ) -> & Path {
483
+ & self . venv_path
484
+ }
485
+ }
486
+
487
+ impl UvWithReadOnlyVenv {
488
+ pub fn new ( uv : Uv , venv_dir : & Path ) -> Self {
489
+ UvWithReadOnlyVenv {
490
+ uv,
491
+ venv_path : venv_dir. to_path_buf ( ) ,
492
+ }
493
+ }
494
+ }
495
+
416
496
impl UvWithWritableVenv {
417
497
pub fn new ( uv : Uv , venv_dir : & Path , version : & PythonVersion ) -> Self {
418
498
UvWithWritableVenv {
@@ -422,15 +502,6 @@ impl UvWithWritableVenv {
422
502
}
423
503
}
424
504
425
- /// Returns a new command with the uv binary as the command to run.
426
- /// The command will have the correct proxy settings and verbosity level based on CommandOutput.
427
- /// The command will also have the VIRTUAL_ENV environment variable set to the venv path.
428
- fn venv_cmd ( & self ) -> Command {
429
- let mut cmd = self . uv . cmd ( ) ;
430
- cmd. env ( "VIRTUAL_ENV" , & self . venv_path ) ;
431
- cmd
432
- }
433
-
434
505
/// Writes a rye-venv.json for this venv.
435
506
pub fn write_marker ( & self ) -> Result < ( ) , Error > {
436
507
write_venv_marker ( & self . venv_path , & self . py_version )
@@ -473,26 +544,6 @@ impl UvWithWritableVenv {
473
544
Ok ( ( ) )
474
545
}
475
546
476
- /// Freezes the venv.
477
- pub fn freeze ( & self ) -> Result < ( ) , Error > {
478
- let status = self
479
- . venv_cmd ( )
480
- . arg ( "pip" )
481
- . arg ( "freeze" )
482
- . status ( )
483
- . with_context ( || format ! ( "unable to freeze venv at {}" , self . venv_path. display( ) ) ) ?;
484
-
485
- if !status. success ( ) {
486
- return Err ( anyhow ! (
487
- "Failed to freeze venv at {}. uv exited with status: {}" ,
488
- self . venv_path. display( ) ,
489
- status
490
- ) ) ;
491
- }
492
-
493
- Ok ( ( ) )
494
- }
495
-
496
547
/// Installs the given requirement in the venv.
497
548
///
498
549
/// If you provide a list of extras, they will be installed as well.
0 commit comments