Show build output by default in uv build (#6912)

## Summary

This is a big improvement IMO:

![Screenshot 2024-09-01 at 12 52
28 PM](https://github.com/user-attachments/assets/2d8b1370-3385-429a-9a1d-e1d44611a2b4)
This commit is contained in:
Charlie Marsh 2024-09-04 11:39:21 -04:00 committed by GitHub
parent 5d8e99045a
commit 05ed4bc11d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 719 additions and 44 deletions

1
Cargo.lock generated
View file

@ -4561,6 +4561,7 @@ dependencies = [
name = "uv-build"
version = "0.0.1"
dependencies = [
"anstream",
"anyhow",
"distribution-types",
"fs-err",

View file

@ -24,6 +24,7 @@ uv-python = { workspace = true }
uv-types = { workspace = true }
uv-virtualenv = { workspace = true }
anstream = { workspace = true }
anyhow = { workspace = true }
fs-err = { workspace = true }
indoc = { workspace = true }

View file

@ -10,6 +10,7 @@ use rustc_hash::FxHashMap;
use serde::de::{value, SeqAccess, Visitor};
use serde::{de, Deserialize, Deserializer};
use std::ffi::OsString;
use std::fmt::Write;
use std::fmt::{Display, Formatter};
use std::io;
use std::path::{Path, PathBuf};
@ -29,7 +30,7 @@ use distribution_types::Resolution;
use pep440_rs::Version;
use pep508_rs::PackageName;
use pypi_types::{Requirement, VerbatimParsedUrl};
use uv_configuration::{BuildKind, ConfigSettings};
use uv_configuration::{BuildKind, BuildOutput, ConfigSettings};
use uv_fs::{rename_with_retry, PythonExt, Simplified};
use uv_python::{Interpreter, PythonEnvironment};
use uv_types::{BuildContext, BuildIsolation, SourceBuildTrait};
@ -93,7 +94,7 @@ pub enum Error {
#[error("Failed to run `{0}`")]
CommandFailed(PathBuf, #[source] io::Error),
#[error("{message} with {exit_code}\n--- stdout:\n{stdout}\n--- stderr:\n{stderr}\n---")]
BuildBackend {
BuildBackendOutput {
message: String,
exit_code: ExitStatus,
stdout: String,
@ -101,7 +102,7 @@ pub enum Error {
},
/// Nudge the user towards installing the missing dev library
#[error("{message} with {exit_code}\n--- stdout:\n{stdout}\n--- stderr:\n{stderr}\n---")]
MissingHeader {
MissingHeaderOutput {
message: String,
exit_code: ExitStatus,
stdout: String,
@ -109,6 +110,18 @@ pub enum Error {
#[source]
missing_header_cause: MissingHeaderCause,
},
#[error("{message} with {exit_code}")]
BuildBackend {
message: String,
exit_code: ExitStatus,
},
#[error("{message} with {exit_code}")]
MissingHeader {
message: String,
exit_code: ExitStatus,
#[source]
missing_header_cause: MissingHeaderCause,
},
#[error("Failed to build PATH for build script")]
BuildScriptPath(#[source] env::JoinPathsError),
}
@ -161,6 +174,7 @@ impl Error {
fn from_command_output(
message: String,
output: &PythonRunnerOutput,
level: BuildOutput,
version_id: impl Into<String>,
) -> Self {
// In the cases I've seen it was the 5th and 3rd last line (see test case), 10 seems like a reasonable cutoff.
@ -186,27 +200,74 @@ impl Error {
});
if let Some(missing_library) = missing_library {
return Self::MissingHeader {
message,
exit_code: output.status,
stdout: output.stdout.iter().join("\n"),
stderr: output.stderr.iter().join("\n"),
missing_header_cause: MissingHeaderCause {
missing_library,
version_id: version_id.into(),
return match level {
BuildOutput::Stderr => Self::MissingHeader {
message,
exit_code: output.status,
missing_header_cause: MissingHeaderCause {
missing_library,
version_id: version_id.into(),
},
},
BuildOutput::Debug => Self::MissingHeaderOutput {
message,
exit_code: output.status,
stdout: output.stdout.iter().join("\n"),
stderr: output.stderr.iter().join("\n"),
missing_header_cause: MissingHeaderCause {
missing_library,
version_id: version_id.into(),
},
},
};
}
Self::BuildBackend {
message,
exit_code: output.status,
stdout: output.stdout.iter().join("\n"),
stderr: output.stderr.iter().join("\n"),
match level {
BuildOutput::Stderr => Self::BuildBackend {
message,
exit_code: output.status,
},
BuildOutput::Debug => Self::BuildBackendOutput {
message,
exit_code: output.status,
stdout: output.stdout.iter().join("\n"),
stderr: output.stderr.iter().join("\n"),
},
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Printer {
/// Send the build backend output to `stderr`.
Stderr,
/// Send the build backend output to `tracing`.
Debug,
}
impl From<BuildOutput> for Printer {
fn from(output: BuildOutput) -> Self {
match output {
BuildOutput::Stderr => Self::Stderr,
BuildOutput::Debug => Self::Debug,
}
}
}
impl Write for Printer {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
match self {
Self::Stderr => {
anstream::eprint!("{s}");
}
Self::Debug => {
debug!("{}", s);
}
}
Ok(())
}
}
/// A `pyproject.toml` as specified in PEP 517.
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
@ -380,6 +441,8 @@ pub struct SourceBuild {
version_id: String,
/// Whether we do a regular PEP 517 build or an PEP 660 editable build
build_kind: BuildKind,
/// Whether to send build output to `stderr` or `tracing`, etc.
level: BuildOutput,
/// Modified PATH that contains the `venv_bin`, `user_path` and `system_path` variables in that order
modified_path: OsString,
/// Environment variables to be passed in during metadata or wheel building
@ -405,6 +468,7 @@ impl SourceBuild {
build_isolation: BuildIsolation<'_>,
build_kind: BuildKind,
mut environment_variables: FxHashMap<OsString, OsString>,
level: BuildOutput,
concurrent_builds: usize,
) -> Result<Self, Error> {
let temp_dir = build_context.cache().environment()?;
@ -488,7 +552,7 @@ impl SourceBuild {
// Create the PEP 517 build environment. If build isolation is disabled, we assume the build
// environment is already setup.
let runner = PythonRunner::new(concurrent_builds);
let runner = PythonRunner::new(concurrent_builds, level);
if build_isolation.is_isolated(package_name) {
create_pep517_build_environment(
&runner,
@ -498,6 +562,7 @@ impl SourceBuild {
build_context,
&version_id,
build_kind,
level,
&config_settings,
&environment_variables,
&modified_path,
@ -513,6 +578,7 @@ impl SourceBuild {
project,
venv,
build_kind,
level,
config_settings,
metadata_directory: None,
version_id,
@ -698,6 +764,7 @@ impl SourceBuild {
return Err(Error::from_command_output(
format!("Build backend failed to determine metadata through `prepare_metadata_for_build_{}`", self.build_kind),
&output,
self.level,
&self.version_id,
));
}
@ -827,6 +894,7 @@ impl SourceBuild {
self.build_kind, self.build_kind,
),
&output,
self.level,
&self.version_id,
));
}
@ -839,6 +907,7 @@ impl SourceBuild {
self.build_kind, self.build_kind,
),
&output,
self.level,
&self.version_id,
));
}
@ -871,6 +940,7 @@ async fn create_pep517_build_environment(
build_context: &impl BuildContext,
version_id: &str,
build_kind: BuildKind,
level: BuildOutput,
config_settings: &ConfigSettings,
environment_variables: &FxHashMap<OsString, OsString>,
modified_path: &OsString,
@ -924,6 +994,7 @@ async fn create_pep517_build_environment(
return Err(Error::from_command_output(
format!("Build backend failed to determine extra requires with `build_{build_kind}()`"),
&output,
level,
version_id,
));
}
@ -935,6 +1006,7 @@ async fn create_pep517_build_environment(
"Build backend failed to read extra requires from `get_requires_for_build_{build_kind}`: {err}"
),
&output,
level,
version_id,
)
})?;
@ -946,6 +1018,7 @@ async fn create_pep517_build_environment(
"Build backend failed to return extra requires with `get_requires_for_build_{build_kind}`: {err}"
),
&output,
level,
version_id,
)
})?;
@ -985,6 +1058,7 @@ async fn create_pep517_build_environment(
#[derive(Debug)]
struct PythonRunner {
control: Semaphore,
level: BuildOutput,
}
#[derive(Debug)]
@ -995,10 +1069,11 @@ struct PythonRunnerOutput {
}
impl PythonRunner {
/// Create a `PythonRunner` with the provided concurrency limit.
fn new(concurrency: usize) -> PythonRunner {
PythonRunner {
/// Create a `PythonRunner` with the provided concurrency limit and output level.
fn new(concurrency: usize, level: BuildOutput) -> Self {
Self {
control: Semaphore::new(concurrency),
level,
}
}
@ -1019,12 +1094,13 @@ impl PythonRunner {
/// Read lines from a reader and store them in a buffer.
async fn read_from(
mut reader: tokio::io::Lines<tokio::io::BufReader<impl tokio::io::AsyncRead + Unpin>>,
mut printer: Printer,
buffer: &mut Vec<String>,
) -> io::Result<()> {
loop {
match reader.next_line().await? {
Some(line) => {
debug!("{line}");
let _ = writeln!(printer, "{line}");
buffer.push(line);
}
None => return Ok(()),
@ -1055,9 +1131,10 @@ impl PythonRunner {
let stderr_reader = tokio::io::BufReader::new(child.stderr.take().unwrap()).lines();
// Asynchronously read from the in-memory pipes.
let printer = Printer::from(self.level);
let result = tokio::join!(
read_from(stdout_reader, &mut stdout_buf),
read_from(stderr_reader, &mut stderr_buf),
read_from(stdout_reader, printer, &mut stdout_buf),
read_from(stderr_reader, printer, &mut stderr_buf),
);
match result {
(Ok(()), Ok(())) => {}
@ -1087,9 +1164,9 @@ impl PythonRunner {
mod test {
use std::process::ExitStatus;
use indoc::indoc;
use crate::{Error, PythonRunnerOutput};
use indoc::indoc;
use uv_configuration::BuildOutput;
#[test]
fn missing_header() {
@ -1120,9 +1197,10 @@ mod test {
let err = Error::from_command_output(
"Failed building wheel through setup.py".to_string(),
&output,
BuildOutput::Debug,
"pygraphviz-1.11",
);
assert!(matches!(err, Error::MissingHeader { .. }));
assert!(matches!(err, Error::MissingHeaderOutput { .. }));
// Unix uses exit status, Windows uses exit code.
let formatted = err.to_string().replace("exit status: ", "exit code: ");
insta::assert_snapshot!(formatted, @r###"
@ -1172,9 +1250,10 @@ mod test {
let err = Error::from_command_output(
"Failed building wheel through setup.py".to_string(),
&output,
BuildOutput::Debug,
"pygraphviz-1.11",
);
assert!(matches!(err, Error::MissingHeader { .. }));
assert!(matches!(err, Error::MissingHeaderOutput { .. }));
// Unix uses exit status, Windows uses exit code.
let formatted = err.to_string().replace("exit status: ", "exit code: ");
insta::assert_snapshot!(formatted, @r###"
@ -1217,9 +1296,10 @@ mod test {
let err = Error::from_command_output(
"Failed building wheel through setup.py".to_string(),
&output,
BuildOutput::Debug,
"pygraphviz-1.11",
);
assert!(matches!(err, Error::MissingHeader { .. }));
assert!(matches!(err, Error::MissingHeaderOutput { .. }));
// Unix uses exit status, Windows uses exit code.
let formatted = err.to_string().replace("exit status: ", "exit code: ");
insta::assert_snapshot!(formatted, @r###"

View file

@ -25,6 +25,14 @@ impl Display for BuildKind {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BuildOutput {
/// Send the build backend output to `stderr`.
Stderr,
/// Send the build backend output to `tracing`.
Debug,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct BuildOptions {

View file

@ -16,10 +16,10 @@ use pypi_types::Requirement;
use uv_build::{SourceBuild, SourceBuildContext};
use uv_cache::Cache;
use uv_client::RegistryClient;
use uv_configuration::Concurrency;
use uv_configuration::{
BuildKind, BuildOptions, ConfigSettings, Constraints, IndexStrategy, Reinstall, SourceStrategy,
};
use uv_configuration::{BuildOutput, Concurrency};
use uv_distribution::DistributionDatabase;
use uv_git::GitResolver;
use uv_installer::{Installer, Plan, Planner, Preparer, SitePackages};
@ -299,6 +299,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
version_id: &'data str,
dist: Option<&'data SourceDist>,
build_kind: BuildKind,
build_output: BuildOutput,
) -> Result<SourceBuild> {
let dist_name = dist.map(distribution_types::Name::name);
// Note we can only prevent builds by name for packages with names
@ -330,6 +331,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
self.build_isolation,
build_kind,
self.build_extra_env_vars.clone(),
build_output,
self.concurrency.builds,
)
.boxed_local()

View file

@ -27,7 +27,7 @@ use uv_cache::{
use uv_client::{
CacheControl, CachedClientError, Connectivity, DataWithCachePolicy, RegistryClient,
};
use uv_configuration::BuildKind;
use uv_configuration::{BuildKind, BuildOutput};
use uv_extract::hash::Hasher;
use uv_fs::{rename_with_retry, write_atomic, LockedFile};
use uv_types::{BuildContext, SourceBuildTrait};
@ -1436,6 +1436,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
} else {
BuildKind::Wheel
},
BuildOutput::Debug,
)
.await
.map_err(Error::Build)?
@ -1477,6 +1478,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
} else {
BuildKind::Wheel
},
BuildOutput::Debug,
)
.await
.map_err(Error::Build)?;

View file

@ -7,7 +7,7 @@ use distribution_types::{CachedDist, IndexLocations, InstalledDist, Resolution,
use pep508_rs::PackageName;
use pypi_types::Requirement;
use uv_cache::Cache;
use uv_configuration::{BuildKind, BuildOptions, SourceStrategy};
use uv_configuration::{BuildKind, BuildOptions, BuildOutput, SourceStrategy};
use uv_git::GitResolver;
use uv_python::PythonEnvironment;
@ -97,6 +97,7 @@ pub trait BuildContext {
version_id: &'a str,
dist: Option<&'a SourceDist>,
build_kind: BuildKind,
build_output: BuildOutput,
) -> impl Future<Output = Result<Self::SourceDistBuilder>> + 'a;
}

View file

@ -12,7 +12,7 @@ use std::path::{Path, PathBuf};
use uv_auth::store_credentials_from_url;
use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{BuildKind, Concurrency};
use uv_configuration::{BuildKind, BuildOutput, Concurrency};
use uv_dispatch::BuildDispatch;
use uv_fs::{Simplified, CWD};
use uv_python::{
@ -303,6 +303,8 @@ async fn build_impl(
let assets = match plan {
BuildPlan::SdistToWheel => {
anstream::eprintln!("{}", "Building source distribution...".bold());
// Build the sdist.
let builder = build_dispatch
.setup_build(
@ -311,6 +313,7 @@ async fn build_impl(
&version_id,
dist,
BuildKind::Sdist,
BuildOutput::Stderr,
)
.await?;
let sdist = builder.build(&output_dir).await?;
@ -331,6 +334,8 @@ async fn build_impl(
Err(err) => return Err(err.into()),
};
anstream::eprintln!("{}", "Building wheel from source distribution...".bold());
// Build a wheel from the source distribution.
let builder = build_dispatch
.setup_build(
@ -339,6 +344,7 @@ async fn build_impl(
&version_id,
dist,
BuildKind::Wheel,
BuildOutput::Stderr,
)
.await?;
let wheel = builder.build(&output_dir).await?;
@ -346,6 +352,8 @@ async fn build_impl(
BuiltDistributions::Both(output_dir.join(sdist), output_dir.join(wheel))
}
BuildPlan::Sdist => {
anstream::eprintln!("{}", "Building source distribution...".bold());
let builder = build_dispatch
.setup_build(
src.path(),
@ -353,6 +361,7 @@ async fn build_impl(
&version_id,
dist,
BuildKind::Sdist,
BuildOutput::Stderr,
)
.await?;
let sdist = builder.build(&output_dir).await?;
@ -360,6 +369,8 @@ async fn build_impl(
BuiltDistributions::Sdist(output_dir.join(sdist))
}
BuildPlan::Wheel => {
anstream::eprintln!("{}", "Building wheel...".bold());
let builder = build_dispatch
.setup_build(
src.path(),
@ -367,6 +378,7 @@ async fn build_impl(
&version_id,
dist,
BuildKind::Wheel,
BuildOutput::Stderr,
)
.await?;
let wheel = builder.build(&output_dir).await?;
@ -374,6 +386,7 @@ async fn build_impl(
BuiltDistributions::Wheel(output_dir.join(wheel))
}
BuildPlan::SdistAndWheel => {
anstream::eprintln!("{}", "Building source distribution...".bold());
let builder = build_dispatch
.setup_build(
src.path(),
@ -381,10 +394,12 @@ async fn build_impl(
&version_id,
dist,
BuildKind::Sdist,
BuildOutput::Stderr,
)
.await?;
let sdist = builder.build(&output_dir).await?;
anstream::eprintln!("{}", "Building wheel...".bold());
let builder = build_dispatch
.setup_build(
src.path(),
@ -392,6 +407,7 @@ async fn build_impl(
&version_id,
dist,
BuildKind::Wheel,
BuildOutput::Stderr,
)
.await?;
let wheel = builder.build(&output_dir).await?;
@ -399,6 +415,8 @@ async fn build_impl(
BuiltDistributions::Both(output_dir.join(&sdist), output_dir.join(&wheel))
}
BuildPlan::WheelFromSdist => {
anstream::eprintln!("{}", "Building source distribution from wheel...".bold());
// Extract the source distribution into a temporary directory.
let reader = fs_err::tokio::File::open(src.path()).await?;
let ext = SourceDistExtension::from_path(src.path()).map_err(|err| {
@ -422,6 +440,7 @@ async fn build_impl(
&version_id,
dist,
BuildKind::Wheel,
BuildOutput::Stderr,
)
.await?;
let wheel = builder.build(&output_dir).await?;

View file

@ -10,6 +10,15 @@ mod common;
#[test]
fn build() -> Result<()> {
let context = TestContext::new("3.12");
let filters = context
.filters()
.into_iter()
.chain([
(r"exit code: 1", "exit status: 1"),
(r"bdist\.[^/\\\s]+-[^/\\\s]+", "bdist.linux-x86_64"),
(r"\\\.", ""),
])
.collect::<Vec<_>>();
let project = context.temp_dir.child("project");
@ -29,14 +38,88 @@ fn build() -> Result<()> {
)?;
project.child("src").child("__init__.py").touch()?;
project.child("README").touch()?;
// Build the specified path.
uv_snapshot!(context.filters(), context.build().arg("project"), @r###"
uv_snapshot!(&filters, context.build().arg("project"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Building source distribution...
running egg_info
creating src/project.egg-info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
writing manifest file 'src/project.egg-info/SOURCES.txt'
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running sdist
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running check
creating project-0.1.0
creating project-0.1.0/src
creating project-0.1.0/src/project.egg-info
copying files to project-0.1.0...
copying README -> project-0.1.0
copying pyproject.toml -> project-0.1.0
copying src/__init__.py -> project-0.1.0/src
copying src/project.egg-info/PKG-INFO -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/dependency_links.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/requires.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/top_level.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
Writing project-0.1.0/setup.cfg
Creating tar archive
removing 'project-0.1.0' (and everything under it)
Building wheel from source distribution...
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running bdist_wheel
running build
running build_py
creating build
creating build/lib
copying src/__init__.py -> build/lib
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
copying build/lib/__init__.py -> build/bdist.linux-x86_64/wheel
running install_egg_info
Copying src/project.egg-info to build/bdist.linux-x86_64/wheel/project-0.1.0-py3.12.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/project-0.1.0.dist-info/WHEEL
creating '[TEMP_DIR]/project/dist/[TMP]/wheel' to it
adding '__init__.py'
adding 'project-0.1.0.dist-info/METADATA'
adding 'project-0.1.0.dist-info/WHEEL'
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built project/dist/project-0.1.0.tar.gz and project/dist/project-0.1.0-py3-none-any.whl
"###);
@ -52,12 +135,83 @@ fn build() -> Result<()> {
fs_err::remove_dir_all(project.child("dist"))?;
// Build the current working directory.
uv_snapshot!(context.filters(), context.build().current_dir(project.path()), @r###"
uv_snapshot!(&filters, context.build().current_dir(project.path()), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Building source distribution...
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running sdist
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running check
creating project-0.1.0
creating project-0.1.0/src
creating project-0.1.0/src/project.egg-info
copying files to project-0.1.0...
copying README -> project-0.1.0
copying pyproject.toml -> project-0.1.0
copying src/__init__.py -> project-0.1.0/src
copying src/project.egg-info/PKG-INFO -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/dependency_links.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/requires.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/top_level.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
Writing project-0.1.0/setup.cfg
Creating tar archive
removing 'project-0.1.0' (and everything under it)
Building wheel from source distribution...
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running bdist_wheel
running build
running build_py
creating build
creating build/lib
copying src/__init__.py -> build/lib
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
copying build/lib/__init__.py -> build/bdist.linux-x86_64/wheel
running install_egg_info
Copying src/project.egg-info to build/bdist.linux-x86_64/wheel/project-0.1.0-py3.12.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/project-0.1.0.dist-info/WHEEL
creating '[TEMP_DIR]/project/dist/[TMP]/wheel' to it
adding '__init__.py'
adding 'project-0.1.0.dist-info/METADATA'
adding 'project-0.1.0.dist-info/WHEEL'
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
"###);
@ -73,22 +227,95 @@ fn build() -> Result<()> {
fs_err::remove_dir_all(project.child("dist"))?;
// Error if there's nothing to build.
uv_snapshot!(context.filters(), context.build(), @r###"
uv_snapshot!(&filters, context.build(), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Building source distribution...
error: [TEMP_DIR]/ does not appear to be a Python project, as neither `pyproject.toml` nor `setup.py` are present in the directory
"###);
// Build to a specified path.
uv_snapshot!(context.filters(), context.build().arg("--out-dir").arg("out").current_dir(project.path()), @r###"
uv_snapshot!(&filters, context.build().arg("--out-dir").arg("out").current_dir(project.path()), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Building source distribution...
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running sdist
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running check
creating project-0.1.0
creating project-0.1.0/src
creating project-0.1.0/src/project.egg-info
copying files to project-0.1.0...
copying README -> project-0.1.0
copying pyproject.toml -> project-0.1.0
copying src/__init__.py -> project-0.1.0/src
copying src/project.egg-info/PKG-INFO -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/dependency_links.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/requires.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/top_level.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
Writing project-0.1.0/setup.cfg
Creating tar archive
removing 'project-0.1.0' (and everything under it)
Building wheel from source distribution...
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running bdist_wheel
running build
running build_py
creating build
creating build/lib
copying src/__init__.py -> build/lib
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
copying build/lib/__init__.py -> build/bdist.linux-x86_64/wheel
running install_egg_info
Copying src/project.egg-info to build/bdist.linux-x86_64/wheel/project-0.1.0-py3.12.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/project-0.1.0.dist-info/WHEEL
creating '[TEMP_DIR]/project/out/[TMP]/wheel' to it
adding '__init__.py'
adding 'project-0.1.0.dist-info/METADATA'
adding 'project-0.1.0.dist-info/WHEEL'
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built out/project-0.1.0.tar.gz and out/project-0.1.0-py3-none-any.whl
"###);
@ -107,6 +334,15 @@ fn build() -> Result<()> {
#[test]
fn sdist() -> Result<()> {
let context = TestContext::new("3.12");
let filters = context
.filters()
.into_iter()
.chain([
(r"exit code: 1", "exit status: 1"),
(r"bdist\.[^/\\\s]+-[^/\\\s]+", "bdist.linux-x86_64"),
(r"\\\.", ""),
])
.collect::<Vec<_>>();
let project = context.temp_dir.child("project");
@ -126,14 +362,50 @@ fn sdist() -> Result<()> {
)?;
project.child("src").child("__init__.py").touch()?;
project.child("README").touch()?;
// Build the specified path.
uv_snapshot!(context.filters(), context.build().arg("--sdist").current_dir(&project), @r###"
uv_snapshot!(&filters, context.build().arg("--sdist").current_dir(&project), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Building source distribution...
running egg_info
creating src/project.egg-info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
writing manifest file 'src/project.egg-info/SOURCES.txt'
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running sdist
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running check
creating project-0.1.0
creating project-0.1.0/src
creating project-0.1.0/src/project.egg-info
copying files to project-0.1.0...
copying README -> project-0.1.0
copying pyproject.toml -> project-0.1.0
copying src/__init__.py -> project-0.1.0/src
copying src/project.egg-info/PKG-INFO -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/dependency_links.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/requires.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/top_level.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
Writing project-0.1.0/setup.cfg
Creating tar archive
removing 'project-0.1.0' (and everything under it)
Successfully built dist/project-0.1.0.tar.gz
"###);
@ -152,6 +424,15 @@ fn sdist() -> Result<()> {
#[test]
fn wheel() -> Result<()> {
let context = TestContext::new("3.12");
let filters = context
.filters()
.into_iter()
.chain([
(r"exit code: 1", "exit status: 1"),
(r"bdist\.[^/\\\s]+-[^/\\\s]+", "bdist.linux-x86_64"),
(r"\\\.", ""),
])
.collect::<Vec<_>>();
let project = context.temp_dir.child("project");
@ -171,14 +452,55 @@ fn wheel() -> Result<()> {
)?;
project.child("src").child("__init__.py").touch()?;
project.child("README").touch()?;
// Build the specified path.
uv_snapshot!(context.filters(), context.build().arg("--wheel").current_dir(&project), @r###"
uv_snapshot!(&filters, context.build().arg("--wheel").current_dir(&project), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Building wheel...
running egg_info
creating src/project.egg-info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
writing manifest file 'src/project.egg-info/SOURCES.txt'
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running bdist_wheel
running build
running build_py
creating build
creating build/lib
copying src/__init__.py -> build/lib
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
copying build/lib/__init__.py -> build/bdist.linux-x86_64/wheel
running install_egg_info
Copying src/project.egg-info to build/bdist.linux-x86_64/wheel/project-0.1.0-py3.12.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/project-0.1.0.dist-info/WHEEL
creating '[TEMP_DIR]/project/dist/[TMP]/wheel' to it
adding '__init__.py'
adding 'project-0.1.0.dist-info/METADATA'
adding 'project-0.1.0.dist-info/WHEEL'
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/project-0.1.0-py3-none-any.whl
"###);
@ -197,6 +519,15 @@ fn wheel() -> Result<()> {
#[test]
fn sdist_wheel() -> Result<()> {
let context = TestContext::new("3.12");
let filters = context
.filters()
.into_iter()
.chain([
(r"exit code: 1", "exit status: 1"),
(r"bdist\.[^/\\\s]+-[^/\\\s]+", "bdist.linux-x86_64"),
(r"\\\.", ""),
])
.collect::<Vec<_>>();
let project = context.temp_dir.child("project");
@ -216,14 +547,88 @@ fn sdist_wheel() -> Result<()> {
)?;
project.child("src").child("__init__.py").touch()?;
project.child("README").touch()?;
// Build the specified path.
uv_snapshot!(context.filters(), context.build().arg("--sdist").arg("--wheel").current_dir(&project), @r###"
uv_snapshot!(&filters, context.build().arg("--sdist").arg("--wheel").current_dir(&project), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Building source distribution...
running egg_info
creating src/project.egg-info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
writing manifest file 'src/project.egg-info/SOURCES.txt'
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running sdist
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running check
creating project-0.1.0
creating project-0.1.0/src
creating project-0.1.0/src/project.egg-info
copying files to project-0.1.0...
copying README -> project-0.1.0
copying pyproject.toml -> project-0.1.0
copying src/__init__.py -> project-0.1.0/src
copying src/project.egg-info/PKG-INFO -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/dependency_links.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/requires.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/top_level.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
Writing project-0.1.0/setup.cfg
Creating tar archive
removing 'project-0.1.0' (and everything under it)
Building wheel...
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running bdist_wheel
running build
running build_py
creating build
creating build/lib
copying src/__init__.py -> build/lib
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
copying build/lib/__init__.py -> build/bdist.linux-x86_64/wheel
running install_egg_info
Copying src/project.egg-info to build/bdist.linux-x86_64/wheel/project-0.1.0-py3.12.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/project-0.1.0.dist-info/WHEEL
creating '[TEMP_DIR]/project/dist/[TMP]/wheel' to it
adding '__init__.py'
adding 'project-0.1.0.dist-info/METADATA'
adding 'project-0.1.0.dist-info/WHEEL'
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
"###);
@ -242,6 +647,15 @@ fn sdist_wheel() -> Result<()> {
#[test]
fn wheel_from_sdist() -> Result<()> {
let context = TestContext::new("3.12");
let filters = context
.filters()
.into_iter()
.chain([
(r"exit code: 1", "exit status: 1"),
(r"bdist\.[^/\\\s]+-[^/\\\s]+", "bdist.linux-x86_64"),
(r"\\\.", ""),
])
.collect::<Vec<_>>();
let project = context.temp_dir.child("project");
@ -261,14 +675,50 @@ fn wheel_from_sdist() -> Result<()> {
)?;
project.child("src").child("__init__.py").touch()?;
project.child("README").touch()?;
// Build the sdist.
uv_snapshot!(context.filters(), context.build().arg("--sdist").current_dir(&project), @r###"
uv_snapshot!(&filters, context.build().arg("--sdist").current_dir(&project), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Building source distribution...
running egg_info
creating src/project.egg-info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
writing manifest file 'src/project.egg-info/SOURCES.txt'
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running sdist
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running check
creating project-0.1.0
creating project-0.1.0/src
creating project-0.1.0/src/project.egg-info
copying files to project-0.1.0...
copying README -> project-0.1.0
copying pyproject.toml -> project-0.1.0
copying src/__init__.py -> project-0.1.0/src
copying src/project.egg-info/PKG-INFO -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/dependency_links.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/requires.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/top_level.txt -> project-0.1.0/src/project.egg-info
copying src/project.egg-info/SOURCES.txt -> project-0.1.0/src/project.egg-info
Writing project-0.1.0/setup.cfg
Creating tar archive
removing 'project-0.1.0' (and everything under it)
Successfully built dist/project-0.1.0.tar.gz
"###);
@ -282,7 +732,7 @@ fn wheel_from_sdist() -> Result<()> {
.assert(predicate::path::missing());
// Error if `--wheel` is not specified.
uv_snapshot!(context.filters(), context.build().arg("./dist/project-0.1.0.tar.gz").current_dir(&project), @r###"
uv_snapshot!(&filters, context.build().arg("./dist/project-0.1.0.tar.gz").current_dir(&project), @r###"
success: false
exit_code: 2
----- stdout -----
@ -292,7 +742,7 @@ fn wheel_from_sdist() -> Result<()> {
"###);
// Error if `--sdist` is specified.
uv_snapshot!(context.filters(), context.build().arg("./dist/project-0.1.0.tar.gz").arg("--sdist").current_dir(&project), @r###"
uv_snapshot!(&filters, context.build().arg("./dist/project-0.1.0.tar.gz").arg("--sdist").current_dir(&project), @r###"
success: false
exit_code: 2
----- stdout -----
@ -302,12 +752,50 @@ fn wheel_from_sdist() -> Result<()> {
"###);
// Build the wheel from the sdist.
uv_snapshot!(context.filters(), context.build().arg("./dist/project-0.1.0.tar.gz").arg("--wheel").current_dir(&project), @r###"
uv_snapshot!(&filters, context.build().arg("./dist/project-0.1.0.tar.gz").arg("--wheel").current_dir(&project), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Building source distribution from wheel...
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
running bdist_wheel
running build
running build_py
creating build
creating build/lib
copying src/__init__.py -> build/lib
running egg_info
writing src/project.egg-info/PKG-INFO
writing dependency_links to src/project.egg-info/dependency_links.txt
writing requirements to src/project.egg-info/requires.txt
writing top-level names to src/project.egg-info/top_level.txt
reading manifest file 'src/project.egg-info/SOURCES.txt'
writing manifest file 'src/project.egg-info/SOURCES.txt'
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
copying build/lib/__init__.py -> build/bdist.linux-x86_64/wheel
running install_egg_info
Copying src/project.egg-info to build/bdist.linux-x86_64/wheel/project-0.1.0-py3.12.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/project-0.1.0.dist-info/WHEEL
creating '[TEMP_DIR]/project/dist/[TMP]/wheel' to it
adding '__init__.py'
adding 'project-0.1.0.dist-info/METADATA'
adding 'project-0.1.0.dist-info/WHEEL'
adding 'project-0.1.0.dist-info/top_level.txt'
adding 'project-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dist/project-0.1.0-py3-none-any.whl
"###);
@ -321,14 +809,87 @@ fn wheel_from_sdist() -> Result<()> {
.assert(predicate::path::is_file());
// Passing a wheel is an error.
uv_snapshot!(context.filters(), context.build().arg("./dist/project-0.1.0-py3-none-any.whl").arg("--wheel").current_dir(&project), @r###"
uv_snapshot!(&filters, context.build().arg("./dist/project-0.1.0-py3-none-any.whl").arg("--wheel").current_dir(&project), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Building source distribution from wheel...
error: `dist/project-0.1.0-py3-none-any.whl` is not a valid build source. Expected to receive a source directory, or a source distribution ending in one of: `.zip`, `.tar.gz`, `.tar.bz2`, `.tar.xz`, or `.tar.zst`.
"###);
Ok(())
}
#[test]
fn fail() -> Result<()> {
let context = TestContext::new("3.12");
let filters = context
.filters()
.into_iter()
.chain([
(r"exit code: 1", "exit status: 1"),
(r"bdist\.[^/\\\s]+-[^/\\\s]+", "bdist.linux-x86_64"),
(r"\\\.", ""),
])
.collect::<Vec<_>>();
let project = context.temp_dir.child("project");
let pyproject_toml = project.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"
"#,
)?;
project.child("src").child("__init__.py").touch()?;
project.child("README").touch()?;
project.child("setup.py").write_str(
r#"
from setuptools import setup
setup(
name="project",
version="0.1.0",
packages=["project"],
install_requires=["foo==3.7.0"],
)
"#,
)?;
// Build the specified path.
uv_snapshot!(&filters, context.build().arg("project"), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Building source distribution...
Traceback (most recent call last):
File "<string>", line 14, in <module>
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 328, in get_requires_for_build_sdist
return self._get_build_requires(config_settings, requirements=[])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 295, in _get_build_requires
self.run_setup()
File "[CACHE_DIR]/builds-v0/[TMP]/build_meta.py", line 311, in run_setup
exec(code, locals())
File "<string>", line 2
from setuptools import setup
IndentationError: unexpected indent
error: Build backend failed to determine extra requires with `build_sdist()` with exit status: 1
"###);
Ok(())
}