scout-gear 7.2.0 → 8.0.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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +51 -6
  3. data/VERSION +1 -1
  4. data/bin/scout +6 -3
  5. data/lib/rbbt-scout.rb +1 -0
  6. data/lib/scout/cmd.rb +1 -1
  7. data/lib/scout/concurrent_stream.rb +33 -29
  8. data/lib/scout/config.rb +1 -1
  9. data/lib/scout/exceptions.rb +1 -0
  10. data/lib/scout/log/color.rb +4 -2
  11. data/lib/scout/log/progress/report.rb +1 -1
  12. data/lib/scout/log/progress/util.rb +71 -2
  13. data/lib/scout/log/progress.rb +1 -1
  14. data/lib/scout/log/trap.rb +107 -0
  15. data/lib/scout/log.rb +56 -21
  16. data/lib/scout/meta_extension.rb +13 -6
  17. data/lib/scout/misc/digest.rb +1 -1
  18. data/lib/scout/misc/format.rb +12 -0
  19. data/lib/scout/misc/helper.rb +31 -0
  20. data/lib/scout/misc/insist.rb +1 -1
  21. data/lib/scout/misc/monitor.rb +12 -1
  22. data/lib/scout/misc/system.rb +10 -0
  23. data/lib/scout/misc.rb +1 -0
  24. data/lib/scout/named_array.rb +65 -3
  25. data/lib/scout/open/lock/lockfile.rb +587 -0
  26. data/lib/scout/open/lock.rb +28 -2
  27. data/lib/scout/open/remote.rb +4 -0
  28. data/lib/scout/open/stream.rb +111 -42
  29. data/lib/scout/open/util.rb +13 -3
  30. data/lib/scout/path/find.rb +9 -1
  31. data/lib/scout/path/util.rb +35 -0
  32. data/lib/scout/persist/serialize.rb +18 -5
  33. data/lib/scout/persist.rb +60 -30
  34. data/lib/scout/resource/path.rb +53 -0
  35. data/lib/scout/resource/produce.rb +0 -8
  36. data/lib/scout/resource/util.rb +2 -1
  37. data/lib/scout/semaphore.rb +8 -1
  38. data/lib/scout/tmpfile.rb +7 -8
  39. data/lib/scout/tsv/attach.rb +177 -0
  40. data/lib/scout/tsv/change_id.rb +40 -0
  41. data/lib/scout/tsv/dumper.rb +85 -54
  42. data/lib/scout/tsv/index.rb +188 -20
  43. data/lib/scout/tsv/open.rb +182 -0
  44. data/lib/scout/tsv/parser.rb +200 -118
  45. data/lib/scout/tsv/path.rb +5 -6
  46. data/lib/scout/tsv/persist/adapter.rb +26 -37
  47. data/lib/scout/tsv/persist/fix_width_table.rb +327 -0
  48. data/lib/scout/tsv/persist/serialize.rb +117 -0
  49. data/lib/scout/tsv/persist/tokyocabinet.rb +6 -3
  50. data/lib/scout/tsv/persist.rb +4 -2
  51. data/lib/scout/tsv/transformer.rb +141 -0
  52. data/lib/scout/tsv/traverse.rb +136 -37
  53. data/lib/scout/tsv/util/filter.rb +312 -0
  54. data/lib/scout/tsv/util/process.rb +73 -0
  55. data/lib/scout/tsv/util/reorder.rb +81 -0
  56. data/lib/scout/tsv/util/select.rb +265 -0
  57. data/lib/scout/tsv/util/unzip.rb +86 -0
  58. data/lib/scout/tsv/util.rb +126 -19
  59. data/lib/scout/tsv.rb +28 -5
  60. data/lib/scout/work_queue/socket.rb +6 -1
  61. data/lib/scout/work_queue/worker.rb +5 -2
  62. data/lib/scout/work_queue.rb +15 -8
  63. data/lib/scout/workflow/definition.rb +29 -2
  64. data/lib/scout/workflow/step/dependencies.rb +24 -4
  65. data/lib/scout/workflow/step/info.rb +40 -5
  66. data/lib/scout/workflow/step/progress.rb +14 -0
  67. data/lib/scout/workflow/step/provenance.rb +8 -7
  68. data/lib/scout/workflow/step/status.rb +45 -0
  69. data/lib/scout/workflow/step.rb +104 -33
  70. data/lib/scout/workflow/task/inputs.rb +14 -20
  71. data/lib/scout/workflow/task.rb +86 -47
  72. data/lib/scout/workflow/usage.rb +10 -6
  73. data/scout-gear.gemspec +30 -3
  74. data/scout_commands/workflow/task +37 -9
  75. data/scout_commands/workflow/task_old +2 -2
  76. data/test/scout/open/test_stream.rb +61 -59
  77. data/test/scout/path/test_find.rb +10 -1
  78. data/test/scout/resource/test_produce.rb +15 -0
  79. data/test/scout/test_meta_extension.rb +25 -0
  80. data/test/scout/test_named_array.rb +18 -0
  81. data/test/scout/test_persist.rb +67 -0
  82. data/test/scout/test_tmpfile.rb +1 -1
  83. data/test/scout/test_tsv.rb +222 -3
  84. data/test/scout/test_work_queue.rb +21 -18
  85. data/test/scout/tsv/persist/test_adapter.rb +11 -1
  86. data/test/scout/tsv/persist/test_fix_width_table.rb +134 -0
  87. data/test/scout/tsv/persist/test_tokyocabinet.rb +29 -1
  88. data/test/scout/tsv/test_attach.rb +227 -0
  89. data/test/scout/tsv/test_change_id.rb +98 -0
  90. data/test/scout/tsv/test_dumper.rb +1 -1
  91. data/test/scout/tsv/test_index.rb +127 -3
  92. data/test/scout/tsv/test_open.rb +167 -0
  93. data/test/scout/tsv/test_parser.rb +45 -3
  94. data/test/scout/tsv/test_persist.rb +9 -0
  95. data/test/scout/tsv/test_transformer.rb +108 -0
  96. data/test/scout/tsv/test_traverse.rb +195 -3
  97. data/test/scout/tsv/test_util.rb +24 -0
  98. data/test/scout/tsv/util/test_filter.rb +188 -0
  99. data/test/scout/tsv/util/test_process.rb +47 -0
  100. data/test/scout/tsv/util/test_reorder.rb +94 -0
  101. data/test/scout/tsv/util/test_select.rb +58 -0
  102. data/test/scout/tsv/util/test_unzip.rb +112 -0
  103. data/test/scout/work_queue/test_socket.rb +0 -1
  104. data/test/scout/work_queue/test_worker.rb +63 -6
  105. data/test/scout/workflow/step/test_load.rb +3 -3
  106. data/test/scout/workflow/step/test_status.rb +31 -0
  107. data/test/scout/workflow/task/test_inputs.rb +14 -14
  108. data/test/scout/workflow/test_step.rb +13 -13
  109. data/test/scout/workflow/test_task.rb +168 -32
  110. data/test/scout/workflow/test_usage.rb +33 -6
  111. data/test/test_helper.rb +3 -1
  112. metadata +29 -2
