diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 731fbd01f..1cd465d82 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -894,13 +894,17 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl Some(spec) => { debug!("Syncing ephemeral requirements"); + // Read the build constraints from the lock file. + let build_constraints = lock + .as_ref() + .map(|(lock, path)| lock.build_constraints(path)); + let result = CachedEnvironment::from_spec( EnvironmentSpecification::from(spec).with_lock( lock.as_ref() .map(|(lock, install_path)| (lock, install_path.as_ref())), ), - // TODO(bluefact): Respect build constraints for `uv run --with` dependencies. - Constraints::default(), + build_constraints.unwrap_or_default(), &base_interpreter, &settings, &network_settings, diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs index b7cbfd9c1..f970ed8a0 100644 --- a/crates/uv/tests/it/run.rs +++ b/crates/uv/tests/it/run.rs @@ -1266,6 +1266,81 @@ fn run_with() -> Result<()> { Ok(()) } +#[test] +fn run_with_build_constraints() -> Result<()> { + let context = TestContext::new("3.8"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str(indoc! { r#" + [project] + name = "foo" + version = "1.0.0" + requires-python = ">=3.8" + dependencies = ["anyio"] + + [tool.uv] + build-constraint-dependencies = ["setuptools==1"] + "# + })?; + + let test_script = context.temp_dir.child("main.py"); + test_script.write_str(indoc! { r" + import os + " + })?; + + // Installing requests with incompatible build constraints should fail. + uv_snapshot!(context.filters(), context.run().arg("--with").arg("requests==1.2").arg("main.py"), @r###" + success: false + exit_code: 1 + ----- stdout ----- + + ----- stderr ----- + Resolved 6 packages in [TIME] + Prepared 5 packages in [TIME] + Installed 5 packages in [TIME] + + anyio==4.3.0 + + exceptiongroup==1.2.0 + + idna==3.6 + + sniffio==1.3.1 + + typing-extensions==4.10.0 + Resolved 1 package in [TIME] + × Failed to download and build `requests==1.2.0` + ├─▶ Failed to resolve requirements from `setup.py` build + ├─▶ No solution found when resolving: `setuptools>=40.8.0` + ╰─▶ Because you require setuptools>=40.8.0 and setuptools==1, we can conclude that your requirements are unsatisfiable. + "###); + + // Change the build constraint to be compatible with `requests==1.2`. + pyproject_toml.write_str(indoc! { r#" + [project] + name = "foo" + version = "1.0.0" + requires-python = ">=3.8" + dependencies = ["anyio"] + + [tool.uv] + build-constraint-dependencies = ["setuptools>=42"] + "# + })?; + + uv_snapshot!(context.filters(), context.run().arg("--with").arg("requests==1.2").arg("main.py"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 6 packages in [TIME] + Audited 5 packages in [TIME] + Resolved 1 package in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + requests==1.2.0 + "###); + + Ok(()) +} + /// Sync all members in a workspace. #[test] fn run_in_workspace() -> Result<()> {