Reuse existing virtualenvs with --no-project (#5846)

## Summary

Closes https://github.com/astral-sh/uv/issues/5840.
This commit is contained in:
Charlie Marsh 2024-08-06 22:52:15 -04:00 committed by GitHub
parent 345c167774
commit e58c503f65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 113 additions and 21 deletions

View file

@ -158,16 +158,14 @@ pub(crate) async fn run(
None
};
let temp_dir;
// Discover and sync the base environment.
let temp_dir;
let base_interpreter = if let Some(script_interpreter) = script_interpreter {
Some(script_interpreter)
} else if no_project {
// package is `None` (`--no-project` and `--package` are marked as conflicting in Clap).
None
} else {
let project = if let Some(package) = package {
let project = if no_project {
None
} else if let Some(package) = package {
// We need a workspace, but we don't need to have a current package, we can be e.g. in
// the root of a virtual workspace and then switch into the selected package.
Some(VirtualProject::Project(
@ -199,6 +197,8 @@ pub(crate) async fn run(
}
let venv = if isolated {
debug!("Creating isolated virtual environment");
// If we're isolating the environment, use an ephemeral virtual environment as the
// base environment for the project.
let interpreter = {
@ -301,23 +301,43 @@ pub(crate) async fn run(
} else {
debug!("No project found; searching for Python interpreter");
let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
.native_tls(native_tls);
let interpreter = {
let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
.native_tls(native_tls);
let python = PythonInstallation::find_or_fetch(
python.as_deref().map(PythonRequest::parse),
// No opt-in is required for system environments, since we are not mutating it.
EnvironmentPreference::Any,
python_preference,
python_fetch,
&client_builder,
cache,
Some(&reporter),
)
.await?;
let python = PythonInstallation::find_or_fetch(
python.as_deref().map(PythonRequest::parse),
// No opt-in is required for system environments, since we are not mutating it.
EnvironmentPreference::Any,
python_preference,
python_fetch,
&client_builder,
cache,
Some(&reporter),
)
.await?;
python.into_interpreter()
python.into_interpreter()
};
if isolated {
debug!("Creating isolated virtual environment");
// If we're isolating the environment, use an ephemeral virtual environment.
temp_dir = cache.environment()?;
let venv = uv_virtualenv::create_venv(
temp_dir.path(),
interpreter,
uv_virtualenv::Prompt::None,
false,
false,
false,
)?;
venv.into_interpreter()
} else {
interpreter
}
};
Some(interpreter)

View file

@ -1014,3 +1014,75 @@ fn run_isolated_python_version() -> Result<()> {
Ok(())
}
/// Ignore the existing project when executing with `--no-project`.
#[test]
fn run_no_project() -> Result<()> {
let context = TestContext::new("3.12")
.with_filtered_python_names()
.with_filtered_virtualenv_bin()
.with_filtered_exe_suffix();
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "foo"
version = "1.0.0"
requires-python = ">=3.8"
dependencies = ["anyio"]
"#
})?;
let src = context.temp_dir.child("src").child("foo");
src.create_dir_all()?;
let init = src.child("__init__.py");
init.touch()?;
// `run` should run in the context of the project.
uv_snapshot!(context.filters(), context.run().arg("python").arg("-c").arg("import sys; print(sys.executable)"), @r###"
success: true
exit_code: 0
----- stdout -----
[VENV]/[BIN]/python
----- stderr -----
warning: `uv run` is experimental and may change without warning
Resolved 6 packages in [TIME]
Prepared 4 packages in [TIME]
Installed 4 packages in [TIME]
+ anyio==4.3.0
+ foo==1.0.0 (from file://[TEMP_DIR]/)
+ idna==3.6
+ sniffio==1.3.1
"###);
// `run --no-project` should not (but it should still run in the same environment, as it would
// if there were no project at all).
uv_snapshot!(context.filters(), context.run().arg("--no-project").arg("python").arg("-c").arg("import sys; print(sys.executable)"), @r###"
success: true
exit_code: 0
----- stdout -----
[VENV]/[BIN]/python
----- stderr -----
warning: `uv run` is experimental and may change without warning
"###);
// `run --no-project --isolated` should run in an entirely isolated environment.
uv_snapshot!(context.filters(), context.run().arg("--no-project").arg("--isolated").arg("python").arg("-c").arg("import sys; print(sys.executable)"), @r###"
success: true
exit_code: 0
----- stdout -----
[CACHE_DIR]/builds-v0/[TMP]/python
----- stderr -----
warning: `uv run` is experimental and may change without warning
"###);
Ok(())
}