@@ -46,7 +46,11 @@ class WorkQueue
46
46
  end
47
47
 
48
48
  def process(&callback)
49
- @reader = Thread.new do |parent|
49
+ @workers.each do |w|
50
+ w.process @input, @output, &@worker_proc
51
+ end
52
+
53
+ @reader = Thread.new(Thread.current) do |parent|
50
54
  begin
51
55
  Thread.current.report_on_exception = false
52
56
  Thread.current["name"] = "Output reader #{Process.pid}"
@@ -71,8 +75,9 @@ class WorkQueue
71
75
  rescue DoneProcessing
72
76
  rescue Aborted
73
77
  rescue WorkerException
74
- Log.error "Exception in worker #{obj.pid} in queue #{Process.pid}: #{obj.message}"
78
+ Log.error "Exception in worker #{obj.pid} in queue #{Process.pid}: #{obj.worker_exception.message}"
75
79
  self.abort
80
+ @input.abort obj.worker_exception
76
81
  raise obj.worker_exception
77
82
  rescue
78
83
  Log.error "Exception processing output in queue #{Process.pid}: #{$!.message}"
@@ -81,10 +86,6 @@ class WorkQueue
81
86
  end
82
87
  end
83
88
 
84
- @workers.each do |w|
85
- w.process @input, @output, &@worker_proc
86
- end
87
-
88
89
  Thread.pass until @reader["name"]
