Skip to content

Commit

Permalink
Avoid deleting the project environment directory if it is not a virtu…
Browse files Browse the repository at this point in the history
…al environment
  • Loading branch information
zanieb committed Sep 19, 2024
1 parent 0cc36a4 commit 57b5356
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
11 changes: 11 additions & 0 deletions crates/uv/src/commands/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ pub(crate) enum ProjectError {
#[error("Environment marker is empty")]
EmptyEnvironment,

#[error("Project virtual environment directory `{0}` cannot be used because it has existing, non-virtual environment content")]
InvalidProjectEnvironmentDir(PathBuf),

#[error("Failed to parse `pyproject.toml`")]
TomlParse(#[source] toml::de::Error),

Expand Down Expand Up @@ -488,6 +491,14 @@ pub(crate) async fn get_or_init_environment(
FoundInterpreter::Interpreter(interpreter) => {
let venv = workspace.venv();

// Before deleting the target directory, we confirm that it is either (1) a virtual
// environment or (2) an empty directory.
if PythonEnvironment::from_root(&venv, cache).is_err()
&& fs_err::read_dir(&venv).is_ok_and(|mut dir| dir.next().is_some())
{
return Err(ProjectError::InvalidProjectEnvironmentDir(venv));
}

// Remove the existing virtual environment if it doesn't meet the requirements.
match fs_err::remove_dir_all(&venv) {
Ok(()) => {
Expand Down
52 changes: 51 additions & 1 deletion crates/uv/tests/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1613,7 +1613,9 @@ fn convert_to_package() -> Result<()> {

#[test]
fn sync_custom_environment_path() -> Result<()> {
let mut context = TestContext::new("3.12");
let mut context = TestContext::new_with_versions(&["3.11", "3.12"])
.with_filtered_virtualenv_bin()
.with_filtered_python_names();

let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
Expand All @@ -1633,6 +1635,8 @@ fn sync_custom_environment_path() -> Result<()> {
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Creating virtualenv at: .venv
Resolved 2 packages in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
Expand Down Expand Up @@ -1733,6 +1737,52 @@ fn sync_custom_environment_path() -> Result<()> {
.child(".venv")
.assert(predicate::path::is_dir());

// If the directory already exists and is not a virtual environment we should fail with an error
fs_err::remove_dir_all(context.temp_dir.join("foo"))?;
fs_err::create_dir(context.temp_dir.join("foo"))?;
fs_err::write(context.temp_dir.join("foo").join("file"), b"")?;
uv_snapshot!(context.filters(), context.sync().env("UV_PROJECT_ENVIRONMENT", "foo"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
warning: Ignoring existing virtual environment linked to non-existent Python interpreter: foo/[BIN]/python
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
error: Project virtual environment directory `[TEMP_DIR]/foo` cannot be used because it has existing, non-virtual environment content
"###);

// But if it's just an incompatible virtual environment...
fs_err::remove_dir_all(context.temp_dir.join("foo"))?;
uv_snapshot!(context.filters(), context.venv().arg("foo").arg("--python").arg("3.11"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.11.[X] interpreter at: [PYTHON-3.11]
Creating virtualenv at: foo
Activate with: source foo/[BIN]/activate
"###);

// Even with some extraneous content...
fs_err::write(context.temp_dir.join("foo").join("file"), b"")?;

// We can delete and use it
uv_snapshot!(context.filters(), context.sync().env("UV_PROJECT_ENVIRONMENT", "foo"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Using Python 3.12.[X] interpreter at: [PYTHON-3.12]
Removed virtual environment at: foo
Creating virtualenv at: foo
Resolved 2 packages in [TIME]
Installed 1 package in [TIME]
+ iniconfig==2.0.0
"###);

Ok(())
}

Expand Down

0 comments on commit 57b5356

Please sign in to comment.