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"