jli  Linuxx86_641.10.3v1.10.30b4590a5507d3f3046e5bafc007cacbbfc9b310bsExceptionUnwrappingC$ F< 7H/opt/julia/packages/ExceptionUnwrapping/kba8L/src/ExceptionUnwrapping.jl5A7/opt/julia/packages/ExceptionUnwrapping/kba8L/README.md5AH/opt/julia/packages/ExceptionUnwrapping/kba8L/src/test_throws_wrapped.jl5A@ O#e^,TestF/opt/julia/packages/ExceptionUnwrapping/kba8L/src/exception_summary.jl5A CoremуJ5Basemу]J5MainmуJ5ArgToolsBń x(mуF K5 Artifactsmr-V3|mу K5Base64UlD*_mу> K5CRC32c\y.jmуj K5 FileWatchingXzsy`{,zmуh& K5LibdluVW59˗,mу-" K5LoggingT{VhUXM=mуrU" K5MmapP~:xg,Omу|' K5NetworkOptionsC0YW,mуʠ, K5SHAQ<$!<%mу1 K5 Serialization [)*k1mу-G K5Sockets1V$ bdސݗmуYBY K5UnicodeP>I>Nrmуeszo K5 LinearAlgebraSm7̏mуuux K5 OpenBLAS_jll[(Śb6EcQ FmуDux K5libblastrampoline_jllLSۆ }lxӠmу^} K5MarkdownZPn7z`smу/Ed~ K5Printfg^cX׸QDmу;h K5Random_ɢ?\Ymу? K5TarOi>աmу!t, K5DatesEY8pj2 mуX K5FuturebS;3{I xVMmуsD K5InteractiveUtilsWL ~@'ZmуVg K5LibGit2Z[&RPTv3EКRmу8J K5 LibGit2_jll YXg}]$mуD K5 MbedTLS_jllAX 3ȡ_mу- K5 LibSSH2_jlloTZk)߆= v"1.7.0-" include("exception_summary.jl") end """ has_wrapped_exception(e, ExceptionType)::Bool Returns true if the given exception instance, `e`, contains an exception of type `T` anywhere in its chain of unwrapped exceptions. Application code should prefer to use `has_wrapped_exception(e, T)` instead of `e isa T` in catch-blocks, to keep code from breaking when libraries wrap user's exceptions. This makes application code resilient to library changes that may cause wrapped exceptions, such as e.g. changes to underlying concurrency decisions (thus maintaining concurrency's cooperative benefits). # Example ```julia try # If this becomes concurrent in the future, the catch-block doesn't need to change. library_function(args...) catch e if has_wrapped_exception(e, MyExceptionType) unwrapped = unwrap_exception_until(e, MyExceptionType) handle_my_exception(unwrapped, caught=e) else rethrow() end end ``` """ function has_wrapped_exception end """ is_wrapped_exception(e)::Bool Returns true if the given exception instance, `e` is a wrapped exception, such that `unwrap_exception(e)` would return something different than `e`. """ function is_wrapped_exception end """ unwrap_exception(exception_wrapper) -> wrapped_exception unwrap_exception(normal_exception) -> normal_exception # Add overrides for custom exception types ExceptionUnwrapping.unwrap_exception(e::MyWrappedException) = e.wrapped_exception Unwraps a wrapped exception by one level. *New wrapped exception types should add a method to this function.* One example of a wrapped exception is the `TaskFailedException`, which wraps an exception thrown by a `Task` with a new `Exception` describing the task failure. It is useful to unwrap the exception to test what kind of exception was thrown in the first place, which is useful in case you need different exception handling behavior for different types of exceptions. Authors of new wrapped exception types can overload this to indicate what field their exception is wrapping, by adding an overload, e.g.: ```julia ExceptionUnwrapping.unwrap_exception(e::MyWrappedException) = e.wrapped_exception ``` This is used in the implementations of the other functions in the module: - [`has_wrapped_exception(e, ::Type)`](@ref) - [`unwrap_exception_to_root(e)`](@ref) """ function unwrap_exception end """ unwrap_exception_until(e, ExceptionType)::ExceptionType Recursively unwrap a wrapped exception `e` until reaching an instance of `ExceptionType`. """ function unwrap_exception_until end """ unwrap_exception_to_root(exception_wrapper) -> wrapped_exception unwrap_exception_to_root(normal_exception) -> normal_exception Unwrap a wrapped exception to its bottom layer. """ function unwrap_exception_to_root end struct UnwrappedExceptionNotFound{RequestedType, ExceptionType} <: Base.Exception exception::ExceptionType end UnwrappedExceptionNotFound{R}(e::E) where {R,E} = UnwrappedExceptionNotFound{R,E}(e) # We have confirmed via Cthulhu and the Allocations profiler that these seem to correctly # not be specializing, and not allocating. @nospecialize # Base case is that e -> e unwrap_exception(e) = e # Add overloads for wrapped exception types to unwrap the exception. # TaskFailedExceptions wrap a failed task, which contains the exception that caused it # to fail. You can unwrap the exception to discover the root cause of the failure. unwrap_exception(e::Base.TaskFailedException) = e.task.exception unwrap_exception(e::Base.CapturedException) = e.ex has_wrapped_exception(::T, ::Type{T}) where T = true # Types don't match, do the unrolling, but prevent inference since this happens at runtime # and only during exception catch blocks, and might have arbitrarily nested types. And in # practice, we've seen julia's inference really struggles here. # The inferencebarrier blocks the callee from being inferred until it's actually called at # runtime, so that we don't pay for expensive inference if the exception path isn't # triggered. function has_wrapped_exception(e, ::Type{T}) where T Base.inferencebarrier(_has_wrapped_exception)(e, T) end function _has_wrapped_exception(e, ::Type{T}) where T while !(e isa T) && is_wrapped_exception(e) e::Any = unwrap_exception(e) end return e isa T end function is_wrapped_exception(e) return e !== unwrap_exception(e) end @specialize unwrap_exception_until(e::T, ::Type{T}) where T = e @nospecialize function unwrap_exception_until(e, ::Type{T}) where T Base.inferencebarrier(_unwrap_exception_until)(e, T) end function _unwrap_exception_until(e, ::Type{T}) where T while !(e isa T) && is_wrapped_exception(e) e::Any = unwrap_exception(e) end if e isa T return e else throw(UnwrappedExceptionNotFound{T}(e)) end end function unwrap_exception_to_root(e) Base.inferencebarrier(_unwrap_exception_to_root)(e) end function _unwrap_exception_to_root(e) while is_wrapped_exception(e) e::Any = unwrap_exception(e) end return e end @specialize end # module H/opt/julia/packages/ExceptionUnwrapping/kba8L/src/test_throws_wrapped.jl import Test # For @test_throws_wrapped using Test: Returned, Threw, Fail, Pass, ExecutionResult """ @test_throws_wrapped exception expr Similar to `Test.@test_throws`, but this tests that the expression `expr` either throws `exception`, OR that it throws an expression _wrapping_ `exception`. Users can use this function if they aren't concerned about whether an exception is wrapped or not, e.g. not caring whether the user is using concurrency. # Examples ```jldoctest julia> @test_throws_wrapped BoundsError [1, 2, 3][4] Test Passed Thrown: BoundsError julia> @test_throws_wrapped DimensionMismatch fetch(@async [1, 2, 3] + [1, 2]) Test Passed Thrown: DimensionMismatch ``` """ macro test_throws_wrapped(extype, ex) orig_ex = Expr(:inert, ex) result = quote try Returned($(esc(ex)), nothing, $(QuoteNode(__source__))) catch _e if $(esc(extype)) != InterruptException && _e isa InterruptException rethrow() end Threw(_e, nothing, $(QuoteNode(__source__))) end end Base.remove_linenums!(result) :(do_test_throws_wrapped($result, $orig_ex, $(esc(extype)))) end # An internal function, called by the code generated by @test_throws # to evaluate and catch the thrown exception - if it exists function do_test_throws_wrapped(result::ExecutionResult, @nospecialize(orig_expr), @nospecialize(extype)) if isa(result, Threw) # Check that the right type of exception was thrown success = false exc = result.exception if isa(extype, Type) success = has_wrapped_exception(exc, extype) else if has_wrapped_exception(exc, typeof(extype)) exc = unwrap_exception_until(exc, typeof(extype)) success = true for fld in 1:nfields(extype) if !isequal(getfield(extype, fld), getfield(exc, fld)) success = false break end end end end if success testres = Pass(:test_throws, nothing, nothing, exc) else testres = Fail(:test_throws_wrong, orig_expr, extype, exc, result.source) end else testres = Fail(:test_throws_nothing, orig_expr, extype, nothing, result.source) end Test.record(Test.get_testset(), testres) end F/opt/julia/packages/ExceptionUnwrapping/kba8L/src/exception_summary.jl############### #= # TODOs - Call it from our codebase - Unit tests - Seen set, for deduplication =# # Consider adding a _summarize_exception() overload for DistributedException # Pros: less noise # Cons: possibly hiding intermediate exceptions that might have been helpful to see. const TITLE = "=== EXCEPTION SUMMARY ===" const SEPARATOR = "--" const INDENT_LENGTH = 4 """ summarize_current_exceptions(io::IO = Base.stderr, task = current_task()) Print a summary of the [current] task's exceptions to `io`. This is particularly helpful in cases where the exception stack is large, the backtraces are large, and CompositeExceptions with multiple parts are involved. """ function summarize_current_exceptions(io::IO = Base.stderr, task::Task = current_task()) _indent_print(io, TITLE, '\n'; color=Base.info_color()) println(io) _summarize_task_exceptions(io, task) return nothing end function _indent_print(io::IO, x...; color=:normal, prefix = nothing) indent = get(io, :indent, 0) if prefix !== nothing ind = max(0, indent - length(prefix)) printstyled(io, " "^ind, prefix, x...; color=color) else printstyled(io, " "^indent, x...; color=color) end end function _indent_println(io::IO, x...; color=:normal, prefix = nothing) _indent_print(io, x..., "\n"; color=color, prefix=prefix) end function _indent_print(io::IO, io_src::IO; prefix = nothing) indent = get(io, :indent, 0) for (i, line) in enumerate(eachline(io_src)) if prefix !== nothing && i == 1 ind = max(0, indent - length(prefix)) printstyled(io, " "^ind, prefix, line) else i !== 1 && println(io) printstyled(io, " "^indent, line) end end end function _summarize_task_exceptions(io::IO, task::Task; prefix = nothing) exception_stack = current_exceptions(task) for (i, (e, stack)) in enumerate(exception_stack) if i != 1 # TODO: should the indention increase here? println(io) # Clear out the prefix after the first exception being printed prefix = nothing _indent_println(io, "which caused:"; color=Base.error_color()) end _summarize_exception(io, e, stack, prefix = prefix) end end """ _summarize_exception(io::IO, e::TaskFailedException, _) _summarize_exception(io::IO, e::CompositeException, stack) _summarize_exception(io::IO, e::Exception, stack) The secret sauce that lets us unwrap TaskFailedExceptions and CompositeExceptions, and summarize the actual exception. TaskFailedException simply wraps a task, so it is just unwrapped, and processed by _summarize_task_exceptions(). CompositeException simply wraps a Vector of Exceptions. Each of the individual Exceptions is summarized. All other exceptions are printed via [`Base.showerror()`](@ref). The first stackframe in the backtrace is also printed. """ function _summarize_exception(io::IO, e::TaskFailedException, _unused_ ; prefix = nothing) # recurse down the exception stack to find the original exception _summarize_task_exceptions(io, e.task, prefix = prefix) end function _summarize_exception(io::IO, e::CompositeException, stack; prefix = nothing) # If only one Exception is wrapped, go directly to it to avoid a level of indentation. if length(e) == 1 return _summarize_exception(io, only(e.exceptions), stack; prefix = prefix) end _indent_println(io, "CompositeException (", length(e), " tasks):", prefix = prefix) indent = get(io, :indent, 0) io = IOContext(io, :indent => indent + INDENT_LENGTH) for (i, ex) in enumerate(e.exceptions) _summarize_exception(io, ex, stack; prefix = "$i. ") # print something to separate the multiple exceptions wrapped by CompositeException if i != length(e.exceptions) sep_io = IOContext(io, :indent => indent+1) _indent_println(sep_io, SEPARATOR) end end end # This is the overload that prints the actual exception that occurred. function _summarize_exception(io::IO, exc, stack; prefix = nothing) # First, check that this exception isn't some other kind of user-defined # wrapped exception. We want to unwrap this layer as well, so that we are # printing just the true exceptions in the summary, not any exception # wrappers. if is_wrapped_exception(exc) unwrapped = unwrap_exception(exc) return _summarize_exception(io, unwrapped, stack; prefix) end # Otherwise, we are at the fully unwrapped exception, now. indent = get(io, :indent, 0) # used for print_stackframe # Print the unwrapped exception. exc_io = IOBuffer() Base.showerror(exc_io, exc) seekstart(exc_io) # Print all lines of the exception indented. _indent_print(io, exc_io; prefix = prefix) println(io) # Print the source line number of the where the exception occurred. # In order to save performance, only process the backtrace up until the first printable # frame. (Julia skips frames from the C runtime when printing backtraces.) # A report was received about an error where bt was not defined. Band-aid by # initializing it as an empty vector. It's not understood why there was no backtrace. bt = [] for i in eachindex(stack) bt = Base.process_backtrace(stack[i:i]) if !isempty(bt) break end end # Now print just the very first frame we've collected: if isempty(bt) # A report was received about bt being a 0-element Vector. It's not clear why the # stacktrace is missing, but this should tide us over in the meantime. _indent_println(io, "no stacktrace available") else (frame, n) = bt[1] # borrowed from julia/base/errorshow.jl modulecolordict = copy(Base.STACKTRACE_FIXEDCOLORS) modulecolorcycler = Iterators.Stateful(Iterators.cycle(Base.STACKTRACE_MODULECOLORS)) Base.print_stackframe(io, 1, frame, n, indent+1, modulecolordict, modulecolorcycler) println(io) end end 7