Skip to content

Commit

Permalink
Port Base.current_exceptions() to older Julia versions
Browse files Browse the repository at this point in the history
See JuliaLang/julia#29901

This is limited to julia-1.1 and above because earlier versions don't
have the necessary runtime library support (Base.catch_stack() etc).
  • Loading branch information
c42f committed Aug 16, 2021
1 parent ad9a668 commit e3c0637
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Compat"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.33.0"
version = "3.34.0"

[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ changes in `julia`.

## Supported features

* The function `current_exceptions()` has been added to get the current exception stack. ([#29901]) (since Compat 3.34)

* The methods `Base.include(::Function, ::Module, path)` and
`Base.include_string(::Function, ::Module, code[, filename])` have been added. ([#34595]) (since Compat 3.33)

Expand Down
39 changes: 39 additions & 0 deletions src/Compat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,45 @@ if VERSION < v"1.5.0-DEV.263"
end
end

# https://github.com/JuliaLang/julia/pull/29901
if VERSION >= v"1.1" && VERSION < v"1.7.0-DEV.1106"
struct ExceptionStack <: AbstractArray{Any,1}
stack
end

function current_exceptions(task=current_task(); backtrace=true)
stack = Base.catch_stack(task, include_bt=backtrace)
ExceptionStack(Any[(exception=x[1],backtrace=x[2]) for x in stack])
end

Base.size(s::ExceptionStack) = size(s.stack)
Base.getindex(s::ExceptionStack, i::Int) = s.stack[i]

function show_exception_stack(io::IO, stack)
# Display exception stack with the top of the stack first. This ordering
# means that the user doesn't have to scroll up in the REPL to discover the
# root cause.
nexc = length(stack)
for i = nexc:-1:1
if nexc != i
printstyled(io, "\ncaused by: ", color=Base.error_color())
end
exc, bt = stack[i]
showerror(io, exc, bt, backtrace = bt!==nothing)
i == 1 || println(io)
end
end

function Base.show(io::IO, ::MIME"text/plain", stack::ExceptionStack)
nexc = length(stack)
printstyled(io, nexc, "-element ExceptionStack", nexc == 0 ? "" : ":\n")
show_exception_stack(io, stack)
end
Base.show(io::IO, stack::ExceptionStack) = show(io, MIME("text/plain"), stack)

export current_exceptions
end

include("iterators.jl")
include("deprecated.jl")

Expand Down
25 changes: 25 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1109,3 +1109,28 @@ end
@test m.y === 3
@test !isdefined(m, :x)
end

# https://github.com/JuliaLang/julia/pull/29901
VERSION >= v"1.1" && @testset "current_exceptions" begin
# Display of errors which cause more than one entry on the exception stack
excs = try
try
__not_a_binding__
catch
1 ÷ 0 # Generate error while handling error
end
catch
current_exceptions()
end

@test typeof.(first.(excs)) == [UndefVarError, DivideError]

@test occursin(r"""
2-element ExceptionStack:
DivideError: integer division error
Stacktrace:.*
caused by: UndefVarError: __not_a_binding__ not defined
Stacktrace:.*
"""s, sprint(show, excs))
end

0 comments on commit e3c0637

Please sign in to comment.