scout-gear 6.0.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.vimproject +436 -432
- data/VERSION +1 -1
- data/lib/scout/exceptions.rb +8 -0
- data/lib/scout/log/color.rb +29 -2
- data/lib/scout/log/progress/util.rb +2 -0
- data/lib/scout/log/progress.rb +2 -0
- data/lib/scout/log.rb +5 -1
- data/lib/scout/misc/digest.rb +1 -3
- data/lib/scout/open/stream.rb +20 -19
- data/lib/scout/tsv/parser.rb +144 -0
- data/lib/scout/tsv.rb +14 -0
- data/lib/scout/work_queue/worker.rb +16 -11
- data/lib/scout/work_queue.rb +48 -21
- data/lib/scout/workflow/step/info.rb +2 -2
- data/lib/scout/workflow/step.rb +2 -1
- data/lib/scout/workflow/task.rb +2 -2
- data/scout-gear.gemspec +7 -3
- data/test/scout/open/test_stream.rb +1 -1
- data/test/scout/test_semaphore.rb +1 -1
- data/test/scout/test_tsv.rb +34 -0
- data/test/scout/test_work_queue.rb +28 -0
- data/test/scout/tsv/test_parser.rb +87 -0
- data/test/scout/work_queue/test_worker.rb +48 -0
- metadata +6 -2
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            7.1.0
         | 
    
        data/lib/scout/exceptions.rb
    CHANGED
    
    | @@ -84,6 +84,14 @@ class DoneProcessing < Exception | |
| 84 84 | 
             
              end
         | 
| 85 85 | 
             
            end
         | 
| 86 86 |  | 
| 87 | 
            +
            class WorkerException < ScoutException
         | 
| 88 | 
            +
              attr_accessor :exception, :pid
         | 
| 89 | 
            +
              def initialize(exception, pid)
         | 
| 90 | 
            +
                @exception = exception
         | 
| 91 | 
            +
                @pid = pid
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
            end
         | 
| 94 | 
            +
             | 
| 87 95 |  | 
| 88 96 | 
             
            #class OpenGzipError < StandardError; end
         | 
| 89 97 | 
             
            #
         | 
    
        data/lib/scout/log/color.rb
    CHANGED
    
    | @@ -143,13 +143,14 @@ module Log | |
| 143 143 | 
             
              CONCEPT_COLORS = IndiferentHash.setup({
         | 
| 144 144 | 
             
                :title => magenta,
         | 
| 145 145 | 
             
                :path => blue,
         | 
| 146 | 
            -
                :input =>  | 
| 146 | 
            +
                :input => cyan,
         | 
| 147 147 | 
             
                :value => green,
         | 
| 148 148 | 
             
                :integer => green,
         | 
| 149 149 | 
             
                :negative => red,
         | 
| 150 150 | 
             
                :float => green,
         | 
| 151 151 | 
             
                :waiting => yellow,
         | 
| 152 | 
            -
                :started =>  | 
| 152 | 
            +
                :started => cyan,
         | 
| 153 | 
            +
                :start => cyan,
         | 
| 153 154 | 
             
                :done => green,
         | 
| 154 155 | 
             
                :error => red,
         | 
| 155 156 | 
             
              })
         | 
| @@ -166,10 +167,36 @@ module Log | |
| 166 167 | 
             
              def self.color(color, str = nil, reset = false)
         | 
| 167 168 | 
             
                return str.dup || "" if nocolor 
         | 
| 168 169 |  | 
| 170 | 
            +
                if (color == :integer || color == :float) && Numeric === str
         | 
| 171 | 
            +
                  color = if str < 0
         | 
| 172 | 
            +
                            :red
         | 
| 173 | 
            +
                          elsif str > 1
         | 
| 174 | 
            +
                            :cyan
         | 
| 175 | 
            +
                          else
         | 
| 176 | 
            +
                            :green
         | 
| 177 | 
            +
                          end
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                if color == :status 
         | 
| 181 | 
            +
                  color = case str.to_sym
         | 
| 182 | 
            +
                          when :done
         | 
| 183 | 
            +
                            :green
         | 
| 184 | 
            +
                          when :error, :aborted
         | 
| 185 | 
            +
                            :red
         | 
| 186 | 
            +
                          when :waiting, :queued
         | 
| 187 | 
            +
                            :yellow
         | 
| 188 | 
            +
                          when :started, :start, :streamming
         | 
| 189 | 
            +
                            :cyan
         | 
| 190 | 
            +
                          else
         | 
| 191 | 
            +
                            :cyan
         | 
| 192 | 
            +
                          end
         | 
| 193 | 
            +
                end
         | 
| 194 | 
            +
             | 
| 169 195 | 
             
                color = SEVERITY_COLOR[color] if Integer === color
         | 
| 170 196 | 
             
                color = CONCEPT_COLORS[color] if CONCEPT_COLORS.include?(color)
         | 
