![]() |
Trapping errors.
begin
#... rescue => e #... end will trap e if it is a StandardError. SystemCallErrrors are supposed to handle Errorcodes from the OS. All of these are subclasses of Exception. So why do I get this failure under Cygwin: $ ruby BACKUP.RB "C:\\" "D:\\buzz_c" cp -rp C:\ D:\buzz_c /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `initialize': Device or resource busy - C:\/WINDOWS/WIN386.SWP (Errno::EBUSY) from /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `copy_file' from /usr/lib/ruby/1.8/new_fileutils.rb:1221:in `copy' from /usr/lib/ruby/1.8/new_fileutils.rb:455:in `copy_entry' from /usr/lib/ruby/1.8/new_fileutils.rb:1314:in `traverse' from /usr/lib/ruby/1.8/new_fileutils.rb:453:in `copy_entry' from /usr/lib/ruby/1.8/new_fileutils.rb:424:in `cp_r' from /usr/lib/ruby/1.8/new_fileutils.rb:1385:in `fu_each_src_dest' from /usr/lib/ruby/1.8/new_fileutils.rb:1401:in `fu_each_src_dest0' from /usr/lib/ruby/1.8/new_fileutils.rb:1383:in `fu_each_src_dest' from /usr/lib/ruby/1.8/new_fileutils.rb:422:in `cp_r' from BACKUP.RB:27 hgs@buzz ~/downloads when my modified FileUtils.cp_r has begin copy_entry ... rescue Exception => e logger.error("backup"){"Error was #{e}") end (essentially. Theres a bit more to it than that, but the details shouldn't matter for my question.) So why can't I rescue it? (I'm trying to log, and skip files I can't backup so at least I get most of the files, and know which ones I have not.) Anyone also reading ruby-core will realize that my patch didn't work in practice. Thank you, Hugh |
Re: Trapping errors.
Hugh Sasse <hgs@dmu.ac.uk> wrote:
> begin > #... > rescue => e > #... > end > > will trap e if it is a StandardError. SystemCallErrrors are > supposed to handle Errorcodes from the OS. All of these are > subclasses of Exception. So why do I get this failure under Cygwin: > > $ ruby BACKUP.RB "C:\\" "D:\\buzz_c" > cp -rp C:\ D:\buzz_c > /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `initialize': Device or > resource busy - C:\/WINDOWS/WIN386.SWP (Errno::EBUSY) > from /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `copy_file' > from /usr/lib/ruby/1.8/new_fileutils.rb:1221:in `copy' > from /usr/lib/ruby/1.8/new_fileutils.rb:455:in `copy_entry' > from /usr/lib/ruby/1.8/new_fileutils.rb:1314:in `traverse' > from /usr/lib/ruby/1.8/new_fileutils.rb:453:in `copy_entry' > from /usr/lib/ruby/1.8/new_fileutils.rb:424:in `cp_r' > from /usr/lib/ruby/1.8/new_fileutils.rb:1385:in > `fu_each_src_dest' from > /usr/lib/ruby/1.8/new_fileutils.rb:1401:in `fu_each_src_dest0' > from /usr/lib/ruby/1.8/new_fileutils.rb:1383:in > `fu_each_src_dest' from > /usr/lib/ruby/1.8/new_fileutils.rb:422:in `cp_r' from BACKUP.RB:27 > > hgs@buzz ~/downloads > > when my modified FileUtils.cp_r has > > begin > copy_entry ... > rescue Exception => e > logger.error("backup"){"Error was #{e}") > end > > (essentially. Theres a bit more to it than that, but the details > shouldn't matter for my question.) So why can't I rescue it? (I'm > trying to log, and skip files I can't backup so at least I get most > of the files, and know which ones I have not.) Maybe it's in another thread. Or your code is actually not between "begin" and "rescue" but outside of that. Kind regards robert |
Re: Trapping errors.
On Tue, 1 Nov 2005, Robert Klemme wrote:
> Hugh Sasse <hgs@dmu.ac.uk> wrote: > > begin > > #... > > rescue => e > > #... > > end > > > > will trap e if it is a StandardError. SystemCallErrrors are > > supposed to handle Errorcodes from the OS. All of these are > > subclasses of Exception. So why do I get this failure under Cygwin: > > > > $ ruby BACKUP.RB "C:\\" "D:\\buzz_c" > > cp -rp C:\ D:\buzz_c > > /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `initialize': Device or > > resource busy - C:\/WINDOWS/WIN386.SWP (Errno::EBUSY) > > from /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `copy_file' > > from /usr/lib/ruby/1.8/new_fileutils.rb:1221:in `copy' > > from /usr/lib/ruby/1.8/new_fileutils.rb:455:in `copy_entry' > > from /usr/lib/ruby/1.8/new_fileutils.rb:1314:in `traverse' > > from /usr/lib/ruby/1.8/new_fileutils.rb:453:in `copy_entry' > > from /usr/lib/ruby/1.8/new_fileutils.rb:424:in `cp_r' > > from /usr/lib/ruby/1.8/new_fileutils.rb:1385:in > > `fu_each_src_dest' from > > /usr/lib/ruby/1.8/new_fileutils.rb:1401:in `fu_each_src_dest0' > > from /usr/lib/ruby/1.8/new_fileutils.rb:1383:in > > `fu_each_src_dest' from > > /usr/lib/ruby/1.8/new_fileutils.rb:422:in `cp_r' from BACKUP.RB:27 > > > > hgs@buzz ~/downloads > > > > when my modified FileUtils.cp_r has > > > > begin > > copy_entry ... > > rescue Exception => e > > logger.error("backup"){"Error was #{e}") > > end > > > > (essentially. Theres a bit more to it than that, but the details > > shouldn't matter for my question.) So why can't I rescue it? (I'm > > trying to log, and skip files I can't backup so at least I get most > > of the files, and know which ones I have not.) > > Maybe it's in another thread. Or your code is actually not between "begin" > and "rescue" but outside of that. there's no threading in there, and I'm pretty certain it is within that, because it is in the call to copy_entry > > Kind regards > > robert > The modified fileutils is (heavily pruned) below I've changed cp_r. Hugh # # = fileutils.rb # # Copyright (c) 2000-2005 Minero Aoki <aamine@loveruby.net> # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # # == module FileUtils # # Namespace for several file utility methods for copying, moving, removing, etc. # # === Module Functions # # [...] # cp_r(src, dest, options) {|s,d,e|...} # cp_r(list, dir, options) {|s,d,e|...} # [...] # # The <tt>options</tt> parameter is a hash of options, taken from the list # <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>. # <tt>:noop</tt> means that no changes are made. The other two are obvious. # Each method documents the options that it honours. # # All methods that have the concept of a "source" file or directory can take # either one file or a list of files in that argument. See the method # documentation for examples. # # There are some `low level' methods, which do not accept any option: # # copy_entry(src, dest, preserve = false, dereference = false) # copy_file(src, dest, preserve = false, dereference = true) # [...] # # == module FileUtils::Verbose # # This module has all methods of FileUtils module, but it outputs messages # before acting. This equates to passing the <tt>:verbose</tt> flag to methods # in FileUtils. # # == module FileUtils::NoWrite # # This module has all methods of FileUtils module, but never changes # files/directories. This equates to passing the <tt>:noop</tt> flag to methods # in FileUtils. # # == module FileUtils::DryRun # # This module has all methods of FileUtils module, but never changes # files/directories. This equates to passing the <tt>:noop</tt> and # <tt>:verbose</tt> flags to methods in FileUtils. # module FileUtils def self.private_module_function(name) #:nodoc: module_function name private_class_method name end # This hash table holds command options. OPT_TABLE = {} #:nodoc: internal use only # [...] def fu_mkdir(path, mode) #:nodoc: path = path.sub(%r</\z>, '') if mode Dir.mkdir path, mode File.chmod mode, path else Dir.mkdir path end end private_module_function :fu_mkdir # [...] # # Options: preserve noop verbose dereference_root # # Copies +src+ to +dest+. If +src+ is a directory, this method copies # all its contents recursively. If +dest+ is a directory, copies # +src+ to +dest/src+. # # +src+ can be a list of files. # # # Installing ruby library "mylib" under the site_ruby # FileUtils.rm_r site_ruby + '/mylib', :force # FileUtils.cp_r 'lib/', site_ruby + '/mylib' # # # Examples of copying several files to target directory. # FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail' # FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true # # # If you want to copy all contents of a directory instead of the # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, # # use following code. # FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes src/dest, # # but this doesn't. # def cp_r(src, dest, options = {}) fu_check_options options, :preserve, :noop, :verbose, :dereference_root fu_output_message "cp -r#{options[:preserve] ? 'p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] return if options[:noop] options[:dereference_root] = true unless options.key?(:dereference_root) fu_each_src_dest(src, dest) do |s, d| begin copy_entry s, d, options[:preserve], options[:dereference_root] rescue Exception => e stop = true if block_given? stop = yield s,d,e end raise if stop end end end module_function :cp_r OPT_TABLE['cp_r'] = %w( noop verbose preserve dereference_root ) # # Copies a file system entry +src+ to +dest+. # If +src+ is a directory, this method copies its contents recursively. # This method preserves file types, c.f. symlink, directory... # (FIFO, device files and etc. are not supported yet) # # Both of +src+ and +dest+ must be a path name. # +src+ must exist, +dest+ must not exist. # # If +preserve+ is true, this method preserves owner, group, permissions # and modified time. # # If +dereference_root+ is true, this method dereference tree root. # def copy_entry(src, dest, preserve = false, dereference_root = false) Entry_.new(src, nil, dereference_root).traverse do |ent| destent = Entry_.new(dest, ent.rel, false) ent.copy destent.path ent.copy_metadata destent.path if preserve end end module_function :copy_entry # # Copies file contents of +src+ to +dest+. # Both of +src+ and +dest+ must be a path name. # def copy_file(src, dest, preserve = false, dereference = true) ent = Entry_.new(src, nil, dereference) ent.copy_file dest ent.copy_metadata dest if preserve end module_function :copy_file # [...] class Entry_ #:nodoc: internal use only include StreamUtils_ def initialize(a, b = nil, deref = false) @prefix = @rel = @path = nil if b @prefix = a @rel = b else @path = a end @deref = deref @stat = nil @lstat = nil end def inspect "\#<#{self.class} #{path()}>" end def path if @path @path.to_str else join(@prefix, @rel) end end def prefix @prefix || @path end def rel @rel end def dereference? @deref end def exist? lstat! ? true : false end def file? s = lstat! s and s.file? end def directory? s = lstat! s and s.directory? end def symlink? s = lstat! s and s.symlink? end def chardev? s = lstat! s and s.chardev? end def blockdev? s = lstat! s and s.blockdev? end def socket? s = lstat! s and s.socket? end def pipe? s = lstat! s and s.pipe? end S_IF_DOOR = 0xD000 def door? s = lstat! s and (s.mode & 0xF000 == S_IF_DOOR) end def entries Dir.entries(path())\ .reject {|n| n == '.' or n == '..' }\ .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } end def stat return @stat if @stat if lstat() and lstat().symlink? @stat = File.stat(path()) else @stat = lstat() end @stat end def stat! return @stat if @stat if lstat! and lstat!.symlink? @stat = File.stat(path()) else @stat = lstat! end @stat rescue SystemCallError nil end def lstat if dereference? @lstat ||= File.stat(path()) else @lstat ||= File.lstat(path()) end end def lstat! lstat() rescue SystemCallError nil end def chmod(mode) if symlink? File.lchmod mode, path() if have_lchmod? else File.chmod mode, path() end end def chown(uid, gid) if symlink? File.lchown uid, gid, path() if have_lchown? else File.chown uid, gid, path() end end def copy(dest) case when file? copy_file dest when directory? begin Dir.mkdir dest rescue raise unless File.directory?(dest) end when symlink? File.symlink File.readlink(path()), dest when chardev? raise "cannot handle device file" unless File.respond_to?(:mknod) mknod dest, ?c, 0666, lstat().rdev when blockdev? raise "cannot handle device file" unless File.respond_to?(:mknod) mknod dest, ?b, 0666, lstat().rdev when socket? raise "cannot handle socket" unless File.respond_to?(:mknod) mknod dest, nil, lstat().mode, 0 when pipe? raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) mkfifo dest, 0666 when door? raise "cannot handle door: #{path()}" else raise "unknown file type: #{path()}" end end def copy_file(dest) st = stat() File.open(path(), 'rb') {|r| File.open(dest, 'wb', st.mode) {|w| fu_copy_stream0 r, w, (fu_blksize(st) || fu_default_blksize()) } } end def copy_metadata(path) st = lstat() File.utime st.atime, st.mtime, path begin File.chown st.uid, st.gid, path rescue Errno::EPERM # clear setuid/setgid File.chmod st.mode & 01777, path else File.chmod st.mode, path end end # [...] def platform_support return yield unless fu_windows? first_time_p = true begin yield rescue Errno::ENOENT raise rescue => err if first_time_p first_time_p = false begin File.chmod 0700, path() # Windows does not have symlink retry rescue SystemCallError end end raise err end end def preorder_traverse stack = [self] while ent = stack.pop yield ent stack.concat ent.entries.reverse if ent.directory? end end alias traverse preorder_traverse def postorder_traverse if directory? entries().each do |ent| ent.postorder_traverse do |e| yield e end end end yield self end # [...] def join(dir, base) return dir.to_str if not base or base == '.' return base.to_str if not dir or dir == '.' File.join(dir, base) end end # class Entry_ def fu_list(arg) #:nodoc: [arg].flatten.map {|path| path.to_str } end private_module_function :fu_list def fu_each_src_dest(src, dest) #:nodoc: fu_each_src_dest0(src, dest) do |s, d| raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d) yield s, d end end private_module_function :fu_each_src_dest def fu_each_src_dest0(src, dest) #:nodoc: if src.is_a?(Array) src.each do |s| s = s.to_str yield s, File.join(dest, File.basename(s)) end else src = src.to_str if File.directory?(dest) yield src, File.join(dest, File.basename(src)) else yield src, dest.to_str end end end private_module_function :fu_each_src_dest0 def fu_same?(a, b) #:nodoc: if fu_have_st_ino? st1 = File.stat(a) st2 = File.stat(b) st1.dev == st2.dev and st1.ino == st2.ino else File.expand_path(a) == File.expand_path(b) end rescue Errno::ENOENT return false end private_module_function :fu_same? def fu_have_st_ino? #:nodoc: not fu_windows? end private_module_function :fu_have_st_ino? def fu_check_options(options, *optdecl) #:nodoc: h = options.dup optdecl.each do |name| h.delete name end raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty? end private_module_function :fu_check_options def fu_update_option(args, new) #:nodoc: if args.last.is_a?(Hash) args[-1] = args.last.dup.update(new) else args.push new end args end private_module_function :fu_update_option @fileutils_output = $stderr @fileutils_label = '' def fu_output_message(msg) #:nodoc: @fileutils_output ||= $stderr @fileutils_label ||= '' @fileutils_output.puts @fileutils_label + msg end private_module_function :fu_output_message # # Returns an Array of method names which have any options. # # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] # def FileUtils.commands OPT_TABLE.keys end # # Returns an Array of option names. # # p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] # def FileUtils.options OPT_TABLE.values.flatten.uniq end # # Returns true if the method +mid+ have an option +opt+. # # p FileUtils.have_option?(:cp, :noop) #=> true # p FileUtils.have_option?(:rm, :force) #=> true # p FileUtils.have_option?(:rm, :perserve) #=> false # def FileUtils.have_option?(mid, opt) li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}" li.include?(opt.to_s) end # # Returns an Array of option names of the method +mid+. # # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"] # def FileUtils.options_of(mid) OPT_TABLE[mid.to_s] end # [...] end |
Re: Trapping errors.
I see my changes to fileutils are now in the Ruby CVS.
However, even with rescue Exception, SystemCallError => e or rescue Exception, Errno::EACCES, Errno::EBUSY => e I still cannot trap this error. From the call stack this part of the code is being used, so why won't the error cause ruby to go back up the callstack until it finds this rescue clause? Hugh On Tue, 1 Nov 2005, Hugh Sasse wrote: > On Tue, 1 Nov 2005, Robert Klemme wrote: > > > Hugh Sasse <hgs@dmu.ac.uk> wrote: > > > begin > > > #... > > > rescue => e > > > #... > > > end > > > > > > will trap e if it is a StandardError. SystemCallErrrors are > > > supposed to handle Errorcodes from the OS. All of these are > > > subclasses of Exception. So why do I get this failure under Cygwin: > > > > > > $ ruby BACKUP.RB "C:\\" "D:\\buzz_c" > > > cp -rp C:\ D:\buzz_c > > > /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `initialize': Device or > > > resource busy - C:\/WINDOWS/WIN386.SWP (Errno::EBUSY) > > > from /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `copy_file' > > > from /usr/lib/ruby/1.8/new_fileutils.rb:1221:in `copy' > > > from /usr/lib/ruby/1.8/new_fileutils.rb:455:in `copy_entry' > > > from /usr/lib/ruby/1.8/new_fileutils.rb:1314:in `traverse' > > > from /usr/lib/ruby/1.8/new_fileutils.rb:453:in `copy_entry' > > > from /usr/lib/ruby/1.8/new_fileutils.rb:424:in `cp_r' > > > from /usr/lib/ruby/1.8/new_fileutils.rb:1385:in > > > `fu_each_src_dest' from > > > /usr/lib/ruby/1.8/new_fileutils.rb:1401:in `fu_each_src_dest0' > > > from /usr/lib/ruby/1.8/new_fileutils.rb:1383:in > > > `fu_each_src_dest' from > > > /usr/lib/ruby/1.8/new_fileutils.rb:422:in `cp_r' from BACKUP.RB:27 > > > > > > hgs@buzz ~/downloads > > > > > > when my modified FileUtils.cp_r has > > > > > > begin > > > copy_entry ... > > > rescue Exception => e > > > logger.error("backup"){"Error was #{e}") > > > end > > > > > > (essentially. Theres a bit more to it than that, but the details > > > shouldn't matter for my question.) So why can't I rescue it? (I'm > > > trying to log, and skip files I can't backup so at least I get most > > > of the files, and know which ones I have not.) > > > > Maybe it's in another thread. Or your code is actually not between "begin" > > and "rescue" but outside of that. > > there's no threading in there, and I'm pretty certain it is within > that, because it is in the call to copy_entry > > > > > Kind regards > > > > robert > > > > The modified fileutils is (heavily pruned) below > I've changed cp_r. > > Hugh > > # > # = fileutils.rb > # > # Copyright (c) 2000-2005 Minero Aoki <aamine@loveruby.net> > # > # This program is free software. > # You can distribute/modify this program under the same terms of ruby. > # > # == module FileUtils > # > # Namespace for several file utility methods for copying, moving, removing, etc. > # > # === Module Functions > # > # [...] > # cp_r(src, dest, options) {|s,d,e|...} > # cp_r(list, dir, options) {|s,d,e|...} > # [...] > # > # The <tt>options</tt> parameter is a hash of options, taken from the list > # <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>. > # <tt>:noop</tt> means that no changes are made. The other two are obvious. > # Each method documents the options that it honours. > # > # All methods that have the concept of a "source" file or directory can take > # either one file or a list of files in that argument. See the method > # documentation for examples. > # > # There are some `low level' methods, which do not accept any option: > # > # copy_entry(src, dest, preserve = false, dereference = false) > # copy_file(src, dest, preserve = false, dereference = true) > # [...] > # > # == module FileUtils::Verbose > # > # This module has all methods of FileUtils module, but it outputs messages > # before acting. This equates to passing the <tt>:verbose</tt> flag to methods > # in FileUtils. > # > # == module FileUtils::NoWrite > # > # This module has all methods of FileUtils module, but never changes > # files/directories. This equates to passing the <tt>:noop</tt> flag to methods > # in FileUtils. > # > # == module FileUtils::DryRun > # > # This module has all methods of FileUtils module, but never changes > # files/directories. This equates to passing the <tt>:noop</tt> and > # <tt>:verbose</tt> flags to methods in FileUtils. > # > > module FileUtils > > def self.private_module_function(name) #:nodoc: > module_function name > private_class_method name > end > > # This hash table holds command options. > OPT_TABLE = {} #:nodoc: internal use only > > # [...] > > def fu_mkdir(path, mode) #:nodoc: > path = path.sub(%r</\z>, '') > if mode > Dir.mkdir path, mode > File.chmod mode, path > else > Dir.mkdir path > end > end > private_module_function :fu_mkdir > > # [...] > > # > # Options: preserve noop verbose dereference_root > # > # Copies +src+ to +dest+. If +src+ is a directory, this method copies > # all its contents recursively. If +dest+ is a directory, copies > # +src+ to +dest/src+. > # > # +src+ can be a list of files. > # > # # Installing ruby library "mylib" under the site_ruby > # FileUtils.rm_r site_ruby + '/mylib', :force > # FileUtils.cp_r 'lib/', site_ruby + '/mylib' > # > # # Examples of copying several files to target directory. > # FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail' > # FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true > # > # # If you want to copy all contents of a directory instead of the > # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, > # # use following code. > # FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes src/dest, > # # but this doesn't. > # > def cp_r(src, dest, options = {}) > fu_check_options options, :preserve, :noop, :verbose, :dereference_root > fu_output_message "cp -r#{options[:preserve] ? 'p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] > return if options[:noop] > options[:dereference_root] = true unless options.key?(:dereference_root) > fu_each_src_dest(src, dest) do |s, d| > begin > copy_entry s, d, options[:preserve], options[:dereference_root] > rescue Exception => e > stop = true > if block_given? > stop = yield s,d,e > end > raise if stop > end > end > end > module_function :cp_r > > OPT_TABLE['cp_r'] = %w( noop verbose preserve dereference_root ) > > # > # Copies a file system entry +src+ to +dest+. > # If +src+ is a directory, this method copies its contents recursively. > # This method preserves file types, c.f. symlink, directory... > # (FIFO, device files and etc. are not supported yet) > # > # Both of +src+ and +dest+ must be a path name. > # +src+ must exist, +dest+ must not exist. > # > # If +preserve+ is true, this method preserves owner, group, permissions > # and modified time. > # > # If +dereference_root+ is true, this method dereference tree root. > # > def copy_entry(src, dest, preserve = false, dereference_root = false) > Entry_.new(src, nil, dereference_root).traverse do |ent| > destent = Entry_.new(dest, ent.rel, false) > ent.copy destent.path > ent.copy_metadata destent.path if preserve > end > end > module_function :copy_entry > > # > # Copies file contents of +src+ to +dest+. > # Both of +src+ and +dest+ must be a path name. > # > def copy_file(src, dest, preserve = false, dereference = true) > ent = Entry_.new(src, nil, dereference) > ent.copy_file dest > ent.copy_metadata dest if preserve > end > module_function :copy_file > > # [...] > > > class Entry_ #:nodoc: internal use only > include StreamUtils_ > > def initialize(a, b = nil, deref = false) > @prefix = @rel = @path = nil > if b > @prefix = a > @rel = b > else > @path = a > end > @deref = deref > @stat = nil > @lstat = nil > end > > def inspect > "\#<#{self.class} #{path()}>" > end > > def path > if @path > @path.to_str > else > join(@prefix, @rel) > end > end > > def prefix > @prefix || @path > end > > def rel > @rel > end > > def dereference? > @deref > end > > def exist? > lstat! ? true : false > end > > def file? > s = lstat! > s and s.file? > end > > def directory? > s = lstat! > s and s.directory? > end > > def symlink? > s = lstat! > s and s.symlink? > end > > def chardev? > s = lstat! > s and s.chardev? > end > > def blockdev? > s = lstat! > s and s.blockdev? > end > > def socket? > s = lstat! > s and s.socket? > end > > def pipe? > s = lstat! > s and s.pipe? > end > > S_IF_DOOR = 0xD000 > > def door? > s = lstat! > s and (s.mode & 0xF000 == S_IF_DOOR) > end > > def entries > Dir.entries(path())\ > .reject {|n| n == '.' or n == '..' }\ > .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } > end > > def stat > return @stat if @stat > if lstat() and lstat().symlink? > @stat = File.stat(path()) > else > @stat = lstat() > end > @stat > end > > def stat! > return @stat if @stat > if lstat! and lstat!.symlink? > @stat = File.stat(path()) > else > @stat = lstat! > end > @stat > rescue SystemCallError > nil > end > > def lstat > if dereference? > @lstat ||= File.stat(path()) > else > @lstat ||= File.lstat(path()) > end > end > > def lstat! > lstat() > rescue SystemCallError > nil > end > > def chmod(mode) > if symlink? > File.lchmod mode, path() if have_lchmod? > else > File.chmod mode, path() > end > end > > def chown(uid, gid) > if symlink? > File.lchown uid, gid, path() if have_lchown? > else > File.chown uid, gid, path() > end > end > > def copy(dest) > case > when file? > copy_file dest > when directory? > begin > Dir.mkdir dest > rescue > raise unless File.directory?(dest) > end > when symlink? > File.symlink File.readlink(path()), dest > when chardev? > raise "cannot handle device file" unless File.respond_to?(:mknod) > mknod dest, ?c, 0666, lstat().rdev > when blockdev? > raise "cannot handle device file" unless File.respond_to?(:mknod) > mknod dest, ?b, 0666, lstat().rdev > when socket? > raise "cannot handle socket" unless File.respond_to?(:mknod) > mknod dest, nil, lstat().mode, 0 > when pipe? > raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) > mkfifo dest, 0666 > when door? > raise "cannot handle door: #{path()}" > else > raise "unknown file type: #{path()}" > end > end > > def copy_file(dest) > st = stat() > File.open(path(), 'rb') {|r| > File.open(dest, 'wb', st.mode) {|w| > fu_copy_stream0 r, w, (fu_blksize(st) || fu_default_blksize()) > } > } > end > > def copy_metadata(path) > st = lstat() > File.utime st.atime, st.mtime, path > begin > File.chown st.uid, st.gid, path > rescue Errno::EPERM > # clear setuid/setgid > File.chmod st.mode & 01777, path > else > File.chmod st.mode, path > end > end > > # [...] > > def platform_support > return yield unless fu_windows? > first_time_p = true > begin > yield > rescue Errno::ENOENT > raise > rescue => err > if first_time_p > first_time_p = false > begin > File.chmod 0700, path() # Windows does not have symlink > retry > rescue SystemCallError > end > end > raise err > end > end > > def preorder_traverse > stack = [self] > while ent = stack.pop > yield ent > stack.concat ent.entries.reverse if ent.directory? > end > end > > alias traverse preorder_traverse > > def postorder_traverse > if directory? > entries().each do |ent| > ent.postorder_traverse do |e| > yield e > end > end > end > yield self > end > > > # [...] > > def join(dir, base) > return dir.to_str if not base or base == '.' > return base.to_str if not dir or dir == '.' > File.join(dir, base) > end > end # class Entry_ > > def fu_list(arg) #:nodoc: > [arg].flatten.map {|path| path.to_str } > end > private_module_function :fu_list > > def fu_each_src_dest(src, dest) #:nodoc: > fu_each_src_dest0(src, dest) do |s, d| > raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d) > yield s, d > end > end > private_module_function :fu_each_src_dest > > def fu_each_src_dest0(src, dest) #:nodoc: > if src.is_a?(Array) > src.each do |s| > s = s.to_str > yield s, File.join(dest, File.basename(s)) > end > else > src = src.to_str > if File.directory?(dest) > yield src, File.join(dest, File.basename(src)) > else > yield src, dest.to_str > end > end > end > private_module_function :fu_each_src_dest0 > > def fu_same?(a, b) #:nodoc: > if fu_have_st_ino? > st1 = File.stat(a) > st2 = File.stat(b) > st1.dev == st2.dev and st1.ino == st2.ino > else > File.expand_path(a) == File.expand_path(b) > end > rescue Errno::ENOENT > return false > end > private_module_function :fu_same? > > def fu_have_st_ino? #:nodoc: > not fu_windows? > end > private_module_function :fu_have_st_ino? > > def fu_check_options(options, *optdecl) #:nodoc: > h = options.dup > optdecl.each do |name| > h.delete name > end > raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty? > end > private_module_function :fu_check_options > > def fu_update_option(args, new) #:nodoc: > if args.last.is_a?(Hash) > args[-1] = args.last.dup.update(new) > else > args.push new > end > args > end > private_module_function :fu_update_option > > @fileutils_output = $stderr > @fileutils_label = '' > > def fu_output_message(msg) #:nodoc: > @fileutils_output ||= $stderr > @fileutils_label ||= '' > @fileutils_output.puts @fileutils_label + msg > end > private_module_function :fu_output_message > > # > # Returns an Array of method names which have any options. > # > # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] > # > def FileUtils.commands > OPT_TABLE.keys > end > > # > # Returns an Array of option names. > # > # p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] > # > def FileUtils.options > OPT_TABLE.values.flatten.uniq > end > > # > # Returns true if the method +mid+ have an option +opt+. > # > # p FileUtils.have_option?(:cp, :noop) #=> true > # p FileUtils.have_option?(:rm, :force) #=> true > # p FileUtils.have_option?(:rm, :perserve) #=> false > # > def FileUtils.have_option?(mid, opt) > li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}" > li.include?(opt.to_s) > end > > # > # Returns an Array of option names of the method +mid+. > # > # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"] > # > def FileUtils.options_of(mid) > OPT_TABLE[mid.to_s] > end > > # [...] > > end > > |
Re: Trapping errors.
Hugh Sasse wrote:
> I see my changes to fileutils are now in the Ruby CVS. > However, even with > rescue Exception, SystemCallError => e > or > rescue Exception, Errno::EACCES, Errno::EBUSY => e > > I still cannot trap this error. From the call stack this part of > the code is being used, so why won't the error cause ruby to go back > up the callstack until it finds this rescue clause? Maybe there's another rescue clause that is closer to the place where the exception is thrown... robert > > Hugh > > On Tue, 1 Nov 2005, Hugh Sasse wrote: > >> On Tue, 1 Nov 2005, Robert Klemme wrote: >> >>> Hugh Sasse <hgs@dmu.ac.uk> wrote: >>>> begin >>>> #... >>>> rescue => e >>>> #... >>>> end >>>> >>>> will trap e if it is a StandardError. SystemCallErrrors are >>>> supposed to handle Errorcodes from the OS. All of these are >>>> subclasses of Exception. So why do I get this failure under >>>> Cygwin: >>>> >>>> $ ruby BACKUP.RB "C:\\" "D:\\buzz_c" >>>> cp -rp C:\ D:\buzz_c >>>> /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `initialize': Device or >>>> resource busy - C:\/WINDOWS/WIN386.SWP (Errno::EBUSY) >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `copy_file' >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:1221:in `copy' >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:455:in `copy_entry' >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:1314:in `traverse' >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:453:in `copy_entry' >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:424:in `cp_r' >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:1385:in >>>> `fu_each_src_dest' from >>>> /usr/lib/ruby/1.8/new_fileutils.rb:1401:in >>>> `fu_each_src_dest0' from >>>> /usr/lib/ruby/1.8/new_fileutils.rb:1383:in >>>> `fu_each_src_dest' from /usr/lib/ruby/1.8/new_fileutils.rb:422:in >>>> `cp_r' from BACKUP.RB:27 >>>> >>>> hgs@buzz ~/downloads >>>> >>>> when my modified FileUtils.cp_r has >>>> >>>> begin >>>> copy_entry ... >>>> rescue Exception => e >>>> logger.error("backup"){"Error was #{e}") >>>> end >>>> >>>> (essentially. Theres a bit more to it than that, but the details >>>> shouldn't matter for my question.) So why can't I rescue it? (I'm >>>> trying to log, and skip files I can't backup so at least I get most >>>> of the files, and know which ones I have not.) >>> >>> Maybe it's in another thread. Or your code is actually not between >>> "begin" and "rescue" but outside of that. >> >> there's no threading in there, and I'm pretty certain it is within >> that, because it is in the call to copy_entry >> >>> >>> Kind regards >>> >>> robert >>> >> >> The modified fileutils is (heavily pruned) below >> I've changed cp_r. >> >> Hugh >> >> # >> # = fileutils.rb >> # >> # Copyright (c) 2000-2005 Minero Aoki <aamine@loveruby.net> >> # >> # This program is free software. >> # You can distribute/modify this program under the same terms of >> ruby. # >> # == module FileUtils >> # >> # Namespace for several file utility methods for copying, moving, >> removing, etc. # >> # === Module Functions >> # >> # [...] >> # cp_r(src, dest, options) {|s,d,e|...} >> # cp_r(list, dir, options) {|s,d,e|...} >> # [...] >> # >> # The <tt>options</tt> parameter is a hash of options, taken from >> the list # <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and >> <tt>:verbose</tt>. # <tt>:noop</tt> means that no changes are made. >> The other two are obvious. # Each method documents the options that >> it honours. # >> # All methods that have the concept of a "source" file or directory >> can take # either one file or a list of files in that argument. See >> the method # documentation for examples. >> # >> # There are some `low level' methods, which do not accept any option: >> # >> # copy_entry(src, dest, preserve = false, dereference = false) >> # copy_file(src, dest, preserve = false, dereference = true) >> # [...] >> # >> # == module FileUtils::Verbose >> # >> # This module has all methods of FileUtils module, but it outputs >> messages # before acting. This equates to passing the >> <tt>:verbose</tt> flag to methods # in FileUtils. >> # >> # == module FileUtils::NoWrite >> # >> # This module has all methods of FileUtils module, but never changes >> # files/directories. This equates to passing the <tt>:noop</tt> >> flag to methods # in FileUtils. >> # >> # == module FileUtils::DryRun >> # >> # This module has all methods of FileUtils module, but never changes >> # files/directories. This equates to passing the <tt>:noop</tt> and >> # <tt>:verbose</tt> flags to methods in FileUtils. >> # >> >> module FileUtils >> >> def self.private_module_function(name) #:nodoc: >> module_function name >> private_class_method name >> end >> >> # This hash table holds command options. >> OPT_TABLE = {} #:nodoc: internal use only >> >> # [...] >> >> def fu_mkdir(path, mode) #:nodoc: >> path = path.sub(%r</\z>, '') >> if mode >> Dir.mkdir path, mode >> File.chmod mode, path >> else >> Dir.mkdir path >> end >> end >> private_module_function :fu_mkdir >> >> # [...] >> >> # >> # Options: preserve noop verbose dereference_root >> # >> # Copies +src+ to +dest+. If +src+ is a directory, this method >> copies # all its contents recursively. If +dest+ is a directory, >> copies # +src+ to +dest/src+. >> # >> # +src+ can be a list of files. >> # >> # # Installing ruby library "mylib" under the site_ruby >> # FileUtils.rm_r site_ruby + '/mylib', :force >> # FileUtils.cp_r 'lib/', site_ruby + '/mylib' >> # >> # # Examples of copying several files to target directory. >> # FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + >> '/tmail' # FileUtils.cp_r Dir.glob('*.rb'), >> '/home/aamine/lib/ruby', :noop => true, :verbose => true # >> # # If you want to copy all contents of a directory instead of >> the # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, >> # # use following code. >> # FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes >> src/dest, # # but this >> doesn't. # >> def cp_r(src, dest, options = {}) >> fu_check_options options, :preserve, :noop, :verbose, >> :dereference_root fu_output_message "cp -r#{options[:preserve] ? >> 'p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] >> return if options[:noop] options[:dereference_root] = true >> unless options.key?(:dereference_root) fu_each_src_dest(src, >> dest) do |s, d| begin >> copy_entry s, d, options[:preserve], >> options[:dereference_root] rescue Exception => e >> stop = true >> if block_given? >> stop = yield s,d,e >> end >> raise if stop >> end >> end >> end >> module_function :cp_r >> >> OPT_TABLE['cp_r'] = %w( noop verbose preserve dereference_root ) >> >> # >> # Copies a file system entry +src+ to +dest+. >> # If +src+ is a directory, this method copies its contents >> recursively. # This method preserves file types, c.f. symlink, >> directory... # (FIFO, device files and etc. are not supported yet) >> # >> # Both of +src+ and +dest+ must be a path name. >> # +src+ must exist, +dest+ must not exist. >> # >> # If +preserve+ is true, this method preserves owner, group, >> permissions # and modified time. >> # >> # If +dereference_root+ is true, this method dereference tree root. >> # >> def copy_entry(src, dest, preserve = false, dereference_root = >> false) Entry_.new(src, nil, dereference_root).traverse do |ent| >> destent = Entry_.new(dest, ent.rel, false) >> ent.copy destent.path >> ent.copy_metadata destent.path if preserve >> end >> end >> module_function :copy_entry >> >> # >> # Copies file contents of +src+ to +dest+. >> # Both of +src+ and +dest+ must be a path name. >> # >> def copy_file(src, dest, preserve = false, dereference = true) >> ent = Entry_.new(src, nil, dereference) >> ent.copy_file dest >> ent.copy_metadata dest if preserve >> end >> module_function :copy_file >> >> # [...] >> >> >> class Entry_ #:nodoc: internal use only >> include StreamUtils_ >> >> def initialize(a, b = nil, deref = false) >> @prefix = @rel = @path = nil >> if b >> @prefix = a >> @rel = b >> else >> @path = a >> end >> @deref = deref >> @stat = nil >> @lstat = nil >> end >> >> def inspect >> "\#<#{self.class} #{path()}>" >> end >> >> def path >> if @path >> @path.to_str >> else >> join(@prefix, @rel) >> end >> end >> >> def prefix >> @prefix || @path >> end >> >> def rel >> @rel >> end >> >> def dereference? >> @deref >> end >> >> def exist? >> lstat! ? true : false >> end >> >> def file? >> s = lstat! >> s and s.file? >> end >> >> def directory? >> s = lstat! >> s and s.directory? >> end >> >> def symlink? >> s = lstat! >> s and s.symlink? >> end >> >> def chardev? >> s = lstat! >> s and s.chardev? >> end >> >> def blockdev? >> s = lstat! >> s and s.blockdev? >> end >> >> def socket? >> s = lstat! >> s and s.socket? >> end >> >> def pipe? >> s = lstat! >> s and s.pipe? >> end >> >> S_IF_DOOR = 0xD000 >> >> def door? >> s = lstat! >> s and (s.mode & 0xF000 == S_IF_DOOR) >> end >> >> def entries >> Dir.entries(path())\ >> .reject {|n| n == '.' or n == '..' }\ >> .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } >> end >> >> def stat >> return @stat if @stat >> if lstat() and lstat().symlink? >> @stat = File.stat(path()) >> else >> @stat = lstat() >> end >> @stat >> end >> >> def stat! >> return @stat if @stat >> if lstat! and lstat!.symlink? >> @stat = File.stat(path()) >> else >> @stat = lstat! >> end >> @stat >> rescue SystemCallError >> nil >> end >> >> def lstat >> if dereference? >> @lstat ||= File.stat(path()) >> else >> @lstat ||= File.lstat(path()) >> end >> end >> >> def lstat! >> lstat() >> rescue SystemCallError >> nil >> end >> >> def chmod(mode) >> if symlink? >> File.lchmod mode, path() if have_lchmod? >> else >> File.chmod mode, path() >> end >> end >> >> def chown(uid, gid) >> if symlink? >> File.lchown uid, gid, path() if have_lchown? >> else >> File.chown uid, gid, path() >> end >> end >> >> def copy(dest) >> case >> when file? >> copy_file dest >> when directory? >> begin >> Dir.mkdir dest >> rescue >> raise unless File.directory?(dest) >> end >> when symlink? >> File.symlink File.readlink(path()), dest >> when chardev? >> raise "cannot handle device file" unless >> File.respond_to?(:mknod) mknod dest, ?c, 0666, lstat().rdev >> when blockdev? >> raise "cannot handle device file" unless >> File.respond_to?(:mknod) mknod dest, ?b, 0666, lstat().rdev >> when socket? >> raise "cannot handle socket" unless File.respond_to?(:mknod) >> mknod dest, nil, lstat().mode, 0 >> when pipe? >> raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) >> mkfifo dest, 0666 >> when door? >> raise "cannot handle door: #{path()}" >> else >> raise "unknown file type: #{path()}" >> end >> end >> >> def copy_file(dest) >> st = stat() >> File.open(path(), 'rb') {|r| >> File.open(dest, 'wb', st.mode) {|w| >> fu_copy_stream0 r, w, (fu_blksize(st) || >> fu_default_blksize()) } >> } >> end >> >> def copy_metadata(path) >> st = lstat() >> File.utime st.atime, st.mtime, path >> begin >> File.chown st.uid, st.gid, path >> rescue Errno::EPERM >> # clear setuid/setgid >> File.chmod st.mode & 01777, path >> else >> File.chmod st.mode, path >> end >> end >> >> # [...] >> >> def platform_support >> return yield unless fu_windows? >> first_time_p = true >> begin >> yield >> rescue Errno::ENOENT >> raise >> rescue => err >> if first_time_p >> first_time_p = false >> begin >> File.chmod 0700, path() # Windows does not have symlink >> retry >> rescue SystemCallError >> end >> end >> raise err >> end >> end >> >> def preorder_traverse >> stack = [self] >> while ent = stack.pop >> yield ent >> stack.concat ent.entries.reverse if ent.directory? >> end >> end >> >> alias traverse preorder_traverse >> >> def postorder_traverse >> if directory? >> entries().each do |ent| >> ent.postorder_traverse do |e| >> yield e >> end >> end >> end >> yield self >> end >> >> >> # [...] >> >> def join(dir, base) >> return dir.to_str if not base or base == '.' >> return base.to_str if not dir or dir == '.' >> File.join(dir, base) >> end >> end # class Entry_ >> >> def fu_list(arg) #:nodoc: >> [arg].flatten.map {|path| path.to_str } >> end >> private_module_function :fu_list >> >> def fu_each_src_dest(src, dest) #:nodoc: >> fu_each_src_dest0(src, dest) do |s, d| >> raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, >> d) yield s, d >> end >> end >> private_module_function :fu_each_src_dest >> >> def fu_each_src_dest0(src, dest) #:nodoc: >> if src.is_a?(Array) >> src.each do |s| >> s = s.to_str >> yield s, File.join(dest, File.basename(s)) >> end >> else >> src = src.to_str >> if File.directory?(dest) >> yield src, File.join(dest, File.basename(src)) >> else >> yield src, dest.to_str >> end >> end >> end >> private_module_function :fu_each_src_dest0 >> >> def fu_same?(a, b) #:nodoc: >> if fu_have_st_ino? >> st1 = File.stat(a) >> st2 = File.stat(b) >> st1.dev == st2.dev and st1.ino == st2.ino >> else >> File.expand_path(a) == File.expand_path(b) >> end >> rescue Errno::ENOENT >> return false >> end >> private_module_function :fu_same? >> >> def fu_have_st_ino? #:nodoc: >> not fu_windows? >> end >> private_module_function :fu_have_st_ino? >> >> def fu_check_options(options, *optdecl) #:nodoc: >> h = options.dup >> optdecl.each do |name| >> h.delete name >> end >> raise ArgumentError, "no such option: #{h.keys.join(' ')}" >> unless h.empty? end >> private_module_function :fu_check_options >> >> def fu_update_option(args, new) #:nodoc: >> if args.last.is_a?(Hash) >> args[-1] = args.last.dup.update(new) >> else >> args.push new >> end >> args >> end >> private_module_function :fu_update_option >> >> @fileutils_output = $stderr >> @fileutils_label = '' >> >> def fu_output_message(msg) #:nodoc: >> @fileutils_output ||= $stderr >> @fileutils_label ||= '' >> @fileutils_output.puts @fileutils_label + msg >> end >> private_module_function :fu_output_message >> >> # >> # Returns an Array of method names which have any options. >> # >> # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", >> ...] # >> def FileUtils.commands >> OPT_TABLE.keys >> end >> >> # >> # Returns an Array of option names. >> # >> # p FileUtils.options #=> ["noop", "force", "verbose", >> "preserve", "mode"] # >> def FileUtils.options >> OPT_TABLE.values.flatten.uniq >> end >> >> # >> # Returns true if the method +mid+ have an option +opt+. >> # >> # p FileUtils.have_option?(:cp, :noop) #=> true >> # p FileUtils.have_option?(:rm, :force) #=> true >> # p FileUtils.have_option?(:rm, :perserve) #=> false >> # >> def FileUtils.have_option?(mid, opt) >> li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such >> method: #{mid}" li.include?(opt.to_s) >> end >> >> # >> # Returns an Array of option names of the method +mid+. >> # >> # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"] >> # >> def FileUtils.options_of(mid) >> OPT_TABLE[mid.to_s] >> end >> >> # [...] >> >> end |
Re: Trapping errors.
On Thu, 3 Nov 2005, Robert Klemme wrote:
> Hugh Sasse wrote: > > I see my changes to fileutils are now in the Ruby CVS. > > However, even with > > rescue Exception, SystemCallError => e > > or > > rescue Exception, Errno::EACCES, Errno::EBUSY => e > > > > I still cannot trap this error. From the call stack this part of > > the code is being used, so why won't the error cause ruby to go back > > up the callstack until it finds this rescue clause? > > Maybe there's another rescue clause that is closer to the place where the > exception is thrown... But unless I have completely misunderstood the point of rescue, even if that calls raise, the exception will still be caught by my enclosing rescue. neelix hgs 15 %> irb irb(main):001:0> begin irb(main):002:1* begin irb(main):003:2* raise StandardError irb(main):004:2> rescue irb(main):005:2> raise irb(main):006:2> end irb(main):007:1> rescue irb(main):008:1> puts "caught here" irb(main):009:1> end caught here => nil irb(main):010:0> So, if I'm doing rescue Exception => e how can Errno::EACCES or Errno::EBUSY get past that and crash out? > > robert > > > > > Hugh > > > > On Tue, 1 Nov 2005, Hugh Sasse wrote: > > > >> On Tue, 1 Nov 2005, Robert Klemme wrote: > >> > >>> Hugh Sasse <hgs@dmu.ac.uk> wrote: > >>>> begin > >>>> #... > >>>> rescue => e > >>>> #... > >>>> end > >>>> > >>>> will trap e if it is a StandardError. SystemCallErrrors are > >>>> supposed to handle Errorcodes from the OS. All of these are > >>>> subclasses of Exception. So why do I get this failure under > >>>> Cygwin: > >>>> > >>>> $ ruby BACKUP.RB "C:\\" "D:\\buzz_c" > >>>> cp -rp C:\ D:\buzz_c > >>>> /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `initialize': Device or > >>>> resource busy - C:\/WINDOWS/WIN386.SWP (Errno::EBUSY) > >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `copy_file' > >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:1221:in `copy' > >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:455:in `copy_entry' > >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:1314:in `traverse' > >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:453:in `copy_entry' > >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:424:in `cp_r' > >>>> from /usr/lib/ruby/1.8/new_fileutils.rb:1385:in > >>>> `fu_each_src_dest' from > >>>> /usr/lib/ruby/1.8/new_fileutils.rb:1401:in > >>>> `fu_each_src_dest0' from > >>>> /usr/lib/ruby/1.8/new_fileutils.rb:1383:in > >>>> `fu_each_src_dest' from /usr/lib/ruby/1.8/new_fileutils.rb:422:in > >>>> `cp_r' from BACKUP.RB:27 > >>>> > >>>> hgs@buzz ~/downloads > >>>> > >>>> when my modified FileUtils.cp_r has > >>>> > >>>> begin > >>>> copy_entry ... > >>>> rescue Exception => e > >>>> logger.error("backup"){"Error was #{e}") > >>>> end > >>>> > >>>> (essentially. Theres a bit more to it than that, but the details > >>>> shouldn't matter for my question.) So why can't I rescue it? (I'm > >>>> trying to log, and skip files I can't backup so at least I get most > >>>> of the files, and know which ones I have not.) > >>> > >>> Maybe it's in another thread. Or your code is actually not between > >>> "begin" and "rescue" but outside of that. > >> > >> there's no threading in there, and I'm pretty certain it is within > >> that, because it is in the call to copy_entry > >> > >>> > >>> Kind regards > >>> > >>> robert > >>> > >> > >> The modified fileutils is (heavily pruned) below > >> I've changed cp_r. > >> > >> Hugh > >> > >> # > >> # = fileutils.rb > >> # > >> # Copyright (c) 2000-2005 Minero Aoki <aamine@loveruby.net> > >> # > >> # This program is free software. > >> # You can distribute/modify this program under the same terms of > >> ruby. # > >> # == module FileUtils > >> # > >> # Namespace for several file utility methods for copying, moving, > >> removing, etc. # > >> # === Module Functions > >> # > >> # [...] > >> # cp_r(src, dest, options) {|s,d,e|...} > >> # cp_r(list, dir, options) {|s,d,e|...} > >> # [...] > >> # > >> # The <tt>options</tt> parameter is a hash of options, taken from > >> the list # <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and > >> <tt>:verbose</tt>. # <tt>:noop</tt> means that no changes are made. > >> The other two are obvious. # Each method documents the options that > >> it honours. # > >> # All methods that have the concept of a "source" file or directory > >> can take # either one file or a list of files in that argument. See > >> the method # documentation for examples. > >> # > >> # There are some `low level' methods, which do not accept any option: > >> # > >> # copy_entry(src, dest, preserve = false, dereference = false) > >> # copy_file(src, dest, preserve = false, dereference = true) > >> # [...] > >> # > >> # == module FileUtils::Verbose > >> # > >> # This module has all methods of FileUtils module, but it outputs > >> messages # before acting. This equates to passing the > >> <tt>:verbose</tt> flag to methods # in FileUtils. > >> # > >> # == module FileUtils::NoWrite > >> # > >> # This module has all methods of FileUtils module, but never changes > >> # files/directories. This equates to passing the <tt>:noop</tt> > >> flag to methods # in FileUtils. > >> # > >> # == module FileUtils::DryRun > >> # > >> # This module has all methods of FileUtils module, but never changes > >> # files/directories. This equates to passing the <tt>:noop</tt> and > >> # <tt>:verbose</tt> flags to methods in FileUtils. > >> # > >> > >> module FileUtils > >> > >> def self.private_module_function(name) #:nodoc: > >> module_function name > >> private_class_method name > >> end > >> > >> # This hash table holds command options. > >> OPT_TABLE = {} #:nodoc: internal use only > >> > >> # [...] > >> > >> def fu_mkdir(path, mode) #:nodoc: > >> path = path.sub(%r</\z>, '') > >> if mode > >> Dir.mkdir path, mode > >> File.chmod mode, path > >> else > >> Dir.mkdir path > >> end > >> end > >> private_module_function :fu_mkdir > >> > >> # [...] > >> > >> # > >> # Options: preserve noop verbose dereference_root > >> # > >> # Copies +src+ to +dest+. If +src+ is a directory, this method > >> copies # all its contents recursively. If +dest+ is a directory, > >> copies # +src+ to +dest/src+. > >> # > >> # +src+ can be a list of files. > >> # > >> # # Installing ruby library "mylib" under the site_ruby > >> # FileUtils.rm_r site_ruby + '/mylib', :force > >> # FileUtils.cp_r 'lib/', site_ruby + '/mylib' > >> # > >> # # Examples of copying several files to target directory. > >> # FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + > >> '/tmail' # FileUtils.cp_r Dir.glob('*.rb'), > >> '/home/aamine/lib/ruby', :noop => true, :verbose => true # > >> # # If you want to copy all contents of a directory instead of > >> the # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, > >> # # use following code. > >> # FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes > >> src/dest, # # but this > >> doesn't. # > >> def cp_r(src, dest, options = {}) > >> fu_check_options options, :preserve, :noop, :verbose, > >> :dereference_root fu_output_message "cp -r#{options[:preserve] ? > >> 'p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] > >> return if options[:noop] options[:dereference_root] = true > >> unless options.key?(:dereference_root) fu_each_src_dest(src, > >> dest) do |s, d| begin > >> copy_entry s, d, options[:preserve], > >> options[:dereference_root] rescue Exception => e > >> stop = true > >> if block_given? > >> stop = yield s,d,e > >> end > >> raise if stop > >> end > >> end > >> end > >> module_function :cp_r > >> > >> OPT_TABLE['cp_r'] = %w( noop verbose preserve dereference_root ) > >> > >> # > >> # Copies a file system entry +src+ to +dest+. > >> # If +src+ is a directory, this method copies its contents > >> recursively. # This method preserves file types, c.f. symlink, > >> directory... # (FIFO, device files and etc. are not supported yet) > >> # > >> # Both of +src+ and +dest+ must be a path name. > >> # +src+ must exist, +dest+ must not exist. > >> # > >> # If +preserve+ is true, this method preserves owner, group, > >> permissions # and modified time. > >> # > >> # If +dereference_root+ is true, this method dereference tree root. > >> # > >> def copy_entry(src, dest, preserve = false, dereference_root = > >> false) Entry_.new(src, nil, dereference_root).traverse do |ent| > >> destent = Entry_.new(dest, ent.rel, false) > >> ent.copy destent.path > >> ent.copy_metadata destent.path if preserve > >> end > >> end > >> module_function :copy_entry > >> > >> # > >> # Copies file contents of +src+ to +dest+. > >> # Both of +src+ and +dest+ must be a path name. > >> # > >> def copy_file(src, dest, preserve = false, dereference = true) > >> ent = Entry_.new(src, nil, dereference) > >> ent.copy_file dest > >> ent.copy_metadata dest if preserve > >> end > >> module_function :copy_file > >> > >> # [...] > >> > >> > >> class Entry_ #:nodoc: internal use only > >> include StreamUtils_ > >> > >> def initialize(a, b = nil, deref = false) > >> @prefix = @rel = @path = nil > >> if b > >> @prefix = a > >> @rel = b > >> else > >> @path = a > >> end > >> @deref = deref > >> @stat = nil > >> @lstat = nil > >> end > >> > >> def inspect > >> "\#<#{self.class} #{path()}>" > >> end > >> > >> def path > >> if @path > >> @path.to_str > >> else > >> join(@prefix, @rel) > >> end > >> end > >> > >> def prefix > >> @prefix || @path > >> end > >> > >> def rel > >> @rel > >> end > >> > >> def dereference? > >> @deref > >> end > >> > >> def exist? > >> lstat! ? true : false > >> end > >> > >> def file? > >> s = lstat! > >> s and s.file? > >> end > >> > >> def directory? > >> s = lstat! > >> s and s.directory? > >> end > >> > >> def symlink? > >> s = lstat! > >> s and s.symlink? > >> end > >> > >> def chardev? > >> s = lstat! > >> s and s.chardev? > >> end > >> > >> def blockdev? > >> s = lstat! > >> s and s.blockdev? > >> end > >> > >> def socket? > >> s = lstat! > >> s and s.socket? > >> end > >> > >> def pipe? > >> s = lstat! > >> s and s.pipe? > >> end > >> > >> S_IF_DOOR = 0xD000 > >> > >> def door? > >> s = lstat! > >> s and (s.mode & 0xF000 == S_IF_DOOR) > >> end > >> > >> def entries > >> Dir.entries(path())\ > >> .reject {|n| n == '.' or n == '..' }\ > >> .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } > >> end > >> > >> def stat > >> return @stat if @stat > >> if lstat() and lstat().symlink? > >> @stat = File.stat(path()) > >> else > >> @stat = lstat() > >> end > >> @stat > >> end > >> > >> def stat! > >> return @stat if @stat > >> if lstat! and lstat!.symlink? > >> @stat = File.stat(path()) > >> else > >> @stat = lstat! > >> end > >> @stat > >> rescue SystemCallError > >> nil > >> end > >> > >> def lstat > >> if dereference? > >> @lstat ||= File.stat(path()) > >> else > >> @lstat ||= File.lstat(path()) > >> end > >> end > >> > >> def lstat! > >> lstat() > >> rescue SystemCallError > >> nil > >> end > >> > >> def chmod(mode) > >> if symlink? > >> File.lchmod mode, path() if have_lchmod? > >> else > >> File.chmod mode, path() > >> end > >> end > >> > >> def chown(uid, gid) > >> if symlink? > >> File.lchown uid, gid, path() if have_lchown? > >> else > >> File.chown uid, gid, path() > >> end > >> end > >> > >> def copy(dest) > >> case > >> when file? > >> copy_file dest > >> when directory? > >> begin > >> Dir.mkdir dest > >> rescue > >> raise unless File.directory?(dest) > >> end > >> when symlink? > >> File.symlink File.readlink(path()), dest > >> when chardev? > >> raise "cannot handle device file" unless > >> File.respond_to?(:mknod) mknod dest, ?c, 0666, lstat().rdev > >> when blockdev? > >> raise "cannot handle device file" unless > >> File.respond_to?(:mknod) mknod dest, ?b, 0666, lstat().rdev > >> when socket? > >> raise "cannot handle socket" unless File.respond_to?(:mknod) > >> mknod dest, nil, lstat().mode, 0 > >> when pipe? > >> raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) > >> mkfifo dest, 0666 > >> when door? > >> raise "cannot handle door: #{path()}" > >> else > >> raise "unknown file type: #{path()}" > >> end > >> end > >> > >> def copy_file(dest) > >> st = stat() > >> File.open(path(), 'rb') {|r| > >> File.open(dest, 'wb', st.mode) {|w| > >> fu_copy_stream0 r, w, (fu_blksize(st) || > >> fu_default_blksize()) } > >> } > >> end > >> > >> def copy_metadata(path) > >> st = lstat() > >> File.utime st.atime, st.mtime, path > >> begin > >> File.chown st.uid, st.gid, path > >> rescue Errno::EPERM > >> # clear setuid/setgid > >> File.chmod st.mode & 01777, path > >> else > >> File.chmod st.mode, path > >> end > >> end > >> > >> # [...] > >> > >> def platform_support > >> return yield unless fu_windows? > >> first_time_p = true > >> begin > >> yield > >> rescue Errno::ENOENT > >> raise > >> rescue => err > >> if first_time_p > >> first_time_p = false > >> begin > >> File.chmod 0700, path() # Windows does not have symlink > >> retry > >> rescue SystemCallError > >> end > >> end > >> raise err > >> end > >> end > >> > >> def preorder_traverse > >> stack = [self] > >> while ent = stack.pop > >> yield ent > >> stack.concat ent.entries.reverse if ent.directory? > >> end > >> end > >> > >> alias traverse preorder_traverse > >> > >> def postorder_traverse > >> if directory? > >> entries().each do |ent| > >> ent.postorder_traverse do |e| > >> yield e > >> end > >> end > >> end > >> yield self > >> end > >> > >> > >> # [...] > >> > >> def join(dir, base) > >> return dir.to_str if not base or base == '.' > >> return base.to_str if not dir or dir == '.' > >> File.join(dir, base) > >> end > >> end # class Entry_ > >> > >> def fu_list(arg) #:nodoc: > >> [arg].flatten.map {|path| path.to_str } > >> end > >> private_module_function :fu_list > >> > >> def fu_each_src_dest(src, dest) #:nodoc: > >> fu_each_src_dest0(src, dest) do |s, d| > >> raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, > >> d) yield s, d > >> end > >> end > >> private_module_function :fu_each_src_dest > >> > >> def fu_each_src_dest0(src, dest) #:nodoc: > >> if src.is_a?(Array) > >> src.each do |s| > >> s = s.to_str > >> yield s, File.join(dest, File.basename(s)) > >> end > >> else > >> src = src.to_str > >> if File.directory?(dest) > >> yield src, File.join(dest, File.basename(src)) > >> else > >> yield src, dest.to_str > >> end > >> end > >> end > >> private_module_function :fu_each_src_dest0 > >> > >> def fu_same?(a, b) #:nodoc: > >> if fu_have_st_ino? > >> st1 = File.stat(a) > >> st2 = File.stat(b) > >> st1.dev == st2.dev and st1.ino == st2.ino > >> else > >> File.expand_path(a) == File.expand_path(b) > >> end > >> rescue Errno::ENOENT > >> return false > >> end > >> private_module_function :fu_same? > >> > >> def fu_have_st_ino? #:nodoc: > >> not fu_windows? > >> end > >> private_module_function :fu_have_st_ino? > >> > >> def fu_check_options(options, *optdecl) #:nodoc: > >> h = options.dup > >> optdecl.each do |name| > >> h.delete name > >> end > >> raise ArgumentError, "no such option: #{h.keys.join(' ')}" > >> unless h.empty? end > >> private_module_function :fu_check_options > >> > >> def fu_update_option(args, new) #:nodoc: > >> if args.last.is_a?(Hash) > >> args[-1] = args.last.dup.update(new) > >> else > >> args.push new > >> end > >> args > >> end > >> private_module_function :fu_update_option > >> > >> @fileutils_output = $stderr > >> @fileutils_label = '' > >> > >> def fu_output_message(msg) #:nodoc: > >> @fileutils_output ||= $stderr > >> @fileutils_label ||= '' > >> @fileutils_output.puts @fileutils_label + msg > >> end > >> private_module_function :fu_output_message > >> > >> # > >> # Returns an Array of method names which have any options. > >> # > >> # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", > >> ...] # > >> def FileUtils.commands > >> OPT_TABLE.keys > >> end > >> > >> # > >> # Returns an Array of option names. > >> # > >> # p FileUtils.options #=> ["noop", "force", "verbose", > >> "preserve", "mode"] # > >> def FileUtils.options > >> OPT_TABLE.values.flatten.uniq > >> end > >> > >> # > >> # Returns true if the method +mid+ have an option +opt+. > >> # > >> # p FileUtils.have_option?(:cp, :noop) #=> true > >> # p FileUtils.have_option?(:rm, :force) #=> true > >> # p FileUtils.have_option?(:rm, :perserve) #=> false > >> # > >> def FileUtils.have_option?(mid, opt) > >> li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such > >> method: #{mid}" li.include?(opt.to_s) > >> end > >> > >> # > >> # Returns an Array of option names of the method +mid+. > >> # > >> # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"] > >> # > >> def FileUtils.options_of(mid) > >> OPT_TABLE[mid.to_s] > >> end > >> > >> # [...] > >> > >> end > > > |
Re: Trapping errors.
Hugh Sasse wrote:
> On Thu, 3 Nov 2005, Robert Klemme wrote: > >> Hugh Sasse wrote: >>> I see my changes to fileutils are now in the Ruby CVS. >>> However, even with >>> rescue Exception, SystemCallError => e >>> or >>> rescue Exception, Errno::EACCES, Errno::EBUSY => e >>> >>> I still cannot trap this error. From the call stack this part of >>> the code is being used, so why won't the error cause ruby to go back >>> up the callstack until it finds this rescue clause? >> >> Maybe there's another rescue clause that is closer to the place >> where the exception is thrown... > > But unless I have completely misunderstood the point of rescue, even > if that calls raise, the exception will still be caught by my > enclosing rescue. But who guarantees that the other rescue clause actually throws again? If it doesn't you can't catch it. .... rescue Exception => e puts "Catch me if you can >:-}" end ;-) Cheers robert |
Re: Trapping errors.
On Thu, 3 Nov 2005, Robert Klemme wrote:
> Hugh Sasse wrote: > > On Thu, 3 Nov 2005, Robert Klemme wrote: > > > >> Hugh Sasse wrote: > >>> I see my changes to fileutils are now in the Ruby CVS. > >>> However, even with > >>> rescue Exception, SystemCallError => e > >>> or > >>> rescue Exception, Errno::EACCES, Errno::EBUSY => e > >>> > >>> I still cannot trap this error. From the call stack this part of > >>> the code is being used, so why won't the error cause ruby to go back > >>> up the callstack until it finds this rescue clause? > >> > >> Maybe there's another rescue clause that is closer to the place > >> where the exception is thrown... > > > > But unless I have completely misunderstood the point of rescue, even > > if that calls raise, the exception will still be caught by my > > enclosing rescue. > > But who guarantees that the other rescue clause actually throws again? If > it doesn't you can't catch it. > > .... > rescue Exception => e > puts "Catch me if you can >:-}" > end > > ;-) > Then it's already caught, and I should never see the error. neelix hgs 58 %> irb irb(main):001:0> begin irb(main):002:1* begin irb(main):003:2* raise StandardError irb(main):004:2> rescue => e irb(main):005:2> puts "catch me if you can #{e}" irb(main):006:2> end irb(main):007:1> rescue irb(main):008:1> puts "caught something" irb(main):009:1> end catch me if you can StandardError => nil irb(main):010:0> i.e. that's a normal exit. So, I have an error that is not already caught, and I can't catch it. > Cheers > > robert > Hugh |
Re: Trapping errors.
Hugh Sasse <hgs@dmu.ac.uk> wrote:
> On Thu, 3 Nov 2005, Robert Klemme wrote: > >> Hugh Sasse wrote: >>> On Thu, 3 Nov 2005, Robert Klemme wrote: >>> >>>> Hugh Sasse wrote: >>>>> I see my changes to fileutils are now in the Ruby CVS. >>>>> However, even with >>>>> rescue Exception, SystemCallError => e >>>>> or >>>>> rescue Exception, Errno::EACCES, Errno::EBUSY => e >>>>> >>>>> I still cannot trap this error. From the call stack this part of >>>>> the code is being used, so why won't the error cause ruby to go >>>>> back up the callstack until it finds this rescue clause? >>>> >>>> Maybe there's another rescue clause that is closer to the place >>>> where the exception is thrown... >>> >>> But unless I have completely misunderstood the point of rescue, even >>> if that calls raise, the exception will still be caught by my >>> enclosing rescue. >> >> But who guarantees that the other rescue clause actually throws >> again? If it doesn't you can't catch it. >> >> .... >> rescue Exception => e >> puts "Catch me if you can >:-}" >> end >> >> ;-) >> > > Then it's already caught, and I should never see the error. Why not? If it's printed in the other rescue clause you would see it. Or am I missing something here? robert > > neelix hgs 58 %> irb > irb(main):001:0> begin > irb(main):002:1* begin > irb(main):003:2* raise StandardError > irb(main):004:2> rescue => e > irb(main):005:2> puts "catch me if you can #{e}" > irb(main):006:2> end > irb(main):007:1> rescue > irb(main):008:1> puts "caught something" > irb(main):009:1> end > catch me if you can StandardError > => nil > irb(main):010:0> > > i.e. that's a normal exit. > > So, I have an error that is not already caught, and I can't catch > it. > >> Cheers >> >> robert >> > > Hugh |
Re: Trapping errors.
On Thu, 3 Nov 2005, Robert Klemme wrote:
> Hugh Sasse <hgs@dmu.ac.uk> wrote: > > On Thu, 3 Nov 2005, Robert Klemme wrote: > > > > > Hugh Sasse wrote: > > > > On Thu, 3 Nov 2005, Robert Klemme wrote: > > > > > > > > > Hugh Sasse wrote: > > > > > > I see my changes to fileutils are now in the Ruby CVS. > > > > > > However, even with > > > > > > rescue Exception, SystemCallError => e > > > > > > or > > > > > > rescue Exception, Errno::EACCES, Errno::EBUSY => e > > > > > > > > > > > > I still cannot trap this error. From the call stack this part of > > > > > > the code is being used, so why won't the error cause ruby to go > > > > > > back up the callstack until it finds this rescue clause? > > > > > > > > > > Maybe there's another rescue clause that is closer to the place > > > > > where the exception is thrown... > > > > > > > > But unless I have completely misunderstood the point of rescue, even > > > > if that calls raise, the exception will still be caught by my > > > > enclosing rescue. > > > > > > But who guarantees that the other rescue clause actually throws > > > again? If it doesn't you can't catch it. > > > > > > .... > > > rescue Exception => e > > > puts "Catch me if you can >:-}" > > > end > > > > > > ;-) > > > > > > > Then it's already caught, and I should never see the error. > > Why not? If it's printed in the other rescue clause you would see it. Or am > I missing something here? I wouldn't see it as an error which kills my program. It would have been caught, and dealt with, or caught and re-raised, in which case the outer block would catch it. 1 We have established that a begin...rescue...end block will rescue any errors raised inside it, provided the rescue clause matches that error type. 2 We have established that if a begin ... rescue ... raise ... end block re-raises an error, it can be caught by a surrounding begin...rescue...end block 3 My case is that I have a begin part1 rescue Exception, SystemCallError, Errno::EACCES, Errno::EBUSY => e part2 end block and it is not catching a Errno::EBUSY error from part1: The error crashes the program. So: If that error has been raised in part1, and not caught in part1 then I should be able to catch it. If that error has been raised in part1, and caught in part1, part2 should never come into play, and the program should continue 4 How can a subclass of Exception not be caught by Exception? 5 Why doesn't SystemCallError catch it? 6 Why doesn't the Errno::%s form catch it? i.e everything between "Exception" and " =>" should be unnecessary, but even with those it doesn't catch the error. > > robert > Hugh |
| All times are GMT. The time now is 03:49 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.