mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-28 10:50:29 +00:00
Show a dedicated error for venvs in source trees (#15748)
A user in the support chat had an error message for `uv build` with the `uv_build` backend they didn't understand, which was caused by them having a venv in their build directory. This PR adds a dedicated error message when adding something to a distribution that looks like a venv.
This commit is contained in:
parent
9d3a3843c3
commit
12764df8b2
5 changed files with 79 additions and 3 deletions
|
|
@ -9,6 +9,7 @@ pub use settings::{BuildBackendSettings, WheelDataIncludes};
|
||||||
pub use source_dist::{build_source_dist, list_source_dist};
|
pub use source_dist::{build_source_dist, list_source_dist};
|
||||||
pub use wheel::{build_editable, build_wheel, list_wheel, metadata};
|
pub use wheel::{build_editable, build_wheel, list_wheel, metadata};
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
@ -69,6 +70,8 @@ pub enum Error {
|
||||||
/// Either an absolute path or a parent path through `..`.
|
/// Either an absolute path or a parent path through `..`.
|
||||||
#[error("The path for the data directory {} must be inside the project: `{}`", name, path.user_display())]
|
#[error("The path for the data directory {} must be inside the project: `{}`", name, path.user_display())]
|
||||||
InvalidDataRoot { name: String, path: PathBuf },
|
InvalidDataRoot { name: String, path: PathBuf },
|
||||||
|
#[error("Virtual environments must not be added to source distributions or wheels, remove the directory or exclude it from the build: {}", _0.user_display())]
|
||||||
|
VenvInSourceTree(PathBuf),
|
||||||
#[error("Inconsistent metadata between prepare and build step: `{0}`")]
|
#[error("Inconsistent metadata between prepare and build step: `{0}`")]
|
||||||
InconsistentSteps(&'static str),
|
InconsistentSteps(&'static str),
|
||||||
#[error("Failed to write to {}", _0.user_display())]
|
#[error("Failed to write to {}", _0.user_display())]
|
||||||
|
|
@ -352,6 +355,27 @@ fn module_path_from_module_name(src_root: &Path, module_name: &str) -> Result<Pa
|
||||||
Ok(module_relative)
|
Ok(module_relative)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error if we're adding a venv to a distribution.
|
||||||
|
pub(crate) fn error_on_venv(file_name: &OsStr, path: &Path) -> Result<(), Error> {
|
||||||
|
// On 64-bit Unix, `lib64` is a (compatibility) symlink to lib. If we traverse `lib64` before
|
||||||
|
// `pyvenv.cfg`, we show a generic error for symlink directories instead.
|
||||||
|
if !(file_name == "pyvenv.cfg" || file_name == "lib64") {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(parent) = path.parent() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
if parent.join("bin").join("python").is_symlink()
|
||||||
|
|| parent.join("Scripts").join("python.exe").is_file()
|
||||||
|
{
|
||||||
|
return Err(Error::VenvInSourceTree(parent.to_path_buf()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use uv_pep508::{
|
||||||
use uv_pypi_types::{Metadata23, VerbatimParsedUrl};
|
use uv_pypi_types::{Metadata23, VerbatimParsedUrl};
|
||||||
|
|
||||||
use crate::serde_verbatim::SerdeVerbatim;
|
use crate::serde_verbatim::SerdeVerbatim;
|
||||||
use crate::{BuildBackendSettings, Error};
|
use crate::{BuildBackendSettings, Error, error_on_venv};
|
||||||
|
|
||||||
/// By default, we ignore generated python files.
|
/// By default, we ignore generated python files.
|
||||||
pub(crate) const DEFAULT_EXCLUDES: &[&str] = &["__pycache__", "*.pyc", "*.pyo"];
|
pub(crate) const DEFAULT_EXCLUDES: &[&str] = &["__pycache__", "*.pyc", "*.pyo"];
|
||||||
|
|
@ -448,6 +448,8 @@ impl PyProjectToml {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_on_venv(entry.file_name(), entry.path())?;
|
||||||
|
|
||||||
debug!("License files match: `{}`", relative.user_display());
|
debug!("License files match: `{}`", relative.user_display());
|
||||||
license_files.push(relative.portable_display().to_string());
|
license_files.push(relative.portable_display().to_string());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::metadata::DEFAULT_EXCLUDES;
|
use crate::metadata::DEFAULT_EXCLUDES;
|
||||||
use crate::wheel::build_exclude_matcher;
|
use crate::wheel::build_exclude_matcher;
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildBackendSettings, DirectoryWriter, Error, FileList, ListWriter, PyProjectToml, find_roots,
|
BuildBackendSettings, DirectoryWriter, Error, FileList, ListWriter, PyProjectToml,
|
||||||
|
error_on_venv, find_roots,
|
||||||
};
|
};
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
|
|
@ -266,6 +267,8 @@ fn write_source_dist(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_on_venv(entry.file_name(), entry.path())?;
|
||||||
|
|
||||||
let entry_path = Path::new(&top_level)
|
let entry_path = Path::new(&top_level)
|
||||||
.join(relative)
|
.join(relative)
|
||||||
.portable_display()
|
.portable_display()
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ use uv_warnings::warn_user_once;
|
||||||
|
|
||||||
use crate::metadata::DEFAULT_EXCLUDES;
|
use crate::metadata::DEFAULT_EXCLUDES;
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildBackendSettings, DirectoryWriter, Error, FileList, ListWriter, PyProjectToml, find_roots,
|
BuildBackendSettings, DirectoryWriter, Error, FileList, ListWriter, PyProjectToml,
|
||||||
|
error_on_venv, find_roots,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Build a wheel from the source tree and place it in the output directory.
|
/// Build a wheel from the source tree and place it in the output directory.
|
||||||
|
|
@ -180,6 +181,8 @@ fn write_wheel(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_on_venv(entry.file_name(), entry.path())?;
|
||||||
|
|
||||||
let entry_path = entry_path.portable_display().to_string();
|
let entry_path = entry_path.portable_display().to_string();
|
||||||
debug!("Adding to wheel: {entry_path}");
|
debug!("Adding to wheel: {entry_path}");
|
||||||
wheel_writer.write_dir_entry(&entry, &entry_path)?;
|
wheel_writer.write_dir_entry(&entry, &entry_path)?;
|
||||||
|
|
@ -529,6 +532,8 @@ fn wheel_subdir_from_globs(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error_on_venv(entry.file_name(), entry.path())?;
|
||||||
|
|
||||||
let license_path = Path::new(target)
|
let license_path = Path::new(target)
|
||||||
.join(relative)
|
.join(relative)
|
||||||
.portable_display()
|
.portable_display()
|
||||||
|
|
|
||||||
|
|
@ -987,3 +987,45 @@ fn error_on_relative_data_dir_outside_project_root() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Show an explicit error when there is a venv in source tree.
|
||||||
|
#[test]
|
||||||
|
fn venv_in_source_tree() {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
context
|
||||||
|
.init()
|
||||||
|
.arg("--lib")
|
||||||
|
.arg("--name")
|
||||||
|
.arg("foo")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
context
|
||||||
|
.venv()
|
||||||
|
.arg(context.temp_dir.join("src").join("foo").join(".venv"))
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.build(), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Building source distribution (uv build backend)...
|
||||||
|
× Failed to build `[TEMP_DIR]/`
|
||||||
|
╰─▶ Virtual environments must not be added to source distributions or wheels, remove the directory or exclude it from the build: src/foo/.venv
|
||||||
|
");
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.build().arg("--wheel"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Building wheel (uv build backend)...
|
||||||
|
× Failed to build `[TEMP_DIR]/`
|
||||||
|
╰─▶ Virtual environments must not be added to source distributions or wheels, remove the directory or exclude it from the build: src/foo/.venv
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue