Skip to content

Commit d9fba3d

Browse files
committed
Fix exception handling for Base.run()
1 parent 4c2f7f4 commit d9fba3d

File tree

3 files changed

+21
-14
lines changed

3 files changed

+21
-14
lines changed

docs/src/changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ Changelog](https://keepachangelog.com).
2424
### Fixed
2525

2626
- Improved handling of possible errors in [`Base.readdir()`](@ref) ([#20]).
27+
- Fixed exception handling for [`Base.run()`](@ref), now it throws a
28+
[`SshProcessFailedException`](@ref) or [`LibSSHException`](@ref) on command
29+
failure instead of a plain `TaskFailedException` ([#25]).
2730

2831
## [v0.6.0] - 2024-10-11
2932

src/channel.jl

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -493,8 +493,18 @@ $(TYPEDSIGNATURES)
493493
function Base.wait(process::SshProcess)
494494
try
495495
wait(process._task)
496-
catch ex
497-
if process.cmd isa Cmd && !process.cmd.ignorestatus
496+
catch task_ex
497+
ex = process._task.exception
498+
499+
# The idea is that SshProcessFailedException's and LibSSHException's are
500+
# somewhat expected so we always unwrap them from the
501+
# TaskFailedException before throwing, which is a slightly nicer API to
502+
# work with.
503+
if ex isa SshProcessFailedException || ex isa LibSSHException
504+
if !(process.cmd isa Cmd && process.cmd.ignorestatus)
505+
throw(process._task.exception)
506+
end
507+
else
498508
rethrow()
499509
end
500510
end
@@ -591,6 +601,8 @@ An easy way of getting around these restrictions is to pass the command as a
591601
# Throws
592602
- [`SshProcessFailedException`](@ref): if the command fails and `ignorestatus()`
593603
wasn't used.
604+
- [`LibSSHException`](@ref): if running the command fails for some other
605+
reason.
594606
595607
# Arguments
596608
- `cmd`: The command to run. This will be converted to a string for running
@@ -652,10 +664,10 @@ function Base.run(cmd::Union{Cmd, String}, session::Session;
652664
set_channel_callbacks(process._sshchan, callbacks)
653665

654666
process._task = Threads.@spawn _exec_command(process)
655-
errormonitor(process._task)
667+
656668
if wait
657669
# Note the use of Base.wait() to avoid aliasing with the `wait` argument
658-
Base.wait(process._task)
670+
Base.wait(process)
659671

660672
if print_out
661673
print(String(copy(process.out)))

test/LibSSHTests.jl

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -564,16 +564,8 @@ end
564564
cmd = setenv(`echo \$foo`, "foo" => "bar")
565565
@test readchomp(cmd, session) == "bar"
566566

567-
# Test command failure. We redirect error output to devnull to hide
568-
# the errors displayed by the errormonitor() task.
569-
try
570-
redirect_stderr(devnull) do
571-
run(`foo`, session)
572-
end
573-
catch ex
574-
@test ex isa TaskFailedException
575-
@test current_exceptions(ex.task)[1][1] isa ssh.SshProcessFailedException
576-
end
567+
# Test command failure
568+
@test_throws ssh.SshProcessFailedException run(`foo`, session)
577569

578570
# Test passing a String instead of a Cmd
579571
mktempdir() do tmpdir

0 commit comments

Comments
 (0)