Spec-Zone.ru › Ruby 3.4

класс Exception

Родитель:
Объект

Класс Exception и его подклассы используются для обозначения возникшей ошибки или другой проблемы, которая может потребовать обработки. См. Исключения.

Объект Exception несёт определённую информацию:

  • Тип (класс исключения), обычно StandardError, RuntimeError или подкласс одного из них; см. Иерархия встроенных классов исключений.

  • Необязательное описательное сообщение; см. методы ::new, message.

  • Необязательная информация о стеке вызовов; см. методы backtrace, backtrace_locations, set_backtrace.

  • Необязательная причина; см. метод cause.

Иерархия встроенных классов исключений

Иерархия встроенных подклассов класса Exception:

  • NoMemoryError

  • ScriptError

    • LoadError

    • NotImplementedError

    • SyntaxError

  • SecurityError

  • SignalException

    • Interrupt

  • StandardError

    • ArgumentError

      • UncaughtThrowError

    • EncodingError

    • FiberError

    • IOError

      • EOFError

    • IndexError

      • KeyError

      • StopIteration

        • ClosedQueueError

    • LocalJumpError

    • NameError

      • NoMethodError

    • RangeError

      • FloatDomainError

    • RegexpError

    • RuntimeError

      • FrozenError

    • SystemCallError

      • Errno (и его подклассы, представляющие системные ошибки)

    • ThreadError

    • TypeError

    • ZeroDivisionError

  • SystemExit

  • SystemStackError

  • fatal

Публичные методы класса

exception(message = nil) → self or new_exception

Возвращает объект исключения того же класса, что и 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
json_create (object)
Исходный код
# 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.

new(message = nil) → exception
Исходный код
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>
to_tty? → true or false
Исходный код
static VALUE
exc_s_to_tty_p(VALUE self)
{
    return RBOOL(rb_stderr_tty_p());
}

Возвращает true если сообщения об исключениях будут отправлены на устройство терминала.

END_OF_DOCUMENT_MARKER

Методы экземпляра публичного интерфейса

self == object → true или false
Исходный код
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.

as_json (*)
Исходный код
# 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>
backtrace → массив или nil
Исходный код
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, предоставляющий то же значение в виде структурированных объектов. (Обратите внимание, что два значения могут не совпадать, если стеки вызовов вручную корректируются.)

См. Стеки вызовов.

backtrace_locations → массив или nil
Исходный код
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, предоставляющий то же значение в виде массива строк. (Обратите внимание, что два значения могут не совпадать, если стеки вызовов вручную корректируются.)

См. Стеки вызовов.

cause → исключение или nil
Исходный код
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.
detailed_message(highlight: false, **kwargs) → строка
Исходный код
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 для добавления информации:

  • DidYouMean::Correctable#detailed_message.

  • ErrorHighlight::CoreExt#detailed_message.

  • SyntaxSuggest#detailed_message.

Метод, производящий переопределение, должен быть совместим с переданными ключевыми аргументами, которые могут включать (но не ограничиваются):

  • :highlight.

  • :did_you_mean.

  • :error_highlight.

  • :syntax_suggest.

Метод, производящий переопределение, также должен быть внимательным к улучшениям кодов ANSI; см. Сообщения.

exception(message = nil) → self или new_exception
Исходный код
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
full_message(highlight: true, order: :top) → строка
Исходный код
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; см. Сообщения.

inspect → строка
Исходный код
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>"
message → строка
Исходный код
static VALUE
exc_message(VALUE exc)
{
    return rb_funcallv(exc, idTo_s, 0, 0);
}

Возвращает to_s.

См. Сообщения.

set_backtrace(value) → value
Исходный код
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

См. Стеки вызовов.

to_json (*args)
Исходный код
# 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}
to_s → string
Исходный код
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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API