Skip to content

Commit

Permalink
deprecate spawn(cmd) to run(cmd, wait=false)
Browse files Browse the repository at this point in the history
add `read` and `write` keyword arguments to `open` for processes

fixes #25965
  • Loading branch information
JeffBezanson committed Mar 1, 2018
1 parent be4e39f commit 1f8f54f
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 68 deletions.
3 changes: 3 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,9 @@ end
@deprecate round(x, digits, base) round(x, digits, base = base)
@deprecate signif(x, digits, base) signif(x, digits, base = base)

# issue #25965
@deprecate spawn(cmds::AbstractCmd) run(cmds, wait = false)

# Remember to delete the module when removing this
@eval Base.Math module JuliaLibm
Base.@deprecate log Base.log
Expand Down
1 change: 0 additions & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,6 @@ export
process_running,
run,
setenv,
spawn,
success,
withenv,

Expand Down
2 changes: 0 additions & 2 deletions base/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ precompile(Tuple{getfield(Base, Symbol("#kw##printstyled")), NamedTuple{(:bold,
precompile(Tuple{getfield(Base, Symbol("#kw##printstyled")), NamedTuple{(:bold, :color), Tuple{Bool, Symbol}}, typeof(Base.printstyled), REPL.Terminals.TTYTerminal, String})
precompile(Tuple{getfield(Base, Symbol("#kw##printstyled")), NamedTuple{(:color,), Tuple{Symbol}}, typeof(Base.printstyled), Base.IOContext{Base.GenericIOBuffer{Array{UInt8, 1}}}, String})
precompile(Tuple{getfield(Base, Symbol("#kw##show_trace_entry")), NamedTuple{(:prefix,), Tuple{String}}, typeof(Base.show_trace_entry), Base.IOContext{REPL.Terminals.TTYTerminal}, Base.StackTraces.StackFrame, Int64})
precompile(Tuple{getfield(Base, Symbol("#kw##spawn")), NamedTuple{(:chain,), Tuple{Nothing}}, typeof(Base.spawn), Base.Cmd, Tuple{Base.Pipe, Base.TTY, Base.IOStream}})
precompile(Tuple{getfield(Base, Symbol("#kw##unindent")), NamedTuple{(:tabwidth,), Tuple{Int64}}, typeof(Base.unindent), String, Int64})
precompile(Tuple{getfield(Base, Symbol("#kw##with_output_color")), NamedTuple{(:bold,), Tuple{Bool}}, typeof(Base.with_output_color), typeof(Base.print), Symbol, Base.IOContext{Base.GenericIOBuffer{Array{UInt8, 1}}}, String})
precompile(Tuple{getfield(Base, Symbol("#kw##with_output_color")), NamedTuple{(:bold,), Tuple{Bool}}, typeof(Base.with_output_color), typeof(Base.print), Symbol, REPL.Terminals.TTYTerminal, String})
Expand Down Expand Up @@ -579,7 +578,6 @@ precompile(Tuple{typeof(Base.similar), Array{Float64, 1}})
precompile(Tuple{typeof(Base.sort!), Array{Int64, 1}, Base.Sort.QuickSortAlg, Base.Order.Perm{Base.Order.ForwardOrdering, Array{Tuple{Float64, Int64}, 1}}})
precompile(Tuple{typeof(Base.sort!), Array{Int64, 1}, Base.Sort.QuickSortAlg, Base.Order.Perm{Base.Order.ForwardOrdering, Array{Tuple{Int64, Float64}, 1}}})
precompile(Tuple{typeof(Base.sort), Array{Float64, 1}})
precompile(Tuple{typeof(Base.spawn), Base.CmdRedirect, Tuple{Base.Pipe, Base.TTY, Base.IOStream}})
precompile(Tuple{typeof(Base.start), Array{AbstractString, 1}})
precompile(Tuple{typeof(Base.start), Array{Expr, 1}})
precompile(Tuple{typeof(Base.start), Array{Function, 1}})
Expand Down
103 changes: 61 additions & 42 deletions base/process.jl
Original file line number Diff line number Diff line change
Expand Up @@ -397,37 +397,37 @@ function _uv_hook_close(proc::Process)
notify(proc.closenotify)
end

function spawn(redirect::CmdRedirect, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
spawn(redirect.cmd,
(redirect.stream_no == STDIN_NO ? redirect.handle : stdios[1],
redirect.stream_no == STDOUT_NO ? redirect.handle : stdios[2],
redirect.stream_no == STDERR_NO ? redirect.handle : stdios[3]),
function _spawn(redirect::CmdRedirect, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
_spawn(redirect.cmd,
(redirect.stream_no == STDIN_NO ? redirect.handle : stdios[1],
redirect.stream_no == STDOUT_NO ? redirect.handle : stdios[2],
redirect.stream_no == STDERR_NO ? redirect.handle : stdios[3]),
chain=chain)
end

function spawn(cmds::OrCmds, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
function _spawn(cmds::OrCmds, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
if chain === nothing
chain = ProcessChain(stdios)
end
in_pipe, out_pipe = link_pipe(false, false)
try
spawn(cmds.a, (stdios[1], out_pipe, stdios[3]), chain=chain)
spawn(cmds.b, (in_pipe, stdios[2], stdios[3]), chain=chain)
_spawn(cmds.a, (stdios[1], out_pipe, stdios[3]), chain=chain)
_spawn(cmds.b, (in_pipe, stdios[2], stdios[3]), chain=chain)
finally
close_pipe_sync(out_pipe)
close_pipe_sync(in_pipe)
end
return chain
end

function spawn(cmds::ErrOrCmds, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
function _spawn(cmds::ErrOrCmds, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
if chain === nothing
chain = ProcessChain(stdios)
end
in_pipe, out_pipe = link_pipe(false, false)
try
spawn(cmds.a, (stdios[1], stdios[2], out_pipe), chain=chain)
spawn(cmds.b, (in_pipe, stdios[2], stdios[3]), chain=chain)
_spawn(cmds.a, (stdios[1], stdios[2], out_pipe), chain=chain)
_spawn(cmds.b, (in_pipe, stdios[2], stdios[3]), chain=chain)
finally
close_pipe_sync(out_pipe)
close_pipe_sync(in_pipe)
Expand Down Expand Up @@ -503,7 +503,7 @@ function setup_stdio(anon::Function, stdio::StdIOSet)
nothing
end

function spawn(cmd::Cmd, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
function _spawn(cmd::Cmd, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
if isempty(cmd.exec)
throw(ArgumentError("cannot spawn empty command"))
end
Expand All @@ -519,13 +519,13 @@ function spawn(cmd::Cmd, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=n
return pp
end

function spawn(cmds::AndCmds, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
function _spawn(cmds::AndCmds, stdios::StdIOSet; chain::Union{ProcessChain, Nothing}=nothing)
if chain === nothing
chain = ProcessChain(stdios)
end
setup_stdio(stdios) do stdios
spawn(cmds.a, stdios, chain=chain)
spawn(cmds.b, stdios, chain=chain)
_spawn(cmds.a, stdios, chain=chain)
_spawn(cmds.b, stdios, chain=chain)
end
return chain
end
Expand All @@ -548,68 +548,75 @@ spawn_opts_inherit(stdios::StdIOSet) = (stdios,)
spawn_opts_inherit(in::Redirectable=RawFD(0), out::Redirectable=RawFD(1), err::Redirectable=RawFD(2), args...) =
((in, out, err), args...)

"""
spawn(command)
Run a command object asynchronously, returning the resulting `Process` object.
"""
spawn(cmds::AbstractCmd, args...; chain::Union{ProcessChain, Nothing}=nothing) =
spawn(cmds, spawn_opts_swallow(args...)...; chain=chain)
_spawn(cmds::AbstractCmd, args...; chain::Union{ProcessChain, Nothing}=nothing) =
_spawn(cmds, spawn_opts_swallow(args...)...; chain=chain)

function eachline(cmd::AbstractCmd; chomp=nothing, keep::Bool=false)
if chomp !== nothing
keep = !chomp
depwarn("The `chomp=$chomp` argument to `eachline` is deprecated in favor of `keep=$keep`.", :eachline)
end
_stdout = Pipe()
processes = spawn(cmd, (devnull, _stdout, stderr))
processes = _spawn(cmd, (devnull, _stdout, stderr))
close(_stdout.in)
out = _stdout.out
# implicitly close after reading lines, since we opened
return EachLine(out, keep=keep,
ondone=()->(close(out); success(processes) || pipeline_error(processes)))::EachLine
end

function open(cmds::AbstractCmd, mode::AbstractString, other::Redirectable=devnull)
if mode == "r+" || mode == "w+"
return open(cmds, other, read = true, write = true)
elseif mode == "r"
return open(cmds, other)
elseif mode == "w"
return open(cmds, other, write = true)
else
throw(ArgumentError("mode must be \"r\" or \"w\", not \"$mode\""))
end
end

# return a Process object to read-to/write-from the pipeline
"""
open(command, mode::AbstractString="r", stdio=devnull)
open(command, stdio=devnull; write::Bool = false, read::Bool = !write)
Start running `command` asynchronously, and return a tuple `(stream,process)`. If `mode` is
`"r"`, then `stream` reads from the process's standard output and `stdio` optionally
specifies the process's standard input stream. If `mode` is `"w"`, then `stream` writes to
Start running `command` asynchronously, and return a tuple `(stream,process)`. If `read` is
true, then `stream` reads from the process's standard output and `stdio` optionally
specifies the process's standard input stream. If `write` is true, then `stream` writes to
the process's standard input and `stdio` optionally specifies the process's standard output
stream.
"""
function open(cmds::AbstractCmd, mode::AbstractString="r", other::Redirectable=devnull)
if mode == "r+" || mode == "w+"
other === devnull || throw(ArgumentError("no other stream for mode rw+"))
function open(cmds::AbstractCmd, other::Redirectable=devnull; write::Bool = false, read::Bool = !write)
if read && write
other === devnull || throw(ArgumentError("no other stream can be specified in read-write mode"))
in = Pipe()
out = Pipe()
processes = spawn(cmds, (in,out,stderr))
processes = _spawn(cmds, (in,out,stderr))
close(in.out)
close(out.in)
elseif mode == "r"
elseif read
in = other
out = Pipe()
processes = spawn(cmds, (in,out,stderr))
processes = _spawn(cmds, (in,out,stderr))
close(out.in)
if isa(processes, ProcessChain) # for open(cmd) deprecation
processes = ProcessChain(processes, :out)
else
processes.openstream = :out
end
elseif mode == "w"
elseif write
in = Pipe()
out = other
processes = spawn(cmds, (in,out,stderr))
processes = _spawn(cmds, (in,out,stderr))
close(in.out)
if isa(processes, ProcessChain) # for open(cmd) deprecation
processes = ProcessChain(processes, :in)
else
processes.openstream = :in
end
else
throw(ArgumentError("mode must be \"r\" or \"w\", not \"$mode\""))
processes = _spawn(cmds)
end
return processes
end
Expand Down Expand Up @@ -645,14 +652,26 @@ end
read(cmd::AbstractCmd, ::Type{String}) = String(read(cmd))

"""
run(command, args...)
run(command, args...; wait::Bool = true)
Run a command object, constructed with backticks. Throws an error if anything goes wrong,
including the process exiting with a non-zero status.
including the process exiting with a non-zero status (when `wait` is true).
If `wait` is false, the process runs asynchronously. You can later wait for it and check
its exit status by calling `success` on the returned process object.
When `wait` is false, the process' I/O streams are directed to `devnull`.
When `wait` is true, I/O streams are shared with the parent process.
Use [`pipeline`](@ref) to control I/O redirection.
"""
function run(cmds::AbstractCmd, args...)
ps = spawn(cmds, spawn_opts_inherit(args...)...)
success(ps) ? nothing : pipeline_error(ps)
function run(cmds::AbstractCmd, args...; wait::Bool = true)
if wait
ps = _spawn(cmds, spawn_opts_inherit(args...)...)
success(ps) || pipeline_error(ps)
else
ps = _spawn(cmds, spawn_opts_swallow(args...)...)
end
return ps
end

# some common signal numbers that are usually available on all platforms
Expand Down Expand Up @@ -686,7 +705,7 @@ success(procs::ProcessChain) = success(procs.processes)
Run a command object, constructed with backticks, and tell whether it was successful (exited
with a code of 0). An exception is raised if the process cannot be started.
"""
success(cmd::AbstractCmd) = success(spawn(cmd))
success(cmd::AbstractCmd) = success(_spawn(cmd))

function pipeline_error(proc::Process)
if !proc.cmd.ignorestatus
Expand Down
2 changes: 1 addition & 1 deletion base/stream.jl
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ julia> err = Pipe()
# After this `err` will be initialized and you may read `foo`'s
# stderr from the `err` pipe.
julia> spawn(pipeline(pipeline(`foo`, stderr=err), `cat`))
julia> run(pipeline(pipeline(`foo`, stderr=err), `cat`), wait=false)
```
"""
Pipe() = Pipe(PipeEndpoint(), PipeEndpoint())
Expand Down
1 change: 0 additions & 1 deletion doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ Base.skipmissing

```@docs
Base.run
Base.spawn
Base.devnull
Base.success
Base.process_running
Expand Down
2 changes: 1 addition & 1 deletion stdlib/InteractiveUtils/src/editless.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function edit(path::AbstractString, line::Integer=0)
(Ptr{Cvoid}, Cwstring, Cwstring, Ptr{Cvoid}, Ptr{Cvoid}, Cint),
C_NULL, "open", path, C_NULL, C_NULL, 10) 32)
elseif background
spawn(pipeline(cmd, stderr=stderr))
run(pipeline(cmd, stderr=stderr), wait=false)
else
run(cmd)
end
Expand Down
4 changes: 2 additions & 2 deletions stdlib/LibGit2/test/libgit2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function challenge_prompt(cmd::Cmd, challenges; timeout::Integer=10, debug::Bool
end
out = IOBuffer()
with_fake_pty() do slave, master
p = spawn(detach(cmd), slave, slave, slave)
p = run(detach(cmd), slave, slave, slave, wait=false)

# Kill the process if it takes too long. Typically occurs when process is waiting
# for input.
Expand Down Expand Up @@ -2703,7 +2703,7 @@ mktempdir() do dir
# certificate. The minimal server can't actually serve a Git repository.
mkdir(joinpath(root, "Example.jl"))
pobj = cd(root) do
spawn(`openssl s_server -key $key -cert $cert -WWW -accept $port`)
run(`openssl s_server -key $key -cert $cert -WWW -accept $port`, wait=false)
end

errfile = joinpath(root, "error")
Expand Down
2 changes: 1 addition & 1 deletion stdlib/REPL/test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ let exename = Base.julia_cmd()
TestHelpers.with_fake_pty() do slave, master
nENV = copy(ENV)
nENV["TERM"] = "dumb"
p = spawn(setenv(`$exename --startup-file=no -q`,nENV),slave,slave,slave)
p = run(setenv(`$exename --startup-file=no -q`,nENV),slave,slave,slave,wait=false)
output = readuntil(master,"julia> ",keep=true)
if ccall(:jl_running_on_valgrind,Cint,()) == 0
# If --trace-children=yes is passed to valgrind, we will get a
Expand Down
6 changes: 3 additions & 3 deletions test/cmdlineargs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ end
function readchomperrors(exename::Cmd)
out = Base.PipeEndpoint()
err = Base.PipeEndpoint()
p = spawn(exename, devnull, out, err)
p = run(exename, devnull, out, err, wait=false)
o = @async(readchomp(out))
e = @async(readchomp(err))
return (success(p), fetch(o), fetch(e))
Expand Down Expand Up @@ -390,7 +390,7 @@ let exename = joinpath(Sys.BINDIR, Base.julia_exename()),
"$sysname.nonexistent",
)
let err = Pipe(),
p = spawn(pipeline(`$exename --sysimage=$nonexist_image`, stderr=err))
p = run(pipeline(`$exename --sysimage=$nonexist_image`, stderr=err), wait=false)
close(err.in)
let s = read(err, String)
@test contains(s, "ERROR: could not load library \"$nonexist_image\"\n")
Expand All @@ -403,7 +403,7 @@ let exename = joinpath(Sys.BINDIR, Base.julia_exename()),
end
end
let err = Pipe(),
p = spawn(pipeline(`$exename --sysimage=$libjulia`, stderr=err))
p = run(pipeline(`$exename --sysimage=$libjulia`, stderr=err), wait=false)
close(err.in)
let s = read(err, String)
@test s == "ERROR: System image file failed consistency check: maybe opened the wrong version?\n"
Expand Down
2 changes: 1 addition & 1 deletion test/embedding/embedding-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ end
@testset "embedding example" begin
out = Pipe()
err = Pipe()
p = spawn(pipeline(Cmd(ARGS), stdin=devnull, stdout=out, stderr=err))
p = run(pipeline(Cmd(ARGS), stdin=devnull, stdout=out, stderr=err), wait=false)
close(out.in)
close(err.in)
out_task = @async readlines(out)
Expand Down
Loading

0 comments on commit 1f8f54f

Please sign in to comment.