-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Allow packages to provide custom hints for Exceptions #35094
Conversation
Package authors may be able to predict likely user errors, and it can be nice to create an informative hint. For MethodErrors, we've done this repeatedly for Base; this PR makes it possible for packages to get in on the fun. The procedure is described in the docstring for `Base.methoderror_hints`.
IIUC, from a package maintainer's view, adding this feature into codebase means he'll need to maintain two branches: one for julia 1.0 and one for julia >= 1.5. So it's likely that only a few people will use it until the next LTS version release. |
If you don't intend to backport the hints to older versions of the package, I don't think you need to create branches. if VERSION >= v"1.5"
push!(Base.methoderror_hints, recommend_oneunit)
end However, I don't really like the interface using an array. Is that unavoidable for performance? |
I think a shared registry is unavoidable because we want downstream packages to reuse hints defined by upstream packages. For example, let Introducing a |
What exactly does "reuse" mean? I don't think downstream hints should depend on the upstream hints. 😕 |
By saing "reuse" I mean we'd better not redefine hints like this in
# julia nightly build on https://github.com/JuliaLang/julia/pull/34642
julia> x = rand(4, 4);
julia> x + 2
ERROR: MethodError: no method matching +(::Array{Float64,2}, ::Int64)
For element-wise addition, use broadcasting with dot syntax: array .+ scalar
Closest candidates are:
+(::Any, ::Any, ::Any, ::Any...) at operators.jl:529
+(::Complex{Bool}, ::Real) at complex.jl:301
+(::Missing, ::Number) at missing.jl:115
...
Stacktrace:
[1] top-level scope at REPL[2]:1 |
I don't understand the reason why @timholy used the array and |
@kimikage, How frequently do you expect your programs to generate errors? |
It depends on what we assume. I'm not good at English, so it will take at least 5 seconds to understand an error message. So the frequency is less than 0.2 Hz. 😄 Improving the performance of programs which won't work only makes sense in the cases of using CI systems and so on, because the programs won't work. Edit: |
My point is that worrying about performance of generating error messages is unnecessary. |
I don't think it makes sense to do this by multiple dispatch. Catching the error is handled by the runtime, so the exact dispatch of which method threw the error is not going to be compiled into some specialized MethodInstance. As a consequence, it's going to have to use runtime dispatch. Since all such handlers would have to be methods of the same function, and because there's probably little clear specialization by subtyping, the method table will essentially be a linear list. Consequently |
For reference, my test handler took 50ns to run. Imagine there are 1000 such handlers (which I don't think is out of the realm of possibility); if each takes 50ns, then the total run time is 50us. That's still way below the threshold for caring. We could have 100ns/handler and accommodate up to 10^6 such handlers before getting to 0.1s, which I'd say is the point at which I'd start caring about performance. |
@johnnychen94, just to be clear, when one handler issues a message, it doesn't block others from doing so too. One can imagine cases where that would be undesirable, but on balance I think it's the best choice if we're going to keep this simple. (Otherwise we'd need to develop a whole protocol for resolution, and that starts to smell overengineered.) |
True, my expression wasn't very accurate. I meant to say "sort the order of displaying from nearest(downstream package) to farthest(e.g., Base)" :P |
I simply care that |
Take the This is a circumstance where we're definitely not building real code, so normal rules don't really apply. You'll note the handlers already built into Base work essentially by trying each one. |
You could achieve that with |
Would this be affected by precompilation and/or package loading order? |
Loading order, yes. Not by precompilation order (you should |
This is just cosmetic, but I think it's better to quote the example with ```julia...```. 😄 |
Really great suggestions, @tkf! In addition, I wrapped the handler call in a |
I'll hold off a bit longer (there has been a lot of very useful feedback that has improved this considerably!), but from my standpoint this is good to merge. The only part I'm unhappy with is #35114, but that's an issue for another day. |
Holy cow, I missed this and it looks awesome. This completely supersedes #24299 (and I like just using an array of functions much much better than leaning so heavily on multiple dispatch)... excepting there are a few more ideas about the sorts of things to be registered there. |
register_error_hint(MethodError) do io, exc, argtypes, kwargs | ||
if exc.f == only_int | ||
# Color is not necessary, this is just to show it's possible. | ||
print(io, "\\nDid you mean to call ") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we recommend folks use @info
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's printing to a buffer io
so I don't think we can replace it with @info
.
(Or you are thinking to temporarily create a "buffer" logger to collect messages within a dynamic scope? It might work but I feel it's too fancy to do during error printing.)
I'm requesting triage here since this adds new exports. I admit, I greatly dislike adding things to Base. The functionality is good, I'm mostly thinking about where it goes. It's too bad how much UI-ish stuff is in Base; in fact I think that probably extends to the whole Global state is also always a problem. I think this could have issues with building system images; if you load packages that add hints and then save a system image, the handlers will be saved and then possibly re-added on startup. Emptying the dictionary in |
I guess an alternative idea may be to register a function via |
Sounds fine. Let me know if you want changes or just go ahead and revert if necessary. |
Package authors may be able to predict likely user errors, and it can be nice to create an informative hint. This PR makes it possible for packages to register hint-handlers for a variety of error types via `register_error_hint`. For packages that create their own custom Exception types, there is also `show_error_hints` which may be called from the `showerror` method.
Package authors may be able to predict likely user errors, and it can be nice to create an informative hint. This PR makes it possible for packages to register hint-handlers for a variety of error types via `register_error_hint`. For packages that create their own custom Exception types, there is also `show_error_hints` which may be called from the `showerror` method.
Package authors may be able to predict likely user errors, and it can be nice to create an informative hint. This PR makes it possible for packages to register hint-handlers for a variety of error types via `register_error_hint`. For packages that create their own custom Exception types, there is also `show_error_hints` which may be called from the `showerror` method.
For what it's worth I'd love if these functions were moved out of |
From triage: move the exports to the |
I will move it. Do you see a good solution to the global state problem? I am happy to work towards an alternative. |
In our short discussion on triage, Jeff (and others) talked about one reason to simply move it is because we don't have an obvious alternative but it sure is helpful. |
Package authors may be able to predict likely user errors, and it can be nice to create an informative hint. For MethodErrors, we've done this repeatedly for Base; this PR makes it possible for packages to get in on the fun.
The procedure is described in the docstring for
Base.methoderror_hintsregister_error_hint
.