At_exit handler *except* for fatal runtime error

How do we execute a handler (I assume via at_exit) when a program
terminates *except* when there was a fatal runtime error (e.g. any
error such as "divide by zero" that terminates the program)?

We thought perhaps 'caller' would give us some clues but it turns out
to be useless.

Ben

synergism wrote:

How do we execute a handler (I assume via at_exit) when a program
terminates *except* when there was a fatal runtime error (e.g. any
error such as "divide by zero" that terminates the program)?

Inspect the value of $! (or $ERROR_INFO) in your at_exit handler. This will contain the last exception raised. eg

Kernel.at_exit {
   if $!.kind_of? ZeroDivisionError
     puts "don't run"
   else
     puts "run it"
   end
}

5 / 0

hth
a

synergism wrote:

How do we execute a handler (I assume via at_exit) when a program
terminates *except* when there was a fatal runtime error (e.g. any
error such as "divide by zero" that terminates the program)?

"divide by zero" isn't a fatal error - you can catch it. But if you call
"exit!" then the program will abort without running at_exit handlers.

begin
  0/0
rescue ZeroDivisionError
  exit!
end

So,if by "fatal runtime error" you just mean "any uncaught exception",
then you can just wrap your entire program with

begin
  ... rest of program
rescue Exception
  exit!
end

···

--
Posted via http://www.ruby-forum.com/\.

You can use exit! for this:

18:09:02 ~$ ruby -e 'at_exit { puts "exiting" }'
exiting
18:09:08 ~$ ruby -e 'at_exit { puts "exiting" }; exit 1'
exiting
18:09:14 ~$ ruby -e 'at_exit { puts "exiting" }; raise "Foo"'
exiting
-e:1: Foo (RuntimeError)
18:09:21 ~$ ruby -e 'at_exit { puts "exiting" }; exit! 1'
18:09:27 ~$

Catch specific errors and use exit! to exit the program. Then your
handlers won't be invoked.

Cheers

robert

···

2008/9/18 synergism <synergism@gmail.com>:

How do we execute a handler (I assume via at_exit) when a program
terminates *except* when there was a fatal runtime error (e.g. any
error such as "divide by zero" that terminates the program)?

--
use.inject do |as, often| as.you_can - without end

Our situation is a little more complicated:

In our particular case we are creating run-time at_exit{} handlers

To give a concrete example:

We have a generic OpenOutputFile(...) routine which opens a "tentative
output" file
(output which is conditional on the module successfully completing).
We generate a temporary work file and at_exit we rename this file to
its "permanent name".
If the program aborts for any reason, this rename should not happen
( we do leave the failed working file around for later problem
investigation)

···

On Sep 18, 10:03 am, Brian Candler <b.cand...@pobox.com> wrote:

synergism wrote:
> How do we execute a handler (I assume via at_exit) when a program
> terminates *except* when there was a fatal runtime error (e.g. any
> error such as "divide by zero" that terminates the program)?

"divide by zero" isn't a fatal error - you can catch it. But if you call
"exit!" then the program will abort without running at_exit handlers.

begin
0/0
rescue ZeroDivisionError
exit!
end

So,if by "fatal runtime error" you just mean "any uncaught exception",
then you can just wrap your entire program with

begin
... rest of program
rescue Exception
exit!
end
--
Posted viahttp://www.ruby-forum.com/.

> How do we execute a handler (I assume via at_exit) when a program
> terminates *except* when there was a fatal runtime error (e.g. any
> error such as "divide by zero" that terminates the program)?

You can use exit! for this:

...

-e:1: Foo (RuntimeError)
18:09:21 ~$ ruby -e 'at_exit { puts "exiting" }; exit! 1'
18:09:27 ~$

Catch specific errors and use exit! to exit the program. Then your
handlers won't be invoked.

But out above example we want to suppress the file rename for any
"unanticipated exception" -- which
is just the opposite of the solution you presented :=).

···

On Sep 18, 1:03 pm, Robert Klemme <shortcut...@googlemail.com> wrote:

2008/9/18 synergism <synerg...@gmail.com>:

you can do this