| 171 197 | 
             
                color = Term::ANSIColor.send(color) if Symbol === color and Term::ANSIColor.respond_to?(color)
         | 
| 172 198 |  | 
| 199 | 
            +
                str = str.to_s unless str.nil?
         | 
| 173 200 | 
             
                return str if Symbol === color
         | 
| 174 201 | 
             
                color_str = reset ? Term::ANSIColor.reset : ""
         | 
| 175 202 | 
             
                color_str << color
         | 
    
        data/lib/scout/log/progress.rb
    CHANGED
    
    
    
        data/lib/scout/log.rb
    CHANGED
    
    | @@ -206,7 +206,11 @@ module Log | |
| 206 206 | 
             
                  line = line.sub('`',"'")
         | 
| 207 207 | 
             
                  color = :green if line =~ /workflow/
         | 
| 208 208 | 
             
                  color = :blue if line =~ /scout-/
         | 
| 209 | 
            -
                   | 
| 209 | 
            +
                  if color
         | 
| 210 | 
            +
                    Log.color color, line
         | 
| 211 | 
            +
                  else
         | 
| 212 | 
            +
                    line
         | 
| 213 | 
            +
                  end
         | 
| 210 214 | 
             
                end unless stack.nil?
         | 
| 211 215 | 
             
              end
         | 
| 212 216 |  | 
    
        data/lib/scout/misc/digest.rb
    CHANGED
    
    
    
        data/lib/scout/open/stream.rb
    CHANGED
    
    | @@ -31,13 +31,16 @@ module Open | |
| 31 31 | 
             
                    Thread.current.report_on_exception = false
         | 
| 32 32 | 
             
                    consume_stream(io, false, into, into_close)
         | 
| 33 33 | 
             
                  end
         | 
| 34 | 
            +
             | 
| 34 35 | 
             
                  io.threads.push(consumer_thread) if io.respond_to?(:threads)
         | 
| 36 | 
            +
                  Thread.pass until consumer_thread["name"]
         | 
| 37 | 
            +
             | 
| 35 38 | 
             
                  consumer_thread
         | 
| 36 39 | 
             
                else
         | 
| 37 40 | 
             
                  if into
         | 
| 38 | 
            -
                    Log. | 
| 41 | 
            +
                    Log.low "Consuming stream #{Log.fingerprint io} -> #{Log.fingerprint into}"
         | 
| 39 42 | 
             
                  else
         | 
| 40 | 
            -
                    Log. | 
| 43 | 
            +
                    Log.low "Consuming stream #{Log.fingerprint io}"
         | 
| 41 44 | 
             
                  end
         | 
| 42 45 |  | 
| 43 46 | 
             
                  begin
         | 
| @@ -53,7 +56,6 @@ module Open | |
| 53 56 | 
             
                    into_close = false unless into.respond_to? :close
         | 
| 54 57 | 
             
                    io.sync = true
         | 
| 55 58 |  | 
| 56 | 
            -
                    Log.high "started consuming stream #{Log.fingerprint io}"
         | 
| 57 59 | 
             
                    begin
         | 
| 58 60 | 
             
                      while c = io.readpartial(BLOCK_SIZE)
         | 
| 59 61 | 
             
                        into << c if into
         | 
| @@ -67,15 +69,14 @@ module Open | |
| 67 69 | 
             
                    into.close if into and into_close and not into.closed?
         | 
| 68 70 | 
             
                    block.call if block_given?
         | 
| 69 71 |  | 
| 70 | 
            -
                    Log.high "Done consuming stream #{Log.fingerprint io} into #{into_path || into}"
         | 
| 71 72 | 
             
                    c
         | 
| 72 73 | 
             
                  rescue Aborted
         | 
| 73 | 
            -
                    Log. | 
| 74 | 
            +
                    Log.low "Consume stream Aborted #{Log.fingerprint io} into #{into_path || into}"
         | 
| 74 75 | 
             
                    io.abort $! if io.respond_to? :abort
         | 
| 75 76 | 
             
                    into.close if into.respond_to?(:closed?) && ! into.closed?
         | 
| 76 77 | 
             
                    FileUtils.rm into_path if into_path and File.exist?(into_path)
         | 
| 77 78 | 
             
                  rescue Exception
         | 
| 78 | 
            -
                    Log. | 
| 79 | 
            +
                    Log.low "Consume stream Exception reading #{Log.fingerprint io} into #{into_path || into} - #{$!.message}"
         | 
| 79 80 | 
             
                    exception = io.stream_exception || $!
         | 
| 80 81 | 
             
                    io.abort exception if io.respond_to? :abort
         | 
| 81 82 | 
             
                    into.close if into.respond_to?(:closed?) && ! into.closed?
         | 
