Feature #13557
closedthere's no way to pass backtrace locations as a massaged backtrace
Description
When re-raising exceptions, it is sometimes useful to "massage" the backtrace (especially in DSLs).
There is currently no way to do it using only backtrace locations. This causes the new exception to have #backtrace_locations return nil, and thus makes backtrace_locations unreliable as a whole.
Example:
def test
raise ArgumentError, "", caller_locations
end
begin
test
rescue ArgumentError => e
p e.backtrace_locations
end
attempting to pass caller_location
to Kernel#raise
in the test
method fails with bla.rb:2:in `set_backtrace': backtrace must be Array of String (TypeError)
Updated by sylvain.joyeux (Sylvain Joyeux) over 7 years ago
Did a mistake. The code example uses caller_locations
(and therefore causes the TypeError
exception) while I meant it to use caller
and therefore cause e.backtrace_locations
to be nil
Updated by Eregon (Benoit Daloze) over 7 years ago
+1 This would be a nice feature and let the VM still keep the backtrace information in an flexible way (instead of dumping it to a String like Exception#backtrace).
Updated by jeremyevans0 (Jeremy Evans) over 5 years ago
- Tracker changed from Bug to Feature
- ruby -v deleted (
ruby 2.3.4p301 (2017-03-30 revision 58214) [x86_64-linux], ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]) - Backport deleted (
2.2: UNKNOWN, 2.3: UNKNOWN, 2.4: UNKNOWN)
Updated by byroot (Jean Boussier) 10 months ago
I'll try to find some time to implement this in the near future, because I just ran into this today, and resorted to the following hack:
unless exception.backtrace
begin
raise exception
rescue exception.class => raised_exception
raised_exception.backtrace.shift
raised_exception.backtrace_locations.shift
exception = raised_exception
end
end
I think both Kernel#raise
and Exception#set_backtrace
would need to accept array of Thread::Backtrace::Location
.
Updated by Dan0042 (Daniel DeLorme) 10 months ago
I think both
Kernel#raise
andException#set_backtrace
would need to accept array ofThread::Backtrace::Location
.
That's an idea I fully support. Although when I need to modify an error's backtrace 95% of the time it's because I want to skip the first few frames, like raise Error, "message", caller[3..-1]
. So it would be nice to have a shortcut idiom for this, like perhaps raise Error, "message", skip: 3
Updated by byroot (Jean Boussier) 10 months ago
Proposed patch here if someone feels like reviewing it: https://github.com/ruby/ruby/pull/10017
Updated by byroot (Jean Boussier) 10 months ago
So it would be nice to have a shortcut idiom for this, like perhaps raise Error, "message", skip: 3
With my patch it would be:
raise Error, "message", caller_locations(3)
Which I think is simple enough to not warrant and extra skip:
parameter to raise
.
Updated by Dan0042 (Daniel DeLorme) 10 months ago
byroot (Jean Boussier) wrote in #note-7:
Which I think is simple enough to not warrant and extra
skip:
parameter toraise
.
Good point, I'm convinced.
Updated by ko1 (Koichi Sasada) 9 months ago
I'm not against but could you summarize usages?
Updated by byroot (Jean Boussier) 9 months ago
@ko1 (Koichi Sasada) all the same use cases arrays of strings are currently used for:
E.g. raising an error with an existing backtrace:
raise NicerError, "some message", original_error.backtrace_locations
Or with some frames skipped:
raise SomeError, "some message", caller_locations(2) # skip 2 frames
Creating an exception with a thread backtrace:
async_error = SomeError.new("thread timed out")
async_error.set_backtrace(other_thread.backtrace_locations)
report(async_error)
And probably plenty others I'm not thinking of. This is only an improved version of passing set_backtrace(exc.backtrace)
.
Updated by byroot (Jean Boussier) 9 months ago
- Status changed from Open to Closed
Applied in changeset git|315bde5a0f95562f58405a43456ec6715ef20d32.
Exception#set_backtrace
accept arrays of Backtrace::Location
[Feature #13557]
Setting the backtrace with an array of strings is lossy. The resulting
exception will return nil on #backtrace_locations
.
By accepting an array of Backtrace::Location
instance, we can rebuild
a Backtrace
instance and have a fully functioning Exception.
Co-Authored-By: Étienne Barrié etienne.barrie@gmail.com