class Exception
Exception 类及其子类用于指示发生了错误或其他问题,可能需要处理。请参阅 异常。
一个 Exception 对象携带某些信息
-
类型(异常的类),通常是
StandardError,RuntimeError,或者其中一个的子类;请参阅 内置异常类层次结构。 -
可选的回溯信息;请参阅方法
backtrace,backtrace_locations,set_backtrace。 -
可选的原因;请参阅方法
cause。
内置异常类层次结构¶ ↑
Exception 类的内置子类的层次结构
-
-
-
Errno(及其子类,表示系统错误)
-
公共类方法
返回与 self 相同类的异常对象;对于创建相似的异常但具有不同的消息很有用。
当 message 为 nil 时,返回 self
x0 = StandardError.new('Boom') # => #<StandardError: Boom> x1 = x0.exception # => #<StandardError: Boom> x0.__id__ == x1.__id__ # => true
当 message 为 可转换为字符串的对象(即使与原始消息相同)时,返回一个新的异常对象,该对象的类与 self 相同,并且其消息是给定的 message
x1 = x0.exception('Boom') # => #<StandardError: Boom> x0..equal?(x1) # => false
返回一个新的异常对象。
给定的 message 应该是可转换为字符串的对象;请参阅方法 message;如果未给出,则消息是新实例的类名(可能是子类的名称)
示例
Exception.new # => #<Exception: Exception> LoadError.new # => #<LoadError: LoadError> # Subclass of Exception. Exception.new('Boom') # => #<Exception: Boom>
static VALUE
exc_initialize(int argc, VALUE *argv, VALUE exc)
{
VALUE arg;
arg = (!rb_check_arity(argc, 0, 1) ? Qnil : argv[0]);
return exc_init(exc, arg);
}
如果异常消息将发送到终端设备,则返回 true。
static VALUE
exc_s_to_tty_p(VALUE self)
{
return RBOOL(rb_stderr_tty_p());
}
公共实例方法
返回 object 是否与 self 的类相同,并且其 message 和 backtrace 是否与 self 的相等。
static VALUE
exc_equal(VALUE exc, VALUE obj)
{
VALUE mesg, backtrace;
if (exc == obj) return Qtrue;
if (rb_obj_class(exc) != rb_obj_class(obj)) {
int state;
obj = rb_protect(try_convert_to_exception, obj, &state);
if (state || UNDEF_P(obj)) {
rb_set_errinfo(Qnil);
return Qfalse;
}
if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse;
mesg = rb_check_funcall(obj, id_message, 0, 0);
if (UNDEF_P(mesg)) return Qfalse;
backtrace = rb_check_funcall(obj, id_backtrace, 0, 0);
if (UNDEF_P(backtrace)) return Qfalse;
}
else {
mesg = rb_attr_get(obj, id_mesg);
backtrace = exc_backtrace(obj);
}
if (!rb_equal(rb_attr_get(exc, id_mesg), mesg))
return Qfalse;
return rb_equal(exc_backtrace(exc), backtrace);
}
将回溯(导致异常的代码位置列表)作为字符串数组返回。
示例(假设代码存储在名为 t.rb 的文件中)
def division(numerator, denominator) numerator / denominator end begin division(1, 0) rescue => ex p ex.backtrace # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"] loc = ex.backtrace.first p loc.class # String end
当引发(请参阅 Kernel#raise)或在 set_backtrace 的中间处理过程中,此方法返回的值可能会被调整。
另请参阅 backtrace_locations,它以结构化对象的形式提供相同的值。(请注意,当手动调整回溯时,两个值可能不一致。)
请参阅回溯。
static VALUE
exc_backtrace(VALUE exc)
{
VALUE obj;
obj = rb_attr_get(exc, id_bt);
if (rb_backtrace_p(obj)) {
obj = rb_backtrace_to_str_ary(obj);
/* rb_ivar_set(exc, id_bt, obj); */
}
return obj;
}
将回溯(导致异常的代码位置列表)作为 Thread::Backtrace::Location 实例的数组返回。
示例(假设代码存储在名为 t.rb 的文件中)
def division(numerator, denominator) numerator / denominator end begin division(1, 0) rescue => ex p ex.backtrace_locations # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"] loc = ex.backtrace_locations.first p loc.class # Thread::Backtrace::Location p loc.path # "t.rb" p loc.lineno # 2 p loc.label # "Integer#/" end
当引发(请参阅 Kernel#raise)或在 set_backtrace 的中间处理过程中,此方法返回的值可能会被调整。
另请参阅 backtrace,它以字符串数组的形式提供相同的值。(请注意,当手动调整回溯时,两个值可能不一致。)
请参阅 回溯。
static VALUE
exc_backtrace_locations(VALUE exc)
{
VALUE obj;
obj = rb_attr_get(exc, id_bt_locations);
if (!NIL_P(obj)) {
obj = rb_backtrace_to_location_ary(obj);
}
return obj;
}
返回全局变量 $! 的先前值,该值可能为 nil (请参阅 全局变量)
begin raise('Boom 0') rescue => x0 puts "Exception: #{x0}; $!: #{$!}; cause: #{x0.cause.inspect}." begin raise('Boom 1') rescue => x1 puts "Exception: #{x1}; $!: #{$!}; cause: #{x1.cause}." begin raise('Boom 2') rescue => x2 puts "Exception: #{x2}; $!: #{$!}; cause: #{x2.cause}." end end end
输出
Exception: Boom 0; $!: Boom 0; cause: nil. Exception: Boom 1; $!: Boom 1; cause: Boom 0. Exception: Boom 2; $!: Boom 2; cause: Boom 1.
static VALUE
exc_cause(VALUE exc)
{
return rb_attr_get(exc, id_cause);
}
返回带有增强功能的消息字符串
-
在第一行中包含异常类名。
-
如果关键字
highlight的值为true,则包括粗体和下划线 ANSI 代码(见下文)以增强消息的外观。
示例
begin 1 / 0 rescue => x p x.message p x.detailed_message # Class name added. p x.detailed_message(highlight: true) # Class name, bolding, and underlining added. end
输出
"divided by 0" "divided by 0 (ZeroDivisionError)" "\e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m"
此方法被 Ruby 标准库中的一些 gem 重写以添加信息
-
DidYouMean::Correctable#detailed_message。
-
ErrorHighlight::CoreExt#detailed_message。
-
SyntaxSuggest#detailed_message。
重写方法必须容忍传递的关键字参数,其中可能包括(但不限于)
-
:highlight. -
:did_you_mean. -
:error_highlight. -
:syntax_suggest.
重写方法还应谨慎使用 ANSI 代码增强功能;请参阅 消息。
static VALUE
exc_detailed_message(int argc, VALUE *argv, VALUE exc)
{
VALUE opt;
rb_scan_args(argc, argv, "0:", &opt);
VALUE highlight = check_highlight_keyword(opt, 0);
extern VALUE rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight);
return rb_decorate_message(CLASS_OF(exc), rb_get_message(exc), RTEST(highlight));
}
返回与 self 相同类的异常对象;对于创建相似的异常但具有不同的消息很有用。
当 message 为 nil 时,返回 self
x0 = StandardError.new('Boom') # => #<StandardError: Boom> x1 = x0.exception # => #<StandardError: Boom> x0.__id__ == x1.__id__ # => true
当 message 为 可转换为字符串的对象(即使与原始消息相同)时,返回一个新的异常对象,该对象的类与 self 相同,并且其消息是给定的 message
x1 = x0.exception('Boom') # => #<StandardError: Boom> x0..equal?(x1) # => false
static VALUE
exc_exception(int argc, VALUE *argv, VALUE self)
{
VALUE exc;
argc = rb_check_arity(argc, 0, 1);
if (argc == 0) return self;
if (argc == 1 && self == argv[0]) return self;
exc = rb_obj_clone(self);
rb_ivar_set(exc, id_mesg, argv[0]);
return exc;
}
返回增强的消息字符串
-
包括异常类名。
-
如果关键字
highlight的值为 true(不是nil或false),则包括粗体 ANSI 代码(见下文)以增强消息的外观。 -
包括回溯
-
如果关键字
order的值为:top(默认值),则首先列出错误消息和最内层的回溯条目。 -
如果关键字
order的值为:bottom,则最后列出错误消息和最内层的条目。
-
示例
def baz begin 1 / 0 rescue => x pp x.message pp x.full_message(highlight: false).split("\n") pp x.full_message.split("\n") end end def bar; baz; end def foo; bar; end foo
输出
"divided by 0" ["t.rb:3:in 'Integer#/': divided by 0 (ZeroDivisionError)", "\tfrom t.rb:3:in 'Object#baz'", "\tfrom t.rb:10:in 'Object#bar'", "\tfrom t.rb:11:in 'Object#foo'", "\tfrom t.rb:12:in '<main>'"] ["t.rb:3:in 'Integer#/': \e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m", "\tfrom t.rb:3:in 'Object#baz'", "\tfrom t.rb:10:in 'Object#bar'", "\tfrom t.rb:11:in 'Object#foo'", "\tfrom t.rb:12:in '<main>'"]
重写方法应谨慎使用 ANSI 代码增强功能;请参阅 消息。
static VALUE
exc_full_message(int argc, VALUE *argv, VALUE exc)
{
VALUE opt, str, emesg, errat;
VALUE highlight, order;
rb_scan_args(argc, argv, "0:", &opt);
highlight = check_highlight_keyword(opt, 1);
order = check_order_keyword(opt);
{
if (NIL_P(opt)) opt = rb_hash_new();
rb_hash_aset(opt, sym_highlight, highlight);
}
str = rb_str_new2("");
errat = rb_get_backtrace(exc);
emesg = rb_get_detailed_message(exc, opt);
rb_error_write(exc, emesg, errat, str, opt, highlight, order);
return str;
}
返回 self 的字符串表示形式
x = RuntimeError.new('Boom') x.inspect # => "#<RuntimeError: Boom>" x = RuntimeError.new x.inspect # => "#<RuntimeError: RuntimeError>"
static VALUE
exc_inspect(VALUE exc)
{
VALUE str, klass;
klass = CLASS_OF(exc);
exc = rb_obj_as_string(exc);
if (RSTRING_LEN(exc) == 0) {
return rb_class_name(klass);
}
str = rb_str_buf_new2("#<");
klass = rb_class_name(klass);
rb_str_buf_append(str, klass);
if (RTEST(rb_str_include(exc, rb_str_new2("\n")))) {
rb_str_catf(str, ":%+"PRIsVALUE, exc);
}
else {
rb_str_buf_cat(str, ": ", 2);
rb_str_buf_append(str, exc);
}
rb_str_buf_cat(str, ">", 1);
return str;
}
设置 self 的回溯值;返回给定的 value。
value 可能为
使用 Thread::Backtrace::Location 数组是最一致的选择:它同时设置了 backtrace 和 backtrace_locations。如果可能,应优先使用。可以从 Kernel#caller_locations 获取合适的位置数组,从另一个错误复制,或者仅设置为当前错误的 backtrace_locations 的调整结果
require 'json' def parse_payload(text) JSON.parse(text) # test.rb, line 4 rescue JSON::ParserError => ex ex.set_backtrace(ex.backtrace_locations[2...]) raise end parse_payload('{"wrong: "json"') # test.rb:4:in 'Object#parse_payload': unexpected token at '{"wrong: "json"' (JSON::ParserError) # # An error points to the body of parse_payload method, # hiding the parts of the backtrace related to the internals # of the "json" library # The error has both #backtace and #backtrace_locations set # consistently: begin parse_payload('{"wrong: "json"') rescue => ex p ex.backtrace # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"] p ex.backtrace_locations # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"] end
当所需的位置堆栈不可用且应从头开始构造时,可以使用字符串数组或单个字符串。在这种情况下,只有 backtrace 受影响
def parse_payload(text) JSON.parse(text) rescue JSON::ParserError => ex ex.set_backtrace(["dsl.rb:34", "framework.rb:1"]) # The error have the new value in #backtrace: p ex.backtrace # ["dsl.rb:34", "framework.rb:1"] # but the original one in #backtrace_locations p ex.backtrace_locations # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...] end parse_payload('{"wrong: "json"')
使用 nil 调用 set_backtrace 会清除 backtrace,但不会影响 backtrace_locations
def parse_payload(text) JSON.parse(text) rescue JSON::ParserError => ex ex.set_backtrace(nil) p ex.backtrace # nil p ex.backtrace_locations # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...] end parse_payload('{"wrong: "json"')
在重新引发此类异常时,backtrace 和 backtrace_locations 都将设置为重新引发的位置
def parse_payload(text) JSON.parse(text) rescue JSON::ParserError => ex ex.set_backtrace(nil) raise # test.rb, line 7 end begin parse_payload('{"wrong: "json"') rescue => ex p ex.backtrace # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"] p ex.backtrace_locations # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"] end
请参阅 回溯。
static VALUE
exc_set_backtrace(VALUE exc, VALUE bt)
{
VALUE btobj = rb_location_ary_to_backtrace(bt);
if (RTEST(btobj)) {
rb_ivar_set(exc, id_bt, btobj);
rb_ivar_set(exc, id_bt_locations, btobj);
return bt;
}
else {
return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
}
}
返回 self 的字符串表示形式
x = RuntimeError.new('Boom') x.to_s # => "Boom" x = RuntimeError.new x.to_s # => "RuntimeError"
static VALUE
exc_to_s(VALUE exc)
{
VALUE mesg = rb_attr_get(exc, idMesg);
if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
return rb_String(mesg);
}