at_exit {
   rename_files unless $!
}

but you can also make it vastly simpler: do not use at exit handlers

setup a global list of renames to occur, for instance

   module Rename
     Map = {}

     def file hash = {}
       src, dst, *ignored = hash.to_a.first
       Map[src.to_s] = dst.to_s
     end

      def files!
        require 'fileutils'
        Map.each{|src,dst| FileUtils.mv src, dst}
        Map.clear
      end

      extend self
   end

then, in the code, add files to be renamed

   Rename.file 'foo' => 'bar'

this does not rename the file, it only notes that it needs to be done later

now *as the last line of your program do*

   Rename.files!

since this line is the last line of you program, it will never execute under exceptional or error conditions. if you prefer you can then wrap this up with an at_exit handler

at_exit {
   Rename.files! unless $!
}

but be warned - calling exit or exit! itself will set $!

cfp:~ > ruby -e' begin; exit; ensure; p $!.class; end '
SystemExit

so, in fact, you have to be very, very, very careful about doing things in at exit handlers based on 'error' conditions. this is a bug

at_exit{ cleanup! unless $! }
exit 0

that's why a solution that does not depend on at_exit or $! is much better - simply defer the renaming until the very end. it's a sure thing that you'll introduce bugs otherwise.

in any case it seems like you are working too hard - just 'normal' ruby code performs *exactly* as per your requirements anyhow

   do_the_work
   rename_the_file # does *not* execute if previous line raises

cheers.

a @ http://codeforpeople.com/

···

On Sep 18, 2008, at 9:02 AM, DMisener wrote:

Our situation is a little more complicated:

In our particular case we are creating run-time at_exit{} handlers

To give a concrete example:

We have a generic OpenOutputFile(...) routine which opens a "tentative
output" file
(output which is conditional on the module successfully completing).
We generate a temporary work file and at_exit we rename this file to
its "permanent name".
If the program aborts for any reason, this rename should not happen
( we do leave the failed working file around for later problem
investigation)

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Oh, come on. You can easily use that information to build what you want.

  robert

···

On 18.09.2008 18:31, DMisener wrote:

On Sep 18, 1:03 pm, Robert Klemme <shortcut...@googlemail.com> wrote:

2008/9/18 synergism <synerg...@gmail.com>:

How do we execute a handler (I assume via at_exit) when a program
terminates *except* when there was a fatal runtime error (e.g. any
error such as "divide by zero" that terminates the program)?

You can use exit! for this:

...

-e:1: Foo (RuntimeError)
18:09:21 ~$ ruby -e 'at_exit { puts "exiting" }; exit! 1'
18:09:27 ~$

Catch specific errors and use exit! to exit the program. Then your
handlers won't be invoked.

But out above example we want to suppress the file rename for any
"unanticipated exception" -- which
is just the opposite of the solution you presented :=).

On Thu, Sep 18, 2008 at 10:07 PM, Robert Klemme

Oh, come on. You can easily use that information to build what you want.

       robert

Hmm maybe not Robert, my feeling is that they should not do what they
are doing in the at_exit block.
OP can you give us the rationale behind this design. At first sight I
believe you
should do what you want in the main script. Look at Ara's ideas e.g.

Cheers
Robert

···

--
C'est véritablement utile puisque c'est joli.

Antoine de Saint Exupéry

On Thu, Sep 18, 2008 at 10:07 PM, Robert Klemme> Oh, come on.
You can easily use that information to build what you want.

Actually inverting the logic isn't that easy (at least for me)

Hmm maybe not Robert, my feeling is that they should not do what they
are doing in the at_exit block.

I'm certainly not married to the the at_exit approach.

OP can you give us the rationale behind this design. At first sight I
believe you should do what you want in the main script. Look at Ara's ideas e.g.

Simple problem:

Create a tentative output working file. If the program exits
"cleanly" then rename the work file
to its "final" name. If not leave the work file for possible
examination.

Ideally this File.open_tentative_output_file would be plug
"compatible" with the simple File.open
(i.e. support modes and blocks).

