Is there a way to re-raise an exception without replacing its backtrace
with the current one?
The reason I want to do this is that I am catching an exception and
passing it to a handler. The handler needs to make a decision and then,
possibly, re-raise the same exception, preferrably with the same
information as it originally contained.
But if I do the following, the exception takes on the backtrace of the
handler, which has lost some information. I know I can add the original
backtrace to the exception’s message, but that’s not the same thing…
$ cat exception.rb
class MyError < StandardError; end
def foo
raise MyError
end
def main
foo
rescue Exception => e
handler(e)
end
def handler(e)
ok = false
if ok
return true
else
puts “backtrace:”, e.backtrace, "—"
raise e
end
end
main
$ ruby exception.rb
backtrace:
exception.rb:4:in foo' exception.rb:8:in
main’
exception.rb:23
···
exception.rb:19:in handler': MyError (MyError) from exception.rb:10:in
main’
from exception.rb:23
Note that the backtrace of e knows about foo, but the re-raised
exception does not.
David A. Black wrote:
Hi –
Is there a way to re-raise an exception without replacing its backtrace
with the current one?
The reason I want to do this is that I am catching an exception and
passing it to a handler. The handler needs to make a decision and then,
possibly, re-raise the same exception, preferrably with the same
information as it originally contained.
But if I do the following, the exception takes on the backtrace of the
handler, which has lost some information. I know I can add the original
backtrace to the exception’s message, but that’s not the same thing…
$ cat exception.rb
class MyError < StandardError; end
def foo
raise MyError
end
def main
foo
rescue Exception => e
handler(e)
end
def handler(e)
ok = false
if ok
return true
else
puts “backtrace:”, e.backtrace, “—”
raise e
I think if you just do ‘raise’ there, instead of ‘raise e’, it will
propagate the original exception and backtrace.
Ooh, that’s nice. I thought ‘raise’ would just re-raise the last
exception, but it’s actually sensitive to the dynamic context. The
example below would fail in the former case, but since it succeeds, I
guess raise looks back down the stack and re-raises the first exception
it finds there?
$ cat exception.rb
class MyError < StandardError; end
def foo
raise MyError
end
def main
foo
rescue Exception => e
handler(e)
end
class OtherError < StandardError; end
def do_something_that_might_use_exceptions
raise OtherError
rescue Exception => e
p e
false
end
def handler(e)
ok = do_something_that_might_use_exceptions
if ok
return true
else
puts “backtrace:”, e.backtrace, “—”
raise
end
end
main
$ ruby exception.rb
#<OtherError: OtherError>
backtrace:
exception.rb:4:in foo' exception.rb:8:in
main’
exception.rb:31
···
On Sat, 29 Nov 2003, Joel VanderWerf wrote:
exception.rb:4:in foo': MyError (MyError) from exception.rb:8:in
main’
from exception.rb:31
Hi,
Ooh, that’s nice. I thought ‘raise’ would just re-raise the last
exception, but it’s actually sensitive to the dynamic context. The
example below would fail in the former case, but since it succeeds, I
guess raise looks back down the stack and re-raises the first exception
it finds there?
But I found that outer $! was preserved only in rescue clauses,
in 1.6.8 and 1.8.1.
$ cat exception.rb
class MyError < StandardError; end
def foo
raise MyError
end
def main
foo
rescue Exception => e
handler(e)
end
class OtherError < StandardError; end
def do_something_that_might_use_exceptions(retrying = false)
raise OtherError
rescue Exception => e
retry if (retrying ^= true)
p e
false
end
def handler(e)
ok = do_something_that_might_use_exceptions
if ok
return true
else
puts “backtrace:”, e.backtrace, “—”
raise
end
end
main
$ ruby -v exception.rb
ruby 1.8.1 (2003-11-28) [i686-linux]
#<OtherError: OtherError>
backtrace:
exception.rb:4:in foo' exception.rb:8:in
main’
exception.rb:32
···
At Sat, 29 Nov 2003 08:53:40 +0900, Joel VanderWerf wrote:
exception.rb:28:in handler': unhandled exception from exception.rb:10:in
main’
from exception.rb:32
In other words, only changing $! in rescue clauses has no
effect for outside.
I suspect that rescue blocks should preserve $! value when no
exception raised and $! should not be cleared at retry.
Index: eval.c
RCS file: /cvs/ruby/src/ruby/eval.c,v
retrieving revision 1.598
diff -u -2 -p -r1.598 eval.c
— eval.c 27 Nov 2003 19:15:29 -0000 1.598
+++ eval.c 29 Nov 2003 09:48:56 -0000
@@ -2857,4 +2857,5 @@ rb_eval(self, n)
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
@@ -2865,9 +2866,8 @@ rb_eval(self, n)
else if (state == TAG_RETRY) {
rescuing = state = 0;
@@ -2883,5 +2883,4 @@ rb_eval(self, n)
rescuing = 1;
result = rb_eval(self, resq->nd_body);
@@ -2893,4 +2892,5 @@ rb_eval(self, n)
}
POP_TAG();
–
Nobu Nakada