mirror of https://github.com/astral-sh/uv
Ensure extras trigger an install (#1727)
## Summary We weren't respecting extras when auditing the existing environment. Closes https://github.com/astral-sh/uv/issues/1726.
This commit is contained in:
parent
a5372d4e4d
commit
4b5b9835fd
|
|
@ -190,24 +190,24 @@ impl<'a> SitePackages<'a> {
|
|||
}
|
||||
|
||||
// Verify that the dependencies are installed.
|
||||
for requirement in &metadata.requires_dist {
|
||||
if !requirement.evaluate_markers(self.venv.interpreter().markers(), &[]) {
|
||||
for dependency in &metadata.requires_dist {
|
||||
if !dependency.evaluate_markers(self.venv.interpreter().markers(), &[]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(installed) = self
|
||||
.by_name
|
||||
.get(&requirement.name)
|
||||
.get(&dependency.name)
|
||||
.map(|idx| &self.distributions[*idx])
|
||||
else {
|
||||
diagnostics.push(Diagnostic::MissingDependency {
|
||||
package: package.clone(),
|
||||
requirement: requirement.clone(),
|
||||
requirement: dependency.clone(),
|
||||
});
|
||||
continue;
|
||||
};
|
||||
|
||||
match &requirement.version_or_url {
|
||||
match &dependency.version_or_url {
|
||||
None | Some(pep508_rs::VersionOrUrl::Url(_)) => {
|
||||
// Nothing to do (accept any installed version).
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ impl<'a> SitePackages<'a> {
|
|||
diagnostics.push(Diagnostic::IncompatibleDependency {
|
||||
package: package.clone(),
|
||||
version: installed.version().clone(),
|
||||
requirement: requirement.clone(),
|
||||
requirement: dependency.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -234,10 +234,19 @@ impl<'a> SitePackages<'a> {
|
|||
editables: &[EditableRequirement],
|
||||
constraints: &[Requirement],
|
||||
) -> Result<bool> {
|
||||
let mut requirements = requirements.to_vec();
|
||||
let mut stack = Vec::<Requirement>::with_capacity(requirements.len());
|
||||
let mut seen =
|
||||
FxHashSet::with_capacity_and_hasher(requirements.len(), BuildHasherDefault::default());
|
||||
|
||||
// Add the direct requirements to the queue.
|
||||
for dependency in requirements {
|
||||
if dependency.evaluate_markers(self.venv.interpreter().markers(), &[]) {
|
||||
if seen.insert(dependency.clone()) {
|
||||
stack.push(dependency.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that all editable requirements are met.
|
||||
for requirement in editables {
|
||||
let Some(distribution) = self
|
||||
|
|
@ -253,15 +262,21 @@ impl<'a> SitePackages<'a> {
|
|||
let metadata = distribution
|
||||
.metadata()
|
||||
.with_context(|| format!("Failed to read metadata for: {distribution}"))?;
|
||||
requirements.extend(metadata.requires_dist);
|
||||
|
||||
// Add the dependencies to the queue.
|
||||
for dependency in metadata.requires_dist {
|
||||
if dependency
|
||||
.evaluate_markers(self.venv.interpreter().markers(), &requirement.extras)
|
||||
{
|
||||
if seen.insert(dependency.clone()) {
|
||||
stack.push(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that all non-editable requirements are met.
|
||||
while let Some(requirement) = requirements.pop() {
|
||||
if !requirement.evaluate_markers(self.venv.interpreter().markers(), &[]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while let Some(requirement) = stack.pop() {
|
||||
let Some(distribution) = self
|
||||
.by_name
|
||||
.get(&requirement.name)
|
||||
|
|
@ -300,11 +315,19 @@ impl<'a> SitePackages<'a> {
|
|||
}
|
||||
|
||||
// Recurse into the dependencies.
|
||||
if seen.insert(requirement) {
|
||||
let metadata = distribution
|
||||
.metadata()
|
||||
.with_context(|| format!("Failed to read metadata for: {distribution}"))?;
|
||||
requirements.extend(metadata.requires_dist);
|
||||
let metadata = distribution
|
||||
.metadata()
|
||||
.with_context(|| format!("Failed to read metadata for: {distribution}"))?;
|
||||
|
||||
// Add the dependencies to the queue.
|
||||
for dependency in metadata.requires_dist {
|
||||
if dependency
|
||||
.evaluate_markers(self.venv.interpreter().markers(), &requirement.extras)
|
||||
{
|
||||
if seen.insert(dependency.clone()) {
|
||||
stack.push(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -317,6 +317,65 @@ fn respect_installed_and_reinstall() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Respect installed versions when resolving.
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn reinstall_extras() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Install anyio.
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
requirements_txt.write_str("anyio")?;
|
||||
|
||||
uv_snapshot!(command(&context)
|
||||
.arg("-r")
|
||||
.arg("requirements.txt")
|
||||
.arg("--strict"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 3 packages in [TIME]
|
||||
Downloaded 3 packages in [TIME]
|
||||
Installed 3 packages in [TIME]
|
||||
+ anyio==4.0.0
|
||||
+ idna==3.4
|
||||
+ sniffio==1.3.0
|
||||
"###
|
||||
);
|
||||
|
||||
context.assert_command("import anyio").success();
|
||||
|
||||
// Re-install anyio, with an extra.
|
||||
let requirements_txt = context.temp_dir.child("requirements.txt");
|
||||
requirements_txt.touch()?;
|
||||
requirements_txt.write_str("anyio[trio]")?;
|
||||
|
||||
uv_snapshot!(command(&context)
|
||||
.arg("-r")
|
||||
.arg("requirements.txt")
|
||||
.arg("--strict"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Resolved 7 packages in [TIME]
|
||||
Downloaded 4 packages in [TIME]
|
||||
Installed 4 packages in [TIME]
|
||||
+ attrs==23.1.0
|
||||
+ outcome==1.3.0.post0
|
||||
+ sortedcontainers==2.4.0
|
||||
+ trio==0.23.1
|
||||
"###
|
||||
);
|
||||
|
||||
context.assert_command("import anyio").success();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Like `pip`, we (unfortunately) allow incompatible environments.
|
||||
#[test]
|
||||
fn allow_incompatibilities() -> Result<()> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue