Skip to content

Commit

Permalink
Print exception stacks in the REPL using display_error
Browse files Browse the repository at this point in the history
This required a bit of refactoring to disentangle and factor out some of
the error handling in the various REPL code paths.
  • Loading branch information
c42f committed Feb 1, 2019
1 parent bcd472f commit 87e9039
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 54 deletions.
92 changes: 39 additions & 53 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import Base:
display,
show,
AnyDict,
==
==,
catch_stack


include("Terminals.jl")
Expand Down Expand Up @@ -61,7 +62,7 @@ const JULIA_PROMPT = "julia> "
mutable struct REPLBackend
"channel for AST"
repl_channel::Channel
"channel for results: (value, nothing) or (error, backtrace)"
"channel for results: (value, iserror)"
response_channel::Channel
"flag indicating the state of this backend"
in_eval::Bool
Expand All @@ -73,28 +74,28 @@ mutable struct REPLBackend
end

function eval_user_input(@nospecialize(ast), backend::REPLBackend)
iserr, lasterr = false, ((), nothing)
lasterr = nothing
Base.sigatomic_begin()
while true
try
Base.sigatomic_end()
if iserr
put!(backend.response_channel, lasterr)
if lasterr !== nothing
put!(backend.response_channel, (lasterr,true))
else
backend.in_eval = true
value = Core.eval(Main, ast)
backend.in_eval = false
# note: use jl_set_global to make sure value isn't passed through `expand`
ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :ans, value)
put!(backend.response_channel, (value, nothing))
put!(backend.response_channel, (value,false))
end
break
catch err
if iserr
if lasterr !== nothing
println("SYSTEM ERROR: Failed to report error to REPL frontend")
println(err)
end
iserr, lasterr = true, (err, catch_backtrace())
lasterr = catch_stack()
end
end
Base.sigatomic_end()
Expand Down Expand Up @@ -134,20 +135,20 @@ function display(d::REPLDisplay, mime::MIME"text/plain", x)
end
display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x)

