mirror of https://github.com/astral-sh/uv
Add test cases for `find_uv_bin` (#15110)
Adds test cases to unblock - https://github.com/astral-sh/uv/pull/14181 - https://github.com/astral-sh/uv/pull/14182 - https://github.com/astral-sh/uv/pull/14184 - https://github.com/astral-sh/uv/pull/14184 - https://github.com/tox-dev/pre-commit-uv/issues/70 We use a package with a symlink to the Python module to get a mock installation of uv without building (or packaging) the uv binary. This lets us test real patterns like `uv pip install --prefix` without encoding logic about where things are placed during those installs. --------- Co-authored-by: konstin <konstin@mailbox.org>
This commit is contained in:
parent
aec90f0a3c
commit
278295ef02
|
|
@ -465,6 +465,12 @@ impl TestContext {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a custom filter to the `TestContext`.
|
||||||
|
pub fn with_filter(mut self, filter: (String, String)) -> Self {
|
||||||
|
self.filters.push(filter);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear filters on `TestContext`.
|
/// Clear filters on `TestContext`.
|
||||||
pub fn clear_filters(mut self) -> Self {
|
pub fn clear_filters(mut self) -> Self {
|
||||||
self.filters.clear();
|
self.filters.clear();
|
||||||
|
|
@ -608,6 +614,26 @@ impl TestContext {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|pattern| (pattern, "[VENV]/".to_string())),
|
.map(|pattern| (pattern, "[VENV]/".to_string())),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Account for [`Simplified::user_display`] which is relative to the command working directory
|
||||||
|
if let Some(site_packages) = site_packages {
|
||||||
|
filters.push((
|
||||||
|
Self::path_pattern(
|
||||||
|
site_packages
|
||||||
|
.strip_prefix(&canonical_temp_dir)
|
||||||
|
.expect("The test site-packages directory is always in the tempdir"),
|
||||||
|
),
|
||||||
|
"[SITE_PACKAGES]/".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter Python library path differences between Windows and Unix
|
||||||
|
filters.push((
|
||||||
|
r"[\\/]lib[\\/]python\d+\.\d+[\\/]".to_string(),
|
||||||
|
"/[PYTHON-LIB]/".to_string(),
|
||||||
|
));
|
||||||
|
filters.push((r"[\\/]Lib[\\/]".to_string(), "/[PYTHON-LIB]/".to_string()));
|
||||||
|
|
||||||
for (version, executable) in &python_versions {
|
for (version, executable) in &python_versions {
|
||||||
// Add filtering for the interpreter path
|
// Add filtering for the interpreter path
|
||||||
filters.extend(
|
filters.extend(
|
||||||
|
|
@ -680,18 +706,6 @@ impl TestContext {
|
||||||
"Activate with: source $1/[BIN]/activate".to_string(),
|
"Activate with: source $1/[BIN]/activate".to_string(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Account for [`Simplified::user_display`] which is relative to the command working directory
|
|
||||||
if let Some(site_packages) = site_packages {
|
|
||||||
filters.push((
|
|
||||||
Self::path_pattern(
|
|
||||||
site_packages
|
|
||||||
.strip_prefix(&canonical_temp_dir)
|
|
||||||
.expect("The test site-packages directory is always in the tempdir"),
|
|
||||||
),
|
|
||||||
"[SITE_PACKAGES]/".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter non-deterministic temporary directory names
|
// Filter non-deterministic temporary directory names
|
||||||
// Note we apply this _after_ all the full paths to avoid breaking their matching
|
// Note we apply this _after_ all the full paths to avoid breaking their matching
|
||||||
filters.push((r"(\\|\/)\.tmp.*(\\|\/)".to_string(), "/[TMP]/".to_string()));
|
filters.push((r"(\\|\/)\.tmp.*(\\|\/)".to_string(), "/[TMP]/".to_string()));
|
||||||
|
|
@ -727,6 +741,11 @@ impl TestContext {
|
||||||
r"environments-v(\d+)[\\/](\w+)-[a-z0-9]+".to_string(),
|
r"environments-v(\d+)[\\/](\w+)-[a-z0-9]+".to_string(),
|
||||||
"environments-v$1/$2-[HASH]".to_string(),
|
"environments-v$1/$2-[HASH]".to_string(),
|
||||||
));
|
));
|
||||||
|
// Filter archive hashes
|
||||||
|
filters.push((
|
||||||
|
r"archive-v(\d+)[\\/][A-Za-z0-9\-\_]+".to_string(),
|
||||||
|
"archive-v$1/[HASH]".to_string(),
|
||||||
|
));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
root: ChildPath::new(root.path()),
|
root: ChildPath::new(root.path()),
|
||||||
|
|
@ -748,7 +767,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a uv command for testing.
|
/// Create a uv command for testing.
|
||||||
pub fn command(&self) -> Command {
|
pub fn command(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
@ -826,6 +845,20 @@ impl TestContext {
|
||||||
.env_remove(EnvVars::UV_TOOL_BIN_DIR)
|
.env_remove(EnvVars::UV_TOOL_BIN_DIR)
|
||||||
.env_remove(EnvVars::XDG_CONFIG_HOME)
|
.env_remove(EnvVars::XDG_CONFIG_HOME)
|
||||||
.env_remove(EnvVars::XDG_DATA_HOME)
|
.env_remove(EnvVars::XDG_DATA_HOME)
|
||||||
|
// I believe the intent of all tests is that they are run outside the
|
||||||
|
// context of an existing git repository. And when they aren't, state
|
||||||
|
// from the parent git repository can bleed into the behavior of `uv
|
||||||
|
// init` in a way that makes it difficult to test consistently. By
|
||||||
|
// setting GIT_CEILING_DIRECTORIES, we specifically prevent git from
|
||||||
|
// climbing up past the root of our test directory to look for any
|
||||||
|
// other git repos.
|
||||||
|
//
|
||||||
|
// If one wants to write a test specifically targeting uv within a
|
||||||
|
// pre-existing git repository, then the test should make the parent
|
||||||
|
// git repo explicitly. The GIT_CEILING_DIRECTORIES here shouldn't
|
||||||
|
// impact it, since it only prevents git from discovering repositories
|
||||||
|
// at or above the root.
|
||||||
|
.env(EnvVars::GIT_CEILING_DIRECTORIES, self.root.path())
|
||||||
.current_dir(self.temp_dir.path());
|
.current_dir(self.temp_dir.path());
|
||||||
|
|
||||||
for (key, value) in &self.extra_env {
|
for (key, value) in &self.extra_env {
|
||||||
|
|
@ -844,7 +877,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `pip compile` command for testing.
|
/// Create a `pip compile` command for testing.
|
||||||
pub fn pip_compile(&self) -> Command {
|
pub fn pip_compile(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("compile");
|
command.arg("pip").arg("compile");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -852,14 +885,14 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `pip compile` command for testing.
|
/// Create a `pip compile` command for testing.
|
||||||
pub fn pip_sync(&self) -> Command {
|
pub fn pip_sync(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("sync");
|
command.arg("pip").arg("sync");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pip_show(&self) -> Command {
|
pub fn pip_show(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("show");
|
command.arg("pip").arg("show");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -867,7 +900,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `pip freeze` command with options shared across scenarios.
|
/// Create a `pip freeze` command with options shared across scenarios.
|
||||||
pub fn pip_freeze(&self) -> Command {
|
pub fn pip_freeze(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("freeze");
|
command.arg("pip").arg("freeze");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -875,14 +908,14 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `pip check` command with options shared across scenarios.
|
/// Create a `pip check` command with options shared across scenarios.
|
||||||
pub fn pip_check(&self) -> Command {
|
pub fn pip_check(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("check");
|
command.arg("pip").arg("check");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pip_list(&self) -> Command {
|
pub fn pip_list(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("list");
|
command.arg("pip").arg("list");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -890,7 +923,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv venv` command
|
/// Create a `uv venv` command
|
||||||
pub fn venv(&self) -> Command {
|
pub fn venv(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("venv");
|
command.arg("venv");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -898,7 +931,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `pip install` command with options shared across scenarios.
|
/// Create a `pip install` command with options shared across scenarios.
|
||||||
pub fn pip_install(&self) -> Command {
|
pub fn pip_install(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("install");
|
command.arg("pip").arg("install");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -906,7 +939,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `pip uninstall` command with options shared across scenarios.
|
/// Create a `pip uninstall` command with options shared across scenarios.
|
||||||
pub fn pip_uninstall(&self) -> Command {
|
pub fn pip_uninstall(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("uninstall");
|
command.arg("pip").arg("uninstall");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -914,7 +947,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `pip tree` command for testing.
|
/// Create a `pip tree` command for testing.
|
||||||
pub fn pip_tree(&self) -> Command {
|
pub fn pip_tree(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("pip").arg("tree");
|
command.arg("pip").arg("tree");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -923,7 +956,7 @@ impl TestContext {
|
||||||
/// Create a `uv help` command with options shared across scenarios.
|
/// Create a `uv help` command with options shared across scenarios.
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
pub fn help(&self) -> Command {
|
pub fn help(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("help");
|
command.arg("help");
|
||||||
command.env_remove(EnvVars::UV_CACHE_DIR);
|
command.env_remove(EnvVars::UV_CACHE_DIR);
|
||||||
command
|
command
|
||||||
|
|
@ -932,7 +965,7 @@ impl TestContext {
|
||||||
/// Create a `uv init` command with options shared across scenarios and
|
/// Create a `uv init` command with options shared across scenarios and
|
||||||
/// isolated from any git repository that may exist in a parent directory.
|
/// isolated from any git repository that may exist in a parent directory.
|
||||||
pub fn init(&self) -> Command {
|
pub fn init(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("init");
|
command.arg("init");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -940,7 +973,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv sync` command with options shared across scenarios.
|
/// Create a `uv sync` command with options shared across scenarios.
|
||||||
pub fn sync(&self) -> Command {
|
pub fn sync(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("sync");
|
command.arg("sync");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -948,7 +981,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv lock` command with options shared across scenarios.
|
/// Create a `uv lock` command with options shared across scenarios.
|
||||||
pub fn lock(&self) -> Command {
|
pub fn lock(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("lock");
|
command.arg("lock");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -956,7 +989,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv export` command with options shared across scenarios.
|
/// Create a `uv export` command with options shared across scenarios.
|
||||||
pub fn export(&self) -> Command {
|
pub fn export(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("export");
|
command.arg("export");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -964,43 +997,44 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv build` command with options shared across scenarios.
|
/// Create a `uv build` command with options shared across scenarios.
|
||||||
pub fn build(&self) -> Command {
|
pub fn build(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("build");
|
command.arg("build");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(&self) -> Command {
|
pub fn version(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("version");
|
command.arg("version");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn self_version(&self) -> Command {
|
pub fn self_version(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("self").arg("version");
|
command.arg("self").arg("version");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn self_update(&self) -> Command {
|
pub fn self_update(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("self").arg("update");
|
command.arg("self").arg("update");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `uv publish` command with options shared across scenarios.
|
/// Create a `uv publish` command with options shared across scenarios.
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
pub fn publish(&self) -> Command {
|
pub fn publish(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("publish");
|
command.arg("publish");
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `uv python find` command with options shared across scenarios.
|
/// Create a `uv python find` command with options shared across scenarios.
|
||||||
pub fn python_find(&self) -> Command {
|
pub fn python_find(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command
|
command
|
||||||
.arg("python")
|
.arg("python")
|
||||||
.arg("find")
|
.arg("find")
|
||||||
|
|
@ -1012,7 +1046,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv python list` command with options shared across scenarios.
|
/// Create a `uv python list` command with options shared across scenarios.
|
||||||
pub fn python_list(&self) -> Command {
|
pub fn python_list(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command
|
command
|
||||||
.arg("python")
|
.arg("python")
|
||||||
.arg("list")
|
.arg("list")
|
||||||
|
|
@ -1023,7 +1057,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv python install` command with options shared across scenarios.
|
/// Create a `uv python install` command with options shared across scenarios.
|
||||||
pub fn python_install(&self) -> Command {
|
pub fn python_install(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command.arg("python").arg("install");
|
command.arg("python").arg("install");
|
||||||
command
|
command
|
||||||
|
|
@ -1031,7 +1065,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv python uninstall` command with options shared across scenarios.
|
/// Create a `uv python uninstall` command with options shared across scenarios.
|
||||||
pub fn python_uninstall(&self) -> Command {
|
pub fn python_uninstall(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command.arg("python").arg("uninstall");
|
command.arg("python").arg("uninstall");
|
||||||
command
|
command
|
||||||
|
|
@ -1039,7 +1073,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv python upgrade` command with options shared across scenarios.
|
/// Create a `uv python upgrade` command with options shared across scenarios.
|
||||||
pub fn python_upgrade(&self) -> Command {
|
pub fn python_upgrade(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command.arg("python").arg("upgrade");
|
command.arg("python").arg("upgrade");
|
||||||
command
|
command
|
||||||
|
|
@ -1047,7 +1081,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv python pin` command with options shared across scenarios.
|
/// Create a `uv python pin` command with options shared across scenarios.
|
||||||
pub fn python_pin(&self) -> Command {
|
pub fn python_pin(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("python").arg("pin");
|
command.arg("python").arg("pin");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -1055,7 +1089,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv python dir` command with options shared across scenarios.
|
/// Create a `uv python dir` command with options shared across scenarios.
|
||||||
pub fn python_dir(&self) -> Command {
|
pub fn python_dir(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("python").arg("dir");
|
command.arg("python").arg("dir");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -1063,7 +1097,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv run` command with options shared across scenarios.
|
/// Create a `uv run` command with options shared across scenarios.
|
||||||
pub fn run(&self) -> Command {
|
pub fn run(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("run").env(EnvVars::UV_SHOW_RESOLUTION, "1");
|
command.arg("run").env(EnvVars::UV_SHOW_RESOLUTION, "1");
|
||||||
self.add_shared_options(&mut command, true);
|
self.add_shared_options(&mut command, true);
|
||||||
command
|
command
|
||||||
|
|
@ -1071,7 +1105,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv tool run` command with options shared across scenarios.
|
/// Create a `uv tool run` command with options shared across scenarios.
|
||||||
pub fn tool_run(&self) -> Command {
|
pub fn tool_run(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command
|
command
|
||||||
.arg("tool")
|
.arg("tool")
|
||||||
.arg("run")
|
.arg("run")
|
||||||
|
|
@ -1082,7 +1116,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv upgrade run` command with options shared across scenarios.
|
/// Create a `uv upgrade run` command with options shared across scenarios.
|
||||||
pub fn tool_upgrade(&self) -> Command {
|
pub fn tool_upgrade(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("tool").arg("upgrade");
|
command.arg("tool").arg("upgrade");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1090,7 +1124,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv tool install` command with options shared across scenarios.
|
/// Create a `uv tool install` command with options shared across scenarios.
|
||||||
pub fn tool_install(&self) -> Command {
|
pub fn tool_install(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("tool").arg("install");
|
command.arg("tool").arg("install");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1098,7 +1132,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv tool list` command with options shared across scenarios.
|
/// Create a `uv tool list` command with options shared across scenarios.
|
||||||
pub fn tool_list(&self) -> Command {
|
pub fn tool_list(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("tool").arg("list");
|
command.arg("tool").arg("list");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1106,7 +1140,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv tool dir` command with options shared across scenarios.
|
/// Create a `uv tool dir` command with options shared across scenarios.
|
||||||
pub fn tool_dir(&self) -> Command {
|
pub fn tool_dir(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("tool").arg("dir");
|
command.arg("tool").arg("dir");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1114,7 +1148,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv tool uninstall` command with options shared across scenarios.
|
/// Create a `uv tool uninstall` command with options shared across scenarios.
|
||||||
pub fn tool_uninstall(&self) -> Command {
|
pub fn tool_uninstall(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("tool").arg("uninstall");
|
command.arg("tool").arg("uninstall");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1122,7 +1156,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv add` command for the given requirements.
|
/// Create a `uv add` command for the given requirements.
|
||||||
pub fn add(&self) -> Command {
|
pub fn add(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("add");
|
command.arg("add");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1130,7 +1164,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv remove` command for the given requirements.
|
/// Create a `uv remove` command for the given requirements.
|
||||||
pub fn remove(&self) -> Command {
|
pub fn remove(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("remove");
|
command.arg("remove");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1138,7 +1172,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv tree` command with options shared across scenarios.
|
/// Create a `uv tree` command with options shared across scenarios.
|
||||||
pub fn tree(&self) -> Command {
|
pub fn tree(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("tree");
|
command.arg("tree");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1146,7 +1180,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv cache clean` command.
|
/// Create a `uv cache clean` command.
|
||||||
pub fn clean(&self) -> Command {
|
pub fn clean(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("cache").arg("clean");
|
command.arg("cache").arg("clean");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1154,7 +1188,7 @@ impl TestContext {
|
||||||
|
|
||||||
/// Create a `uv cache prune` command.
|
/// Create a `uv cache prune` command.
|
||||||
pub fn prune(&self) -> Command {
|
pub fn prune(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("cache").arg("prune");
|
command.arg("cache").arg("prune");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1164,7 +1198,7 @@ impl TestContext {
|
||||||
///
|
///
|
||||||
/// Note that this command is hidden and only invoking it through a build frontend is supported.
|
/// Note that this command is hidden and only invoking it through a build frontend is supported.
|
||||||
pub fn build_backend(&self) -> Command {
|
pub fn build_backend(&self) -> Command {
|
||||||
let mut command = self.new_command();
|
let mut command = Self::new_command();
|
||||||
command.arg("build-backend");
|
command.arg("build-backend");
|
||||||
self.add_shared_options(&mut command, false);
|
self.add_shared_options(&mut command, false);
|
||||||
command
|
command
|
||||||
|
|
@ -1182,13 +1216,29 @@ impl TestContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn python_command(&self) -> Command {
|
pub fn python_command(&self) -> Command {
|
||||||
let mut command = self.new_command_with(&self.interpreter());
|
let mut interpreter = self.interpreter();
|
||||||
|
|
||||||
|
// If there's not a virtual environment, use the first Python interpreter in the context
|
||||||
|
if !interpreter.exists() {
|
||||||
|
interpreter.clone_from(
|
||||||
|
&self
|
||||||
|
.python_versions
|
||||||
|
.first()
|
||||||
|
.expect("At least one Python version is required")
|
||||||
|
.1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut command = Self::new_command_with(&interpreter);
|
||||||
command
|
command
|
||||||
// Our tests change files in <1s, so we must disable CPython bytecode caching or we'll get stale files
|
// Our tests change files in <1s, so we must disable CPython bytecode caching or we'll get stale files
|
||||||
// https://github.com/python/cpython/issues/75953
|
// https://github.com/python/cpython/issues/75953
|
||||||
.arg("-B")
|
.arg("-B")
|
||||||
// Python on windows
|
// Python on windows
|
||||||
.env(EnvVars::PYTHONUTF8, "1");
|
.env(EnvVars::PYTHONUTF8, "1");
|
||||||
|
|
||||||
|
self.add_shared_env(&mut command, false);
|
||||||
|
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1385,29 +1435,14 @@ impl TestContext {
|
||||||
|
|
||||||
/// Creates a new `Command` that is intended to be suitable for use in
|
/// Creates a new `Command` that is intended to be suitable for use in
|
||||||
/// all tests.
|
/// all tests.
|
||||||
fn new_command(&self) -> Command {
|
fn new_command() -> Command {
|
||||||
self.new_command_with(&get_bin())
|
Self::new_command_with(&get_bin())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `Command` that is intended to be suitable for use in
|
/// Creates a new `Command` that is intended to be suitable for use in
|
||||||
/// all tests, but with the given binary.
|
/// all tests, but with the given binary.
|
||||||
fn new_command_with(&self, bin: &Path) -> Command {
|
fn new_command_with(bin: &Path) -> Command {
|
||||||
let mut command = Command::new(bin);
|
Command::new(bin)
|
||||||
// I believe the intent of all tests is that they are run outside the
|
|
||||||
// context of an existing git repository. And when they aren't, state
|
|
||||||
// from the parent git repository can bleed into the behavior of `uv
|
|
||||||
// init` in a way that makes it difficult to test consistently. By
|
|
||||||
// setting GIT_CEILING_DIRECTORIES, we specifically prevent git from
|
|
||||||
// climbing up past the root of our test directory to look for any
|
|
||||||
// other git repos.
|
|
||||||
//
|
|
||||||
// If one wants to write a test specifically targeting uv within a
|
|
||||||
// pre-existing git repository, then the test should make the parent
|
|
||||||
// git repo explicitly. The GIT_CEILING_DIRECTORIES here shouldn't
|
|
||||||
// impact it, since it only prevents git from discovering repositories
|
|
||||||
// at or above the root.
|
|
||||||
command.env(EnvVars::GIT_CEILING_DIRECTORIES, self.root.path());
|
|
||||||
command
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,9 @@ mod python_find;
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
mod python_list;
|
mod python_list;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "python", feature = "pypi"))]
|
||||||
|
mod python_module;
|
||||||
|
|
||||||
#[cfg(feature = "python-managed")]
|
#[cfg(feature = "python-managed")]
|
||||||
mod python_install;
|
mod python_install;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,14 +153,14 @@ fn missing_record() -> Result<()> {
|
||||||
fs_err::remove_file(dist_info.join("RECORD"))?;
|
fs_err::remove_file(dist_info.join("RECORD"))?;
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.pip_uninstall()
|
uv_snapshot!(context.filters(), context.pip_uninstall()
|
||||||
.arg("MarkupSafe"), @r###"
|
.arg("MarkupSafe"), @r"
|
||||||
success: false
|
success: false
|
||||||
exit_code: 2
|
exit_code: 2
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
error: Cannot uninstall package; `RECORD` file not found at: [SITE_PACKAGES]/MarkupSafe-2.1.3.dist-info/RECORD
|
error: Cannot uninstall package; `RECORD` file not found at: [SITE_PACKAGES]/MarkupSafe-2.1.3.dist-info/RECORD
|
||||||
"###
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,309 @@
|
||||||
|
use assert_cmd::assert::OutputAssertExt;
|
||||||
|
use assert_fs::prelude::{FileWriteStr, PathChild};
|
||||||
|
use indoc::{formatdoc, indoc};
|
||||||
|
|
||||||
|
use uv_fs::Simplified;
|
||||||
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
|
use crate::common::{TestContext, site_packages_path, uv_snapshot};
|
||||||
|
|
||||||
|
/// Filter the user scheme, which differs between Windows and Unix.
|
||||||
|
fn user_scheme_bin_filter() -> (String, String) {
|
||||||
|
if cfg!(windows) {
|
||||||
|
(
|
||||||
|
r"\[USER_CONFIG_DIR\][\\/]Python[\\/]Python\d+".to_string(),
|
||||||
|
"[USER_SCHEME]".to_string(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(r"\[HOME\]/\.local".to_string(), "[USER_SCHEME]".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_uv_bin_venv() {
|
||||||
|
let context = TestContext::new("3.12")
|
||||||
|
.with_filtered_python_names()
|
||||||
|
.with_filtered_virtualenv_bin()
|
||||||
|
.with_filtered_exe_suffix()
|
||||||
|
.with_filter(user_scheme_bin_filter());
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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 -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_uv_bin_target() {
|
||||||
|
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()));
|
||||||
|
|
||||||
|
// Install in a target directory
|
||||||
|
uv_snapshot!(context.filters(), context.pip_install()
|
||||||
|
.arg(context.workspace_root.join("scripts/packages/fake-uv"))
|
||||||
|
.arg("--target")
|
||||||
|
.arg("target"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using CPython 3.12.[X] interpreter at: .venv/[BIN]/[PYTHON]
|
||||||
|
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 target directory
|
||||||
|
uv_snapshot!(context.filters(), context.python_command()
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import uv; print(uv.find_uv_bin())")
|
||||||
|
.env(EnvVars::PYTHONPATH, context.temp_dir.child("target").path()), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[TEMP_DIR]/target/[BIN]/uv
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_uv_bin_prefix() {
|
||||||
|
let context = TestContext::new("3.12")
|
||||||
|
.with_filtered_python_names()
|
||||||
|
.with_filtered_virtualenv_bin()
|
||||||
|
.with_filtered_exe_suffix()
|
||||||
|
.with_filter(user_scheme_bin_filter());
|
||||||
|
|
||||||
|
// Install in a prefix directory
|
||||||
|
let prefix = context.temp_dir.child("prefix");
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.pip_install()
|
||||||
|
.arg(context.workspace_root.join("scripts/packages/fake-uv"))
|
||||||
|
.arg("--prefix")
|
||||||
|
.arg(prefix.path()), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using CPython 3.12.[X] interpreter at: .venv/[BIN]/[PYTHON]
|
||||||
|
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 prefix directory
|
||||||
|
uv_snapshot!(context.filters(), context.python_command()
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import uv; print(uv.find_uv_bin())")
|
||||||
|
.env(
|
||||||
|
EnvVars::PYTHONPATH,
|
||||||
|
site_packages_path(&context.temp_dir.join("prefix"), "python3.12"),
|
||||||
|
), @r#"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<string>", line 1, in <module>
|
||||||
|
File "[TEMP_DIR]/prefix/[PYTHON-LIB]/site-packages/uv/_find_uv.py", line 36, in find_uv_bin
|
||||||
|
raise FileNotFoundError(path)
|
||||||
|
FileNotFoundError: [USER_SCHEME]/[BIN]/uv
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_uv_bin_base_prefix() {
|
||||||
|
let context = TestContext::new("3.12")
|
||||||
|
.with_filtered_python_names()
|
||||||
|
.with_filtered_virtualenv_bin()
|
||||||
|
.with_filtered_exe_suffix()
|
||||||
|
.with_filter(user_scheme_bin_filter());
|
||||||
|
|
||||||
|
// Test base prefix fallback by mutating sys.base_prefix
|
||||||
|
// First, create a "base" environment with fake-uv installed
|
||||||
|
let base_venv = context.temp_dir.child("base-venv");
|
||||||
|
context.venv().arg(base_venv.path()).assert().success();
|
||||||
|
|
||||||
|
// Install fake-uv in the "base" venv
|
||||||
|
uv_snapshot!(context.filters(), context.pip_install()
|
||||||
|
.arg("--python")
|
||||||
|
.arg(base_venv.path())
|
||||||
|
.arg(context.workspace_root.join("scripts/packages/fake-uv")), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using Python 3.12.[X] environment at: base-venv
|
||||||
|
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)
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
context.venv().assert().success();
|
||||||
|
|
||||||
|
// Mutate `base_prefix` to simulate lookup in a system Python installation
|
||||||
|
uv_snapshot!(context.filters(), context.python_command()
|
||||||
|
.arg("-c")
|
||||||
|
.arg(format!(r#"import sys, uv; sys.base_prefix = "{}"; print(uv.find_uv_bin())"#, base_venv.path().portable_display()))
|
||||||
|
.env(EnvVars::PYTHONPATH, site_packages_path(base_venv.path(), "python3.12")), @r#"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<string>", line 1, in <module>
|
||||||
|
File "[TEMP_DIR]/base-venv/[PYTHON-LIB]/site-packages/uv/_find_uv.py", line 36, in find_uv_bin
|
||||||
|
raise FileNotFoundError(path)
|
||||||
|
FileNotFoundError: [USER_SCHEME]/[BIN]/uv
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_uv_bin_in_ephemeral_environment() -> anyhow::Result<()> {
|
||||||
|
let context = TestContext::new("3.12")
|
||||||
|
.with_filtered_python_names()
|
||||||
|
.with_filtered_virtualenv_bin()
|
||||||
|
.with_filtered_exe_suffix()
|
||||||
|
.with_filter(user_scheme_bin_filter());
|
||||||
|
|
||||||
|
// Create a minimal pyproject.toml
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "test-project"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = []
|
||||||
|
"#
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// We should find the binary in an ephemeral `--with` environment
|
||||||
|
uv_snapshot!(context.filters(), context.run()
|
||||||
|
.arg("--with")
|
||||||
|
.arg(context.workspace_root.join("scripts/packages/fake-uv"))
|
||||||
|
.arg("python")
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import uv; print(uv.find_uv_bin())"), @r#"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Audited in [TIME]
|
||||||
|
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)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<string>", line 1, in <module>
|
||||||
|
File "[CACHE_DIR]/archive-v0/[HASH]/[PYTHON-LIB]/site-packages/uv/_find_uv.py", line 36, in find_uv_bin
|
||||||
|
raise FileNotFoundError(path)
|
||||||
|
FileNotFoundError: [USER_SCHEME]/[BIN]/uv
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn find_uv_bin_in_parent_of_ephemeral_environment() -> anyhow::Result<()> {
|
||||||
|
let context = TestContext::new("3.12")
|
||||||
|
.with_filtered_python_names()
|
||||||
|
.with_filtered_virtualenv_bin()
|
||||||
|
.with_filtered_exe_suffix()
|
||||||
|
.with_filter(user_scheme_bin_filter());
|
||||||
|
|
||||||
|
// Add the fake-uv package as a dependency
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(&formatdoc! { r#"
|
||||||
|
[project]
|
||||||
|
name = "test-project"
|
||||||
|
version = "1.0.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
dependencies = ["uv"]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
uv = {{ path = "{}" }}
|
||||||
|
"#,
|
||||||
|
context.workspace_root.join("scripts/packages/fake-uv").portable_display()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// When running in an ephemeral environment, we should find the binary in the project
|
||||||
|
// environment
|
||||||
|
uv_snapshot!(context.filters(), context.run()
|
||||||
|
.arg("--with")
|
||||||
|
.arg("anyio")
|
||||||
|
.arg("python")
|
||||||
|
.arg("-c")
|
||||||
|
.arg("import uv; print(uv.find_uv_bin())"),
|
||||||
|
@r#"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ uv==0.1.0 (from file://[WORKSPACE]/scripts/packages/fake-uv)
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Prepared 3 packages in [TIME]
|
||||||
|
Installed 3 packages in [TIME]
|
||||||
|
+ anyio==4.3.0
|
||||||
|
+ idna==3.6
|
||||||
|
+ sniffio==1.3.1
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<string>", line 1, in <module>
|
||||||
|
File "[SITE_PACKAGES]/uv/_find_uv.py", line 36, in find_uv_bin
|
||||||
|
raise FileNotFoundError(path)
|
||||||
|
FileNotFoundError: [USER_SCHEME]/[BIN]/uv
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
This fake uv package symlinks the Python module of uv in-tree and has a fake `uv` binary, allowing
|
||||||
|
testing of the Python module behaviors. Consumers can replace the `uv` binary with a debug binary or
|
||||||
|
similar if they need it to actually work.
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
[project]
|
||||||
|
name = "uv"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
|
||||||
|
[tool.uv.build-backend.data]
|
||||||
|
scripts = "scripts"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["uv_build>=0.8.0,<0.9"]
|
||||||
|
build-backend = "uv_build"
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
echo "This is a fake uv binary"
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
This is a fake uv binary
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
../../../python/
|
||||||
Loading…
Reference in New Issue