[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: re-raising an exception with the original backtrace

dblack

11/28/2003 11:21:00 PM

2 Answers

Joel VanderWerf

11/28/2003 11:54:00 PM

0

David A. Black wrote:
> Hi --
>
> On Sat, 29 Nov 2003, Joel VanderWerf wrote:
>
>
>>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
---
exception.rb:4:in `foo': MyError (MyError)
from exception.rb:8:in `main'
from exception.rb:31



nobu.nokada

11/29/2003 9:53:00 AM

0

Hi,

At Sat, 29 Nov 2003 08:53:40 +0900,
Joel VanderWerf wrote:
> 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
---
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) {
+ retry_entry:
result = rb_eval(self, node->nd_head);
}
@@ -2865,9 +2866,8 @@ rb_eval(self, n)
else if (state == TAG_RETRY) {
rescuing = state = 0;
- e_info = ruby_errinfo = Qnil;
- result = rb_eval(self, node->nd_head);
+ ruby_errinfo = e_info;
+ goto retry_entry;
}
else if (state != TAG_RAISE) {
- ruby_errinfo = e_info;
result = prot_tag->retval;
}
@@ -2883,5 +2883,4 @@ rb_eval(self, n)
rescuing = 1;
result = rb_eval(self, resq->nd_body);
- ruby_errinfo = e_info;
break;
}
@@ -2893,4 +2892,5 @@ rb_eval(self, n)
}
POP_TAG();
+ if (state != TAG_RAISE) ruby_errinfo = e_info;
if (state) {
if (state == TAG_NEXT) prot_tag->retval = result;


--
Nobu Nakada