класс Exception
Класс Exception и его подклассы используются для обозначения возникшей ошибки или другой проблемы, которая может потребовать обработки. См. Исключения.
Объект Exception несёт определённую информацию:
-
Тип (класс исключения), обычно
StandardError,RuntimeErrorили подкласс одного из них; см. Иерархия встроенных классов исключений. -
Необязательное описательное сообщение; см. методы
::new,message. -
Необязательная информация о стеке вызовов; см. методы
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.
Исходный код
# 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, возвращая хеш из 2 элементов, представляющий self:
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 для добавления информации:
Метод, производящий переопределение, должен быть совместим с переданными ключевыми аргументами, которые могут включать (но не ограничиваются):
-
: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_message(VALUE exc)
{
return rb_funcallv(exc, idTo_s, 0, 0);
} Возвращает to_s.
См. Сообщения.
Исходный код
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.
Значение стека вызовов может быть:
-
массивом
Thread::Backtrace::Location; -
массивом экземпляров
String; -
одним экземпляром
String; или -
nil.
Использование массива 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"')
Вызов set_backtrace со nil очищает 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
Возвращает строку JSON, представляющую self:
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"
Ruby Core © 1993–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.