| @@ -145,12 +146,12 @@ module Open | |
| 145 146 |  | 
| 146 147 | 
             
                      Open.notify_write(path) 
         | 
| 147 148 | 
             
                    rescue Aborted
         | 
| 148 | 
            -
                      Log. | 
| 149 | 
            +
                      Log.low "Aborted sensible_write -- #{ Log.reset << Log.color(:blue, path) }"
         | 
| 149 150 | 
             
                      content.abort if content.respond_to? :abort
         | 
| 150 151 | 
             
                      Open.rm path if File.exist? path
         | 
| 151 152 | 
             
                    rescue Exception
         | 
| 152 153 | 
             
                      exception = (AbortedStream === content and content.exception) ? content.exception : $!
         | 
| 153 | 
            -
                      Log. | 
| 154 | 
            +
                      Log.low "Exception in sensible_write: [#{Process.pid}] #{exception.message} -- #{ Log.color :blue, path }"
         | 
| 154 155 | 
             
                      content.abort if content.respond_to? :abort
         | 
| 155 156 | 
             
                      Open.rm path if File.exist? path
         | 
| 156 157 | 
             
                      raise exception
         | 
| @@ -219,16 +220,15 @@ module Open | |
| 219 220 |  | 
| 220 221 | 
             
                  #parent_pid = Process.pid
         | 
