Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement uv pip show #2115

Merged
merged 28 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1ae4381
copy pip_list.rs to pip_show.rs
ChannyClaus Mar 1, 2024
8bf37bd
copied list over to show, works like list
ChannyClaus Mar 1, 2024
c83fc7a
change the comment
ChannyClaus Mar 1, 2024
f0e66c3
adding sources, need to implement filtering by the name given
ChannyClaus Mar 1, 2024
7116117
remove the pip-list-specific code blocks
ChannyClaus Mar 1, 2024
aac3f9d
remove the unnecessary structs
ChannyClaus Mar 1, 2024
7f55fa0
copy over the distribution logic
ChannyClaus Mar 1, 2024
503385b
name, version and location work
ChannyClaus Mar 1, 2024
1b006f1
use printer instead so that --quiet, etc works
ChannyClaus Mar 1, 2024
638ba9c
remove unused import + add unwrap to writeln!
ChannyClaus Mar 1, 2024
dd04fd4
copy pip list test to pip show
ChannyClaus Mar 1, 2024
d3f934a
handle the case when no package is given
ChannyClaus Mar 1, 2024
488c8af
add a test; need to fix run-dependent temp dir matching
ChannyClaus Mar 1, 2024
de32a29
second test functional
ChannyClaus Mar 1, 2024
2504023
add more tests
ChannyClaus Mar 1, 2024
15bcc34
Merge remote-tracking branch 'upstream/main'
ChannyClaus Mar 1, 2024
78fbb0c
Merge remote-tracking branch 'upstream/main'
ChannyClaus Mar 1, 2024
1c62351
cleanup: removed unused arg + import
ChannyClaus Mar 1, 2024
11bc92b
test for an editable package
ChannyClaus Mar 1, 2024
e6db72a
fix the editable case
ChannyClaus Mar 1, 2024
511652f
simplify the branching for location
ChannyClaus Mar 1, 2024
40097f3
address some of the ci failure issues
ChannyClaus Mar 1, 2024
aa562f9
Merge remote-tracking branch 'upstream/main'
ChannyClaus Mar 5, 2024
2ed2754
Merge branch 'main' into main
ChannyClaus Mar 6, 2024
7b7f807
Merge branch 'main' into ChannyClaus/main
charliermarsh Mar 6, 2024
e4ab93d
retrigger CI
ChannyClaus Mar 6, 2024
345f8d6
Simplify arguments
charliermarsh Mar 6, 2024
94bb603
Fix up tests that Charlie broke
charliermarsh Mar 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/uv/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub(crate) use pip_compile::{extra_name_with_clap_error, pip_compile, Upgrade};
pub(crate) use pip_freeze::pip_freeze;
pub(crate) use pip_install::pip_install;
pub(crate) use pip_list::pip_list;
pub(crate) use pip_show::pip_show;
pub(crate) use pip_sync::pip_sync;
pub(crate) use pip_uninstall::pip_uninstall;
use uv_cache::Cache;
Expand All @@ -29,6 +30,7 @@ mod pip_compile;
mod pip_freeze;
mod pip_install;
mod pip_list;
mod pip_show;
mod pip_sync;
mod pip_uninstall;
mod reporters;
Expand Down
143 changes: 143 additions & 0 deletions crates/uv/src/commands/pip_show.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use std::fmt::Write;

use anyhow::Result;
use owo_colors::OwoColorize;
use tracing::debug;

use anstream::{eprintln, println};
use distribution_types::Name;
use platform_host::Platform;
use uv_cache::Cache;
use uv_fs::Simplified;
use uv_installer::SitePackages;
use uv_interpreter::PythonEnvironment;
use uv_normalize::PackageName;

use crate::commands::ExitStatus;
use crate::printer::Printer;

