Skip to content

Commit

Permalink
Enable freeze and list to introspect non-virtualenv Pythons (#2033)
Browse files Browse the repository at this point in the history
## Summary

Now that we have the ability to introspect the installed packages for
arbitrary Pythons, we can allow `pip freeze` and `pip list` to fall back
to the "default" Python, if no virtualenv is present.

Closes #2005.
  • Loading branch information
charliermarsh authored Feb 28, 2024
1 parent 23afa09 commit 0270328
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 10 deletions.
2 changes: 1 addition & 1 deletion crates/uv-interpreter/src/python_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
11 changes: 10 additions & 1 deletion crates/uv-interpreter/src/virtual_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -65,6 +65,15 @@ impl Virtualenv {
})
}

/// Create a [`Virtualenv`] for the default Python interpreter.
pub fn from_default_python(platform: &Platform, cache: &Cache) -> Result<Self, Error> {
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
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/src/commands/cache_clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExitStatus> {
if !cache.root().exists() {
Expand Down
19 changes: 17 additions & 2 deletions crates/uv/src/commands/pip_freeze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExitStatus> {
pub(crate) fn pip_freeze(
strict: bool,
python: Option<&str>,
cache: &Cache,
mut printer: Printer,
) -> Result<ExitStatus> {
// 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 {}",
Expand Down
15 changes: 13 additions & 2 deletions crates/uv/src/commands/pip_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExitStatus> {
// 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 {}",
Expand Down
35 changes: 32 additions & 3 deletions crates/uv/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
}

#[derive(Args)]
Expand All @@ -751,6 +765,20 @@ struct PipListArgs {
/// Exclude the specified package(s) from the output.
#[clap(long)]
r#exclude: Vec<PackageName>,

/// 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<String>,
}

#[derive(Args)]
Expand Down Expand Up @@ -1160,21 +1188,22 @@ async fn run() -> Result<ExitStatus> {
}
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,
}) => {
Expand Down

0 comments on commit 0270328

Please sign in to comment.