mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-03 02:22:19 +00:00
Support overrides and constraints in PEP 723 scripts (#9162)
Some checks failed
CI / cargo shear (push) Has been cancelled
CI / Determine changes (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typos (push) Has been cancelled
CI / mkdocs (push) Has been cancelled
CI / check system | python on macos x86_64 (push) Has been cancelled
CI / cargo dev generate-all (push) Has been cancelled
CI / integration test | pypy on ubuntu (push) Has been cancelled
CI / cargo clippy | ubuntu (push) Has been cancelled
CI / cargo clippy | windows (push) Has been cancelled
CI / cargo test | ubuntu (push) Has been cancelled
CI / cargo test | macos (push) Has been cancelled
CI / cargo test | windows (push) Has been cancelled
CI / check windows trampoline | aarch64 (push) Has been cancelled
CI / integration test | pypy on windows (push) Has been cancelled
CI / check windows trampoline | i686 (push) Has been cancelled
CI / check windows trampoline | x86_64 (push) Has been cancelled
CI / test windows trampoline | i686 (push) Has been cancelled
CI / test windows trampoline | x86_64 (push) Has been cancelled
CI / build binary | linux (push) Has been cancelled
CI / build binary | macos aarch64 (push) Has been cancelled
CI / build binary | macos x86_64 (push) Has been cancelled
CI / build binary | windows (push) Has been cancelled
CI / cargo build (msrv) (push) Has been cancelled
CI / build binary | freebsd (push) Has been cancelled
CI / ecosystem test | prefecthq/prefect (push) Has been cancelled
CI / ecosystem test | pallets/flask (push) Has been cancelled
CI / integration test | conda on ubuntu (push) Has been cancelled
CI / integration test | free-threaded on linux (push) Has been cancelled
CI / integration test | free-threaded on windows (push) Has been cancelled
CI / integration test | graalpy on ubuntu (push) Has been cancelled
CI / integration test | graalpy on windows (push) Has been cancelled
CI / integration test | github actions (push) Has been cancelled
CI / integration test | determine publish changes (push) Has been cancelled
CI / integration test | uv publish (push) Has been cancelled
CI / check cache | ubuntu (push) Has been cancelled
CI / check cache | macos aarch64 (push) Has been cancelled
CI / check system | python on debian (push) Has been cancelled
CI / check system | python on fedora (push) Has been cancelled
CI / check system | python on ubuntu (push) Has been cancelled
CI / check system | python on opensuse (push) Has been cancelled
CI / check system | homebrew python on macos aarch64 (push) Has been cancelled
CI / check system | python on rocky linux 8 (push) Has been cancelled
CI / check system | python on rocky linux 9 (push) Has been cancelled
CI / check system | pypy on ubuntu (push) Has been cancelled
CI / check system | pyston (push) Has been cancelled
CI / check system | alpine (push) Has been cancelled
CI / check system | python on macos aarch64 (push) Has been cancelled
CI / check system | python3.10 on windows (push) Has been cancelled
CI / check system | python3.10 on windows x86 (push) Has been cancelled
CI / check system | python3.13 on windows (push) Has been cancelled
CI / check system | python3.12 via chocolatey (push) Has been cancelled
CI / check system | python3.9 via pyenv (push) Has been cancelled
CI / check system | python3.13 (push) Has been cancelled
CI / check system | conda3.11 on linux (push) Has been cancelled
CI / check system | conda3.8 on linux (push) Has been cancelled
CI / check system | conda3.11 on macos (push) Has been cancelled
CI / check system | conda3.8 on macos (push) Has been cancelled
CI / check system | conda3.11 on windows (push) Has been cancelled
CI / check system | conda3.8 on windows (push) Has been cancelled
CI / check system | amazonlinux (push) Has been cancelled
CI / check system | embedded python3.10 on windows (push) Has been cancelled
CI / benchmarks (push) Has been cancelled
Some checks failed
CI / cargo shear (push) Has been cancelled
CI / Determine changes (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typos (push) Has been cancelled
CI / mkdocs (push) Has been cancelled
CI / check system | python on macos x86_64 (push) Has been cancelled
CI / cargo dev generate-all (push) Has been cancelled
CI / integration test | pypy on ubuntu (push) Has been cancelled
CI / cargo clippy | ubuntu (push) Has been cancelled
CI / cargo clippy | windows (push) Has been cancelled
CI / cargo test | ubuntu (push) Has been cancelled
CI / cargo test | macos (push) Has been cancelled
CI / cargo test | windows (push) Has been cancelled
CI / check windows trampoline | aarch64 (push) Has been cancelled
CI / integration test | pypy on windows (push) Has been cancelled
CI / check windows trampoline | i686 (push) Has been cancelled
CI / check windows trampoline | x86_64 (push) Has been cancelled
CI / test windows trampoline | i686 (push) Has been cancelled
CI / test windows trampoline | x86_64 (push) Has been cancelled
CI / build binary | linux (push) Has been cancelled
CI / build binary | macos aarch64 (push) Has been cancelled
CI / build binary | macos x86_64 (push) Has been cancelled
CI / build binary | windows (push) Has been cancelled
CI / cargo build (msrv) (push) Has been cancelled
CI / build binary | freebsd (push) Has been cancelled
CI / ecosystem test | prefecthq/prefect (push) Has been cancelled
CI / ecosystem test | pallets/flask (push) Has been cancelled
CI / integration test | conda on ubuntu (push) Has been cancelled
CI / integration test | free-threaded on linux (push) Has been cancelled
CI / integration test | free-threaded on windows (push) Has been cancelled
CI / integration test | graalpy on ubuntu (push) Has been cancelled
CI / integration test | graalpy on windows (push) Has been cancelled
CI / integration test | github actions (push) Has been cancelled
CI / integration test | determine publish changes (push) Has been cancelled
CI / integration test | uv publish (push) Has been cancelled
CI / check cache | ubuntu (push) Has been cancelled
CI / check cache | macos aarch64 (push) Has been cancelled
CI / check system | python on debian (push) Has been cancelled
CI / check system | python on fedora (push) Has been cancelled
CI / check system | python on ubuntu (push) Has been cancelled
CI / check system | python on opensuse (push) Has been cancelled
CI / check system | homebrew python on macos aarch64 (push) Has been cancelled
CI / check system | python on rocky linux 8 (push) Has been cancelled
CI / check system | python on rocky linux 9 (push) Has been cancelled
CI / check system | pypy on ubuntu (push) Has been cancelled
CI / check system | pyston (push) Has been cancelled
CI / check system | alpine (push) Has been cancelled
CI / check system | python on macos aarch64 (push) Has been cancelled
CI / check system | python3.10 on windows (push) Has been cancelled
CI / check system | python3.10 on windows x86 (push) Has been cancelled
CI / check system | python3.13 on windows (push) Has been cancelled
CI / check system | python3.12 via chocolatey (push) Has been cancelled
CI / check system | python3.9 via pyenv (push) Has been cancelled
CI / check system | python3.13 (push) Has been cancelled
CI / check system | conda3.11 on linux (push) Has been cancelled
CI / check system | conda3.8 on linux (push) Has been cancelled
CI / check system | conda3.11 on macos (push) Has been cancelled
CI / check system | conda3.8 on macos (push) Has been cancelled
CI / check system | conda3.11 on windows (push) Has been cancelled
CI / check system | conda3.8 on windows (push) Has been cancelled
CI / check system | amazonlinux (push) Has been cancelled
CI / check system | embedded python3.10 on windows (push) Has been cancelled
CI / benchmarks (push) Has been cancelled
## Summary Closes https://github.com/astral-sh/uv/issues/9141.
This commit is contained in:
parent
14812ff79b
commit
fb3f365d10
5 changed files with 154 additions and 6 deletions
|
@ -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.
|
/// Return true if the specification does not include any requirements to install.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.requirements.is_empty() && self.source_trees.is_empty() && self.overrides.is_empty()
|
self.requirements.is_empty() && self.source_trees.is_empty() && self.overrides.is_empty()
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use memchr::memmem::Finder;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
use memchr::memmem::Finder;
|
||||||
|
use serde::Deserialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use uv_distribution_types::Index;
|
use uv_distribution_types::Index;
|
||||||
use uv_pep440::VersionSpecifiers;
|
use uv_pep440::VersionSpecifiers;
|
||||||
use uv_pep508::PackageName;
|
use uv_pep508::PackageName;
|
||||||
|
@ -264,12 +266,14 @@ pub struct Tool {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
pub struct ToolUv {
|
pub struct ToolUv {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub globals: GlobalOptions,
|
pub globals: GlobalOptions,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub top_level: ResolverInstallerOptions,
|
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 sources: Option<BTreeMap<PackageName, Sources>>,
|
||||||
pub indexes: Option<Vec<Index>>,
|
pub indexes: Option<Vec<Index>>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,7 +274,49 @@ pub(crate) async fn run(
|
||||||
.map_ok(LoweredRequirement::into_inner)
|
.map_ok(LoweredRequirement::into_inner)
|
||||||
})
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.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(
|
let result = CachedEnvironment::get_or_create(
|
||||||
EnvironmentSpecification::from(spec),
|
EnvironmentSpecification::from(spec),
|
||||||
interpreter,
|
interpreter,
|
||||||
|
|
|
@ -1497,7 +1497,7 @@ async fn run_project(
|
||||||
Pep723Item::Remote(_) => unreachable!("`uv remove` does not support remote files"),
|
Pep723Item::Remote(_) => unreachable!("`uv remove` does not support remote files"),
|
||||||
});
|
});
|
||||||
|
|
||||||
commands::remove(
|
Box::pin(commands::remove(
|
||||||
project_dir,
|
project_dir,
|
||||||
args.locked,
|
args.locked,
|
||||||
args.frozen,
|
args.frozen,
|
||||||
|
@ -1518,7 +1518,7 @@ async fn run_project(
|
||||||
no_config,
|
no_config,
|
||||||
&cache,
|
&cache,
|
||||||
printer,
|
printer,
|
||||||
)
|
))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
ProjectCommand::Tree(args) => {
|
ProjectCommand::Tree(args) => {
|
||||||
|
|
|
@ -654,6 +654,84 @@ fn run_pep723_script_metadata() -> Result<()> {
|
||||||
Ok(())
|
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.
|
/// With `managed = false`, we should avoid installing the project itself.
|
||||||
#[test]
|
#[test]
|
||||||
fn run_managed_false() -> Result<()> {
|
fn run_managed_false() -> Result<()> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue