mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-28 10:50:29 +00:00
Add support for tool.uv into distribution building (#3904)
With the change, we remove the special casing of workspace dependencies and resolve `tool.uv` for all git and directory distributions. This gives us support for non-editable workspace dependencies and path dependencies in other workspaces. It removes a lot of special casing around workspaces. These changes are the groundwork for supporting `tool.uv` with dynamic metadata. The basis for this change is moving `Requirement` from `distribution-types` to `pypi-types` and the lowering logic from `uv-requirements` to `uv-distribution`. This changes should be split out in separate PRs. I've included an example workspace `albatross-root-workspace2` where `bird-feeder` depends on `a` from another workspace `ab`. There's a bunch of failing tests and regressed error messages that still need fixing. It does fix the audited package count for the workspace tests.
This commit is contained in:
parent
09f55482a0
commit
081f20c53e
69 changed files with 1159 additions and 1680 deletions
|
|
@ -7,17 +7,26 @@ use futures::stream::FuturesOrdered;
|
|||
use futures::TryStreamExt;
|
||||
use url::Url;
|
||||
|
||||
use distribution_types::{
|
||||
BuildableSource, DirectorySourceUrl, HashPolicy, Requirement, SourceUrl, VersionId,
|
||||
};
|
||||
use distribution_types::{BuildableSource, DirectorySourceUrl, HashPolicy, SourceUrl, VersionId};
|
||||
use pep508_rs::RequirementOrigin;
|
||||
use pypi_types::VerbatimParsedUrl;
|
||||
use pypi_types::Requirement;
|
||||
use uv_configuration::ExtrasSpecification;
|
||||
use uv_distribution::{DistributionDatabase, Reporter};
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::{ExtraName, PackageName};
|
||||
use uv_resolver::{InMemoryIndex, MetadataResponse};
|
||||
use uv_types::{BuildContext, HashStrategy};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SourceTreeResolution {
|
||||
/// The requirements sourced from the source trees.
|
||||
pub requirements: Vec<Requirement>,
|
||||
/// The names of the projects that were resolved.
|
||||
pub project: PackageName,
|
||||
/// The extras used when resolving the requirements.
|
||||
pub extras: Vec<ExtraName>,
|
||||
}
|
||||
|
||||
/// A resolver for requirements specified via source trees.
|
||||
///
|
||||
/// Used, e.g., to determine the input requirements when a user specifies a `pyproject.toml`
|
||||
|
|
@ -63,26 +72,19 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> {
|
|||
}
|
||||
|
||||
/// Resolve the requirements from the provided source trees.
|
||||
pub async fn resolve(self) -> Result<Vec<Requirement>> {
|
||||
let requirements: Vec<_> = self
|
||||
pub async fn resolve(self) -> Result<Vec<SourceTreeResolution>> {
|
||||
let resolutions: Vec<_> = self
|
||||
.source_trees
|
||||
.iter()
|
||||
.map(|source_tree| async { self.resolve_source_tree(source_tree).await })
|
||||
.collect::<FuturesOrdered<_>>()
|
||||
.try_collect()
|
||||
.await?;
|
||||
Ok(requirements
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(Requirement::from)
|
||||
.collect())
|
||||
Ok(resolutions)
|
||||
}
|
||||
|
||||
/// Infer the package name for a given "unnamed" requirement.
|
||||
async fn resolve_source_tree(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<Vec<pep508_rs::Requirement<VerbatimParsedUrl>>> {
|
||||
/// Infer the dependencies for a directory dependency.
|
||||
async fn resolve_source_tree(&self, path: &Path) -> Result<SourceTreeResolution> {
|
||||
// Convert to a buildable source.
|
||||
let source_tree = fs_err::canonicalize(path).with_context(|| {
|
||||
format!(
|
||||
|
|
@ -151,40 +153,59 @@ impl<'a, Context: BuildContext> SourceTreeResolver<'a, Context> {
|
|||
}
|
||||
};
|
||||
|
||||
// Extract the origin.
|
||||
let origin = RequirementOrigin::Project(path.to_path_buf(), metadata.name.clone());
|
||||
|
||||
// Determine the extras to include when resolving the requirements.
|
||||
let extras = match self.extras {
|
||||
ExtrasSpecification::All => metadata.provides_extras.as_slice(),
|
||||
ExtrasSpecification::None => &[],
|
||||
ExtrasSpecification::Some(extras) => extras,
|
||||
};
|
||||
|
||||
// Determine the appropriate requirements to return based on the extras. This involves
|
||||
// evaluating the `extras` expression in any markers, but preserving the remaining marker
|
||||
// conditions.
|
||||
match self.extras {
|
||||
ExtrasSpecification::None => Ok(metadata
|
||||
.requires_dist
|
||||
.into_iter()
|
||||
.map(|requirement| requirement.with_origin(origin.clone()))
|
||||
.collect()),
|
||||
ExtrasSpecification::All => Ok(metadata
|
||||
.requires_dist
|
||||
.into_iter()
|
||||
.map(|requirement| pep508_rs::Requirement {
|
||||
origin: Some(origin.clone()),
|
||||
marker: requirement
|
||||
.marker
|
||||
.and_then(|marker| marker.simplify_extras(&metadata.provides_extras)),
|
||||
..requirement
|
||||
})
|
||||
.collect()),
|
||||
ExtrasSpecification::Some(extras) => Ok(metadata
|
||||
.requires_dist
|
||||
.into_iter()
|
||||
.map(|requirement| pep508_rs::Requirement {
|
||||
origin: Some(origin.clone()),
|
||||
marker: requirement
|
||||
.marker
|
||||
.and_then(|marker| marker.simplify_extras(extras)),
|
||||
..requirement
|
||||
})
|
||||
.collect()),
|
||||
let mut requirements: Vec<Requirement> = metadata
|
||||
.requires_dist
|
||||
.into_iter()
|
||||
.map(|requirement| Requirement {
|
||||
origin: Some(origin.clone()),
|
||||
marker: requirement
|
||||
.marker
|
||||
.and_then(|marker| marker.simplify_extras(extras)),
|
||||
..requirement
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Resolve any recursive extras.
|
||||
loop {
|
||||
// Find the first recursive requirement.
|
||||
// TODO(charlie): Respect markers on recursive extras.
|
||||
let Some(index) = requirements.iter().position(|requirement| {
|
||||
requirement.name == metadata.name && requirement.marker.is_none()
|
||||
}) else {
|
||||
break;
|
||||
};
|
||||
|
||||
// Remove the requirement that points to us.
|
||||
let recursive = requirements.remove(index);
|
||||
|
||||
// Re-simplify the requirements.
|
||||
for requirement in &mut requirements {
|
||||
requirement.marker = requirement
|
||||
.marker
|
||||
.take()
|
||||
.and_then(|marker| marker.simplify_extras(&recursive.extras));
|
||||
}
|
||||
}
|
||||
|
||||
let project = metadata.name;
|
||||
let extras = metadata.provides_extras;
|
||||
|
||||
Ok(SourceTreeResolution {
|
||||
requirements,
|
||||
project,
|
||||
extras,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue