Skip to content

Commit 5a9c916

Browse files
committed
Reflect exit code in uv tool run and uv run
1 parent 3d75df6 commit 5a9c916

File tree

4 files changed

+47
-6
lines changed

4 files changed

+47
-6
lines changed

crates/uv/src/commands/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ pub(crate) enum ExitStatus {
8080

8181
/// The command failed with an unexpected error.
8282
Error,
83+
84+
/// The command's exit status is propagated from an external command.
85+
External(u8),
8386
}
8487

8588
impl From<ExitStatus> for ExitCode {
@@ -88,6 +91,7 @@ impl From<ExitStatus> for ExitCode {
8891
ExitStatus::Success => Self::from(0),
8992
ExitStatus::Failure => Self::from(1),
9093
ExitStatus::Error => Self::from(2),
94+
ExitStatus::External(code) => Self::from(code),
9195
}
9296
}
9397
}

crates/uv/src/commands/project/run.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -679,10 +679,19 @@ pub(crate) async fn run(
679679
let status = handle.wait().await.context("Child process disappeared")?;
680680

681681
// Exit based on the result of the command
682-
// TODO(zanieb): Do we want to exit with the code of the child process? Probably.
683-
if status.success() {
684-
Ok(ExitStatus::Success)
682+
if let Some(code) = status.code() {
683+
debug!("Command exited with code: {code}");
684+
if let Ok(code) = u8::try_from(code) {
685+
Ok(ExitStatus::External(code))
686+
} else {
687+
Ok(ExitStatus::Failure)
688+
}
685689
} else {
690+
#[cfg(unix)]
691+
{
692+
use std::os::unix::process::ExitStatusExt;
693+
debug!("Command exited with signal: {:?}", status.signal());
694+
}
686695
Ok(ExitStatus::Failure)
687696
}
688697
}

crates/uv/src/commands/tool/run.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,19 @@ pub(crate) async fn run(
221221
let status = handle.wait().await.context("Child process disappeared")?;
222222

223223
// Exit based on the result of the command
224-
// TODO(zanieb): Do we want to exit with the code of the child process? Probably.
225-
if status.success() {
226-
Ok(ExitStatus::Success)
224+
if let Some(code) = status.code() {
225+
debug!("Command exited with code: {code}");
226+
if let Ok(code) = u8::try_from(code) {
227+
Ok(ExitStatus::External(code))
228+
} else {
229+
Ok(ExitStatus::Failure)
230+
}
227231
} else {
232+
#[cfg(unix)]
233+
{
234+
use std::os::unix::process::ExitStatusExt;
235+
debug!("Command exited with signal: {:?}", status.signal());
236+
}
228237
Ok(ExitStatus::Failure)
229238
}
230239
}

crates/uv/tests/run.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,3 +1774,22 @@ fn run_compiled_python_file() -> Result<()> {
17741774

17751775
Ok(())
17761776
}
1777+
1778+
#[test]
1779+
fn run_exit_code() -> Result<()> {
1780+
let context = TestContext::new("3.12");
1781+
1782+
let test_script = context.temp_dir.child("script.py");
1783+
test_script.write_str(indoc! { r#"
1784+
# /// script
1785+
# requires-python = ">=3.11"
1786+
# ///
1787+
1788+
exit(42)
1789+
"#
1790+
})?;
1791+
1792+
context.run().arg("script.py").assert().code(42);
1793+
1794+
Ok(())
1795+
}

0 commit comments

Comments
 (0)