Intercepting error messages - HOW?

In my Term.jl project I aim to provide functionality to print nicely formatted, pretty, error messages. A preview:

image

This is inspired by Rich in python that prints error stacks like:

In python you can define an exception hook such that all errors go through it and you can print them as you’d like. I would like to the same in Julia, but haven’t found a way yet.

I haven’t found good docs on how errors are handled internally, but going through the code I’ve found functions like show_backtrace and showerror in Base.errorshow.jl and display_error in Base.client.jl as well as other similarly named functions.

I can re-define this to print out formatted error messages as above, but there’s several things I’m not clear with:

  1. what calls show_backtrace and errorshow? Does it depend on where the code is run (e.g. in VSCode or in the Julia app?). I can re-define show-backtrace and errorshow, but ideally I’d like to intercept error messages before.
  2. what prints out LoadErrors? It seems to bypass show_backtrace and errorshow as I can’t find a way to style that.
  3. Accessing locals: in python’s exceptions hook you have access to locals at all levels in the callstack (see second image above). I know there’s Base.@locals but that only gives the locals where the macro is used. Is there a way to get locals at each frame in a stacktrace or backtrace?
  4. Julia’s errors come with hints provided by Base.Experimental.show_error_hints, which is really nice. However when I try to use show_error_hints in my re-defined errorshow functions it doesn’t give any hint (it returns nothing). Is there a way to access error hints?

Thank you in advance for any help!

15 Likes

Yes, it depends on the IDE. In the REPL it’s handled by the following line: julia/REPL.jl at 2338f5d3040ced10d69f4d15cca5fdca03364d9a · JuliaLang/julia · GitHub. Note however that refining any of these functions is type piracy and strongly discouraged, since it can result in inconsistent behavior and excessive invalidations. Base.Experimental.register_error_hint is how additional hints are intended to be added.

They should go through the REPL as well. Do you have an example?

Yes, @locals always needs to be inserted into the source code. Otherwise you can’t access all locals inside a function unless you use an interpreter like GitHub - JuliaDebug/JuliaInterpreter.jl: Interpreter for Julia code

show_error_hints always returns nothing, the hints are printed to the supplied IO object. You can of course use sprint to get the output as a string or take a look at its source code if you want each separate hint.

1 Like

Thank you, that’s very helpful!

Note however that refining any of these functions is type piracy and strongly discouraged

Arrrr!

I would very much prefer not having to do that! But is there an alternative to it like python’s exception hook?
If lines like Base.invokelatest(Base.display_error, errio, val) that you’ve send above (and similar ones e.g. in VSCode’s Julia Extension) are what prints out the error, then I’m guessing I have to re-define Base.display_error?

At any rate, it wouldn’t be done by default in my package, it would be an option that users have to explicitly activate and I will warn them that it’s a hacky solution.

There is Base.Experimental.register_error_hint, but you seem to already have discovered that. Your projects looks very exciting, so if you have a concrete proposal for what kind of hooks you would need, I think the core devs would be open to discuss potential changes to base.

display_error should end up calling showerror, so redefining that should have similar effects.

Thanks!

I will try starting a discussion with the Julia devs, and perhaps use the hacky fix in the meanwhile.

display_error should end up calling showerror , so redefining that should have similar effects.

Correct, for now I have re-defined display_error and show_backtrace and seems to get me most of the way there.

I’ve also seen redirect_stderr, but I’m not sure if/how I could use that to achieve my goal here.

Either way, this was very helpful thank you for your time!

@simeonschaub I just wanted to thank you again.
The error formatting is coming along fairly well!

8 Likes

Perhaps a solution would be to push your own display to the top of the display stack. Literate.jl uses pushdisplay and popdisplay to catch display calls. I believe something similar is done for e.g. the plot pane in the VS code extensions.

Any chance that these pretty stack traces will become the default REPL experience? :heart_eyes:

1 Like

Thanks @fredrikekre I’ll look into that.

@juliohm that seems unlikely (for now at least). But using term is just a couple lines of code to set them up!

@friederikemeier

AbstractDisplay is something that I didn’t know about, and it might be very useful in the future, but as far as I can tell I can’t make much use of it here. Thanks anyway!