Feature #13557
closed
there's no way to pass backtrace locations as a massaged backtrace
Added by sylvain.joyeux (Sylvain Joyeux) over 7 years ago.
Updated 8 months ago.
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)
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
+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).
- 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)
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
.
I think both Kernel#raise
and Exception#set_backtrace
would need to accept array of Thread::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
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
.
byroot (Jean Boussier) wrote in #note-7:
Which I think is simple enough to not warrant and extra skip:
parameter to raise
.
Good point, I'm convinced.
- Description updated (diff)
I'm not against but could you summarize usages?
@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)
.
- 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
Also available in: Atom
PDF
Like1
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0