89
90
 
90
91
  @waiter = Thread.new do
@@ -104,7 +105,13 @@ class WorkQueue
104
105
  end
105
106
 
106
107
  def write(obj)
107
- @input.write obj
108
+ begin
109
+ @input.write obj
110
+ rescue Exception
111
+ raise $! unless @input.exception
112
+ ensure
113
+ raise @input.exception if @input.exception
114
+ end
108
115
  end
109
116
 
110
117
  def abort
@@ -117,7 +124,7 @@ class WorkQueue
117
124
  def close
118
125
  @closed = true
119
126
  @worker_mutex.synchronize{ @workers.length }.times do
120
- @input.write DoneProcessing.new()
127
+ @input.write DoneProcessing.new() unless @input.closed_write?
121
128
  end
122
129
  end
123
130
 
@@ -76,9 +76,11 @@ module Workflow
76
76
  end
77
77
  when 1
78
78
  task = args.first
79
+ options, task = task, nil if Hash === task
79
80
  end
80
81
  workflow = self if workflow.nil?
81
82
  options = {} if options.nil?
83
+ task = task.to_sym if task
82
84
  annotate_next_task :deps, [workflow, task, options, block, args]
83
85
  end
84
86
 
@@ -103,6 +105,20 @@ module Workflow
103
105
  @tasks ||= IndiferentHash.setup({})
104
106
  begin
105
107
  @annotate_next_task ||= {}
108
+ @annotate_next_task[:extension] ||=
109
+ case type
110
+ when :tsv
111
+ "tsv"
112
+ when :yaml
113
+ "yaml"
114
+ when :marshal
115
+ "marshal"
116
+ when :json
117
+ "json"
118
+ else
119
+ nil
120
+ end
121
+
106
122
  task = Task.setup(block, @annotate_next_task.merge(name: name, type: type, directory: directory[name], workflow: self))
107
123
  @tasks[name] = task
108
124
  ensure
@@ -110,11 +126,14 @@ module Workflow
110
126
  end
111
127
  end
112
128
 
129
+ FORGET_DEP_TASKS = ENV["SCOUT_FORGET_DEP_TASKS"] == "true"
130
+ REMOVE_DEP_TASKS = ENV["SCOUT_REMOVE_DEP_TASKS"] == "true"
113
131
  def task_alias(name, workflow, oname, *rest, &block)
114
132
  dep(workflow, oname, *rest, &block)
115
133
  extension :dep_task unless @extension
116
- returns workflow.tasks[oname].returns if workflow.tasks.include?(oname) unless @returns
117
- task name => nil do
134
+ returns workflow.tasks[oname].returns if @returns.nil?
135
+ type = workflow.tasks[oname].type
136
+ task name => type do
118
137
  raise RbbtException, "dep_task does not have any dependencies" if dependencies.empty?
119
138
  Step.wait_for_jobs dependencies.select{|d| d.streaming? }
120
139
  dep = dependencies.last
@@ -164,4 +183,12 @@ module Workflow
164
183
  end
165
184
 
166
185
  alias dep_task task_alias
186
+
187
+ def export(*args)
188
+ end
189
+
190
+ alias export_synchronous export
191
+ alias export_asynchronous export
192
+ alias export_exec export
193
+ alias export_stream export
167
194
  end
@@ -1,8 +1,13 @@
1
1
  class Step
