From 83a778daa497a07c8f45b8998186f8d814fbd5e1 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 29 Mar 2024 22:15:27 -0400 Subject: [PATCH] Respect subdirectories when reading static metadata --- crates/uv-distribution/src/source/mod.rs | 26 ++++++++++++++++++------ crates/uv/tests/pip_compile.rs | 26 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index 1ba819a4b190..f629d8237698 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -956,7 +956,7 @@ impl<'a, T: BuildContext> SourceDistCachedBuilder<'a, T> { debug!("Preparing metadata for: {source}"); // Attempt to read static metadata from the `PKG-INFO` file. - match read_pkg_info(source_root).await { + match read_pkg_info(source_root, subdirectory).await { Ok(metadata) => { debug!("Found static `PKG-INFO` for: {source}"); @@ -979,7 +979,7 @@ impl<'a, T: BuildContext> SourceDistCachedBuilder<'a, T> { } // Attempt to read static metadata from the `pyproject.toml`. - match read_pyproject_toml(source_root).await { + match read_pyproject_toml(source_root, subdirectory).await { Ok(metadata) => { debug!("Found static `pyproject.toml` for: {source}"); @@ -1112,9 +1112,16 @@ impl ExtractedSource { /// Read the [`Metadata23`] from a source distribution's `PKG-INFO` file, if it uses Metadata 2.2 /// or later _and_ none of the required fields (`Requires-Python`, `Requires-Dist`, and /// `Provides-Extra`) are marked as dynamic. -pub(crate) async fn read_pkg_info(source_tree: &Path) -> Result { +pub(crate) async fn read_pkg_info( + source_tree: &Path, + subdirectory: Option<&Path>, +) -> Result { // Read the `PKG-INFO` file. - let content = match fs::read(source_tree.join("PKG-INFO")).await { + let pkg_info = match subdirectory { + Some(subdirectory) => source_tree.join(subdirectory).join("PKG-INFO"), + None => source_tree.join("PKG-INFO"), + }; + let content = match fs::read(pkg_info).await { Ok(content) => content, Err(err) if err.kind() == std::io::ErrorKind::NotFound => { return Err(Error::MissingPkgInfo); @@ -1130,9 +1137,16 @@ pub(crate) async fn read_pkg_info(source_tree: &Path) -> Result Result { +pub(crate) async fn read_pyproject_toml( + source_tree: &Path, + subdirectory: Option<&Path>, +) -> Result { // Read the `pyproject.toml` file. - let content = match fs::read_to_string(source_tree.join("pyproject.toml")).await { + let pyproject_toml = match subdirectory { + Some(subdirectory) => source_tree.join(subdirectory).join("pyproject.toml"), + None => source_tree.join("pyproject.toml"), + }; + let content = match fs::read_to_string(pyproject_toml).await { Ok(content) => content, Err(err) if err.kind() == std::io::ErrorKind::NotFound => { return Err(Error::MissingPyprojectToml); diff --git a/crates/uv/tests/pip_compile.rs b/crates/uv/tests/pip_compile.rs index 1342b4785d86..9116ca015217 100644 --- a/crates/uv/tests/pip_compile.rs +++ b/crates/uv/tests/pip_compile.rs @@ -1378,6 +1378,32 @@ fn compile_git_mismatched_name() -> Result<()> { Ok(()) } +/// Resolve a specific Git dependency with a subdirectory, where the root directory contains a +/// static `pyproject.toml` file. +#[test] +fn compile_git_subdirectory_static_metadata() -> Result<()> { + let context = TestContext::new("3.12"); + + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("uv-public-pypackage @ git+https://github.com/astral-test/uv-workspace-pypackage#subdirectory=uv-public-pypackage")?; + + uv_snapshot!(context.compile() + .arg("requirements.in"), @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 2024-03-25T00:00:00Z requirements.in + uv-public-pypackage @ git+https://github.com/astral-test/uv-workspace-pypackage#subdirectory=uv-public-pypackage@b8c4e192456d736c27f2c84c61175c896dba8373 + + ----- stderr ----- + Resolved 1 package in [TIME] + "### + ); + + Ok(()) +} + /// Request Flask, but include a URL dependency for Werkzeug, which should avoid adding a /// duplicate dependency from `PyPI`. #[test]