mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 19:08:04 +00:00
Merge 668b3e9f22
into 7603153f5b
This commit is contained in:
commit
704633e8bf
4 changed files with 117 additions and 0 deletions
|
@ -2174,6 +2174,24 @@ pub struct PipListArgs {
|
|||
#[arg(long, overrides_with("outdated"), hide = true)]
|
||||
pub no_outdated: bool,
|
||||
|
||||
/// List each package's required packages.
|
||||
///
|
||||
/// This is only allowed when the output format is `json`.
|
||||
#[arg(long, overrides_with("no_requires"))]
|
||||
pub requires: bool,
|
||||
|
||||
#[arg(long, overrides_with("requires"), hide = true)]
|
||||
pub no_requires: bool,
|
||||
|
||||
/// List which packages require each package.
|
||||
///
|
||||
/// This is only allowed when the output format is `json`.
|
||||
#[arg(long, overrides_with("no_required_by"))]
|
||||
pub required_by: bool,
|
||||
|
||||
#[arg(long, overrides_with("required_by"), hide = true)]
|
||||
pub no_required_by: bool,
|
||||
|
||||
/// Validate the Python environment, to detect packages with missing dependencies and other
|
||||
/// issues.
|
||||
#[arg(long, overrides_with("no_strict"))]
|
||||
|
|
|
@ -42,6 +42,8 @@ pub(crate) async fn pip_list(
|
|||
exclude: &[PackageName],
|
||||
format: &ListFormat,
|
||||
outdated: bool,
|
||||
requires: bool,
|
||||
required_by: bool,
|
||||
prerelease: PrereleaseMode,
|
||||
index_locations: IndexLocations,
|
||||
index_strategy: IndexStrategy,
|
||||
|
@ -61,6 +63,14 @@ pub(crate) async fn pip_list(
|
|||
anyhow::bail!("`--outdated` cannot be used with `--format freeze`");
|
||||
}
|
||||
|
||||
if requires && !matches!(format, ListFormat::Json) {
|
||||
anyhow::bail!("`--requires` can only be used with `--format json`");
|
||||
}
|
||||
|
||||
if required_by && !matches!(format, ListFormat::Json) {
|
||||
anyhow::bail!("`--required_by` can only be used with `--format json`");
|
||||
}
|
||||
|
||||
// Detect the current Python interpreter.
|
||||
let environment = PythonEnvironment::find(
|
||||
&python.map(PythonRequest::parse).unwrap_or_default(),
|
||||
|
@ -159,6 +169,47 @@ pub(crate) async fn pip_list(
|
|||
results
|
||||
};
|
||||
|
||||
let requires_map = if requires || required_by {
|
||||
let mut requires_map = FxHashMap::default();
|
||||
|
||||
// Determine the markers to use for resolution.
|
||||
let markers = environment.interpreter().resolver_marker_environment();
|
||||
|
||||
if required_by {
|
||||
// To compute which packages require a given package, we need to
|
||||
// consider every installed package.
|
||||
for package in site_packages.iter() {
|
||||
if let Ok(metadata) = package.metadata() {
|
||||
let requires = metadata
|
||||
.requires_dist
|
||||
.into_iter()
|
||||
.filter(|req| req.evaluate_markers(&markers, &[]))
|
||||
.map(|req| req.name)
|
||||
.collect_vec();
|
||||
|
||||
requires_map.insert(package.name(), requires);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for package in &results {
|
||||
if let Ok(metadata) = package.metadata() {
|
||||
let requires = metadata
|
||||
.requires_dist
|
||||
.into_iter()
|
||||
.filter(|req| req.evaluate_markers(&markers, &[]))
|
||||
.map(|req| req.name)
|
||||
.collect_vec();
|
||||
|
||||
requires_map.insert(package.name(), requires);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requires_map
|
||||
} else {
|
||||
FxHashMap::default()
|
||||
};
|
||||
|
||||
match format {
|
||||
ListFormat::Json => {
|
||||
let rows = results
|
||||
|
@ -179,6 +230,30 @@ pub(crate) async fn pip_list(
|
|||
editable_project_location: dist
|
||||
.as_editable()
|
||||
.map(|url| url.to_file_path().unwrap().simplified_display().to_string()),
|
||||
requires: requires.then(|| {
|
||||
if let Some(packages) = requires_map.get(dist.name()) {
|
||||
packages
|
||||
.iter()
|
||||
.map(|name| Require { name: name.clone() })
|
||||
.collect_vec()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}),
|
||||
required_by: required_by.then(|| {
|
||||
requires_map
|
||||
.iter()
|
||||
.filter(|(name, pkgs)| {
|
||||
**name != dist.name() && pkgs.iter().any(|pkg| pkg == dist.name())
|
||||
})
|
||||
.map(|(name, _)| name)
|
||||
.sorted_unstable()
|
||||
.dedup()
|
||||
.map(|name| RequiredBy {
|
||||
name: (*name).clone(),
|
||||
})
|
||||
.collect_vec()
|
||||
}),
|
||||
})
|
||||
.collect_vec();
|
||||
let output = serde_json::to_string(&rows)?;
|
||||
|
@ -324,6 +399,16 @@ impl From<&DistFilename> for FileType {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Require {
|
||||
name: PackageName,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RequiredBy {
|
||||
name: PackageName,
|
||||
}
|
||||
|
||||
/// An entry in a JSON list of installed packages.
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Entry {
|
||||
|
@ -335,6 +420,10 @@ struct Entry {
|
|||
latest_filetype: Option<FileType>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
editable_project_location: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
requires: Option<Vec<Require>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
required_by: Option<Vec<RequiredBy>>,
|
||||
}
|
||||
|
||||
/// A column in a table.
|
||||
|
|
|
@ -836,6 +836,8 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
&args.exclude,
|
||||
&args.format,
|
||||
args.outdated,
|
||||
args.requires,
|
||||
args.required_by,
|
||||
args.settings.prerelease,
|
||||
args.settings.index_locations,
|
||||
args.settings.index_strategy,
|
||||
|
|
|
@ -2325,6 +2325,8 @@ pub(crate) struct PipListSettings {
|
|||
pub(crate) exclude: Vec<PackageName>,
|
||||
pub(crate) format: ListFormat,
|
||||
pub(crate) outdated: bool,
|
||||
pub(crate) requires: bool,
|
||||
pub(crate) required_by: bool,
|
||||
pub(crate) settings: PipSettings,
|
||||
}
|
||||
|
||||
|
@ -2338,6 +2340,10 @@ impl PipListSettings {
|
|||
format,
|
||||
outdated,
|
||||
no_outdated,
|
||||
requires,
|
||||
no_requires,
|
||||
required_by,
|
||||
no_required_by,
|
||||
strict,
|
||||
no_strict,
|
||||
fetch,
|
||||
|
@ -2352,6 +2358,8 @@ impl PipListSettings {
|
|||
exclude,
|
||||
format,
|
||||
outdated: flag(outdated, no_outdated).unwrap_or(false),
|
||||
requires: flag(requires, no_requires).unwrap_or(false),
|
||||
required_by: flag(required_by, no_required_by).unwrap_or(false),
|
||||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue