diff --git a/crates/uv-interpreter/src/python_query.rs b/crates/uv-interpreter/src/python_query.rs index b562145fd..13f18551d 100644 --- a/crates/uv-interpreter/src/python_query.rs +++ b/crates/uv-interpreter/src/python_query.rs @@ -68,7 +68,7 @@ pub fn find_requested_python( } } -/// Pick a sensible default for the python a user wants when they didn't specify a version. +/// Pick a sensible default for the Python a user wants when they didn't specify a version. /// /// We prefer the test overwrite `UV_TEST_PYTHON_PATH` if it is set, otherwise `python3`/`python` or /// `python.exe` respectively. diff --git a/crates/uv-interpreter/src/virtual_env.rs b/crates/uv-interpreter/src/virtual_env.rs index dab8b986e..0998ad71c 100644 --- a/crates/uv-interpreter/src/virtual_env.rs +++ b/crates/uv-interpreter/src/virtual_env.rs @@ -9,7 +9,7 @@ use uv_fs::{LockedFile, Normalized}; use crate::cfg::PyVenvConfiguration; use crate::python_platform::PythonPlatform; -use crate::{find_requested_python, Error, Interpreter}; +use crate::{find_default_python, find_requested_python, Error, Interpreter}; /// A Python executable and its associated platform markers. #[derive(Debug, Clone)] @@ -65,6 +65,15 @@ impl Virtualenv { }) } + /// Create a [`Virtualenv`] for the default Python interpreter. + pub fn from_default_python(platform: &Platform, cache: &Cache) -> Result { + let interpreter = find_default_python(platform, cache)?; + Ok(Self { + root: interpreter.base_prefix().to_path_buf(), + interpreter, + }) + } + /// Returns the location of the Python interpreter. pub fn root(&self) -> &Path { &self.root diff --git a/crates/uv/src/commands/cache_clean.rs b/crates/uv/src/commands/cache_clean.rs index c26c39e7d..bfbf37669 100644 --- a/crates/uv/src/commands/cache_clean.rs +++ b/crates/uv/src/commands/cache_clean.rs @@ -12,8 +12,8 @@ use crate::printer::Printer; /// Clear the cache. pub(crate) fn cache_clean( - cache: &Cache, packages: &[PackageName], + cache: &Cache, mut printer: Printer, ) -> Result { if !cache.root().exists() { diff --git a/crates/uv/src/commands/pip_freeze.rs b/crates/uv/src/commands/pip_freeze.rs index cb03d6b0d..e8a57c919 100644 --- a/crates/uv/src/commands/pip_freeze.rs +++ b/crates/uv/src/commands/pip_freeze.rs @@ -17,10 +17,25 @@ use crate::commands::ExitStatus; use crate::printer::Printer; /// Enumerate the installed packages in the current environment. -pub(crate) fn pip_freeze(cache: &Cache, strict: bool, mut printer: Printer) -> Result { +pub(crate) fn pip_freeze( + strict: bool, + python: Option<&str>, + cache: &Cache, + mut printer: Printer, +) -> Result { // Detect the current Python interpreter. let platform = Platform::current()?; - let venv = Virtualenv::from_env(platform, cache)?; + let venv = if let Some(python) = python { + Virtualenv::from_requested_python(python, &platform, cache)? + } else { + match Virtualenv::from_env(platform.clone(), cache) { + Ok(venv) => venv, + Err(uv_interpreter::Error::VenvNotFound) => { + Virtualenv::from_default_python(&platform, cache)? + } + Err(err) => return Err(err.into()), + } + }; debug!( "Using Python {} environment at {}", diff --git a/crates/uv/src/commands/pip_list.rs b/crates/uv/src/commands/pip_list.rs index 6cfe0e6dc..2f4cc7ce6 100644 --- a/crates/uv/src/commands/pip_list.rs +++ b/crates/uv/src/commands/pip_list.rs @@ -21,16 +21,27 @@ use crate::printer::Printer; /// Enumerate the installed packages in the current environment. pub(crate) fn pip_list( - cache: &Cache, strict: bool, editable: bool, exclude_editable: bool, exclude: &[PackageName], + python: Option<&str>, + cache: &Cache, mut printer: Printer, ) -> Result { // Detect the current Python interpreter. let platform = Platform::current()?; - let venv = Virtualenv::from_env(platform, cache)?; + let venv = if let Some(python) = python { + Virtualenv::from_requested_python(python, &platform, cache)? + } else { + match Virtualenv::from_env(platform.clone(), cache) { + Ok(venv) => venv, + Err(uv_interpreter::Error::VenvNotFound) => { + Virtualenv::from_default_python(&platform, cache)? + } + Err(err) => return Err(err.into()), + } + }; debug!( "Using Python {} environment at {}", diff --git a/crates/uv/src/main.rs b/crates/uv/src/main.rs index 150c06a78..6f23931cb 100644 --- a/crates/uv/src/main.rs +++ b/crates/uv/src/main.rs @@ -730,6 +730,20 @@ struct PipFreezeArgs { /// issues. #[clap(long)] strict: bool, + + /// The Python interpreter for which packages should be listed. + /// + /// By default, `uv` lists packages in the currently activated virtual environment, or a virtual + /// environment (`.venv`) located in the current working directory or any parent directory, + /// falling back to the system Python if no virtual environment is found. + /// + /// Supported formats: + /// - `3.10` looks for an installed Python 3.10 using `py --list-paths` on Windows, or + /// `python3.10` on Linux and macOS. (Specifying a patch version is not supported.) + /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. + /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. + #[clap(long, short, verbatim_doc_comment)] + python: Option, } #[derive(Args)] @@ -751,6 +765,20 @@ struct PipListArgs { /// Exclude the specified package(s) from the output. #[clap(long)] r#exclude: Vec, + + /// The Python interpreter for which packages should be listed. + /// + /// By default, `uv` lists packages in the currently activated virtual environment, or a virtual + /// environment (`.venv`) located in the current working directory or any parent directory, + /// falling back to the system Python if no virtual environment is found. + /// + /// Supported formats: + /// - `3.10` looks for an installed Python 3.10 using `py --list-paths` on Windows, or + /// `python3.10` on Linux and macOS. (Specifying a patch version is not supported.) + /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. + /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. + #[clap(long, short, verbatim_doc_comment)] + python: Option, } #[derive(Args)] @@ -1160,21 +1188,22 @@ async fn run() -> Result { } Commands::Pip(PipNamespace { command: PipCommand::Freeze(args), - }) => commands::pip_freeze(&cache, args.strict, printer), + }) => commands::pip_freeze(args.strict, args.python.as_deref(), &cache, printer), Commands::Pip(PipNamespace { command: PipCommand::List(args), }) => commands::pip_list( - &cache, args.strict, args.editable, args.exclude_editable, &args.exclude, + args.python.as_deref(), + &cache, printer, ), Commands::Cache(CacheNamespace { command: CacheCommand::Clean(args), }) - | Commands::Clean(args) => commands::cache_clean(&cache, &args.package, printer), + | Commands::Clean(args) => commands::cache_clean(&args.package, &cache, printer), Commands::Cache(CacheNamespace { command: CacheCommand::Dir, }) => {