mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-08 17:50:25 +00:00
Merge 8f6b1f528f
into 00d3aa3780
This commit is contained in:
commit
a8fe2e3faf
4 changed files with 155 additions and 12 deletions
|
@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use uv_configuration::SourceStrategy;
|
use uv_configuration::SourceStrategy;
|
||||||
use uv_distribution_types::{IndexLocations, Requirement};
|
use uv_distribution_types::{IndexLocations, Requirement, RequiresPython};
|
||||||
use uv_normalize::{GroupName, PackageName};
|
use uv_normalize::{GroupName, PackageName};
|
||||||
use uv_workspace::dependency_groups::FlatDependencyGroups;
|
use uv_workspace::dependency_groups::FlatDependencyGroups;
|
||||||
use uv_workspace::pyproject::{Sources, ToolUvSources};
|
use uv_workspace::pyproject::{Sources, ToolUvSources};
|
||||||
|
@ -45,7 +45,13 @@ use crate::metadata::{GitWorkspaceMember, LoweredRequirement, MetadataError};
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SourcedDependencyGroups {
|
pub struct SourcedDependencyGroups {
|
||||||
pub name: Option<PackageName>,
|
pub name: Option<PackageName>,
|
||||||
pub dependency_groups: BTreeMap<GroupName, Box<[Requirement]>>,
|
pub dependency_groups: BTreeMap<GroupName, SourcedDependencyGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SourcedDependencyGroup {
|
||||||
|
pub requirements: Box<[Requirement]>,
|
||||||
|
pub requires_python: Option<RequiresPython>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourcedDependencyGroups {
|
impl SourcedDependencyGroups {
|
||||||
|
@ -98,12 +104,23 @@ impl SourcedDependencyGroups {
|
||||||
dependency_groups: dependency_groups
|
dependency_groups: dependency_groups
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(name, group)| {
|
.map(|(name, group)| {
|
||||||
|
let requires_python = group
|
||||||
|
.requires_python
|
||||||
|
.as_ref()
|
||||||
|
.map(RequiresPython::from_specifiers);
|
||||||
|
|
||||||
let requirements = group
|
let requirements = group
|
||||||
.requirements
|
.requirements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Requirement::from)
|
.map(Requirement::from)
|
||||||
.collect();
|
.collect();
|
||||||
(name, requirements)
|
(
|
||||||
|
name,
|
||||||
|
SourcedDependencyGroup {
|
||||||
|
requirements,
|
||||||
|
requires_python,
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
});
|
});
|
||||||
|
@ -138,6 +155,11 @@ impl SourcedDependencyGroups {
|
||||||
let dependency_groups = dependency_groups
|
let dependency_groups = dependency_groups
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(name, group)| {
|
.map(|(name, group)| {
|
||||||
|
let requires_python = group
|
||||||
|
.requires_python
|
||||||
|
.as_ref()
|
||||||
|
.map(RequiresPython::from_specifiers);
|
||||||
|
|
||||||
let requirements = group
|
let requirements = group
|
||||||
.requirements
|
.requirements
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -167,7 +189,13 @@ impl SourcedDependencyGroups {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<Box<_>, _>>()?;
|
.collect::<Result<Box<_>, _>>()?;
|
||||||
Ok::<(GroupName, Box<_>), MetadataError>((name, requirements))
|
Ok::<(GroupName, SourcedDependencyGroup), MetadataError>((
|
||||||
|
name,
|
||||||
|
SourcedDependencyGroup {
|
||||||
|
requirements,
|
||||||
|
requires_python,
|
||||||
|
},
|
||||||
|
))
|
||||||
})
|
})
|
||||||
.collect::<Result<BTreeMap<_, _>, _>>()?;
|
.collect::<Result<BTreeMap<_, _>, _>>()?;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub use manifest::Manifest;
|
||||||
pub use options::{Flexibility, Options, OptionsBuilder};
|
pub use options::{Flexibility, Options, OptionsBuilder};
|
||||||
pub use preferences::{Preference, PreferenceError, Preferences};
|
pub use preferences::{Preference, PreferenceError, Preferences};
|
||||||
pub use prerelease::PrereleaseMode;
|
pub use prerelease::PrereleaseMode;
|
||||||
pub use python_requirement::PythonRequirement;
|
pub use python_requirement::{PythonRequirement, PythonRequirementSource};
|
||||||
pub use resolution::{
|
pub use resolution::{
|
||||||
AnnotationStyle, ConflictingDistributionError, DisplayResolutionGraph, ResolverOutput,
|
AnnotationStyle, ConflictingDistributionError, DisplayResolutionGraph, ResolverOutput,
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,7 @@ use uv_fs::Simplified;
|
||||||
use uv_install_wheel::LinkMode;
|
use uv_install_wheel::LinkMode;
|
||||||
use uv_installer::{InstallationStrategy, Plan, Planner, Preparer, SitePackages};
|
use uv_installer::{InstallationStrategy, Plan, Planner, Preparer, SitePackages};
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
|
use uv_pep440::Operator;
|
||||||
use uv_pep508::{MarkerEnvironment, RequirementOrigin};
|
use uv_pep508::{MarkerEnvironment, RequirementOrigin};
|
||||||
use uv_platform_tags::Tags;
|
use uv_platform_tags::Tags;
|
||||||
use uv_preview::Preview;
|
use uv_preview::Preview;
|
||||||
|
@ -235,13 +236,62 @@ pub(crate) async fn resolve<InstalledPackages: InstalledPackagesProvider>(
|
||||||
// Apply dependency-groups
|
// Apply dependency-groups
|
||||||
for (group_name, group) in &metadata.dependency_groups {
|
for (group_name, group) in &metadata.dependency_groups {
|
||||||
if groups.contains(group_name) {
|
if groups.contains(group_name) {
|
||||||
requirements.extend(group.iter().cloned().map(|group| Requirement {
|
if let Some(requires_python) = group.requires_python.as_ref() {
|
||||||
origin: Some(RequirementOrigin::Group(
|
if !python_requirement
|
||||||
pyproject_path.clone(),
|
.target()
|
||||||
metadata.name.clone(),
|
.is_contained_by(requires_python.specifiers())
|
||||||
group_name.clone(),
|
{
|
||||||
)),
|
let required_spec = requires_python.specifiers().to_string();
|
||||||
..group
|
let active_spec = python_requirement.target().specifiers().to_string();
|
||||||
|
let interpreter_version = python_requirement.exact().to_string();
|
||||||
|
|
||||||
|
let suggested_version = requires_python
|
||||||
|
.range()
|
||||||
|
.lower()
|
||||||
|
.specifier()
|
||||||
|
.and_then(|specifier| {
|
||||||
|
let (operator, version) = specifier.into_parts();
|
||||||
|
|
||||||
|
match operator {
|
||||||
|
Operator::GreaterThanEqual
|
||||||
|
| Operator::Equal
|
||||||
|
| Operator::ExactEqual
|
||||||
|
| Operator::EqualStar
|
||||||
|
| Operator::TildeEqual => Some(version),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut message = format!(
|
||||||
|
"Dependency group `{group_name}` in `{}` requires Python `{required_spec}`, but uv is resolving for Python `{active_spec}` (current interpreter: `{interpreter_version}`).",
|
||||||
|
pyproject_path.user_display()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(ref version) = suggested_version {
|
||||||
|
let suggested_python = version.to_string();
|
||||||
|
|
||||||
|
write!(
|
||||||
|
message,
|
||||||
|
"{}",
|
||||||
|
&format!(
|
||||||
|
" Re-run with `--python {suggested_python}` to target a compatible Python version."
|
||||||
|
)
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(anyhow!(message).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requirements.extend(group.requirements.iter().cloned().map(|group| {
|
||||||
|
Requirement {
|
||||||
|
origin: Some(RequirementOrigin::Group(
|
||||||
|
pyproject_path.clone(),
|
||||||
|
metadata.name.clone(),
|
||||||
|
group_name.clone(),
|
||||||
|
)),
|
||||||
|
..group
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16070,6 +16070,71 @@ fn project_and_group_workspace() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn group_requires_python_incompatible_with_interpreter() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.13");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "requires-python"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[tool.uv.dependency-groups.ml1]
|
||||||
|
requires-python = ">=3.14"
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
ml1 = ["tqdm"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.pip_compile()
|
||||||
|
.arg("--group").arg("pyproject.toml:ml1"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Dependency group `ml1` in `pyproject.toml` requires Python `>=3.14`, but uv is resolving for Python `>=3.13.[X]` (current interpreter: `3.13.[X]`). Re-run with `--python 3.14` to target a compatible Python version.
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn group_requires_python_incompatible_with_python_flag() -> Result<()> {
|
||||||
|
let context = TestContext::new_with_versions(&["3.12", "3.13"]);
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "requires-python"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[tool.uv.dependency-groups.ml1]
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
ml1 = ["tqdm"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.pip_compile()
|
||||||
|
.arg("--group").arg("pyproject.toml:ml1")
|
||||||
|
.arg("--python").arg("3.12"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Dependency group `ml1` in `pyproject.toml` requires Python `>=3.13`, but uv is resolving for Python `>=3.12` (current interpreter: `3.12.[X]`). Re-run with `--python 3.13` to target a compatible Python version.
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn directory_and_group() -> Result<()> {
|
fn directory_and_group() -> Result<()> {
|
||||||
// Checking that --directory is handled properly with --group
|
// Checking that --directory is handled properly with --group
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue