mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-29 21:44:51 +00:00
Implement "Requires" field in pip show
(#2347)
## Summary
Follow-up for
395be442fc
adds `Requires` field to pip show output.
I've aimed to make it behave exactly the same as `pip` does for now, but
there seem to be subtle issues that may require some discussion going
forward:
- Should `uv pip show` support extras? `pip` has an open issue for it,
but currently does not support https://github.com/pypa/pip/issues/4824.
- Relatedly, `Requred-by` field (not implemented in this PR) in `pip
show` currently doesn't take the extras into account transparently, i.e.
when `PySocks` has been installed as an extra for `requests[socks]`,
`pip show PySocks` doesn't have `requests` or `requests[socks]` under
`Requred-by` field. Should `uv pip show` for now just replicate `pip`'s
behavior for now for simplicity and parity or try to cover the extras
for completeness?
## Test Plan
Added a couple of tests:
1. `requests==2.31.0` has four dependencies that would be ordered
differently unless sorted. Additionally, it has two dependencies that
are optionally included for extras.
2. `pandas==2.1.3` depends on different versions of `numpy` depending on
the python version used.
This commit is contained in:
parent
e9c16e9aa2
commit
9bb548d251
2 changed files with 150 additions and 3 deletions
|
@ -1,6 +1,8 @@
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use itertools::Itertools;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
@ -62,6 +64,9 @@ pub(crate) fn pip_show(
|
||||||
// Build the installed index.
|
// Build the installed index.
|
||||||
let site_packages = SitePackages::from_executable(&venv)?;
|
let site_packages = SitePackages::from_executable(&venv)?;
|
||||||
|
|
||||||
|
// Determine the markers to use for resolution.
|
||||||
|
let markers = venv.interpreter().markers();
|
||||||
|
|
||||||
// Sort and deduplicate the packages, which are keyed by name.
|
// Sort and deduplicate the packages, which are keyed by name.
|
||||||
packages.sort_unstable();
|
packages.sort_unstable();
|
||||||
packages.dedup();
|
packages.dedup();
|
||||||
|
@ -116,6 +121,25 @@ pub(crate) fn pip_show(
|
||||||
.expect("package path is not root")
|
.expect("package path is not root")
|
||||||
.simplified_display()
|
.simplified_display()
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// If available, print the requirements.
|
||||||
|
if let Ok(metadata) = distribution.metadata() {
|
||||||
|
let requires_dist = metadata
|
||||||
|
.requires_dist
|
||||||
|
.into_iter()
|
||||||
|
.filter(|req| req.evaluate_markers(markers, &[]))
|
||||||
|
.map(|req| req.name)
|
||||||
|
.collect::<BTreeSet<_>>();
|
||||||
|
if requires_dist.is_empty() {
|
||||||
|
writeln!(printer.stdout(), "Requires:")?;
|
||||||
|
} else {
|
||||||
|
writeln!(
|
||||||
|
printer.stdout(),
|
||||||
|
"Requires: {}",
|
||||||
|
requires_dist.into_iter().join(", ")
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate that the environment is consistent.
|
// Validate that the environment is consistent.
|
||||||
|
|
|
@ -56,6 +56,124 @@ fn show_empty() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn show_requires_multiple() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("requests==2.31.0")?;
|
||||||
|
|
||||||
|
uv_snapshot!(install_command(&context)
|
||||||
|
.arg("-r")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--strict"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 5 packages in [TIME]
|
||||||
|
Downloaded 5 packages in [TIME]
|
||||||
|
Installed 5 packages in [TIME]
|
||||||
|
+ certifi==2023.11.17
|
||||||
|
+ charset-normalizer==3.3.2
|
||||||
|
+ idna==3.4
|
||||||
|
+ requests==2.31.0
|
||||||
|
+ urllib3==2.1.0
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
context.assert_command("import requests").success();
|
||||||
|
let filters = [(
|
||||||
|
r"Location:.*site-packages",
|
||||||
|
"Location: [WORKSPACE_DIR]/site-packages",
|
||||||
|
)]
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
// Guards against the package names being sorted.
|
||||||
|
uv_snapshot!(filters, Command::new(get_bin())
|
||||||
|
.arg("pip")
|
||||||
|
.arg("show")
|
||||||
|
.arg("requests")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(context.cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", context.venv.as_os_str())
|
||||||
|
.current_dir(&context.temp_dir), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Name: requests
|
||||||
|
Version: 2.31.0
|
||||||
|
Location: [WORKSPACE_DIR]/site-packages
|
||||||
|
Requires: certifi, charset-normalizer, idna, urllib3
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asserts that the Python version marker in the metadata is correctly evaluated.
|
||||||
|
/// `click` v8.1.7 requires `importlib-metadata`, but only when `python_version < "3.8"`.
|
||||||
|
#[test]
|
||||||
|
fn show_python_version_marker() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||||
|
requirements_txt.touch()?;
|
||||||
|
requirements_txt.write_str("click==8.1.7")?;
|
||||||
|
|
||||||
|
uv_snapshot!(install_command(&context)
|
||||||
|
.arg("-r")
|
||||||
|
.arg("requirements.txt")
|
||||||
|
.arg("--strict"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Downloaded 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ click==8.1.7
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
context.assert_command("import click").success();
|
||||||
|
|
||||||
|
let mut filters = vec![(
|
||||||
|
r"Location:.*site-packages",
|
||||||
|
"Location: [WORKSPACE_DIR]/site-packages",
|
||||||
|
)];
|
||||||
|
if cfg!(windows) {
|
||||||
|
filters.push(("Requires: colorama", "Requires:"));
|
||||||
|
}
|
||||||
|
|
||||||
|
uv_snapshot!(filters, Command::new(get_bin())
|
||||||
|
.arg("pip")
|
||||||
|
.arg("show")
|
||||||
|
.arg("click")
|
||||||
|
.arg("--cache-dir")
|
||||||
|
.arg(context.cache_dir.path())
|
||||||
|
.env("VIRTUAL_ENV", context.venv.as_os_str())
|
||||||
|
.current_dir(&context.temp_dir), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Name: click
|
||||||
|
Version: 8.1.7
|
||||||
|
Location: [WORKSPACE_DIR]/site-packages
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn show_found_single_package() -> Result<()> {
|
fn show_found_single_package() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
@ -81,11 +199,11 @@ fn show_found_single_package() -> Result<()> {
|
||||||
);
|
);
|
||||||
|
|
||||||
context.assert_command("import markupsafe").success();
|
context.assert_command("import markupsafe").success();
|
||||||
let filters = [(
|
|
||||||
|
let filters = vec![(
|
||||||
r"Location:.*site-packages",
|
r"Location:.*site-packages",
|
||||||
"Location: [WORKSPACE_DIR]/site-packages",
|
"Location: [WORKSPACE_DIR]/site-packages",
|
||||||
)]
|
)];
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
uv_snapshot!(filters, Command::new(get_bin())
|
uv_snapshot!(filters, Command::new(get_bin())
|
||||||
.arg("pip")
|
.arg("pip")
|
||||||
|
@ -101,6 +219,7 @@ fn show_found_single_package() -> Result<()> {
|
||||||
Name: markupsafe
|
Name: markupsafe
|
||||||
Version: 2.1.3
|
Version: 2.1.3
|
||||||
Location: [WORKSPACE_DIR]/site-packages
|
Location: [WORKSPACE_DIR]/site-packages
|
||||||
|
Requires:
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
"###
|
"###
|
||||||
|
@ -162,10 +281,12 @@ fn show_found_multiple_packages() -> Result<()> {
|
||||||
Name: markupsafe
|
Name: markupsafe
|
||||||
Version: 2.1.3
|
Version: 2.1.3
|
||||||
Location: [WORKSPACE_DIR]/site-packages
|
Location: [WORKSPACE_DIR]/site-packages
|
||||||
|
Requires:
|
||||||
---
|
---
|
||||||
Name: pip
|
Name: pip
|
||||||
Version: 21.3.1
|
Version: 21.3.1
|
||||||
Location: [WORKSPACE_DIR]/site-packages
|
Location: [WORKSPACE_DIR]/site-packages
|
||||||
|
Requires:
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
"###
|
"###
|
||||||
|
@ -227,6 +348,7 @@ fn show_found_one_out_of_two() -> Result<()> {
|
||||||
Name: markupsafe
|
Name: markupsafe
|
||||||
Version: 2.1.3
|
Version: 2.1.3
|
||||||
Location: [WORKSPACE_DIR]/site-packages
|
Location: [WORKSPACE_DIR]/site-packages
|
||||||
|
Requires:
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
warning: Package(s) not found for: flask
|
warning: Package(s) not found for: flask
|
||||||
|
@ -378,6 +500,7 @@ fn show_editable() -> Result<()> {
|
||||||
Name: poetry-editable
|
Name: poetry-editable
|
||||||
Version: 0.1.0
|
Version: 0.1.0
|
||||||
Location: [WORKSPACE_DIR]/site-packages
|
Location: [WORKSPACE_DIR]/site-packages
|
||||||
|
Requires: numpy
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
"###
|
"###
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue