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
源代码
# File ext/json/lib/json/add/exception.rb, line 9 def self.json_create(object) result = new(object['m']) result.set_backtrace object['b'] result end
请参阅 as_json。
源代码
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);
}
返回一个新的异常对象。
给定的 message 应该是一个可转换为字符串的对象;请参阅方法 message;如果未给出,则消息为新实例的类名(可能是子类的名称)。
示例
Exception.new # => #<Exception: Exception> LoadError.new # => #<LoadError: LoadError> # Subclass of Exception. Exception.new('Boom') # => #<Exception: Boom>
源代码
static VALUE
exc_s_to_tty_p(VALUE self)
{
return RBOOL(rb_stderr_tty_p());
}
如果异常消息将发送到终端设备,则返回 true。
公共实例方法
源代码
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);
}
如果 object 与 self 的类相同,并且其 message 和 backtrace 与 self 的相同,则返回 true。
源代码
# File ext/json/lib/json/add/exception.rb, line 29 def as_json(*) { JSON.create_id => self.class.name, 'm' => message, 'b' => backtrace, } end
方法 Exception#as_json 和 Exception.json_create 可用于序列化和反序列化 Exception 对象;请参阅 Marshal。
方法 Exception#as_json 序列化 self,返回一个表示 self 的 2 元素哈希值。
require 'json/add/exception' x = Exception.new('Foo').as_json # => {"json_class"=>"Exception", "m"=>"Foo", "b"=>nil}
方法 JSON.create 反序列化这样的哈希值,返回一个 Exception 对象。
Exception.json_create(x) # => #<Exception: Foo>
源代码
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;
}
返回回溯(导致异常的代码位置列表),作为字符串数组。
示例(假设代码存储在名为 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_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;
}
返回回溯(导致异常的代码位置列表),作为 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_cause(VALUE exc)
{
return rb_attr_get(exc, id_cause);
}
返回全局变量 $! 的先前值,该值可能为 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_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));
}
返回带有增强功能的消息字符串。
-
在第一行中包含异常类名。
-
如果关键字
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 覆盖以添加信息:
覆盖方法必须容忍传递的关键字参数,其中可能包括(但不限于):
-
:highlight. -
:did_you_mean. -
:error_highlight. -
:syntax_suggest.
覆盖方法还应小心处理 ANSI 代码增强;请参阅 消息。
源代码
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;
}
返回与 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_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;
}
返回增强的消息字符串。
-
包含异常类名。
-
如果关键字
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_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 的字符串表示形式。
x = RuntimeError.new('Boom') x.inspect # => "#<RuntimeError: Boom>" x = RuntimeError.new x.inspect # => "#<RuntimeError: RuntimeError>"
源代码
源代码
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 的回溯值;返回给定的 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
请参阅 回溯。
源代码
# File ext/json/lib/json/add/exception.rb, line 46 def to_json(*args) as_json.to_json(*args) end
返回一个表示 self 的 JSON 字符串。
require 'json/add/exception' puts Exception.new('Foo').to_json
输出
{"json_class":"Exception","m":"Foo","b":null}
源代码
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);
}
返回 self 的字符串表示形式。
x = RuntimeError.new('Boom') x.to_s # => "Boom" x = RuntimeError.new x.to_s # => "RuntimeError"