2
+ def rec_dependencies
3
+ rec_dependencies = dependencies.dup
4
+ dependencies.inject(rec_dependencies){|acc,d| acc.concat d.rec_dependencies }
5
+ end
6
+
2
7
  def recursive_inputs
3
- dependencies.inject(@inputs.annotate(@inputs.dup)) do |acc,dep|
4
- acc.concat(dep.inputs) if dep.inputs
5
- acc
8
+ recursive_inputs = @inputs.to_hash
9
+ dependencies.inject(recursive_inputs) do |acc,dep|
10
+ acc.merge(dep.recursive_inputs)
6
11
  end
7
12
  end
8
13
 
@@ -16,6 +21,10 @@ class Step
16
21
  def prepare_dependencies
17
22
  inverse_dep = {}
18
23
  dependencies.each{|dep|
24
+ if dep.present? && ! dep.updated?
25
+ Log.debug "Clean outdated #{dep.path}"
26
+ dep.clean
27
+ end
19
28
  next if dep.done?
20
29
  if dep.dependencies
21
30
  dep.dependencies.each do |d|
@@ -34,7 +43,18 @@ class Step
34
43
  end
35
44
 
36
45
  def run_dependencies
37
- dependencies.each{|dep| dep.run unless dep.running? || dep.done? }
46
+ dependencies.each{|dep| dep.run(true) unless dep.running? || dep.done? }
38
47
  end
39
48
 
49
+ def abort_dependencies
50
+ dependencies.each{|dep| dep.abort if dep.running? }
51
+ end
52
+
53
+ def self.wait_for_jobs(jobs)
54
+ threads = []
55
+ jobs.each do |job|
56
+ threads << job.join
57
+ end
58
+ threads.each do |t| t.join end
59
+ end
40
60
  end
@@ -1,6 +1,7 @@
1
1
  class Step
2
2
  SERIALIZER = :marshal
3
3
  def info_file
4
+ return nil if @path.nil?
4
5
  @info_file ||= begin
5
6
  info_file = @path + ".info"
6
7
  @path.annotate info_file if Path === @path
@@ -19,6 +20,10 @@ class Step
19
20
  @info_load_time = Time.now
20
21
  end
21
22
 
23
+ def clear_info
24
+ save_info(@info = {})
25
+ end
26
+
22
27
  def info
23
28
  outdated = begin
24
29
  @info_load_time && (mtime = Open.mtime(info_file)) && mtime > @info_load_time
@@ -36,7 +41,19 @@ class Step
36
41
  def merge_info(new_info)
37
42
  info = self.info
38
43
  new_info.each do |key,value|
39
- report_status new_info[:status], new_info[:message] if key == :status
44
+ if key == :status
45
+ message = new_info[:messages]
46
+ if message.nil? && value == :done || value == :error || value == :aborted
47
+ start = info[:start]
48
+ eend = new_info[:end]
49
+ if start && eend
50
+ time = eend - start
51
+ time_str = Misc.format_seconds_short(time)
52
+ message = Log.color(:time, time_str)
53
+ end
54
+ end
55
+ report_status value, message
56
+ end
40
57
  if Exception === value
41
58
  begin
42
59
  Marshal.dump(value)
@@ -72,15 +89,21 @@ class Step
72
89
 
73
90
  def report_status(status, message = nil)
74
91
  if message.nil?
75
- Log.info Log.color(:status, status, true) + " " + Log.color(:path, path)
92
+ Log.info [Log.color(:status, status, true), Log.color(:task, task_name, true), Log.color(:path, path)] * " "
76
93
  else
77
- Log.info Log.color(:status, status, true) + " " + Log.color(:path, path) + " " + message
94
+ Log.info [Log.color(:status, status, true), Log.color(:task, task_name, true), message, Log.color(:path, path)] * " "
78
95
  end
79
96
  end
80
97
 
81
- def log(status, message = nil)
98
+ def log(status, message = nil, &block)
99
+ if block_given?
100
+ time = Misc.exec_time &block
101
+ time_str = Misc.format_seconds_short time
102
+ message = message.nil? ? Log.color(:time, time_str) : "#{Log.color :time, time_str} - #{ message }"
103
+ end
104
+
82
105
  if message
83
- merge_info :status => status, :messages => [message]
106
+ merge_info :status => status, :messages => message
84
107
  else
85
108
  merge_info :status => status
86
109
  end
@@ -101,4 +124,16 @@ class Step
101
124
  def running?
102
125
  ! done? && (info[:pid] && Misc.pid_alive?(info[:pid]))
103
126
  end
127
+
128
+ def exception
129
+ info[:exception]
130
+ end
131
+
132
+ def marshal_dump
133
+ @path
134
+ end
135
+
136
+ def marshal_load(path)
137
+ Step.new path
138
+ end
104
139
  end
@@ -0,0 +1,14 @@
1
+ class Step
2
+ def progress_bar(msg = "Progress", options = nil)
3
+ if Hash === msg and options.nil?
4
+ options = msg
5
+ msg = nil
6
+ end
7
+ options = {} if options.nil?
8
+
9
+ max = options[:max]
10
+ Open.mkdir files_dir
11
+ Log::ProgressBar.new_bar(max, {:desc => msg, :file => (@exec ? nil : file(:progress))}.merge(options))
12
+ end
13
+ end
14
+
@@ -5,7 +5,7 @@ class Step
5
5
 
6
6
  def self.status_color(status)
7
7
  case status.to_sym
8
- when :error, :aborted, :missing, :dead, :unsync
8
+ when :error, :aborted, :dead, :unsync
9
9
  :red
10
10
  when :streaming, :started
11
11
  :cyan
@@ -13,7 +13,7 @@ class Step
13
13
  :green
14
14
  when :dependencies, :waiting, :setup
15
15
  :yellow
16
- when :notfound, :cleaned
16
+ when :notfound, :cleaned, :missing
17
17
  :blue
18
18
  else
19
19
  if status.to_s.index ">"
@@ -91,6 +91,7 @@ class Step
91
91
  info[:task_name] = task
92
92
  path = step.path
93
93
  status = info[:status] || :missing
94
+ status = :noinfo if status == :missing && Open.exist?(path)
94
95
  status = "remote" if Open.remote?(path) || Open.ssh?(path)
95
96
  name = info[:name] || File.basename(path)
96
97
  status = :unsync if status == :done and not Open.exist?(path)
@@ -103,9 +104,9 @@ class Step
103
104
  step.dependencies.each do |dep|
104
105
  if dep.input_dependencies.any?
105
106
  dep.input_dependencies.each do |id|
106
- input_name, _dep = dep.recursive_inputs.fields.zip(dep.recursive_inputs).select{|f,d|
107
+ input_name, _dep = dep.recursive_inputs.select{|f,d|
107
108
  d == id || (String === d && d.start_with?(id.files_dir)) || (Array === d && d.include?(id))
108
- }.last
109
+ }.keys.last
109
110
  if input_name
110
111
  input_dependencies[id] ||= []
111
112
  input_dependencies[id] << [dep, input_name]
@@ -115,10 +116,10 @@ class Step
115
116
  end if step.dependencies
116
117
 
117
118
  str = ""
118
- str = " " * offset + this_step_msg if ENV["RBBT_ORIGINAL_STACK"] == 'true'
119
+ str = " " * offset + this_step_msg if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
119
120
 
120
121
  step.dependencies.dup.tap{|l|
121
- l.reverse! if ENV["RBBT_ORIGINAL_STACK"] == 'true'
122
+ l.reverse! if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
122
123
  }.each do |dep|
123
124
  path = dep.path
124
125
  new = ! seen.include?(path)
@@ -141,7 +142,7 @@ class Step
141
142
  end
142
143
  end if step.dependencies
143
144
 
144
- str += (" " * offset) + this_step_msg unless ENV["RBBT_ORIGINAL_STACK"] == 'true'
145
+ str += (" " * offset) + this_step_msg unless ENV["SCOUT_ORIGINAL_STACK"] == 'true'
145
146
 
146
147
  str
147
148
  end
@@ -0,0 +1,45 @@
1
+ class Step
2
+ def abort(exception = nil)
3
+ while @result && streaming? && stream = self.stream
4
+ stream.abort(exception)
5
+ end
6
+ end
7
+
8
+ def recoverable_error?
9
+ self.error? && ! (ScoutException === self.exception)
10
+ end
11
+
12
+ def updated?
13
+ return false if self.error? && self.recoverable_error?
14
+ return true unless ENV["SCOUT_UPDATE"]
15
+ newer = rec_dependencies.select{|dep| Path.newer?(self.path, dep.path) }
16
+ newer += input_dependencies.select{|dep| Path.newer?(self.path, dep.path) }
17
+
18
+ newer.empty?
19
+ end
20
+
21
+ def clean
22
+ @take_stream = nil
23
+ @result = nil
24
+ @info = nil
25
+ @info_load_time = nil
26
+ Open.rm path if Open.exist?(path)
27
+ Open.rm info_file if Open.exist?(info_file)
28
+ Open.rm_rf files_dir if Open.exist?(files_dir)
29
+ end
30
+
31
+ def present?
32
+ Open.exist?(path) &&
33
+ Open.exist?(info_file) &&
34
+ Open.exist?(files_dir)
35
+ end
36
+
37
+
38
+ def recursive_clean
39
+ dependencies.each do |dep|
40
+ dep.recursive_clean
41
+ end
42
+ clean
43
+ end
44
+
45
+ end
@@ -1,19 +1,23 @@
1
1
  require_relative '../path'
2
2
  require_relative '../persist'
3
3
  require_relative 'step/info'
4
+ require_relative 'step/status'
4
5
  require_relative 'step/load'
5
6
  require_relative 'step/file'
6
7
  require_relative 'step/dependencies'
7
8
  require_relative 'step/provenance'
8
9
  require_relative 'step/config'
10
+ require_relative 'step/progress'
9
11
 
10
12
  class Step
11
13
 
12
- attr_accessor :path, :inputs, :dependencies, :task, :tee_copies
13
- def initialize(path, inputs = nil, dependencies = nil, &task)
14
+ attr_accessor :path, :inputs, :dependencies, :id, :task, :tee_copies, :non_default_inputs
15
+ def initialize(path = nil, inputs = nil, dependencies = nil, id = nil, non_default_inputs = nil, &task)
14
16
  @path = path
15
17
  @inputs = inputs
16
18
  @dependencies = dependencies
19
+ @id = id
20
+ @non_default_inputs = non_default_inputs
17
21
  @task = task
18
22
  @mutex = Mutex.new
19
23
  @tee_copies = 1
@@ -25,7 +29,7 @@ class Step
25
29
 
26
30
  def inputs
27
31
  @inputs ||= begin
28
- if Open.exists?(info_file)
32
+ if info_file && Open.exists?(info_file)
29
33
  info[:inputs]
30
34
  else
31
35
  []
@@ -54,6 +58,13 @@ class Step
54
58
  @name ||= File.basename(@path)
55
59
  end
56
60
 
61
+ def clean_name
62
+ return @id if @id
63
+ return info[:clean_name] if info.include? :clean_name
64
+ return m[1] if m = name.match(/(.*?)(?:_[a-z0-9]{32})?(?:\..*)?/)
65
+ return name.split(".").first
66
+ end
67
+
57
68
  def task_name
58
69
  @task_name ||= @task.name if @task.respond_to?(:name)
59
70
  end
@@ -63,24 +74,65 @@ class Step
63
74
  end
64
75
 
65
76
  def exec
77
+
78
+ if inputs
79
+ if Task === task
80
+ types = task.inputs.collect{|name,type| type }
81
+ new_inputs = inputs.zip(types).collect{|input,info|
82
+ type, desc, default, options = info
83
+ next input unless Step === input
84
+ input.join if input.streaming?
85
+ Task.format_input(input.join.path, type, options)
86
+ }
87
+ else
88
+ new_inputs = inputs.collect{|input|
89
+ Step === input ? input.load : input
90
+ }
91
+ end
92
+ inputs = new_inputs
93
+ end
94
+
66
95
  @result = self.instance_exec(*inputs, &task)
67
96
  end
68
97
 
98
+ def tmp_path
99
+ @tmp_path ||= begin
100
+ basename = File.basename(@path)
101
+ dirname = File.dirname(@path)
102
+ tmp_path = File.join(dirname, '.' + basename)
103
+ @path.setup(tmp_path) if Path === @path
104
+ tmp_path
105
+ end
106
+ end
107
+
69
108
  attr_reader :result
70
- def run
109
+ def run(stream = false)
71
110
  return @result || self.load if done?
72
111
  prepare_dependencies
73
112
  run_dependencies
74
- @result = Persist.persist(name, type, :path => path, :tee_copies => tee_copies) do
113
+ @result =
75
114
  begin
76
- merge_info :status => :start, :start => Time.now,
77
- :pid => Process.pid, :pid_hostname => ENV["HOSTNAME"],
78
- :inputs => inputs, :type => type,
79
- :dependencies => dependencies.collect{|d| d.path }
115
+ Persist.persist(name, type, :path => path, :tee_copies => tee_copies) do
116
+ clear_info
117
+ merge_info :status => :start, :start => Time.now,
118
+ :pid => Process.pid, :pid_hostname => ENV["HOSTNAME"],
119
+ :inputs => inputs, :type => type,
120
+ :dependencies => dependencies.collect{|d| d.path }
121
+
122
+
123
+ @result = exec
80
124
 
81
- exec
125
+ if @result.nil? && File.exist?(self.tmp_path) && ! File.exist?(self.path)
126
+ Open.mv self.tmp_path, self.path
127
+ else
128
+ @result = @result.stream if @result.respond_to?(:stream)
129
+ end
130
+
131
+ @result
132
+ end
82
133
  rescue Exception => e
83
- merge_info :status => :error, :exception => e
134
+ merge_info :status => :error, :exception => e, :end => Time.now
135
+ abort_dependencies
84
136
  raise e
85
137
  ensure
86
138
  if ! (error? || aborted?)
@@ -103,6 +155,17 @@ class Step
103
155
  end
104
156
  end
105
157
  end
158
+
159
+ if stream && ENV["SCOUT_NO_STREAM"].nil?
160
+ @result
161
+ else
162
+ if IO === @result || @result.respond_to?(:stream)
163
+ join
164
+ @result = nil
165
+ self.load
166
+ else
167
+ @result
168
+ end
106
169
  end
107
170
  end
108
171
 
@@ -111,10 +174,10 @@ class Step
111
174
  end
112
175
 
113
176
  def streaming?
114
- @take_stream || IO === @result || StringIO === @result
177
+ @take_stream || IO === @result || StringIO === @result
115
178
  end
116
179
 
117
- def get_stream
180
+ def stream
118
181
  synchronize do
119
182
  if streaming? && ! @result.nil?
120
183
  if @result.next
@@ -136,9 +199,24 @@ class Step
136
199
  end
137
200
  end
138
201
 
202
+ def consume_all_streams
203
+ threads = []
204
+ while @result && streaming? && stream = self.stream
205
+ threads << Open.consume_stream(stream, true)
206
+ end
207
+ threads.each do |t|
208
+ begin
209
+ t.join
210
+ rescue
211
+ threads.each{|t| t.raise(Aborted); t.join }
212
+ raise $!
213
+ end
214
+ end
215
+ end
216
+
139
217
  def join
140
- stream = get_stream if streaming?
141
- Open.consume_stream(stream, false) if stream
218
+ consume_all_streams
219
+ self
142
220
  end
143
221
 
144
222
  def produce
@@ -152,31 +230,24 @@ class Step
152
230
  done? ? Persist.load(path, type) : exec
153
231
  end
154
232
 
155
- def clean
156
- @take_stream = nil
157
- @result = nil
158
- @info = nil
159
- @info_load_time = nil
160
- Open.rm path if Open.exist?(path)
161
- Open.rm info_file if Open.exist?(info_file)
162
- Open.rm_rf files_dir if Open.exist?(files_dir)
163
- end
164
-
165
- def recursive_clean
166
- dependencies.each do |dep|
167
- dep.recursive_clean
168
- end
169
- clean
170
- end
171
-
172
233
  def step(task_name)
173
234
  dependencies.each do |dep|
174
235
  return dep if dep.task_name == task_name
236
+ rec_dep = dep.step(task_name)
237
+ return rec_dep if rec_dep
175
238
  end
176
239
  nil
177
240
  end
178
241
 
242
+ def short_path
243
+ Scout.identify @path
244
+ end
245
+
179
246
  def digest_str
180
- path.dup
247
+ "Step: " + short_path
248
+ end
249
+
250
+ def fingerprint
251
+ digest_str
181
252
  end
182
253
  end
@@ -1,11 +1,10 @@
1
+ require_relative '../../named_array'
1
2
  module Task
3
+ def self.format_input(value, type, options = {})
4
+ return value if IO === value || StringIO === value || Step === value
2
5
 
3
- def format_input(value, type, options = {})
4
- return value if IO === value || StringIO === value
5
-
6
- value = value.load if Step === value
7
- if String === value && ! [:path, :file].include?(type)
8
- if Open.exists?(value)
6
+ if String === value && ! [:path, :file, :folder].include?(type) && (options.nil? || (! options[:noload] || options[:stream]))
7
+ if Open.exists?(value) && ! Open.directory?(value)
9
8
  Persist.load(value, type)
10
9
  else
11
10
  Persist.deserialize(value, type)
@@ -13,7 +12,7 @@ module Task
13
12
  else
14
13
  if m = type.to_s.match(/(.*)_array/)
15
14
  if Array === value
16
- value.collect{|v| format_input(v, m[1].to_sym, options) }
15
+ value.collect{|v| self.format_input(v, m[1].to_sym, options) }
17
16
  end
18
17
  else
19
18
  value
@@ -34,35 +33,30 @@ module Task
34
33
  IndiferentHash.setup(provided_inputs) if Hash === provided_inputs
35
34
 
36
35
  input_array = []
36
+ input_names = []
37
37
  non_default_inputs = []
38
38
  self.inputs.each_with_index do |p,i|
39
39
  name, type, desc, value, options = p
40
+ input_names << name
40
41
  provided = Hash === provided_inputs ? provided_inputs[name] : provided_inputs[i]
41
- provided = format_input(provided, type, options || {})
42
+ provided = Task.format_input(provided, type, options || {})
42
43
  if ! provided.nil? && provided != value
43
- non_default_inputs << name
44
+ non_default_inputs << name.to_sym
44
45
  input_array << provided
45
46
  else
46
47
  input_array << value
47
48
  end
48
49
  end
49
50
 
51
+ NamedArray.setup(input_array, input_names)
52
+
50
53
  [input_array, non_default_inputs]
51
54
  end
52
55
 
53
- def digest_inputs(provided_inputs = {})
54
- input_array, non_default_inputs = assign_inputs(provided_inputs)
55
- if Array === provided_inputs
56
- Misc.digest(input_array)
57
- else
58
- Misc.digest(input_array)
59
- end
60
- end
61
-
62
56
  def process_inputs(provided_inputs = {})
63
57
  input_array, non_default_inputs = assign_inputs provided_inputs
64
- digest = Misc.digest(input_array)
65
- [input_array, non_default_inputs, digest]
58
+ digest_str = Misc.digest_str(input_array)
59
+ [input_array, non_default_inputs, digest_str]
66
60
  end
67
61
 
68
62
  def save_file_input(orig_file, directory)