| 221 222 | 
             
                  pid = Process.fork {
         | 
| 222 | 
            -
                    purge_pipes(sin)
         | 
| 223 | 
            -
                    sout.close
         | 
| 224 223 | 
             
                    begin
         | 
| 224 | 
            +
                      purge_pipes(sin)
         | 
| 225 | 
            +
                      sout.close
         | 
| 225 226 |  | 
| 226 227 | 
             
                      yield sin
         | 
| 227 228 | 
             
                      sin.close if close and not sin.closed? 
         | 
| 228 229 |  | 
| 229 230 | 
             
                    rescue Exception
         | 
| 230 231 | 
             
                      Log.exception $!
         | 
| 231 | 
            -
                      #Process.kill :INT, parent_pid
         | 
| 232 232 | 
             
                      Kernel.exit!(-1)
         | 
| 233 233 | 
             
                    end
         | 
| 234 234 | 
             
                    Kernel.exit! 0
         | 
| @@ -242,18 +242,18 @@ module Open | |
| 242 242 | 
             
                  ConcurrentStream.setup sout, :pair => sin
         | 
| 243 243 |  | 
| 244 244 | 
             
                  thread = Thread.new do 
         | 
| 245 | 
            -
                    Thread.current["name"] = "Pipe input #{Log.fingerprint sin} => #{Log.fingerprint sout}"
         | 
| 246 | 
            -
                    Thread.current.report_on_exception = false
         | 
| 247 245 | 
             
                    begin
         | 
| 246 | 
            +
                      Thread.current.report_on_exception = false
         | 
| 247 | 
            +
                      Thread.current["name"] = "Pipe input #{Log.fingerprint sin} => #{Log.fingerprint sout}"
         | 
| 248 248 |  | 
| 249 249 | 
             
                      yield sin
         | 
| 250 250 |  | 
| 251 251 | 
             
                      sin.close if close and not sin.closed? and not sin.aborted?
         | 
| 252 252 | 
             
                    rescue Aborted
         | 
| 253 | 
            -
                      Log. | 
| 253 | 
            +
                      Log.low "Aborted open_pipe: #{$!.message}"
         | 
| 254 254 | 
             
                      raise $!
         | 
| 255 255 | 
             
                    rescue Exception
         | 
| 256 | 
            -
                      Log. | 
| 256 | 
            +
                      Log.low "Exception in open_pipe: #{$!.message}"
         | 
| 257 257 | 
             
                      begin
         | 
| 258 258 | 
             
                        sout.threads.delete(Thread.current)
         | 
| 259 259 | 
             
                        sout.pair = []
         | 
| @@ -269,6 +269,7 @@ module Open | |
| 269 269 |  | 
| 270 270 | 
             
                  sin.threads = [thread]
         | 
| 271 271 | 
             
                  sout.threads = [thread]
         | 
| 272 | 
            +
                  Thread.pass until thread["name"]
         | 
| 272 273 | 
             
                end
         | 
| 273 274 |  | 
| 274 275 | 
             
                sout
         | 
| @@ -287,8 +288,8 @@ module Open | |
| 287 288 |  | 
| 288 289 | 
             
                splitter_thread = Thread.new(Thread.current) do |parent|
         | 
| 289 290 | 
             
                  begin
         | 
| 290 | 
            -
                    Thread.current["name"] = "Splitter #{Log.fingerprint stream}"
         | 
| 291 291 | 
             
                    Thread.current.report_on_exception = false
         | 
| 292 | 
            +
                    Thread.current["name"] = "Splitter #{Log.fingerprint stream}"
         | 
| 292 293 |  | 
| 293 294 | 
             
                    skip = [false] * num
         | 
| 294 295 | 
             
                    begin
         | 
| @@ -317,7 +318,7 @@ module Open | |
| 317 318 | 
             
                    out_pipes.each do |sout|
         | 
| 318 319 | 
             
                      sout.abort if sout.respond_to? :abort
         | 
| 319 320 | 
             
                    end
         | 
| 320 | 
            -
                    Log. | 
| 321 | 
            +
                    Log.low "Tee aborting #{Log.fingerprint stream}"
         | 
| 321 322 | 
             
                    raise $!
         | 
| 322 323 | 
             
                  rescue Exception
         | 
| 323 324 | 
             
                    begin
         | 
| @@ -332,7 +333,7 @@ module Open | |
| 332 333 | 
             
                      in_pipes.each do |sin|
         | 
| 333 334 | 
             
                        sin.close unless sin.closed?
         | 
| 334 335 | 
             
                      end
         | 
| 335 | 
            -
                      Log. | 
| 336 | 
            +
                      Log.low "Tee exception #{Log.fingerprint stream}"
         | 
| 336 337 | 
             
                    rescue
         | 
| 337 338 | 
             
                      Log.exception $!
         | 
| 338 339 | 
             
                    ensure
         | 
| @@ -348,7 +349,7 @@ module Open | |
| 348 349 | 
             
                out_pipes.each do |sout|
         | 
| 349 350 | 
             
                  ConcurrentStream.setup sout, :threads => splitter_thread, :filename => filename, :pair => stream
         | 
| 350 351 | 
             
                end
         | 
| 351 | 
            -
                 | 
| 352 | 
            +
                Thread.pass until splitter_thread["name"]
         | 
| 352 353 |  | 
| 353 354 | 
             
                main_pipe = out_pipes.first
         | 
| 354 355 | 
             
                main_pipe.autojoin = true
         | 
| @@ -0,0 +1,144 @@ | |
| 1 | 
            +
            module TSV
         | 
| 2 | 
            +
              def self.cast_value(value, cast)
         | 
| 3 | 
            +
                if Array === value
         | 
| 4 | 
            +
                  value.collect{|e| cast_value(e, cast) }
         | 
| 5 | 
            +
                else
         | 
| 6 | 
            +
                  value.send(cast)
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def self.parse_line(line, type: :list, key: 0, positions: nil, sep: "\t", sep2: "|", cast: nil)
         | 
| 11 | 
            +
                items = line.split(sep, -1)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                if positions.nil? && key == 0
         | 
| 14 | 
            +
                  key = items.shift
         | 
| 15 | 
            +
                elsif positions.nil? 
         | 
| 16 | 
            +
                  key = items.delete(key)
         | 
| 17 | 
            +
                else 
         | 
| 18 | 
            +
                  key, items = items[key], items.values_at(*positions)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                items = case type
         | 
| 22 | 
            +
                        when :list
         | 
| 23 | 
            +
                          items
         | 
| 24 | 
            +
                        when :single
         | 
| 25 | 
            +
                          items.first
         | 
| 26 | 
            +
                        when :flat
         | 
| 27 | 
            +
                          [items]
         | 
| 28 | 
            +
                        when :double
         | 
| 29 | 
            +
                          items.collect{|i| i.split(sep2, -1) }
         | 
| 30 | 
            +
                        end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                key = key.partition(sep2).first if type == :double
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                if cast
         | 
| 35 | 
            +
                  items = cast_value(items, cast)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                [key, items]
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def self.parse_stream(stream, data: nil, merge: true, type: :list, fix: true, bar: false, first_line: nil, **kargs, &block)
         | 
| 42 | 
            +
                begin
         | 
| 43 | 
            +
                  bar = Log::ProgressBar.new_bar(bar) if bar
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  data = {} if data.nil?
         | 
| 46 | 
            +
                  merge = false if type != :double
         | 
| 47 | 
            +
                  line = first_line || stream.gets
         | 
| 48 | 
            +
                  while line
         | 
| 49 | 
            +
                    begin
         | 
| 50 | 
            +
                      line.strip!
         | 
| 51 | 
            +
                      line = Misc.fixutf8(line) if fix
         | 
| 52 | 
            +
                      bar.tick if bar
         | 
| 53 | 
            +
                      key, items = parse_line(line, type: type, **kargs)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      if block_given?
         | 
| 56 | 
            +
                        res = block.call(key, items)
         | 
| 57 | 
            +
                        data[key] = res unless res.nil?
         | 
| 58 | 
            +
                        next
         | 
| 59 | 
            +
                      end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                      if ! merge || ! data.include?(key)
         | 
| 62 | 
            +
                        data[key] = items
         | 
| 63 | 
            +
                      else
         | 
| 64 | 
            +
                        current = data[key]
         | 
| 65 | 
            +
                        if merge == :concat
         | 
| 66 | 
            +
                          items.each_with_index do |new,i|
         | 
| 67 | 
            +
                            next if new.empty?
         | 
| 68 | 
            +
                            current[i].concat(new)
         | 
| 69 | 
            +
                          end
         | 
| 70 | 
            +
                        else
         | 
| 71 | 
            +
                          merged = []
         | 
| 72 | 
            +
                          items.each_with_index do |new,i|
         | 
| 73 | 
            +
                            next if new.empty?
         | 
| 74 | 
            +
                            merged[i] = current[i] + new
         | 
| 75 | 
            +
                          end
         | 
| 76 | 
            +
                          data[key] = merged
         | 
| 77 | 
            +
                        end
         | 
| 78 | 
            +
                      end
         | 
| 79 | 
            +
                    ensure
         | 
| 80 | 
            +
                      line = stream.gets
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                  data
         | 
| 84 | 
            +
                ensure
         | 
| 85 | 
            +
                  Log::ProgressBar.remove_bar(bar) if bar
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              def self.parse_header(stream, fix: true, header_hash: '#', sep: "\n")
         | 
| 90 | 
            +
                raise "Closed stream" if IO === stream && stream.closed?
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                options = {}
         | 
| 93 | 
            +
                preamble = []
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                # Get line
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                #Thread.pass while IO.select([stream], nil, nil, 1).nil? if IO === stream
         | 
| 98 | 
            +
                line = stream.gets
         | 
| 99 | 
            +
                return {} if line.nil?
         | 
| 100 | 
            +
                line = Misc.fixutf8 line.chomp if fix
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                # Process options line
         | 
| 103 | 
            +
                if line and (String === header_hash && m = line.match(/^#{header_hash}: (.*)/))
         | 
| 104 | 
            +
                  options = IndiferentHash.string2hash m.captures.first.chomp
         | 
| 105 | 
            +
                  line = stream.gets
         | 
| 106 | 
            +
                  line = Misc.fixutf8 line.chomp if line && fix
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                # Determine separator
         | 
| 110 | 
            +
                sep = options[:sep] if options[:sep]
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                # Process fields line
         | 
| 113 | 
            +
                preamble << line if line
         | 
| 114 | 
            +
                while line && (TrueClass === header_hash || (String === header_hash && line.start_with?(header_hash)))
         | 
| 115 | 
            +
                  fields = line.split(sep, -1)
         | 
| 116 | 
            +
                  key_field = fields.shift
         | 
| 117 | 
            +
                  key_field = key_field.sub(header_hash, '') if String === header_hash && ! header_hash.empty?
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  line = (header_hash != "" ?  stream.gets : nil)
         | 
| 120 | 
            +
                  line = Misc.fixutf8 line.chomp if line
         | 
| 121 | 
            +
                  preamble << line if line
         | 
| 122 | 
            +
                  break if TrueClass === header_hash || header_hash == ""
         | 
| 123 | 
            +
                end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                preamble = preamble[0..-3] * "\n"
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                line ||= stream.gets
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                first_line = line
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                [options, key_field, fields, first_line, preamble]
         | 
| 132 | 
            +
              end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
              def self.parse(stream, **kwargs)
         | 
| 135 | 
            +
                options, key_field, fields, first_line, preamble = parse_header(stream)
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                options.each do |option,value|
         | 
| 138 | 
            +
                  option = option.to_sym
         | 
| 139 | 
            +
                  kwargs[option] = value unless kwargs.include?(option)
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
                data = parse_stream(stream, first_line: first_line, **kwargs)
         | 
| 142 | 
            +
                TSV.setup data, :key_field => key_field, :fields => fields
         | 
| 143 | 
            +
              end
         | 
| 144 | 
            +
            end
         | 
    
        data/lib/scout/tsv.rb
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            require_relative 'meta_extension'
         | 
| 2 | 
            +
            require_relative 'tsv/parser'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module TSV
         | 
| 5 | 
            +
              extend MetaExtension
         | 
| 6 | 
            +
              extension_attr :key_field, :fields
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def self.open(file, options = {})
         | 
| 9 | 
            +
                Open.open(file) do |f|
         | 
| 10 | 
            +
                  TSV.parse(f,**options)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
             | 
| @@ -1,16 +1,18 @@ | |
| 1 1 | 
             
            class WorkQueue
         | 
| 2 2 | 
             
              class Worker
         | 
| 3 3 | 
             
                attr_accessor :pid, :ignore_ouput
         | 
| 4 | 
            -
                def initialize
         | 
| 4 | 
            +
                def initialize(ignore_ouput = false)
         | 
| 5 | 
            +
                  @ignore_output = ignore_ouput
         | 
| 5 6 | 
             
                end
         | 
| 6 7 |  | 
| 7 8 | 
             
                def run
         | 
| 8 9 | 
             
                  @pid = Process.fork do
         | 
| 10 | 
            +
                    Log.debug "Worker start with #{Process.pid}"
         | 
| 9 11 | 
             
                    yield
         | 
| 10 12 | 
             
                  end
         | 
| 11 13 | 
             
                end
         | 
| 12 14 |  | 
| 13 | 
            -
                def process(input, output, &block)
         | 
| 15 | 
            +
                def process(input, output = nil, &block)
         | 
| 14 16 | 
             
                  run do
         | 
| 15 17 | 
             
                    begin
         | 
| 16 18 | 
             
                      while obj = input.read
         | 
| @@ -19,33 +21,36 @@ class WorkQueue | |
| 19 21 | 
             
                          raise obj 
         | 
| 20 22 | 
             
                        end
         | 
| 21 23 | 
             
                        res = block.call obj
         | 
| 22 | 
            -
                        output.write res unless ignore_ouput || res == :ignore 
         | 
| 24 | 
            +
                        output.write res unless output.nil? || ignore_ouput || res == :ignore 
         | 
| 23 25 | 
             
                      end
         | 
| 24 26 | 
             
                    rescue DoneProcessing
         | 
| 25 | 
            -
             | 
| 27 | 
            +
                    rescue Interrupt
         | 
| 26 28 | 
             
                    rescue Exception
         | 
| 27 | 
            -
                       | 
| 29 | 
            +
                      output.write WorkerException.new($!, Process.pid)
         | 
| 28 30 | 
             
                      exit -1
         | 
| 29 31 | 
             
                    end
         | 
| 30 32 | 
             
                  end
         | 
| 31 33 | 
             
                end
         | 
| 32 34 |  | 
| 35 | 
            +
                def abort
         | 
| 36 | 
            +
                  begin
         | 
| 37 | 
            +
                    Log.log "Aborting worker #{@pid}"
         | 
| 38 | 
            +
                    Process.kill "INT", @pid 
         | 
| 39 | 
            +
                  rescue Errno::ECHILD
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 33 43 | 
             
                def join
         | 
| 34 44 | 
             
                  Log.log "Joining worker #{@pid}"
         | 
| 35 45 | 
             
                  Process.waitpid @pid
         | 
| 36 46 | 
             
                end
         | 
| 37 47 |  | 
| 38 | 
            -
                def exit(status)
         | 
| 39 | 
            -
                  Log.log "Worker #{@pid} exited with status #{Log.color(:green, status)}"
         | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
             | 
| 42 48 | 
             
                def self.join(workers)
         | 
| 43 49 | 
             
                  workers = [workers] unless Array === workers
         | 
| 44 50 | 
             
                  begin
         | 
| 45 51 | 
             
                    while pid = Process.wait 
         | 
| 46 52 | 
             
                      status = $?
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                      worker.exit status.exitstatus if worker
         | 
| 53 | 
            +
                        worker = workers.select{|w| w.pid == pid }.first
         | 
| 49 54 | 
             
                    end
         | 
| 50 55 | 
             
                  rescue Errno::ECHILD
         | 
| 51 56 | 
             
                  end
         | 
    
        data/lib/scout/work_queue.rb
    CHANGED
    
    | @@ -35,52 +35,79 @@ class WorkQueue | |
| 35 35 | 
             
              end
         | 
| 36 36 |  | 
| 37 37 | 
             
              def remove_worker(pid)
         | 
| 38 | 
            -
                 | 
| 39 | 
            -
                   | 
| 40 | 
            -
                  @removed_workers | 
| 38 | 
            +
                @worker_mutex.synchronize do
         | 
| 39 | 
            +
                  @workers.delete_if{|w| w.pid == pid }
         | 
| 40 | 
            +
                  @removed_workers << pid
         | 
| 41 41 | 
             
                end
         | 
| 42 42 | 
             
              end
         | 
| 43 43 |  | 
| 44 44 | 
             
              def process(&callback)
         | 
| 45 | 
            -
                @ | 
| 46 | 
            -
                  w.process @input, @output, &@worker_proc
         | 
| 47 | 
            -
                end
         | 
| 48 | 
            -
                @reader = Thread.new do
         | 
| 45 | 
            +
                @reader = Thread.new do |parent|
         | 
| 49 46 | 
             
                  begin
         | 
| 47 | 
            +
                    Thread.current.report_on_exception = false
         | 
| 48 | 
            +
                    Thread.current["name"] = "Output reader #{Process.pid}"
         | 
| 49 | 
            +
                    @done_workers ||= []
         | 
| 50 50 | 
             
                    while true
         | 
| 51 51 | 
             
                      obj = @output.read
         | 
| 52 52 | 
             
                      if DoneProcessing === obj
         | 
| 53 | 
            -
                         | 
| 53 | 
            +
                        done = @worker_mutex.synchronize do
         | 
| 54 | 
            +
                          Log.low "Worker #{obj.pid} done"
         | 
| 55 | 
            +
                          @done_workers << obj.pid
         | 
| 56 | 
            +
                          @done_workers.length == @removed_workers.length + @workers.length
         | 
| 57 | 
            +
                        end
         | 
| 58 | 
            +
                        break if done
         | 
| 59 | 
            +
                      elsif Exception === obj
         | 
| 60 | 
            +
                        raise obj
         | 
| 54 61 | 
             
                      else
         | 
| 55 62 | 
             
                        callback.call obj if callback
         | 
| 56 63 | 
             
                      end
         | 
| 57 64 | 
             
                    end
         | 
| 65 | 
            +
                  rescue DoneProcessing
         | 
| 58 66 | 
             
                  rescue Aborted
         | 
| 67 | 
            +
                  rescue WorkerException
         | 
| 68 | 
            +
                    Log.error "Exception in worker #{obj.pid} #{Log.fingerprint obj.exception}"
         | 
| 69 | 
            +
                    self.abort
         | 
| 70 | 
            +
                    raise obj.exception
         | 
| 59 71 | 
             
                  end
         | 
| 60 | 
            -
                end | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                @workers.each do |w| 
         | 
| 75 | 
            +
                  w.process @input, @output, &@worker_proc
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                Thread.pass until @reader["name"]
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                @waiter = Thread.new do
         | 
| 81 | 
            +
                  begin
         | 
| 82 | 
            +
                    Thread.current.report_on_exception = false
         | 
| 83 | 
            +
                    Thread.current["name"] = "Worker waiter #{Process.pid}"
         | 
| 84 | 
            +
                    while true
         | 
| 85 | 
            +
                      pid = Process.wait
         | 
| 86 | 
            +
                      remove_worker(pid)
         | 
| 87 | 
            +
                      break if workers.empty?
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                Thread.pass until @waiter["name"]
         | 
| 61 93 | 
             
              end
         | 
| 62 94 |  | 
| 63 95 | 
             
              def write(obj)
         | 
| 64 96 | 
             
                @input.write obj
         | 
| 65 97 | 
             
              end
         | 
| 66 98 |  | 
| 99 | 
            +
              def abort
         | 
| 100 | 
            +
                workers.each{|w| w.abort }
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
             | 
| 67 103 | 
             
              def close
         | 
| 68 | 
            -
                 | 
| 69 | 
            -
                   | 
| 70 | 
            -
                    @input.write DoneProcessing.new
         | 
| 71 | 
            -
                    pid = Process.wait
         | 
| 72 | 
            -
                    status = $?
         | 
| 73 | 
            -
                    worker = @worker_mutex.synchronize{ @removed_workers.delete_if{|w| w.pid == pid }.first }
         | 
| 74 | 
            -
                    worker.exit $?.exitstatus if worker
         | 
| 75 | 
            -
                  rescue Errno::ECHILD
         | 
| 76 | 
            -
                    Thread.pass until @workers.length == 0
         | 
| 77 | 
            -
                    break
         | 
| 78 | 
            -
                  end
         | 
| 104 | 
            +
                @worker_mutex.synchronize{ @workers.length }.times do
         | 
| 105 | 
            +
                  @input.write DoneProcessing.new()
         | 
| 79 106 | 
             
                end
         | 
| 80 | 
            -
                @reader.raise Aborted if @reader
         | 
| 81 107 | 
             
              end
         | 
| 82 108 |  | 
| 83 109 | 
             
              def join
         | 
| 110 | 
            +
                @waiter.join if @waiter
         | 
| 84 111 | 
             
                @reader.join if @reader
         | 
| 85 112 | 
             
              end
         | 
| 86 113 | 
             
            end
         | 
| @@ -55,9 +55,9 @@ class Step | |
| 55 55 |  | 
| 56 56 | 
             
              def report_status(status, message = nil)
         | 
| 57 57 | 
             
                if message.nil?
         | 
| 58 | 
            -
                  Log.info Log.color(status, status | 
| 58 | 
            +
                  Log.info Log.color(:status, status, true) + " " + Log.color(:path, path)
         | 
| 59 59 | 
             
                else
         | 
| 60 | 
            -
                  Log.info Log.color(status, status | 
| 60 | 
            +
                  Log.info Log.color(:status, status, true) + " " + Log.color(:path, path) + " " + message
         | 
| 61 61 | 
             
                end
         | 
| 62 62 | 
             
              end
         | 
| 63 63 |  | 
    
        data/lib/scout/workflow/step.rb
    CHANGED
    
    
    
        data/lib/scout/workflow/task.rb
    CHANGED
    
    | @@ -143,8 +143,8 @@ module Task | |
| 143 143 | 
             
                non_default_inputs.concat provided_inputs.keys.select{|k| String === k && k.include?("#") } if Hash === provided_inputs
         | 
| 144 144 |  | 
| 145 145 | 
             
                if non_default_inputs.any?
         | 
| 146 | 
            -
                  hash = Misc.digest(:inputs => input_hash, : | 
| 147 | 
            -
                  Log.debug "Hash #{name} - #{hash}: #{Misc.digest_str(:inputs => inputs, :dependencies => dependencies)}"
         | 
| 146 | 
            +
                  hash = Misc.digest(:inputs => input_hash, :dependencies => dependencies)
         | 
| 147 | 
            +
                  Log.debug "Hash #{name} - #{hash}: #{Misc.digest_str(:inputs => inputs, :non_default_inputs => non_default_inputs, :dependencies => dependencies)}"
         | 
| 148 148 | 
             
                  id = [id, hash] * "_"
         | 
| 149 149 | 
             
                end
         | 
| 150 150 |  | 
    
        data/scout-gear.gemspec
    CHANGED
    
    | @@ -2,16 +2,16 @@ | |
| 2 2 | 
             
            # DO NOT EDIT THIS FILE DIRECTLY
         | 
| 3 3 | 
             
            # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
         | 
| 4 4 | 
             
            # -*- encoding: utf-8 -*-
         | 
| 5 | 
            -
            # stub: scout-gear  | 
| 5 | 
            +
            # stub: scout-gear 7.1.0 ruby lib
         | 
| 6 6 |  | 
| 7 7 | 
             
            Gem::Specification.new do |s|
         | 
| 8 8 | 
             
              s.name = "scout-gear".freeze
         | 
| 9 | 
            -
              s.version = " | 
| 9 | 
            +
              s.version = "7.1.0"
         | 
| 10 10 |  | 
| 11 11 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
         | 
| 12 12 | 
             
              s.require_paths = ["lib".freeze]
         | 
| 13 13 | 
             
              s.authors = ["Miguel Vazquez".freeze]
         | 
| 14 | 
            -
              s.date = "2023- | 
| 14 | 
            +
              s.date = "2023-05-01"
         | 
| 15 15 | 
             
              s.description = "Temporary files, logs, etc.".freeze
         | 
| 16 16 | 
             
              s.email = "mikisvaz@gmail.com".freeze
         | 
| 17 17 | 
             
              s.executables = ["scout".freeze]
         | 
| @@ -77,6 +77,8 @@ Gem::Specification.new do |s| | |
| 77 77 | 
             
                "lib/scout/simple_opt/parse.rb",
         | 
| 78 78 | 
             
                "lib/scout/simple_opt/setup.rb",
         | 
| 79 79 | 
             
                "lib/scout/tmpfile.rb",
         | 
| 80 | 
            +
                "lib/scout/tsv.rb",
         | 
| 81 | 
            +
                "lib/scout/tsv/parser.rb",
         | 
| 80 82 | 
             
                "lib/scout/work_queue.rb",
         | 
| 81 83 | 
             
                "lib/scout/work_queue/socket.rb",
         | 
| 82 84 | 
             
                "lib/scout/work_queue/worker.rb",
         | 
| @@ -137,8 +139,10 @@ Gem::Specification.new do |s| | |
| 137 139 | 
             
                "test/scout/test_resource.rb",
         | 
| 138 140 | 
             
                "test/scout/test_semaphore.rb",
         | 
| 139 141 | 
             
                "test/scout/test_tmpfile.rb",
         | 
| 142 | 
            +
                "test/scout/test_tsv.rb",
         | 
| 140 143 | 
             
                "test/scout/test_work_queue.rb",
         | 
| 141 144 | 
             
                "test/scout/test_workflow.rb",
         | 
| 145 | 
            +
                "test/scout/tsv/test_parser.rb",
         | 
| 142 146 | 
             
                "test/scout/work_queue/test_socket.rb",
         | 
| 143 147 | 
             
                "test/scout/work_queue/test_worker.rb",
         | 
| 144 148 | 
             
                "test/scout/workflow/step/test_info.rb",
         | 
| @@ -4,7 +4,7 @@ require File.expand_path(__FILE__).sub(%r(.*/test/), '').sub(/test_(.*)\.rb/,'\1 | |
| 4 4 | 
             
            require 'scout/work_queue/worker'
         | 
| 5 5 | 
             
            class TestSemaphore < Test::Unit::TestCase
         | 
| 6 6 |  | 
| 7 | 
            -
              def  | 
| 7 | 
            +
              def test_simple
         | 
| 8 8 | 
             
                ScoutSemaphore.with_semaphore 1 do |sem|
         | 
| 9 9 | 
             
                  10.times do
         | 
| 10 10 | 
             
                    ScoutSemaphore.synchronize(sem) do
         |