Perhaps I'm trying to make life "too easy" for the application
programmer by eliminating the need for
an explicit File.close_all_pending_tentative_file routine. That is
really all that I am trying to achieve with the
at_exit{} code. One less step to remember. But perhaps the
implementation complexity isn't worth it.

BTW: It sure would be nice to have an on_abort{} handler.

···

On Sep 19, 6:57 am, Robert Dober <robert.do...@gmail.com> wrote:

there is : 'rescue'

a @ http://codeforpeople.com/

···

On Sep 22, 2008, at 9:51 AM, DMisener wrote:

BTW: It sure would be nice to have an on_abort{} handler.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

class TentativeFile
   Ext = 'tentative'

   def open path, *a, &b
     super "#{ path }.#{ Ext }", *a, &b
   end

   def close
      final = path.to_s.sub %r/[.]#{ Ext }$/, ''
      super
   ensure
     File.rename path, final unless $!
   end
end

a @ http://codeforpeople.com/

···

On Sep 22, 2008, at 9:51 AM, DMisener wrote:

Simple problem:

Create a tentative output working file. If the program exits
"cleanly" then rename the work file
to its "final" name. If not leave the work file for possible
examination.

Ideally this File.open_tentative_output_file would be plug
"compatible" with the simple File.open
(i.e. support modes and blocks).

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

On Thu, Sep 18, 2008 at 10:07 PM, Robert Klemme> Oh, come on. You can easily use that information to build what you want.

Actually inverting the logic isn't that easy (at least for me)

Hmm maybe not Robert, my feeling is that they should not do what they
are doing in the at_exit block.

I'm certainly not married to the the at_exit approach.

OP can you give us the rationale behind this design. At first sight I
believe you should do what you want in the main script. Look at Ara's ideas e.g.

Simple problem:

Create a tentative output working file. If the program exits
"cleanly" then rename the work file
to its "final" name. If not leave the work file for possible
examination.

Ideally this File.open_tentative_output_file would be plug
"compatible" with the simple File.open
(i.e. support modes and blocks).

Perhaps I'm trying to make life "too easy" for the application
programmer by eliminating the need for
an explicit File.close_all_pending_tentative_file routine.

It rather seems that you're making *your* life too hard. :slight_smile:

That is
really all that I am trying to achieve with the
at_exit{} code. One less step to remember. But perhaps the
implementation complexity isn't worth it.

Why do you need at_exit for this - especially if you want to be compatible to File.open?

def File.open_tentative_output_file(tmp, final, mode = "w", &b)
   raise "Need a block" unless b
   File.open(tmp, mode, &b)
   File.rename tmp, final
end

Done.

Cheers

  robert

···

On 22.09.2008 17:56, DMisener wrote:

On Sep 19, 6:57 am, Robert Dober <robert.do...@gmail.com> wrote:

output=TentativeFile.new.open('temp.tmp','w')
output.puts 'Thanks'

without an explict close doesn't automatically rename the file to its
proper form.

With "regular" File.open ... the file is automatically closed without
need of explicit close. That was
the behaviour I was trying to emulate. I'm starting to think this
"frill" is a lost cause.

···

On Sep 22, 1:27 pm, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

On Sep 22, 2008, at 9:51 AM, DMisener wrote:

> Simple problem:

> Create a tentative output working file. If the program exits
> "cleanly" then rename the work file
> to its "final" name. If not leave the work file for possible
> examination.

> Ideally this File.open_tentative_output_file would be plug
> "compatible" with the simple File.open
> (i.e. support modes and blocks).

class TentativeFile
Ext = 'tentative'

def open path, *a, &b
super "#{ path }.#{ Ext }", *a, &b
end

def close
final = path.to_s.sub %r/[.]#{ Ext }$/, ''
super
ensure
File.rename path, final unless $!
end
end

>> On Thu, Sep 18, 2008 at 10:07 PM, Robert Klemme> Oh, come on.
>> You can easily use that information to build what you want.

> Actually inverting the logic isn't that easy (at least for me)

>> Hmm maybe not Robert, my feeling is that they should not do what they
>> are doing in the at_exit block.

> I'm certainly not married to the the at_exit approach.