function print_response(repl::AbstractREPL, @nospecialize(val), bt, show_value::Bool, have_color::Bool)
repl.waserror = bt !== nothing
function print_response(repl::AbstractREPL, @nospecialize(response), show_value::Bool, have_color::Bool)
repl.waserror = response[2]
io = IOContext(outstream(repl), :module => Main)
print_response(io, val, bt, show_value, have_color, specialdisplay(repl))
print_response(io, response, show_value, have_color, specialdisplay(repl))
nothing
end
function print_response(errio::IO, @nospecialize(val), bt, show_value::Bool, have_color::Bool, specialdisplay=nothing)
function print_response(errio::IO, @nospecialize(response), show_value::Bool, have_color::Bool, specialdisplay=nothing)
Base.sigatomic_begin()
val, iserr = response
while true
try
Base.sigatomic_end()
if bt !== nothing
Base.invokelatest(Base.display_error, errio, val, bt)
iserr, lasterr = false, ()
if iserr
Base.invokelatest(Base.display_error, errio, val)
else
if val !== nothing && show_value
try
Expand All @@ -163,15 +164,14 @@ function print_response(errio::IO, @nospecialize(val), bt, show_value::Bool, hav
end
end
break
catch err
if bt !== nothing
println(errio, "SYSTEM: show(lasterr) caused an error")
println(errio, err)
Base.show_backtrace(errio, bt)
catch
if iserr
println(errio, "SYSTEM (REPL): showing an error caused an error")
println(errio, catch_stack())
break
end
val = err
bt = catch_backtrace()
val = catch_stack()
iserr = true
end
end
Base.sigatomic_end()
Expand Down Expand Up @@ -207,7 +207,6 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
d = REPLDisplay(repl)
dopushdisplay = !in(d,Base.Multimedia.displays)
dopushdisplay && pushdisplay(d)
repl_channel, response_channel = backend.repl_channel, backend.response_channel
hit_eof = false
while true
Base.reseteof(repl.terminal)
Expand Down Expand Up @@ -238,17 +237,14 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
(isa(ast,Expr) && ast.head == :incomplete) || break
end
if !isempty(line)
put!(repl_channel, (ast, 1))
val, bt = take!(response_channel)
if !ends_with_semicolon(line)
print_response(repl, val, bt, true, false)
end
response = eval_with_backend(ast, backend)
print_response(repl, response, !ends_with_semicolon(line), false)
end
write(repl.terminal, '\n')
((!interrupted && isempty(line)) || hit_eof) && break
end
# terminate backend
put!(repl_channel, (nothing, -1))
put!(backend.repl_channel, (nothing, -1))
dopushdisplay && popdisplay(d)
nothing
end
Expand Down Expand Up @@ -682,12 +678,9 @@ find_hist_file() = get(ENV, "JULIA_HISTORY",

backend(r::AbstractREPL) = r.backendref

send_to_backend(ast, backend::REPLBackendRef) =
send_to_backend(ast, backend.repl_channel, backend.response_channel)

function send_to_backend(ast, req, rep)
put!(req, (ast, 1))
return take!(rep) # (val, bt)
function eval_with_backend(ast, backend::REPLBackendRef)
put!(backend.repl_channel, (ast, 1))
take!(backend.response_channel) # (val, iserr)
end

function respond(f, repl, main; pass_empty = false)
Expand All @@ -698,17 +691,14 @@ function respond(f, repl, main; pass_empty = false)
line = String(take!(buf))
if !isempty(line) || pass_empty
reset(repl)
local val, bt
local response
try
response = Base.invokelatest(f, line)
val, bt = send_to_backend(response, backend(repl))
catch err
val = err
bt = catch_backtrace()
end
if !ends_with_semicolon(line) || bt !== nothing
print_response(repl, val, bt, true, Base.have_color)
ast = Base.invokelatest(f, line)
response = eval_with_backend(ast, backend(repl))
catch
response = (catch_stack(), true)
end
print_response(repl, response, !ends_with_semicolon(line), Base.have_color)
end
prepare_next(repl)
reset_state(s)
Expand Down Expand Up @@ -848,8 +838,8 @@ function setup_interface(
close(f)
end
hist_from_file(hp, f, hist_path)
catch e
print_response(repl, e, catch_backtrace(), true, Base.have_color)
catch
print_response(repl, (catch_stack(),true), true, Base.have_color)
println(outstream(repl))
@info "Disabling history file for this session"
repl.history_file = false
Expand Down Expand Up @@ -1110,7 +1100,6 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
d = REPLDisplay(repl)
dopushdisplay = !in(d,Base.Multimedia.displays)
dopushdisplay && pushdisplay(d)
repl_channel, response_channel = backend.repl_channel, backend.response_channel
while !eof(repl.stream)
if have_color
print(repl.stream,repl.prompt_color)
Expand All @@ -1125,15 +1114,12 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
if have_color
print(repl.stream, Base.color_normal)
end
put!(repl_channel, (ast, 1))
val, bt = take!(response_channel)
if !ends_with_semicolon(line)
print_response(repl, val, bt, true, have_color)
end
response = eval_with_backend(ast, backend)
print_response(repl, response, !ends_with_semicolon(line), have_color)
end
end
# Terminate Backend
put!(repl_channel, (nothing, -1))
put!(backend.repl_channel, (nothing, -1))
dopushdisplay && popdisplay(d)
nothing
end
Expand Down
3 changes: 2 additions & 1 deletion stdlib/REPL/test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,8 @@ mutable struct Error19864 <: Exception; end
function test19864()
@eval Base.showerror(io::IO, e::Error19864) = print(io, "correct19864")
buf = IOBuffer()
REPL.print_response(buf, Error19864(), [], false, false, nothing)
fake_response = (Any[(Error19864(),[])],true)
REPL.print_response(buf, fake_response, false, false, nothing)
return String(take!(buf))
end
@test occursin("correct19864", test19864())
Expand Down

0 comments on commit 87e9039

Please sign in to comment.