Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 77 additions & 1 deletion crates/uv/tests/it/python_module.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use assert_cmd::assert::OutputAssertExt;
use assert_fs::prelude::{FileWriteStr, PathChild};
use assert_fs::prelude::{FileTouch, FileWriteStr, PathChild, PathCreateDir};
use indoc::{formatdoc, indoc};

use uv_fs::Simplified;
Expand Down Expand Up @@ -306,3 +306,79 @@ fn find_uv_bin_in_parent_of_ephemeral_environment() -> anyhow::Result<()> {

Ok(())
}

#[test]
fn find_uv_bin_user_bin() {
let context = TestContext::new("3.12")
.with_filtered_python_names()
.with_filtered_virtualenv_bin()
.with_filtered_exe_suffix()
.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()));

// Add uv to `~/.local/bin`
let bin = if cfg!(unix) {
context.home_dir.child(".local").child("bin")
} else {
context
.user_config_dir
.child("Python")
.child("Python312")
.child("Scripts")
};
bin.create_dir_all().unwrap();
bin.child(format!("uv{}", std::env::consts::EXE_SUFFIX))
.touch()
.unwrap();

// Install in a virtual environment
uv_snapshot!(context.filters(), context.pip_install()
.arg(context.workspace_root.join("scripts/packages/fake-uv")), @r"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ uv==0.1.0 (from file://[WORKSPACE]/scripts/packages/fake-uv)
"
);

// We should find the binary in the virtual environment first
uv_snapshot!(context.filters(), context.python_command()
.arg("-c")
.arg("import uv; print(uv.find_uv_bin())"), @r"
success: true
exit_code: 0
----- stdout -----
[VENV]/[BIN]/uv

----- stderr -----
"
);

// Remove the virtual environment one for some reason
fs_err::remove_file(if cfg!(unix) {
context.venv.child("bin").child("uv")
} else {
context.venv.child("Scripts").child("uv.exe")
})
.unwrap();

// We should find the binary in the bin now
uv_snapshot!(context.filters(), context.python_command()
.arg("-c")
.arg("import uv; print(uv.find_uv_bin())"), @r"
success: true
exit_code: 0
----- stdout -----
[USER_SCHEME]/[BIN]/uv

----- stderr -----
"
);
}
4 changes: 2 additions & 2 deletions python/uv/_find_uv.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ def find_uv_bin() -> str:
sysconfig.get_path("scripts"),
# The scripts directory for the base prefix
sysconfig.get_path("scripts", vars={"base": sys.base_prefix}),
# The user scheme scripts directory, e.g., `~/.local/bin`
sysconfig.get_path("scripts", scheme=_user_scheme()),
# Above the package root, e.g., from `pip install --prefix` or `uv run --with`
(
# On Windows, with module path `<prefix>/Lib/site-packages/uv`
Expand All @@ -33,6 +31,8 @@ def find_uv_bin() -> str:
# Adjacent to the package root, e.g., from `pip install --target`
# with module path `<target>/uv`
_join(_matching_parents(_module_path(), "uv"), "bin"),
# The user scheme scripts directory, e.g., `~/.local/bin`
sysconfig.get_path("scripts", scheme=_user_scheme()),
]

seen = []
Expand Down
Loading