Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions crates/uv-bench/benches/uv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,16 @@ mod resolver {
};
use uv_dispatch::{BuildDispatch, SharedState};
use uv_distribution::DistributionDatabase;
use uv_distribution_types::{DependencyMetadata, IndexLocations};
use uv_distribution_types::{DependencyMetadata, IndexLocations, RequiresPython};
use uv_install_wheel::LinkMode;
use uv_pep440::Version;
use uv_pep508::{MarkerEnvironment, MarkerEnvironmentBuilder};
use uv_platform_tags::{Arch, Os, Platform, Tags};
use uv_pypi_types::{Conflicts, ResolverMarkerEnvironment};
use uv_python::Interpreter;
use uv_resolver::{
FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, RequiresPython,
Resolver, ResolverEnvironment, ResolverOutput,
FlatIndex, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, Resolver,
ResolverEnvironment, ResolverOutput,
};
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy};
use uv_workspace::WorkspaceCache;
Expand Down
9 changes: 9 additions & 0 deletions crates/uv-configuration/src/dependency_groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,15 @@ pub struct DependencyGroupsWithDefaults {
}

impl DependencyGroupsWithDefaults {
/// Do not enable any groups
///
/// Many places in the code need to know what dependency-groups are active,
/// but various commands or subsystems never enable any dependency-groups,
/// in which case they want this.
pub fn none() -> Self {
DependencyGroups::default().with_defaults(DefaultGroups::default())
}

/// Returns `true` if the specification was enabled, and *only* because it was a default
pub fn contains_because_default(&self, group: &GroupName) -> bool {
self.cur.contains(group) && !self.prev.contains(group)
Expand Down
8 changes: 8 additions & 0 deletions crates/uv-configuration/src/extras.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ pub struct ExtrasSpecificationWithDefaults {
}

impl ExtrasSpecificationWithDefaults {
/// Do not enable any extras
///
/// Many places in the code need to know what extras are active,
/// but various commands or subsystems never enable any extras,
/// in which case they want this.
pub fn none() -> Self {
ExtrasSpecification::default().with_defaults(DefaultExtras::default())
}
/// Returns `true` if the specification was enabled, and *only* because it was a default
pub fn contains_because_default(&self, extra: &ExtraName) -> bool {
self.cur.contains(extra) && !self.prev.contains(extra)
Expand Down
2 changes: 2 additions & 0 deletions crates/uv-distribution-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pub use crate::pip_index::*;
pub use crate::prioritized_distribution::*;
pub use crate::requested::*;
pub use crate::requirement::*;
pub use crate::requires_python::*;
pub use crate::resolution::*;
pub use crate::resolved::*;
pub use crate::specified_requirement::*;
Expand Down Expand Up @@ -100,6 +101,7 @@ mod pip_index;
mod prioritized_distribution;
mod requested;
mod requirement;
mod requires_python;
mod resolution;
mod resolved;
mod specified_requirement;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::Bound;

use pubgrub::Range;
use version_ranges::Ranges;

use uv_distribution_filename::WheelFilename;
use uv_pep440::{
Expand Down Expand Up @@ -68,7 +68,7 @@ impl RequiresPython {
let range = specifiers
.into_iter()
.map(|specifier| release_specifiers_to_ranges(specifier.clone()))
.fold(None, |range: Option<Range<Version>>, requires_python| {
.fold(None, |range: Option<Ranges<Version>>, requires_python| {
if let Some(range) = range {
Some(range.intersection(&requires_python))
} else {
Expand Down Expand Up @@ -97,12 +97,12 @@ impl RequiresPython {
pub fn split(&self, bound: Bound<Version>) -> Option<(Self, Self)> {
let RequiresPythonRange(.., upper) = &self.range;

let upper = Range::from_range_bounds((bound, upper.clone().into()));
let upper = Ranges::from_range_bounds((bound, upper.clone().into()));
let lower = upper.complement();

// Intersect left and right with the existing range.
let lower = lower.intersection(&Range::from(self.range.clone()));
let upper = upper.intersection(&Range::from(self.range.clone()));
let lower = lower.intersection(&Ranges::from(self.range.clone()));
let upper = upper.intersection(&Ranges::from(self.range.clone()));

if lower.is_empty() || upper.is_empty() {
None
Expand Down Expand Up @@ -353,7 +353,7 @@ impl RequiresPython {
/// a lock file are deserialized and turned into a `ResolutionGraph`, the
/// markers are "complexified" to put the `requires-python` assumption back
/// into the marker explicitly.
pub(crate) fn simplify_markers(&self, marker: MarkerTree) -> MarkerTree {
pub fn simplify_markers(&self, marker: MarkerTree) -> MarkerTree {
let (lower, upper) = (self.range().lower(), self.range().upper());
marker.simplify_python_versions(lower.as_ref(), upper.as_ref())
}
Expand All @@ -373,7 +373,7 @@ impl RequiresPython {
/// ```text
/// python_full_version >= '3.8' and python_full_version < '3.12'
/// ```
pub(crate) fn complexify_markers(&self, marker: MarkerTree) -> MarkerTree {
pub fn complexify_markers(&self, marker: MarkerTree) -> MarkerTree {
let (lower, upper) = (self.range().lower(), self.range().upper());
marker.complexify_python_versions(lower.as_ref(), upper.as_ref())
}
Expand Down Expand Up @@ -537,7 +537,7 @@ pub struct RequiresPythonRange(LowerBound, UpperBound);

impl RequiresPythonRange {
/// Initialize a [`RequiresPythonRange`] from a [`Range`].
pub fn from_range(range: &Range<Version>) -> Self {
pub fn from_range(range: &Ranges<Version>) -> Self {
let (lower, upper) = range
.bounding_range()
.map(|(lower_bound, upper_bound)| (lower_bound.cloned(), upper_bound.cloned()))
Expand Down Expand Up @@ -575,9 +575,9 @@ impl Default for RequiresPythonRange {
}
}

impl From<RequiresPythonRange> for Range<Version> {
impl From<RequiresPythonRange> for Ranges<Version> {
fn from(value: RequiresPythonRange) -> Self {
Range::from_range_bounds::<(Bound<Version>, Bound<Version>), _>((
Ranges::from_range_bounds::<(Bound<Version>, Bound<Version>), _>((
value.0.into(),
value.1.into(),
))
Expand All @@ -592,34 +592,31 @@ impl From<RequiresPythonRange> for Range<Version> {
/// a simplified marker, one must re-contextualize it by adding the
/// `requires-python` constraint back to the marker.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord, serde::Deserialize)]
pub(crate) struct SimplifiedMarkerTree(MarkerTree);
pub struct SimplifiedMarkerTree(MarkerTree);

impl SimplifiedMarkerTree {
/// Simplifies the given markers by assuming the given `requires-python`
/// bound is true.
pub(crate) fn new(
requires_python: &RequiresPython,
marker: MarkerTree,
) -> SimplifiedMarkerTree {
pub fn new(requires_python: &RequiresPython, marker: MarkerTree) -> SimplifiedMarkerTree {
SimplifiedMarkerTree(requires_python.simplify_markers(marker))
}

/// Complexifies the given markers by adding the given `requires-python` as
/// a constraint to these simplified markers.
pub(crate) fn into_marker(self, requires_python: &RequiresPython) -> MarkerTree {
pub fn into_marker(self, requires_python: &RequiresPython) -> MarkerTree {
requires_python.complexify_markers(self.0)
}

/// Attempts to convert this simplified marker to a string.
///
/// This only returns `None` when the underlying marker is always true,
/// i.e., it matches all possible marker environments.
pub(crate) fn try_to_string(self) -> Option<String> {
pub fn try_to_string(self) -> Option<String> {
self.0.try_to_string()
}

/// Returns the underlying marker tree without re-complexifying them.
pub(crate) fn as_simplified_marker_tree(self) -> MarkerTree {
pub fn as_simplified_marker_tree(self) -> MarkerTree {
self.0
}
}
Expand Down
59 changes: 16 additions & 43 deletions crates/uv-distribution/src/metadata/requires_dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_hash::FxHashSet;

use uv_configuration::SourceStrategy;
use uv_distribution_types::{IndexLocations, Requirement};
use uv_normalize::{DEV_DEPENDENCIES, ExtraName, GroupName, PackageName};
use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_pep508::MarkerTree;
use uv_workspace::dependency_groups::FlatDependencyGroups;
use uv_workspace::pyproject::{Sources, ToolUvSources};
Expand Down Expand Up @@ -107,41 +107,10 @@ impl RequiresDist {
SourceStrategy::Disabled => &empty,
};

// Collect the dependency groups.
let dependency_groups = {
// First, collect `tool.uv.dev_dependencies`
let dev_dependencies = project_workspace
.current_project()
.pyproject_toml()
.tool
.as_ref()
.and_then(|tool| tool.uv.as_ref())
.and_then(|uv| uv.dev_dependencies.as_ref());

// Then, collect `dependency-groups`
let dependency_groups = project_workspace
.current_project()
.pyproject_toml()
.dependency_groups
.iter()
.flatten()
.collect::<BTreeMap<_, _>>();

// Flatten the dependency groups.
let mut dependency_groups =
FlatDependencyGroups::from_dependency_groups(&dependency_groups)
.map_err(|err| err.with_dev_dependencies(dev_dependencies))?;

// Add the `dev` group, if `dev-dependencies` is defined.
if let Some(dev_dependencies) = dev_dependencies {
dependency_groups
.entry(DEV_DEPENDENCIES.clone())
.or_insert_with(Vec::new)
.extend(dev_dependencies.clone());
}

dependency_groups
};
let dependency_groups = FlatDependencyGroups::from_pyproject_toml(
project_workspace.current_project().root(),
project_workspace.current_project().pyproject_toml(),
)?;

// Now that we've resolved the dependency groups, we can validate that each source references
// a valid extra or group, if present.
Expand All @@ -150,9 +119,10 @@ impl RequiresDist {
// Lower the dependency groups.
let dependency_groups = dependency_groups
.into_iter()
.map(|(name, requirements)| {
.map(|(name, flat_group)| {
let requirements = match source_strategy {
SourceStrategy::Enabled => requirements
SourceStrategy::Enabled => flat_group
.requirements
.into_iter()
.flat_map(|requirement| {
let requirement_name = requirement.name.clone();
Expand Down Expand Up @@ -182,9 +152,11 @@ impl RequiresDist {
)
})
.collect::<Result<Box<_>, _>>(),
SourceStrategy::Disabled => {
Ok(requirements.into_iter().map(Requirement::from).collect())
}
SourceStrategy::Disabled => Ok(flat_group
.requirements
.into_iter()
.map(Requirement::from)
.collect()),
}?;
Ok::<(GroupName, Box<_>), MetadataError>((name, requirements))
})
Expand Down Expand Up @@ -265,15 +237,16 @@ impl RequiresDist {

if let Some(group) = source.group() {
// If the group doesn't exist at all, error.
let Some(dependencies) = dependency_groups.get(group) else {
let Some(flat_group) = dependency_groups.get(group) else {
return Err(MetadataError::MissingSourceGroup(
name.clone(),
group.clone(),
));
};

// If there is no such requirement with the group, error.
if !dependencies
if !flat_group
.requirements
.iter()
.any(|requirement| requirement.name == *name)
{
Expand Down
2 changes: 0 additions & 2 deletions crates/uv-resolver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub use options::{Flexibility, Options, OptionsBuilder};
pub use preferences::{Preference, PreferenceError, Preferences};
pub use prerelease::PrereleaseMode;
pub use python_requirement::PythonRequirement;
pub use requires_python::{RequiresPython, RequiresPythonRange};
pub use resolution::{
AnnotationStyle, ConflictingDistributionError, DisplayResolutionGraph, ResolverOutput,
};
Expand Down Expand Up @@ -58,7 +57,6 @@ mod prerelease;
mod pubgrub;
mod python_requirement;
mod redirect;
mod requires_python;
mod resolution;
mod resolution_mode;
mod resolver;
Expand Down
6 changes: 3 additions & 3 deletions crates/uv-resolver/src/lock/export/pylock_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use uv_distribution_filename::{
use uv_distribution_types::{
BuiltDist, DirectUrlBuiltDist, DirectUrlSourceDist, DirectorySourceDist, Dist, Edge,
FileLocation, GitSourceDist, IndexUrl, Name, Node, PathBuiltDist, PathSourceDist,
RegistryBuiltDist, RegistryBuiltWheel, RegistrySourceDist, RemoteSource, Resolution,
ResolvedDist, SourceDist, ToUrlError, UrlString,
RegistryBuiltDist, RegistryBuiltWheel, RegistrySourceDist, RemoteSource, RequiresPython,
Resolution, ResolvedDist, SourceDist, ToUrlError, UrlString,
};
use uv_fs::{PortablePathBuf, relative_to};
use uv_git::{RepositoryReference, ResolvedRepositoryReference};
Expand All @@ -40,7 +40,7 @@ use uv_small_str::SmallString;
use crate::lock::export::ExportableRequirements;
use crate::lock::{Source, WheelTagHint, each_element_on_its_line_array};
use crate::resolution::ResolutionGraphNode;
use crate::{Installable, LockError, RequiresPython, ResolverOutput};
use crate::{Installable, LockError, ResolverOutput};

#[derive(Debug, thiserror::Error)]
pub enum PylockTomlErrorKind {
Expand Down
8 changes: 3 additions & 5 deletions crates/uv-resolver/src/lock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ use uv_distribution_types::{
BuiltDist, DependencyMetadata, DirectUrlBuiltDist, DirectUrlSourceDist, DirectorySourceDist,
Dist, DistributionMetadata, FileLocation, GitSourceDist, IndexLocations, IndexMetadata,
IndexUrl, Name, PathBuiltDist, PathSourceDist, RegistryBuiltDist, RegistryBuiltWheel,
RegistrySourceDist, RemoteSource, Requirement, RequirementSource, ResolvedDist, StaticMetadata,
ToUrlError, UrlString,
RegistrySourceDist, RemoteSource, Requirement, RequirementSource, RequiresPython, ResolvedDist,
SimplifiedMarkerTree, StaticMetadata, ToUrlError, UrlString,
};
use uv_fs::{PortablePath, PortablePathBuf, relative_to};
use uv_git::{RepositoryReference, ResolvedRepositoryReference};
Expand All @@ -57,12 +57,10 @@ pub use crate::lock::export::{PylockToml, PylockTomlErrorKind};
pub use crate::lock::installable::Installable;
pub use crate::lock::map::PackageMap;
pub use crate::lock::tree::TreeDisplay;
use crate::requires_python::SimplifiedMarkerTree;
use crate::resolution::{AnnotatedDist, ResolutionGraphNode};
use crate::universal_marker::{ConflictMarker, UniversalMarker};
use crate::{
ExcludeNewer, InMemoryIndex, MetadataResponse, PrereleaseMode, RequiresPython, ResolutionMode,
ResolverOutput,
ExcludeNewer, InMemoryIndex, MetadataResponse, PrereleaseMode, ResolutionMode, ResolverOutput,
};

mod export;
Expand Down
2 changes: 1 addition & 1 deletion crates/uv-resolver/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::ops::Bound;
use uv_pep440::{LowerBound, UpperBound, Version};
use uv_pep508::{CanonicalMarkerValueVersion, MarkerTree, MarkerTreeKind};

use crate::requires_python::RequiresPythonRange;
use uv_distribution_types::RequiresPythonRange;

/// Returns the bounding Python versions that can satisfy the [`MarkerTree`], if it's constrained.
pub(crate) fn requires_python(tree: MarkerTree) -> Option<RequiresPythonRange> {
Expand Down
6 changes: 2 additions & 4 deletions crates/uv-resolver/src/pubgrub/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_hash::FxHashMap;
use uv_configuration::{IndexStrategy, NoBinary, NoBuild};
use uv_distribution_types::{
IncompatibleDist, IncompatibleSource, IncompatibleWheel, Index, IndexCapabilities,
IndexLocations, IndexMetadata, IndexUrl,
IndexLocations, IndexMetadata, IndexUrl, RequiresPython,
};
use uv_normalize::PackageName;
use uv_pep440::{Version, VersionSpecifiers};
Expand All @@ -27,9 +27,7 @@ use crate::python_requirement::{PythonRequirement, PythonRequirementSource};
use crate::resolver::{
MetadataUnavailable, UnavailablePackage, UnavailableReason, UnavailableVersion,
};
use crate::{
Flexibility, InMemoryIndex, Options, RequiresPython, ResolverEnvironment, VersionsResponse,
};
use crate::{Flexibility, InMemoryIndex, Options, ResolverEnvironment, VersionsResponse};

#[derive(Debug)]
pub(crate) struct PubGrubReportFormatter<'a> {
Expand Down
3 changes: 1 addition & 2 deletions crates/uv-resolver/src/python_requirement.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::collections::Bound;

use uv_distribution_types::{RequiresPython, RequiresPythonRange};
use uv_pep440::Version;
use uv_pep508::{MarkerEnvironment, MarkerTree};
use uv_python::{Interpreter, PythonVersion};

use crate::{RequiresPython, RequiresPythonRange};

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PythonRequirement {
source: PythonRequirementSource,
Expand Down
Loading
Loading