【问题标题】:Why does a SASS exception become `nil` by calling `backtrace` on it?为什么 SASS 异常通过调用 `backtrace` 会变成`nil`?
【发布时间】:2014-03-21 21:45:26
【问题描述】:

下面的代码定义了一个钩子 Kernel#at_exit 来捕获异常并在退出时执行操作,然后通过传递无效的 SASS 字符串引发 Sass::SyntaxError

require "sass"

module Kernel
  at_exit do
    puts "--Before backtrace"
    p $!
    $!.backtrace
    puts "--After backtrace"
    p $!
  end
end

Sass::Engine.new("Invalid {sass").render

它给出的输出如下:

...
--Before backtrace
#<Sass::SyntaxError: ...>
--After backtrace
nil
...

这表明$! 是一个Sass::SyntaxError,但是在调用backtrace 之后它就变成了nil。为什么$! 只调用backtrace 就改变了?

如下手动引发Sass::SyntaxError时似乎不会出现这种效果:

raise Sass::SyntaxError.new("foo")

或者当出现不同类型的错误时(可能是错误的)。

编辑

我不确定,但当引发 sass 错误时,sass 可能会使用 set_backtrace 操纵回溯。这是为了提供有关在 sass 文件中导致 sass 语法错误的位置的信息。手动引发错误和以编程方式引发错误之间的不同行为让人想起 Ruby 2.1 中的一个错误,该错误中途实现了backtrace_locations,但在某些情况下返回了nil。我大致猜测这些因素会造成干扰,但不确定。

【问题讨论】:

  • 非常好的问题,我正在研究它,但没有任何线索。 BTW:你不需要打开内核模块来调用at_exit
  • @ArupRakshit - 你得到了运行时错误,而不是 Sass::SyntaxError 错误 - 那里抛出了其他东西。
  • @sawa - 关于您的更新:我刚刚创建了自己的异常类,它非常模仿 Sass::SyntaxError 回溯方法,但它按预期工作。不过,我无法想象这将如何清除 $!变量。
  • 好的,再看一点:at_exit { p $@; p $@ } 也返回不同的值(一个数组和 nil)。
  • 看来你是对的 - 这是由这种摆弄引起的,我的异常还不够接近,无法捕捉到它。我会在一分钟内发布答案。

标签: ruby exception sass


【解决方案1】:

它发生的原因是因为这个方法覆盖了回溯方法:

def backtrace
  return nil if super.nil?
  return super if sass_backtrace.all? {|h| h.empty?}
  sass_backtrace.map do |h|
    "#{h[:filename] || "(sass)"}:#{h[:line]}" +
      (h[:mixin] ? ":in `#{h[:mixin]}'" : "")
  end + super
end

其中sass_backtrace 是在初始化程序中填充的哈希数组。导致$! 成为nil 的行是:

return super if sass_backtrace.all? {|h| h.empty?}

仅当all? 返回nil 时才会发生这种情况。我对其进行了一些摆弄,我发现当我们调用任何未完成整个迭代的迭代器时,问题总是会发生(all? 在遇到第一个不令人满意的元素时终止迭代)。该问题可能会简单地通过以下方式重现:

at_exit do
  p $!         #=> #<RuntimeError: hello>
  [<any_non_empty_array>].all? {false}

  # Those would break $! as well
  # [<ANA>].any? {true}
  # [1,2,3].find {|n| n.even?}

  # Those will not break $!
  # [<ANA>].any? {false}
  # [<ANA>].all? {true}
  # [1,2,3].find {|n| n > 4}
  p $!         #=> nil
end

raise 'hello'

我能想到它为什么会这样工作的唯一原因是 ruby​​ 循环在内部受到异常控制。当迭代要停止时,它会在循环外引发和拯救特殊类型的异常来完成。

我的猜测是 Ruby 的创建者不希望这个控制异常在 $! 变量中可见,因为这表明出现了问题,并决定将其设置回 nil

【讨论】:

  • 精彩而完美的答案。我现在明白发生了什么。感谢您抽出宝贵时间。
猜你喜欢
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-30
  • 2021-10-12
  • 2016-01-24
  • 1970-01-01
相关资源
最近更新 更多