>> OP can you give us the rationale behind this design. At first sight I
>> believe you should do what you want in the main script. Look at Ara's ideas e.g.

> Simple problem:

> Create a tentative output working file. If the program exits
> "cleanly" then rename the work file
> to its "final" name. If not leave the work file for possible
> examination.

> Ideally this File.open_tentative_output_file would be plug
> "compatible" with the simple File.open
> (i.e. support modes and blocks).

> Perhaps I'm trying to make life "too easy" for the application
> programmer by eliminating the need for
> an explicit File.close_all_pending_tentative_file routine.

It rather seems that you're making *your* life too hard. :slight_smile:

Probably... :slight_smile:

> That is
> really all that I am trying to achieve with the
> at_exit{} code. One less step to remember. But perhaps the
> implementation complexity isn't worth it.

Why do you need at_exit for this - especially if you want to be
compatible to File.open?

def File.open_tentative_output_file(tmp, final, mode = "w", &b)
raise "Need a block" unless b
File.open(tmp, mode, &b)
File.rename tmp, final
end

So the recommendation is that any "successful program completion"
cleanup be coerced into
the execute a block then cleanup construct cited above.

Enforcing the the block is required is the key. I was hoping to relax
that requirement but
you can't have everything.

That might do the trick...

BTW: here was my previous implementation... actually called OutputFile

# Setup ABORT detection preamble

$ABORTING=true

def exit_program *args
    $ABORTING=false
    __original_exit__(*args)
end

....

alias :exit :exit_program

class OutputFile
    def initialize filename,options={},&block
        @filename,@options,@block=filename.dup,options.dup,block
        @mode=options[:mode] || 'w'
        unless append?
            # TODO: Nice if we could validate valid filename here
instead of at "open?" time
            # TODO: support :nosuperceed, :superceed
            File.send(@options[:backup] ? :backup : :delete,@filename)
if File.exists? @filename
            @work_filename=Filename.work filename
        end
        if block_given?
            yield self
            close
        else
            # TODO: Support suppression of autoclose on fatal_error
            at_exit{close unless $ABORTING}
        end
        @file
    end

    def puts *args
        open?
        @file.puts(*args)
    end

    def print *args
        open?
        @file.puts(*args)
    end

    def close
        if @file
            @file.close
            # TODO: what to do if rename fails for any reason
            File.rename @work_filename,@filename if @work_filename
        end
        @file=nil
    end

    private

    def open? # Defer open until first require output request
        return if @file
        # TODO: support wait on busy
        @file=File.open(@work_filename || @filename,@mode)
    end

    def append?
        /a/i.match(@mode)
    end
end

def OutputFile *args,&block
    OutputFile.new(*args,&block)
end

···

On Sep 22, 3:47 pm, Robert Klemme <shortcut...@googlemail.com> wrote:

On 22.09.2008 17:56, DMisener wrote:
> On Sep 19, 6:57 am, Robert Dober <robert.do...@gmail.com> wrote:

you've got to play with code at least a little!

cfp:~ > cat a.rb
class TentativeFile < File
    Ext = 'tentative'

    def initialize path, *a, &b
      super "#{ path }.#{ Ext }", *a, &b
      at_exit{ close unless closed? } unless b
    end

    def close
       STDERR.puts "closing #{ path }..."
       final = path.to_s.sub %r/[.]#{ Ext }$/, ''
       super
    ensure
      File.rename path, final unless $!
    end
end

output=TentativeFile.new('temp.tmp','w')
output.puts 'Thanks'

cfp:~ > ruby a.rb
closing temp.tmp.tentative...

cfp:~ > cat temp.tmp
Thanks

a @ http://codeforpeople.com/

···

On Sep 22, 2008, at 12:36 PM, DMisener wrote:

output=TentativeFile.new.open('temp.tmp','w')
output.puts 'Thanks'

without an explict close doesn't automatically rename the file to its
proper form.

With "regular" File.open ... the file is automatically closed without
need of explicit close. That was
the behaviour I was trying to emulate. I'm starting to think this
"frill" is a lost cause.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama