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
on 2008-09-18 14:45
on 2008-09-18 15:05
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
on 2008-09-18 15:10
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
on 2008-09-18 17:12
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 2008-09-18 19:06
On Sep 18, 2008, at 9:02 AM, DMisener wrote: > 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) 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 2008-09-18 21:40
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)? 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
on 2008-09-18 22:53
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 2008-09-18 23:16
On 18.09.2008 18:31, DMisener wrote: >> 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 :=). Oh, come on. You can easily use that information to build what you want. robert
on 2008-09-19 12:09
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 2008-09-22 18:30
On Sep 19, 6:57 am, Robert Dober <robert.do...@gmail.com> wrote: > 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 2008-09-22 18:32
On Sep 22, 2008, at 9:51 AM, DMisener wrote: > BTW: It sure would be nice to have an on_abort{} handler. there is : 'rescue' a @ http://codeforpeople.com/
on 2008-09-22 18:36
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 a @ http://codeforpeople.com/
on 2008-09-22 20:47
On Sep 22, 1:27 pm, "ara.t.howard" <ara.t.how...@gmail.com> wrote: > > "compatible" with the simple File.open > final = path.to_s.sub %r/[.]#{ Ext }$/, '' > super > ensure > File.rename path, final unless $! > end > end 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 2008-09-22 20:51
On 22.09.2008 17:56, DMisener wrote: > > 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. :-) > 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 2008-09-22 21:47
On Sep 22, 3:47 pm, Robert Klemme <shortcut...@googlemail.com> wrote: > >> Hmm maybe not Robert, my feeling is that they should not do what they > > "cleanly" then rename the work file > > It rather seems that you're making *your* life too hard. :-) Probably... :-) > 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 2008-09-22 22:07
On Sep 22, 2008, at 12:36 PM, DMisener wrote: > the behaviour I was trying to emulate. I'm starting to think this > "frill" is a lost cause. > > 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/
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.