14

How does SystemExit behave differently from other Exceptions? I think I understand some of the reasoning about why it wouldn't be good to raise a proper Exception. For example, you wouldn't want something strange like this to happen:

begin
  exit
rescue => e
  # Silently swallow up the exception and don't exit
end

But how does the rescue ignore SystemExit? (What criteria does it use?)

Benjamin Oakes
  • 12,262
  • 12
  • 65
  • 83
  • @AndrewGrimm It certainly is a FAQ from mailing list and IRC, but I can't seem to find this question exactly on S/O. The closest I found was _["Which exceptions do you catch when you don't specify an exception class in Ruby"](http://stackoverflow.com/questions/2748515/which-exceptions-do-you-catch-when-you-dont-specify-an-exception-class-in-ruby)_, but it's sort of a duplicate answer, not a duplicate question. – Phrogz Feb 28 '11 at 15:52
  • @Andrew I thought someone else had to have asked the same thing too, but I didn't find anything that specifically answered how `SystemExit` is handled. @Phrogz Thanks for the link to the related Q. – Benjamin Oakes Mar 01 '11 at 15:02

2 Answers2

22

When you write rescue without one or more classes, it is the same as writing:

begin
  ...
rescue StandardError => e
  ...
end

There are Exceptions that do not inherit from StandardError, however. SystemExit is one of these, and so it is not captured. Here is a subset of the hierarchy in Ruby 1.9.2, which you can find out yourself:

BasicObject
  Exception
    NoMemoryError
    ScriptError
      LoadError
        Gem::LoadError
      NotImplementedError
      SyntaxError
    SecurityError
    SignalException
      Interrupt
    StandardError
      ArgumentError
      EncodingError
        Encoding::CompatibilityError
        Encoding::ConverterNotFoundError
        Encoding::InvalidByteSequenceError
        Encoding::UndefinedConversionError
      FiberError
      IOError
        EOFError
      IndexError
        KeyError
        StopIteration
      LocalJumpError
      NameError
        NoMethodError
      RangeError
        FloatDomainError
      RegexpError
      RuntimeError
      SystemCallError
      ThreadError
      TypeError
      ZeroDivisionError
    SystemExit
    SystemStackError
    fatal

You can thus capture just SystemExit with:

begin
  ...
rescue SystemExit => e
  ...
end

...or you can choose to capture every exception, including SystemExit with:

begin
  ...
rescue Exception => e
  ...
end

Try it yourself:

begin
  exit 42
  puts "No no no!"
rescue Exception => e
  puts "Nice try, buddy."
end
puts "And on we run..."

#=> "Nice try, buddy."
#=> "And on we run..."

Note that this example will not work in (some versions of?) IRB, which supplies its own exit method that masks the normal Object#exit.

In 1.8.7:

method :exit
#=> #<Method: Object(IRB::ExtendCommandBundle)#exit>

In 1.9.3:

method :exit
#=> #<Method: main.irb_exit>
rcrogers
  • 2,281
  • 1
  • 17
  • 14
Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • 1
    Thanks for the info. I knew you could capture `SystemExit` by itself, but I didn't know that not specifying an exception class causes only `StandardError` exceptions to be rescued. Seems to explain it -- thanks! – Benjamin Oakes Feb 25 '11 at 20:06
  • This is awesome for testing whether or not something exits – Keith Smiley Apr 29 '13 at 17:40
1

Simple example:

begin
  exit
  puts "never get here"
rescue SystemExit
  puts "rescued a SystemExit exception"
end

puts "after begin block"

The exit status / success?, etc. can be read too:

begin
  exit 1
rescue SystemExit => e
  puts "Success? #{e.success?}" # Success? false
end

begin
  exit
rescue SystemExit => e
  puts "Success? #{e.success?}" # Success? true
end

Full list of methods: [:status, :success?, :exception, :message, :backtrace, :backtrace_locations, :set_backtrace, :cause]

Dorian
  • 22,759
  • 8
  • 120
  • 116