Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ProcessExitedError rather than using error #27900

Merged
merged 18 commits into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ New language features
Multi-threading changes
-----------------------

* The `Condition` type now has a thread-safe replacement, accessed as `Threads.Condition`.
* The `Condition` type now has a thread-safe replacement, accessed as `Threads.Condition`.
With that addition, task scheduling primitives such as `ReentrantLock` are now thread-safe ([#30061]).

Language changes
----------------
* Empty entries in `JULIA_DEPOT_PATH` are now expanded to default depot entries ([#31009]).
* `Enum` now behaves like a scalar when used in broadcasting ([#30670]).
* If a `pipeline` is specified with `append=true` set, but no redirection, an `ArgumentError`
is thrown, rather than a `ErrorException` ([#27900]).
* Functions that invoke commands (e.g. `run(::Cmd)`) now throw a `ProcessFailedException`
rather than an `ErrorException`, if those commands exit with non-zero exit code.
([#27900]).

Command-line option changes
---------------------------
Expand Down
7 changes: 4 additions & 3 deletions base/download.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ if Sys.iswindows()
if proc.exitcode % Int32 == -393216
# appears to be "wrong version" exit code, based on
# https://docs.microsoft.com/en-us/azure/cloud-services/cloud-services-startup-tasks-common
error("Downloading files requires Windows Management Framework 3.0 or later.")
@error "Downloading files requires Windows Management Framework 3.0 or later."
end
pipeline_error(proc)
end
Expand All @@ -39,8 +39,9 @@ function download_curl(curl_exe::AbstractString, url::AbstractString, filename::
err = PipeBuffer()
process = run(pipeline(`$curl_exe -s -S -g -L -f -o $filename $url`, stderr=err), wait=false)
if !success(process)
stderr = readline(err)
error(stderr)
error_msg = readline(err)
@error "Download Failed" error_msg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should've been a string interpolation, fixed in #31620

pipeline_error(process)
end
return filename
end
Expand Down
3 changes: 2 additions & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,14 @@ export
Cwstring,

# Exceptions
DimensionMismatch,
CapturedException,
CompositeException,
DimensionMismatch,
EOFError,
InvalidStateException,
KeyError,
MissingException,
ProcessFailedException,
SystemError,
StringIndexError,

Expand Down
36 changes: 28 additions & 8 deletions base/process.jl
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ run(pipeline(`update`, stdout="log.txt", append=true))
"""
function pipeline(cmd::AbstractCmd; stdin=nothing, stdout=nothing, stderr=nothing, append::Bool=false)
if append && stdout === nothing && stderr === nothing
error("append set to true, but no output redirections specified")
throw(ArgumentError("append set to true, but no output redirections specified"))
end
if stdin !== nothing
cmd = redir_out(stdin, cmd)
Expand Down Expand Up @@ -783,9 +783,34 @@ An exception is raised if the process cannot be started.
"""
success(cmd::AbstractCmd) = success(_spawn(cmd))


"""
ProcessFailedException

Indicates problematic exit status of a process.
When running commands or pipelines, this is thrown to indicate
a nonzero exit code was returned (i.e. that the invoked process failed).
"""
struct ProcessFailedException <: Exception
procs::Vector{Process}
end
ProcessFailedException(proc::Process) = ProcessFailedException([proc])

function showerror(io::IO, err::ProcessFailedException)
if length(err.procs) == 1
proc = err.procs[1]
println(io, "failed process: ", proc, " [", proc.exitcode, "]")
else
println(io, "failed processes:")
for proc in err.procs
println(io, " ", proc, " [", proc.exitcode, "]")
end
end
end

function pipeline_error(proc::Process)
if !proc.cmd.ignorestatus
error("failed process: ", proc, " [", proc.exitcode, "]")
throw(ProcessFailedException(proc))
end
nothing
end
Expand All @@ -798,12 +823,7 @@ function pipeline_error(procs::ProcessChain)
end
end
isempty(failed) && return nothing
length(failed) == 1 && pipeline_error(failed[1])
msg = "failed processes:"
for proc in failed
msg = string(msg, "\n ", proc, " [", proc.exitcode, "]")
end
error(msg)
throw(ProcessFailedException(failed))
end

"""
Expand Down
1 change: 1 addition & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ Base.MissingException
Core.OutOfMemoryError
Core.ReadOnlyMemoryError
Core.OverflowError
Base.ProcessFailedException
Core.StackOverflowError
Base.SystemError
Core.TypeError
Expand Down
4 changes: 2 additions & 2 deletions test/download.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mktempdir() do temp_dir

# Make sure that failed downloads do not leave files around
missing_file = joinpath(temp_dir, "missing")
@test_throws ErrorException download("http://httpbin.org/status/404", missing_file)
@test_throws ProcessFailedException download("http://httpbin.org/status/404", missing_file)
@test !isfile(missing_file)

# Make sure we properly handle metachar '
Expand All @@ -28,6 +28,6 @@ mktempdir() do temp_dir

# Use a TEST-NET (192.0.2.0/24) address which shouldn't be bound
invalid_host_file = joinpath(temp_dir, "invalid_host")
@test_throws ErrorException download("http://192.0.2.1", invalid_host_file)
@test_throws ProcessFailedException download("http://192.0.2.1", invalid_host_file)
@test !isfile(invalid_host_file)
end
20 changes: 15 additions & 5 deletions test/spawn.jl
Original file line number Diff line number Diff line change
Expand Up @@ -420,13 +420,23 @@ end

@test Base.shell_split("\"\\\\\"") == ["\\"]

# issue #13616
pcatcmd = `$catcmd _doesnt_exist__111_`
let p = eachline(pipeline(`$catcmd _doesnt_exist__111_`, stderr=devnull))
@test_throws(ErrorException("failed process: Process($pcatcmd, ProcessExited(1)) [1]"),
collect(p))
# Test failing commands
failing_cmd = `$catcmd _doesnt_exist__111_`
failing_pipeline = pipeline(failing_cmd, stderr=devnull) # make quiet for tests
for testrun in (failing_pipeline, pipeline(failing_pipeline, failing_pipeline))
try
run(testrun)
catch err
@test err isa ProcessFailedException
errmsg = sprint(showerror, err)
@test occursin(string(failing_cmd), errmsg)
end
end

# issue #13616
@test_throws(ProcessFailedException, collect(eachline(failing_pipeline)))


# make sure windows_verbatim strips quotes
if Sys.iswindows()
@test read(`cmd.exe /c dir /b spawn.jl`, String) == read(Cmd(`cmd.exe /c dir /b "\"spawn.jl\""`, windows_verbatim=true), String)
Expand Down