Skip to content

Commit

Permalink
Support overrides and constraints in PEP 723 scripts (#9162)
Browse files Browse the repository at this point in the history
## Summary

Closes #9141.
  • Loading branch information
charliermarsh authored Nov 16, 2024
1 parent 14812ff commit fb3f365
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 6 deletions.
24 changes: 24 additions & 0 deletions crates/uv-requirements/src/specification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,30 @@ impl RequirementsSpecification {
}
}

/// Initialize a [`RequirementsSpecification`] from a list of [`Requirement`], including
/// constraints and overrides.
pub fn from_constraints(
requirements: Vec<Requirement>,
constraints: Vec<Requirement>,
overrides: Vec<Requirement>,
) -> Self {
Self {
requirements: requirements
.into_iter()
.map(UnresolvedRequirementSpecification::from)
.collect(),
constraints: constraints
.into_iter()
.map(NameRequirementSpecification::from)
.collect(),
overrides: overrides
.into_iter()
.map(UnresolvedRequirementSpecification::from)
.collect(),
..Self::default()
}
}

/// Return true if the specification does not include any requirements to install.
pub fn is_empty(&self) -> bool {
self.requirements.is_empty() && self.source_trees.is_empty() && self.overrides.is_empty()
Expand Down
10 changes: 7 additions & 3 deletions crates/uv-scripts/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use memchr::memmem::Finder;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::LazyLock;

use memchr::memmem::Finder;
use serde::Deserialize;
use thiserror::Error;

use uv_distribution_types::Index;
use uv_pep440::VersionSpecifiers;
use uv_pep508::PackageName;
Expand Down Expand Up @@ -264,12 +266,14 @@ pub struct Tool {
}

#[derive(Debug, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct ToolUv {
#[serde(flatten)]
pub globals: GlobalOptions,
#[serde(flatten)]
pub top_level: ResolverInstallerOptions,
pub override_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub sources: Option<BTreeMap<PackageName, Sources>>,
pub indexes: Option<Vec<Index>>,
}
Expand Down
44 changes: 43 additions & 1 deletion crates/uv/src/commands/project/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,49 @@ pub(crate) async fn run(
.map_ok(LoweredRequirement::into_inner)
})
.collect::<Result<_, _>>()?;
let spec = RequirementsSpecification::from_requirements(requirements);
let constraints = script
.tool
.as_ref()
.and_then(|tool| tool.uv.as_ref())
.and_then(|uv| uv.constraint_dependencies.as_ref())
.into_iter()
.flatten()
.cloned()
.flat_map(|requirement| {
LoweredRequirement::from_non_workspace_requirement(
requirement,
script_dir.as_ref(),
script_sources,
script_indexes,
&settings.index_locations,
LowerBound::Allow,
)
.map_ok(LoweredRequirement::into_inner)
})
.collect::<Result<Vec<_>, _>>()?;
let overrides = script
.tool
.as_ref()
.and_then(|tool| tool.uv.as_ref())
.and_then(|uv| uv.override_dependencies.as_ref())
.into_iter()
.flatten()
.cloned()
.flat_map(|requirement| {
LoweredRequirement::from_non_workspace_requirement(
requirement,
script_dir.as_ref(),
script_sources,
script_indexes,
&settings.index_locations,
LowerBound::Allow,
)
.map_ok(LoweredRequirement::into_inner)
})
.collect::<Result<Vec<_>, _>>()?;

let spec =
RequirementsSpecification::from_constraints(requirements, constraints, overrides);
let result = CachedEnvironment::get_or_create(
EnvironmentSpecification::from(spec),
interpreter,
Expand Down
4 changes: 2 additions & 2 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1497,7 +1497,7 @@ async fn run_project(
Pep723Item::Remote(_) => unreachable!("`uv remove` does not support remote files"),
});

commands::remove(
Box::pin(commands::remove(
project_dir,
args.locked,
args.frozen,
Expand All @@ -1518,7 +1518,7 @@ async fn run_project(
no_config,
&cache,
printer,
)
))
.await
}
ProjectCommand::Tree(args) => {
Expand Down
78 changes: 78 additions & 0 deletions crates/uv/tests/it/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,84 @@ fn run_pep723_script_metadata() -> Result<()> {
Ok(())
}

/// Run a PEP 723-compatible script with `tool.uv` constraints.
#[test]
fn run_pep723_script_constraints() -> Result<()> {
let context = TestContext::new("3.12");

let test_script = context.temp_dir.child("main.py");
test_script.write_str(indoc! { r#"
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "anyio>=3",
# ]
#
# [tool.uv]
# constraint-dependencies = ["idna<=3"]
# ///
import anyio
"#
})?;

uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Reading inline script metadata from `main.py`
Resolved 3 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==4.3.0
+ idna==3.0
+ sniffio==1.3.1
"###);

Ok(())
}

/// Run a PEP 723-compatible script with `tool.uv` overrides.
#[test]
fn run_pep723_script_overrides() -> Result<()> {
let context = TestContext::new("3.12");

let test_script = context.temp_dir.child("main.py");
test_script.write_str(indoc! { r#"
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "anyio>=3",
# ]
#
# [tool.uv]
# override-dependencies = ["idna<=2"]
# ///
import anyio
"#
})?;

uv_snapshot!(context.filters(), context.run().arg("main.py"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Reading inline script metadata from `main.py`
Resolved 3 packages in [TIME]
Prepared 3 packages in [TIME]
Installed 3 packages in [TIME]
+ anyio==4.3.0
+ idna==2.0
+ sniffio==1.3.1
"###);

Ok(())
}

/// With `managed = false`, we should avoid installing the project itself.
#[test]
fn run_managed_false() -> Result<()> {
Expand Down

0 comments on commit fb3f365

Please sign in to comment.