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 err global to REPL to store most recent errors #40642

Merged
merged 28 commits into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f789b9e
Add global to store and re-display errors in the REPL
BioTurboNick Apr 28, 2021
1a6ebe7
Add test
BioTurboNick Apr 28, 2021
e1e0beb
Add MIME type
BioTurboNick Apr 28, 2021
d2768a0
Changed to ExceptionStack
BioTurboNick May 3, 2021
a5ccf7d
Fix
BioTurboNick May 3, 2021
0612202
errs => err
BioTurboNick May 3, 2021
dbb1060
update test
BioTurboNick May 3, 2021
dcd610b
Added NEWS
BioTurboNick May 3, 2021
5eb8581
bump
BioTurboNick May 3, 2021
6a3e5f4
Removed unnecessary extra ExceptionStack
BioTurboNick May 20, 2021
370ad13
Fix client, which was wrapping an ExceptionStack in itself.
BioTurboNick Jun 6, 2021
dbf0bcc
Fix REPL to remove unnecessary Exception Stack wrapping
BioTurboNick Jun 6, 2021
c83f65a
Remove unnecessary unwrapping of ExceptionStack
BioTurboNick Jun 6, 2021
33299b3
Corrected test
BioTurboNick Jun 7, 2021
47ce6e0
test fix
BioTurboNick Jun 7, 2021
6000b8d
Testing out limiting storage of an error
BioTurboNick Jun 8, 2021
cfab0ef
Moved global-setting to `display_error`
BioTurboNick Jun 8, 2021
b336657
Test to conform with limit to storing err
BioTurboNick Jun 8, 2021
292bc37
Fix error
BioTurboNick Jun 10, 2021
00d5f51
Fix for saving logic and test
BioTurboNick Jun 10, 2021
3cae5ee
test fixed
BioTurboNick Jun 10, 2021
a46df33
bump
BioTurboNick Oct 13, 2021
31d4bf4
Refinements
BioTurboNick Oct 29, 2021
4fcab0e
restore error-error scrubbing
BioTurboNick Oct 29, 2021
fb8474c
More scrubbing, fixed precompile
BioTurboNick Oct 29, 2021
bac0089
Restore `display_error` with note
BioTurboNick Nov 5, 2021
f54c85c
it's a named tuple
BioTurboNick Nov 5, 2021
1e47f95
Ensure uses Vector{Any}
BioTurboNick Nov 5, 2021
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
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ Standard library changes
argument have a type more specific than `Any`; use SHIFT-TAB instead of TAB
to allow any compatible methods.

* New `err` global variable in `Main` set when an expression throws an exception, akin to `ans`. Typing `err` reprints
the exception information.

#### SparseArrays

#### Dates
Expand Down
29 changes: 15 additions & 14 deletions base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -84,28 +84,26 @@ end

function scrub_repl_backtrace(bt)
if bt !== nothing && !(bt isa Vector{Any}) # ignore our sentinel value types
bt = stacktrace(bt)
bt = bt isa Vector{StackFrame} ? copy(bt) : stacktrace(bt)
# remove REPL-related frames from interactive printing
eval_ind = findlast(frame -> !frame.from_c && frame.func === :eval, bt)
eval_ind === nothing || deleteat!(bt, eval_ind:length(bt))
end
return bt
end
scrub_repl_backtrace(stack::ExceptionStack) =
ExceptionStack([(exception = x[1], backtrace = scrub_repl_backtrace(x[2])) for x in stack])

istrivialerror(stack::ExceptionStack) =
length(stack) == 1 && length(stack[1].backtrace) ≤ 1
# frame 1 = top level; assumes already went through scrub_repl_backtrace

function display_error(io::IO, er, bt)
printstyled(io, "ERROR: "; bold=true, color=Base.error_color())
bt = scrub_repl_backtrace(bt)
showerror(IOContext(io, :limit => true), er, bt, backtrace = bt!==nothing)
println(io)
end
function display_error(io::IO, stack::ExceptionStack)
printstyled(io, "ERROR: "; bold=true, color=Base.error_color())
bt = Any[ (x[1], scrub_repl_backtrace(x[2])) for x in stack ]
show_exception_stack(IOContext(io, :limit => true), bt)
show_exception_stack(IOContext(io, :limit => true), stack)
println(io)
end
display_error(stack::ExceptionStack) = display_error(stderr, stack)
display_error(er, bt=nothing) = display_error(stderr, er, bt)
BioTurboNick marked this conversation as resolved.
Show resolved Hide resolved