/// Show information about one or more installed packages.
pub(crate) fn pip_show(
mut packages: Vec<PackageName>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ChannyClaus - I simplified things a bit here because this method only needs to accept a list of package names (unlike uninstall which needs to accept a list of requirements files, etc.).

strict: bool,
python: Option<&str>,
system: bool,
quiet: bool,
cache: &Cache,
mut printer: Printer,
) -> Result<ExitStatus> {
if packages.is_empty() {
#[allow(clippy::print_stderr)]
{
eprintln!(
"{}{} Please provide a package name or names.",
"warning".yellow().bold(),
":".bold(),
);
}
return Ok(ExitStatus::Failure);
}

// Detect the current Python interpreter.
let platform = Platform::current()?;
let venv = if let Some(python) = python {
PythonEnvironment::from_requested_python(python, &platform, cache)?
} else if system {
PythonEnvironment::from_default_python(&platform, cache)?
} else {
match PythonEnvironment::from_virtualenv(platform.clone(), cache) {
Ok(venv) => venv,
Err(uv_interpreter::Error::VenvNotFound) => {
PythonEnvironment::from_default_python(&platform, cache)?
}
Err(err) => return Err(err.into()),
}
};

debug!(
"Using Python {} environment at {}",
venv.interpreter().python_version(),
venv.python_executable().simplified_display().cyan()
);

// Build the installed index.
let site_packages = SitePackages::from_executable(&venv)?;

// Sort and deduplicate the packages, which are keyed by name.
packages.sort_unstable();
packages.dedup();

// Map to the local distributions.
let distributions = {
let mut distributions = Vec::with_capacity(packages.len());

// Identify all packages that are installed.
for package in &packages {
let installed = site_packages.get_packages(package);
if installed.is_empty() {
writeln!(
printer,
"{}{} Package(s) not found for: {}",
"warning".yellow().bold(),
":".bold(),
package.as_ref().bold()
)?;
} else {
distributions.extend(installed);
}
}

distributions
};

// Like `pip`, if no packages were found, return a failure.
if distributions.is_empty() {
return Ok(ExitStatus::Failure);
}

if !quiet {
// Print the information for each package.
let mut first = true;
for distribution in &distributions {
if first {
first = false;
} else {
// Print a separator between packages.
#[allow(clippy::print_stdout)]
{
println!("---");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also had to change these back to using println, because the printer always prints to stderr, but I think it's important that this info goes to stdout, so we have to do it "manually" (by passing in quiet, etc.). Something we could try to improve in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I improved the situation a bit here: #2227

}
}

// Print the name, version, and location (e.g., the `site-packages` directory).
#[allow(clippy::print_stdout)]
{
println!("Name: {}", distribution.name());
println!("Version: {}", distribution.version());
println!(
"Location: {}",
distribution
.path()
.parent()
.expect("package path is not root")
.simplified_display()
);
}
}

// Validate that the environment is consistent.
if strict {
for diagnostic in site_packages.diagnostics()? {
writeln!(
printer,
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
diagnostic.message().bold()
)?;
}
}
}

Ok(ExitStatus::Success)
}
57 changes: 57 additions & 0 deletions crates/uv/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ enum PipCommand {
Freeze(PipFreezeArgs),
/// Enumerate the installed packages in the current environment.
List(PipListArgs),
/// Show information about one or more installed packages.
Show(PipShowArgs),
}

/// Clap parser for the union of date and datetime
Expand Down Expand Up @@ -947,6 +949,44 @@ struct PipListArgs {
system: bool,
}

#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
struct PipShowArgs {
/// The package(s) to display.
package: Vec<PackageName>,

/// Validate the virtual environment, to detect packages with missing dependencies or other
/// 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.
/// - `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, conflicts_with = "system")]
python: Option<String>,

/// List packages for the system Python.
///
/// 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. The `--system` option
/// instructs `uv` to use the first Python found in the system `PATH`.
///
/// WARNING: `--system` is intended for use in continuous integration (CI) environments and
/// should be used with caution.
#[clap(long, conflicts_with = "python")]
system: bool,
}

#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
struct VenvArgs {
Expand Down Expand Up @@ -1105,6 +1145,12 @@ async fn run() -> Result<ExitStatus> {
ContextValue::String("uv pip list".to_string()),
);
}
"show" => {
err.insert(
ContextKind::SuggestedSubcommand,
ContextValue::String("uv pip show".to_string()),
);
}
_ => {}
}
}
Expand Down Expand Up @@ -1433,6 +1479,17 @@ async fn run() -> Result<ExitStatus> {
&cache,
printer,
),
Commands::Pip(PipNamespace {
command: PipCommand::Show(args),
}) => commands::pip_show(
args.package,
args.strict,
args.python.as_deref(),
args.system,
cli.quiet,
&cache,
printer,
),
Commands::Cache(CacheNamespace {
command: CacheCommand::Clean(args),
})
Expand Down
Loading