diff --git a/crates/uv-python/src/environment.rs b/crates/uv-python/src/environment.rs index 1dff7d3dcf5df..d02c2fe352bad 100644 --- a/crates/uv-python/src/environment.rs +++ b/crates/uv-python/src/environment.rs @@ -43,6 +43,7 @@ pub struct InvalidEnvironment { #[derive(Debug, Clone)] pub enum InvalidEnvironmentKind { NotDirectory, + Empty, MissingExecutable(PathBuf), } @@ -128,6 +129,7 @@ impl std::fmt::Display for InvalidEnvironmentKind { Self::MissingExecutable(path) => { write!(f, "missing Python executable at `{}`", path.user_display()) } + Self::Empty => write!(f, "directory is empty"), } } } @@ -178,6 +180,14 @@ impl PythonEnvironment { .into()); } + if venv.read_dir().is_ok_and(|mut dir| dir.next().is_none()) { + return Err(InvalidEnvironment { + path: venv, + kind: InvalidEnvironmentKind::Empty, + } + .into()); + } + let executable = virtualenv_python_executable(&venv); // Check if the executable exists before querying so we can provide a more specific error diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 054035f1477ec..3d12fba413763 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -689,6 +689,8 @@ impl ProjectInterpreter { )); } } + // If the environment is an empty directory, it's fine to use + InvalidEnvironmentKind::Empty => {} }; } Err(uv_python::Error::Query(uv_python::InterpreterError::NotFound(path))) => { @@ -807,10 +809,15 @@ pub(crate) async fn get_or_init_environment( (Ok(false), Ok(false)) => false, // If it's not a virtual environment, bail (Ok(true), Ok(false)) => { - return Err(ProjectError::InvalidProjectEnvironmentDir( - venv, - "it is not a compatible environment but cannot be recreated because it is not a virtual environment".to_string(), - )); + // Unless it's empty, in which case we just ignore it + if venv.read_dir().is_ok_and(|mut dir| dir.next().is_none()) { + false + } else { + return Err(ProjectError::InvalidProjectEnvironmentDir( + venv, + "it is not a compatible environment but cannot be recreated because it is not a virtual environment".to_string(), + )); + } } // Similarly, if we can't _tell_ if it exists we should bail (_, Err(err)) | (Err(err), _) => { diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index 3c6014912a8f1..d9537c77345dd 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -2795,13 +2795,17 @@ fn sync_empty_virtual_environment() -> Result<()> { // Running `uv sync` should work uv_snapshot!(context.filters(), context.sync(), @r###" - success: false - exit_code: 2 + success: true + exit_code: 0 ----- stdout ----- ----- stderr ----- Using CPython 3.12.[X] interpreter at: [PYTHON-3.12] - error: Project virtual environment directory `[VENV]/` cannot be used because it is not a compatible environment but cannot be recreated because it is not a virtual environment + Creating virtual environment at: .venv + Resolved 2 packages in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + iniconfig==2.0.0 "###); Ok(())