Skip to content

Commit

Permalink
Add support for Hatch's {root:uri} paths in editable installs (#2492)
Browse files Browse the repository at this point in the history
## Summary

If a package uses Hatch's `root.uri` feature, we currently error:

```toml
dependencies = [
  "black @ {root:uri}/../black_editable"
]
```

Even though we're using PEP 517 hooks to get the metadata, which
_should_ support this. The problem is that we load the full
`PyProjectToml`, which means we parse the requirements, which means we
reject what looks like a relative URL in dependencies.

Instead, we should only enforce a limited subset of `pyproject.toml`
(arguably none).

Closes #2475.
  • Loading branch information
charliermarsh authored Mar 16, 2024
1 parent 5a95f50 commit db5898b
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/uv-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ workspace = true

[dependencies]
distribution-types = { path = "../distribution-types" }
pep440_rs = { path = "../pep440-rs" }
pep508_rs = { path = "../pep508-rs" }
pypi-types = { path = "../pypi-types" }
uv-fs = { path = "../uv-fs" }
Expand Down
16 changes: 14 additions & 2 deletions crates/uv-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use fs_err as fs;
use indoc::formatdoc;
use itertools::Itertools;
use once_cell::sync::Lazy;
use pyproject_toml::Project;
use regex::Regex;
use rustc_hash::FxHashMap;
use serde::de::{value, SeqAccess, Visitor};
Expand All @@ -27,6 +26,7 @@ use tokio::sync::Mutex;
use tracing::{debug, info_span, instrument, Instrument};

use distribution_types::Resolution;
use pep440_rs::{Version, VersionSpecifiers};
use pep508_rs::Requirement;
use uv_fs::Simplified;
use uv_interpreter::{Interpreter, PythonEnvironment};
Expand Down Expand Up @@ -193,7 +193,7 @@ impl Error {
}
}

/// A pyproject.toml as specified in PEP 517
/// A pyproject.toml as specified in PEP 517.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct PyProjectToml {
Expand All @@ -203,6 +203,18 @@ pub struct PyProjectToml {
pub project: Option<Project>,
}

/// The `[project]` section of a pyproject.toml as specified in PEP 621.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct Project {
/// The name of the project
pub name: String,
/// The version of the project as supported by PEP 440
pub version: Option<Version>,
/// The Python version requirements of the project
pub requires_python: Option<VersionSpecifiers>,
}

/// The `[build-system]` section of a pyproject.toml as specified in PEP 517.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
Expand Down
36 changes: 36 additions & 0 deletions crates/uv/tests/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5150,3 +5150,39 @@ requires-python = "<=3.8"

Ok(())
}

/// Build an editable package with Hatchling's {root:uri} feature.
#[test]
fn compile_root_uri() -> Result<()> {
let context = TestContext::new("3.12");

let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("-e ${ROOT_PATH}")?;

// In addition to the standard filters, remove the temporary directory from the snapshot.
let filters: Vec<_> = [(r"file://.*/", "file://[TEMP_DIR]/")]
.into_iter()
.chain(INSTA_FILTERS.to_vec())
.collect();

let root_path = current_dir()?.join("../../scripts/editable-installs/root_editable");
uv_snapshot!(filters, context.compile()
.arg("requirements.in")
.env("ROOT_PATH", root_path.as_os_str()), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2023-11-18T12:00:00Z requirements.in
-e ${ROOT_PATH}
black @ file://[TEMP_DIR]/black_editable
# via root-editable
----- stderr -----
Built 1 editable in [TIME]
Resolved 2 packages in [TIME]
"###
);

Ok(())
}
Empty file.
22 changes: 22 additions & 0 deletions scripts/editable-installs/root_editable/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "root-editable"
description = 'A simple editable package with a {root:uri} dependency.'
readme = "README.md"
requires-python = ">=3.7"
license = "MIT"
keywords = []
authors = [
{ name = "Astral Software Inc.", email = "hey@astral.sh" },
]
classifiers = []
dependencies = [
"black @ {root:uri}/../black_editable"
]
version = "0.1.0"

[tool.hatch.metadata]
allow-direct-references = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
pass

0 comments on commit db5898b

Please sign in to comment.