function eval_user_input(errio, @nospecialize(ast), show_value::Bool)
errcount = 0
Expand All @@ -117,6 +115,8 @@ function eval_user_input(errio, @nospecialize(ast), show_value::Bool)
print(color_normal)
end
if lasterr !== nothing
lasterr = scrub_repl_backtrace(lasterr)
istrivialerror(lasterr) || ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, lasterr)
invokelatest(display_error, errio, lasterr)
errcount = 0
lasterr = nothing
Expand All @@ -143,7 +143,8 @@ function eval_user_input(errio, @nospecialize(ast), show_value::Bool)
@error "SYSTEM: display_error(errio, lasterr) caused an error"
end
errcount += 1
lasterr = current_exceptions()
lasterr = scrub_repl_backtrace(current_exceptions())
ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, lasterr)
if errcount > 2
@error "It is likely that something important is broken, and Julia will not be able to continue normally" errcount
break
Expand Down Expand Up @@ -260,7 +261,7 @@ function exec_options(opts)
try
load_julia_startup()
catch
invokelatest(display_error, current_exceptions())
invokelatest(display_error, scrub_repl_backtrace(current_exceptions()))
!(repl || is_interactive::Bool) && exit(1)
end
end
Expand Down Expand Up @@ -294,7 +295,7 @@ function exec_options(opts)
try
include(Main, PROGRAM_FILE)
catch
invokelatest(display_error, current_exceptions())
invokelatest(display_error, scrub_repl_backtrace(current_exceptions()))
if !is_interactive::Bool
exit(1)
end
Expand Down Expand Up @@ -496,7 +497,7 @@ function _start()
try
exec_options(JLOptions())
catch
invokelatest(display_error, current_exceptions())
invokelatest(display_error, scrub_repl_backtrace(current_exceptions()))
exit(1)
end
if is_interactive && get(stdout, :color, false)
Expand Down
4 changes: 2 additions & 2 deletions base/task.jl
Original file line number Diff line number Diff line change
Expand Up @@ -446,13 +446,13 @@ function errormonitor(t::Task)
try # try to display the failure atomically
errio = IOContext(PipeBuffer(), errs::IO)
emphasize(errio, "Unhandled Task ")
display_error(errio, current_exceptions(t))
display_error(errio, scrub_repl_backtrace(current_exceptions(t)))
write(errs, errio)
catch
try # try to display the secondary error atomically
errio = IOContext(PipeBuffer(), errs::IO)
print(errio, "\nSYSTEM: caught exception while trying to print a failed Task notice: ")
display_error(errio, current_exceptions())
display_error(errio, scrub_repl_backtrace(current_exceptions()))
write(errs, errio)
flush(errs)
# and then the actual error, as best we can
Expand Down
4 changes: 1 addition & 3 deletions contrib/generate_precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ precompile(Tuple{typeof(Base.recursive_prefs_merge), Base.Dict{String, Any}})
precompile(Tuple{typeof(isassigned), Core.SimpleVector, Int})
precompile(Tuple{typeof(getindex), Core.SimpleVector, Int})
precompile(Tuple{typeof(Base.Experimental.register_error_hint), Any, Type})
precompile(Tuple{typeof(Base.display_error), MethodError, Vector{Union{Ptr{Nothing}, Base.InterpreterIP}}})
precompile(Tuple{typeof(Base.display_error), ErrorException})
precompile(Tuple{typeof(Base.display_error), BoundsError})
precompile(Tuple{typeof(Base.display_error), Base.ExceptionStack})
precompile(Tuple{Core.kwftype(typeof(Type)), NamedTuple{(:sizehint,), Tuple{Int}}, Type{IOBuffer}})
precompile(Base.CoreLogging.current_logger_for_env, (Base.CoreLogging.LogLevel, String, Module))
precompile(Base.CoreLogging.current_logger_for_env, (Base.CoreLogging.LogLevel, Symbol, Module))
Expand Down
6 changes: 5 additions & 1 deletion stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool,
try
Base.sigatomic_end()
if iserr
val = Base.scrub_repl_backtrace(val)
Base.istrivialerror(val) || ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, val)
Base.invokelatest(Base.display_error, errio, val)
else
if val !== nothing && show_value
Expand All @@ -305,7 +307,9 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool,
println(errio) # an error during printing is likely to leave us mid-line
println(errio, "SYSTEM (REPL): showing an error caused an error")
try
Base.invokelatest(Base.display_error, errio, current_exceptions())
excs = Base.scrub_repl_backtrace(current_exceptions())
ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :err, excs)
Base.invokelatest(Base.display_error, errio, excs)
catch e
# at this point, only print the name of the type as a Symbol to
# minimize the possibility of further errors.
Expand Down
43 changes: 40 additions & 3 deletions stdlib/REPL/test/repl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -885,13 +885,13 @@ end

# Test containers in error messages are limited #18726
let io = IOBuffer()
Base.display_error(io,
try
Base.display_error(io, Base.ExceptionStack([(exception =
BioTurboNick marked this conversation as resolved.
Show resolved Hide resolved
(try
[][trues(6000)]
@assert false
catch e
e
end, [])
end), backtrace = [])]))
@test length(String(take!(io))) < 1500
end

Expand Down Expand Up @@ -1366,3 +1366,40 @@ end
@test isempty(mods)
end
end

# err should reprint error if deeper than top-level
fake_repl() do stdin_write, stdout_read, repl
repltask = @async begin
REPL.run_repl(repl)
end
# initialize `err` to `nothing`
write(stdin_write, "global err = nothing\n")
readline(stdout_read)
readline(stdout_read) == "\e[0m"
readuntil(stdout_read, "julia> ", keep=true)
# generate top-level error
write(stdin_write, "foobar\n")
readline(stdout_read)
@test readline(stdout_read) == "\e[0mERROR: UndefVarError: foobar not defined"
@test readline(stdout_read) == ""
readuntil(stdout_read, "julia> ", keep=true)
# check that top-level error did not change `err`
write(stdin_write, "err\n")
readline(stdout_read)
@test readline(stdout_read) == "\e[0m"
readuntil(stdout_read, "julia> ", keep=true)
# generate deeper error
write(stdin_write, "foo() = foobar\n")
readline(stdout_read)
readuntil(stdout_read, "julia> ", keep=true)
write(stdin_write, "foo()\n")
readline(stdout_read)
@test readline(stdout_read) == "\e[0mERROR: UndefVarError: foobar not defined"
readuntil(stdout_read, "julia> ", keep=true)
# check that deeper error did set `err`
write(stdin_write, "err\n")
readline(stdout_read)
@test readline(stdout_read) == "\e[0m1-element ExceptionStack:"
@test readline(stdout_read) == "UndefVarError: foobar not defined"
@test readline(stdout_read) == "Stacktrace:"
end