Skip to content
4 changes: 2 additions & 2 deletions .github/workflows/setup-dev-drive.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ assign letter=V
Write-Output "Using Dev Drive at $Volume"
}

$Tmp = "$($Drive)\uv-tmp"
$Tmp = "$($Drive)\uv\tmp"

# Create the directory ahead of time in an attempt to avoid race-conditions
New-Item $Tmp -ItemType Directory
Expand All @@ -87,6 +87,6 @@ Write-Output `
"TEMP=$($Tmp)" `
"RUSTUP_HOME=$($Drive)/.rustup" `
"CARGO_HOME=$($Drive)/.cargo" `
"UV_WORKSPACE=$($Drive)/uv" `
"UV_WORKSPACE=$($Drive)/uv/repo" `
"PATH=$($Drive)/.cargo/bin;$env:PATH" `
>> $env:GITHUB_ENV
32 changes: 24 additions & 8 deletions crates/uv-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,17 @@ impl TestContext {
self
}

/// Add filters for the system temporary directory.
#[must_use]
pub fn with_filtered_system_tmp(mut self) -> Self {
self.filters.extend(
Self::path_patterns(std::env::temp_dir())
.into_iter()
.map(|pattern| (pattern, "[SYSTEM_TEMP_DIR]/".to_string())),
);
self
}

/// Add a filter for (bytecode) compilation file counts
#[must_use]
pub fn with_filtered_compiled_file_count(mut self) -> Self {
Expand Down Expand Up @@ -1782,14 +1793,19 @@ impl TestContext {

/// Generate an escaped regex pattern for the given path.
fn path_pattern(path: impl AsRef<Path>) -> String {
format!(
// Trim the trailing separator for cross-platform directories filters
r"{}\\?/?",
regex::escape(&path.as_ref().simplified_display().to_string())
// Make separators platform agnostic because on Windows we will display
// paths with Unix-style separators sometimes
.replace(r"\\", r"(\\|\/)")
)
let escaped = regex::escape(&path.as_ref().simplified_display().to_string())
// Make separators platform agnostic because on Windows we will display
// paths with Unix-style separators sometimes
.replace(r"\\", r"(\\|\/)");

if cfg!(windows) {
// On Windows, paths are case-insensitive. Different tools may output
// different cases for the drive letter (e.g., `D:\` vs `d:\`), so we
// make the entire pattern case-insensitive.
format!(r"(?i:{escaped})\\?/?")
} else {
format!(r"{escaped}\\?/?")
}
}

pub fn python_path(&self) -> OsString {
Expand Down
104 changes: 104 additions & 0 deletions crates/uv/tests/it/python_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,110 @@ fn find_uv_bin_user_bin() {
);
}

#[test]
fn find_uv_bin_pip_build_env() -> anyhow::Result<()> {
let vendor_url = uv_test::build_vendor_links_url();

let context = uv_test::test_context!("3.12")
.with_filtered_python_names()
.with_filtered_virtualenv_bin()
.with_filtered_exe_suffix()
.with_filtered_counts()
.with_filtered_system_tmp()
.with_filter(user_scheme_bin_filter())
// Target installs always use "bin" on all platforms. On Windows,
// `with_filtered_virtualenv_bin` only filters "Scripts", not "bin"
.with_filter((r"[\\/]bin".to_string(), "/[BIN]".to_string()))
.with_filter((r"pip-build-env-[a-z0-9_]+", "pip-build-env-[HASH]"));

// Build fake-uv into a wheel
let wheel_dir = context.temp_dir.child("wheels");
wheel_dir.create_dir_all()?;

uv_snapshot!(context.filters(), context.build()
.arg(context.workspace_root.join("test/packages/fake-uv"))
.arg("--wheel")
.arg("--out-dir")
.arg(wheel_dir.path())
.arg("--python")
.arg("3.12"), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Building wheel (uv build backend)...
Successfully built wheels/uv-0.1.0-py3-none-any.whl
"
);

// Create a project whose build requires fake-uv
let project = context.temp_dir.child("project");
project.child("project").child("__init__.py").touch()?;

project.child("pyproject.toml").write_str(&formatdoc! { r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"

[build-system]
requires = ["setuptools", "uv==0.1.0"]
build-backend = "setuptools.build_meta"
"#
})?;

// The setup.py calls find_uv_bin during the build as a side effect
project.child("setup.py").write_str(indoc! { r#"
import sys
from setuptools import setup
from uv import find_uv_bin
print("FOUND_UV=" + find_uv_bin(), file=sys.stderr)
setup()
"#
})?;

// Use `pip install` to trigger a real pip build environment.
// The `-v` flag is required so pip surfaces subprocess stderr (where
// `setup.py` prints `FOUND_UV=...`).
let result = context
.tool_run()
.arg("pip")
.arg("install")
.arg(project.path())
.arg("-v")
.arg("--no-index")
.arg("--find-links")
.arg(&vendor_url)
.arg("--find-links")
.arg(wheel_dir.path())
.assert()
.success();

// Extract the FOUND_UV= lines from stderr (where setup.py prints them).
let stderr = String::from_utf8(result.get_output().stderr.clone()).unwrap();
let found_uv_lines: String = stderr
.lines()
.filter(|line| line.contains("FOUND_UV="))
.map(str::trim)
.collect::<Vec<_>>()
.join("\n");

assert!(
!found_uv_lines.is_empty(),
"expected FOUND_UV= in pip output, got:\n{stderr}"
);

let snapshot = uv_test::apply_filters(found_uv_lines, context.filters());
insta::assert_snapshot!(snapshot, @"
FOUND_UV=[SYSTEM_TEMP_DIR]/pip-build-env-[HASH]/overlay/[BIN]/uv
FOUND_UV=[SYSTEM_TEMP_DIR]/pip-build-env-[HASH]/overlay/[BIN]/uv
FOUND_UV=[SYSTEM_TEMP_DIR]/pip-build-env-[HASH]/overlay/[BIN]/uv
");

Ok(())
}

#[test]
fn find_uv_bin_error_message() {
let mut context = uv_test::test_context!("3.12")
Expand Down
Loading