scout-gear 8.1.0 → 9.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +22 -0
  3. data/VERSION +1 -1
  4. data/bin/scout +1 -1
  5. data/lib/rbbt-scout.rb +4 -2
  6. data/lib/scout/cmd.rb +9 -5
  7. data/lib/scout/config.rb +1 -1
  8. data/lib/scout/indiferent_hash/options.rb +1 -1
  9. data/lib/scout/indiferent_hash.rb +17 -0
  10. data/lib/scout/log/progress/report.rb +1 -0
  11. data/lib/scout/log.rb +14 -11
  12. data/lib/scout/misc/digest.rb +1 -1
  13. data/lib/scout/misc/filesystem.rb +2 -3
  14. data/lib/scout/offsite/ssh.rb +3 -0
  15. data/lib/scout/offsite/step.rb +20 -3
  16. data/lib/scout/open/stream.rb +0 -1
  17. data/lib/scout/open.rb +8 -8
  18. data/lib/scout/path/util.rb +20 -0
  19. data/lib/scout/path.rb +1 -1
  20. data/lib/scout/persist/serialize.rb +1 -1
  21. data/lib/scout/resource/open.rb +8 -0
  22. data/lib/scout/resource/path.rb +12 -9
  23. data/lib/scout/resource/software.rb +4 -2
  24. data/lib/scout/resource/util.rb +5 -1
  25. data/lib/scout/resource.rb +2 -0
  26. data/lib/scout/tsv/dumper.rb +4 -1
  27. data/lib/scout/tsv/open.rb +7 -1
  28. data/lib/scout/tsv/parser.rb +13 -3
  29. data/lib/scout/tsv/util.rb +1 -1
  30. data/lib/scout/work_queue.rb +1 -0
  31. data/lib/scout/workflow/deployment/orchestrator.rb +17 -8
  32. data/lib/scout/workflow/step/dependencies.rb +11 -4
  33. data/lib/scout/workflow/step/file.rb +5 -1
  34. data/lib/scout/workflow/step/info.rb +16 -2
  35. data/lib/scout/workflow/step/inputs.rb +5 -0
  36. data/lib/scout/workflow/step/provenance.rb +28 -10
  37. data/lib/scout/workflow/step/status.rb +26 -2
  38. data/lib/scout/workflow/step.rb +26 -7
  39. data/lib/scout/workflow/task/dependencies.rb +3 -0
  40. data/lib/scout/workflow/task/inputs.rb +19 -9
  41. data/lib/scout/workflow/task.rb +24 -3
  42. data/lib/scout/workflow/usage.rb +22 -33
  43. data/lib/scout/workflow.rb +25 -12
  44. data/scout-gear.gemspec +9 -3
  45. data/scout_commands/resource/produce +66 -0
  46. data/scout_commands/template +52 -0
  47. data/scout_commands/workflow/install +3 -0
  48. data/scout_commands/workflow/task +25 -7
  49. data/share/software/install_helpers +2 -2
  50. data/share/templates/command +25 -0
  51. data/share/templates/workflow.rb +14 -0
  52. data/test/scout/offsite/test_step.rb +0 -1
  53. data/test/scout/resource/test_produce.rb +15 -0
  54. data/test/scout/resource/test_util.rb +9 -0
  55. data/test/scout/test_persist.rb +2 -2
  56. data/test/scout/test_work_queue.rb +1 -1
  57. data/test/scout/tsv/test_parser.rb +21 -0
  58. data/test/scout/workflow/step/test_info.rb +0 -1
  59. data/test/scout/workflow/step/test_provenance.rb +24 -1
  60. data/test/scout/workflow/task/test_dependencies.rb +2 -0
  61. data/test/scout/workflow/task/test_inputs.rb +0 -1
  62. data/test/scout/workflow/test_definition.rb +1 -1
  63. metadata +8 -2
@@ -6,7 +6,7 @@ module Workflow
6
6
  return workload if job.done? && job.updated?
7
7
 
8
8
  job.dependencies.each do |dep|
9
- next if dep.done? && job.updated?
9
+ next if dep.done? && dep.updated?
10
10
  workload.merge!(job_workload(dep))
11
11
  workload[job] += workload[dep]
12
12
  workload[job] << dep
@@ -14,7 +14,7 @@ module Workflow
14
14
  end
15
15
 
16
16
  job.input_dependencies.each do |dep|
17
- next if dep.done? && job.updated?
17
+ next if dep.done? && dep.updated?
18
18
  workload.merge!(job_workload(dep))
19
19
  workload[job] += workload[dep]
20
20
  workload[job] << dep
@@ -85,15 +85,15 @@ module Workflow
85
85
  candidates = workload.
86
86
  select{|k,v| v.empty? }.
87
87
  collect{|k,v| k }.
88
- reject{|k| k.done? }
88
+ reject{|k| k.done? || k.running? }
89
89
  else
90
90
  candidates = workload. #select{|k,v| Orchestrator.job_rules(rules, k) }.
91
91
  select{|k,v| v.empty? }.
92
92
  collect{|k,v| k }.
93
- reject{|k| k.done? }
93
+ reject{|k| k.done? || k.running? }
94
94
  end
95
95
 
96
- top_level = workload.keys - workload.values.flatten
96
+ #top_level = workload.keys - workload.values.flatten
97
97
 
98
98
  candidates = purge_duplicates candidates
99
99
  candidates = sort_candidates candidates, rules
@@ -192,7 +192,10 @@ module Workflow
192
192
  workload = Orchestrator.workload(jobs)
193
193
  all_jobs = workload.keys
194
194
 
195
+ all_jobs.each{|job| job.clean unless job.done? && job.updated? }
196
+
195
197
  top_level_jobs = jobs.collect{|job| job.path }
198
+ failed_jobs = []
196
199
  while workload.any?
197
200
 
198
201
  candidates = resources_used.keys + Orchestrator.candidates(workload, rules)
@@ -203,9 +206,15 @@ module Workflow
203
206
  case
204
207
  when (job.error? || job.aborted?)
205
208
  begin
206
- if job.recoverable_error?
207
- job.clean
208
- raise TryAgain
209
+ if job.recoverable_error?
210
+ if failed_jobs.include?(job)
211
+ Log.warn "Failed twice #{job.path} with recoverable error"
212
+ next
213
+ else
214
+ failed_jobs << job
215
+ job.clean
216
+ raise TryAgain
217
+ end
209
218
  else
210
219
  next
211
220
  end
@@ -1,11 +1,18 @@
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 }
2
+ def rec_dependencies(connected = false, seen = [])
3
+ direct_deps = []
4
+ dependencies.each do |dep|
5
+ next if seen.include? dep.path
6
+ next if connected && dep.done? && dep.updated?
7
+ direct_deps << dep
8
+ end
9
+ seen.concat direct_deps.collect{|d| d.path }
10
+ direct_deps.inject(direct_deps){|acc,d| acc.concat(d.rec_dependencies(connected, seen)); acc }
5
11
  end
6
12
 
7
13
  def recursive_inputs
8
- recursive_inputs = @inputs.to_hash
14
+ recursive_inputs = NamedArray === @inputs ? @inputs.to_hash : {}
15
+ return recursive_inputs if dependencies.nil?
9
16
  dependencies.inject(recursive_inputs) do |acc,dep|
10
17
  acc.merge(dep.recursive_inputs)
11
18
  end
@@ -2,7 +2,11 @@ class Step
2
2
  def files_dir
3
3
  @files_dir ||= begin
4
4
  dir = @path + ".files"
5
- @path.annotate(dir) if Path === @path
5
+ if Path === @path
6
+ @path.annotate(dir)
7
+ else
8
+ Path.setup(dir)
9
+ end
6
10
  dir.pkgdir = self
7
11
  dir
8
12
  end
@@ -9,12 +9,18 @@ class Step
9
9
  end
10
10
  end
11
11
 
12
+ def self.load_info(info_file)
13
+ info = Persist.load(info_file, SERIALIZER) || {}
14
+ IndiferentHash.setup(info)
15
+ end
16
+
12
17
  def load_info
13
- @info = Persist.load(info_file, SERIALIZER) || {}
14
- IndiferentHash.setup(@info)
18
+ @info = Step.load_info(info_file)
15
19
  @info_load_time = Time.now
16
20
  end
17
21
 
22
+
23
+
18
24
  def save_info(info = nil)
19
25
  Persist.save(info, info_file, SERIALIZER)
20
26
  @info_load_time = Time.now
@@ -127,6 +133,14 @@ class Step
127
133
  ! done? && (info[:pid] && Misc.pid_alive?(info[:pid]))
128
134
  end
129
135
 
136
+ def overriden?
137
+ overriden_task || overriden_workflow || dependencies.select{|d| d.overriden? }.any?
138
+ end
139
+
140
+ def overriden_deps
141
+ rec_dependencies.select{|d| d.overriden? }
142
+ end
143
+
130
144
  def exception
131
145
  info[:exception]
132
146
  end
@@ -0,0 +1,5 @@
1
+ class Step
2
+ def save_inputs(inputs_dir)
3
+ self.task.save_inputs(inputs_dir, provided_inputs)
4
+ end
5
+ end
@@ -83,10 +83,25 @@ class Step
83
83
  end
84
84
  end
85
85
 
86
- str << "\n"
86
+ str
87
87
  end
88
88
 
89
- def self.prov_report(step, offset = 0, task = nil, seen = [], expand_repeats = false, input = nil)
89
+ def self.prov_indent(step, offset, input_dependencies)
90
+ return (" " * (offset))
91
+ if step.alias?
92
+ (" " * offset + "🡵")
93
+ elsif step.overriden_task
94
+ (" " * offset + "🡇")
95
+ elsif input_dependencies.include?(step)
96
+ (" " * offset + "┝")
97
+ elsif step.input_dependencies.any?
98
+ (" " * offset + "╰")
99
+ else
100
+ (" " * (offset+1))
101
+ end
102
+ end
103
+
104
+ def self.prov_report(step, offset = 0, task = nil, seen = [], expand_repeats = false, input = nil, input_dependencies = nil)
90
105
  info = step.info || {}
91
106
  info[:task_name] = task
92
107
  path = step.path
@@ -101,7 +116,7 @@ class Step
101
116
 
102
117
  this_step_msg = prov_report_msg(status, name, path, info, input)
103
118
 
104
- input_dependencies = {}
119
+ input_dependencies ||= {}
105
120
  step.dependencies.each do |dep|
106
121
  if dep.input_dependencies.any?
107
122
  dep.input_dependencies.each do |id|
@@ -116,8 +131,10 @@ class Step
116
131
  end
117
132
  end if step.dependencies
118
133
 
119
- str = ""
120
- str = " " * offset + this_step_msg if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
134
+ str = []
135
+
136
+ indent = prov_indent(step, offset, input_dependencies)
137
+ str << indent + this_step_msg if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
121
138
 
122
139
  step.dependencies.dup.tap{|l|
123
140
  l.reverse! if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
@@ -126,10 +143,10 @@ class Step
126
143
  new = ! seen.include?(path)
127
144
  if new
128
145
  seen << path
129
- str << prov_report(dep, offset + 1, task, seen, expand_repeats, input_dependencies[dep])
146
+ str.concat(prov_report(dep, offset + 1, task, seen, expand_repeats, input_dependencies[dep], input_dependencies.dup).split("\n"))
130
147
  else
131
148
  if expand_repeats
132
- str << Log.color(Step.status_color(dep.status), Log.uncolor(prov_report(dep, offset+1, task)))
149
+ str << Log.color(Step.status_color(dep.status), Log.uncolor(prov_report(dep, offset+1, task, input_dependencies[dep], input_dependencies.dup)))
133
150
  else
134
151
  info = dep.info || {}
135
152
  status = info[:status] || :missing
@@ -138,13 +155,14 @@ class Step
138
155
  status = :unsync if status == :done and not Open.exist?(path)
139
156
  status = :notfound if status == :noinfo and not Open.exist?(path)
140
157
 
141
- str << Log.color(Step.status_color(status), " " * (offset + 1) + Log.uncolor(prov_report_msg(status, name, path, info, input_dependencies[dep])))
158
+ dep_indent = prov_indent(dep, offset+1, input_dependencies)
159
+ str << dep_indent + Log.color(Step.status_color(status), Log.uncolor(prov_report_msg(status, name, path, info, input_dependencies[dep])))
142
160
  end
143
161
  end
144
162
  end if step.dependencies
145
163
 
146
- str += (" " * offset) + this_step_msg unless ENV["SCOUT_ORIGINAL_STACK"] == 'true'
164
+ str << indent + this_step_msg unless ENV["SCOUT_ORIGINAL_STACK"] == 'true'
147
165
 
148
- str
166
+ str * "\n"
149
167
  end
150
168
  end
@@ -1,6 +1,6 @@
1
1
  class Step
2
2
  def abort(exception = nil)
3
- if info[:pid] != Process.pid && Misc.alive?(pid)
3
+ if (pid = info[:pid]) && pid != Process.pid && Misc.pid_alive?(pid)
4
4
  Process.kill pid
5
5
  else
6
6
  while @result && streaming? && stream = self.stream
@@ -15,7 +15,7 @@ class Step
15
15
 
16
16
  def updated?
17
17
  return false if self.error? && self.recoverable_error?
18
- return true unless ENV["SCOUT_UPDATE"]
18
+ return true if self.done? && ! ENV["SCOUT_UPDATE"]
19
19
  newer = rec_dependencies.select{|dep| Path.newer?(self.path, dep.path) }
20
20
  newer += input_dependencies.select{|dep| Path.newer?(self.path, dep.path) }
21
21
 
@@ -28,10 +28,15 @@ class Step
28
28
  @info = nil
29
29
  @info_load_time = nil
30
30
  Open.rm path if Open.exist?(path)
31
+ Open.rm tmp_path if Open.exist?(tmp_path)
31
32
  Open.rm info_file if Open.exist?(info_file)
32
33
  Open.rm_rf files_dir if Open.exist?(files_dir)
33
34
  end
34
35
 
36
+ def self.clean(file)
37
+ Step.new(file).clean
38
+ end
39
+
35
40
 
36
41
  def recursive_clean
37
42
  dependencies.each do |dep|
@@ -40,4 +45,23 @@ class Step
40
45
  clean
41
46
  end
42
47
 
48
+ def canfail?
49
+ @compute && @compute.include?(:canfail)
50
+ end
51
+
52
+ def started?
53
+ return true if done?
54
+ return false unless Open.exist?(info_file)
55
+ pid = info[:pid]
56
+ return false unless pid
57
+ return Misc.pid_alive?(pid)
58
+ end
59
+
60
+ def waiting?
61
+ present? and not started?
62
+ end
63
+
64
+ def dirty?
65
+ done? && ! updated?
66
+ end
43
67
  end
@@ -8,16 +8,18 @@ require_relative 'step/dependencies'
8
8
  require_relative 'step/provenance'
9
9
  require_relative 'step/config'
10
10
  require_relative 'step/progress'
11
+ require_relative 'step/inputs'
11
12
 
12
13
  class Step
13
14
 
14
- attr_accessor :path, :inputs, :dependencies, :id, :task, :tee_copies, :non_default_inputs, :compute
15
- def initialize(path = nil, inputs = nil, dependencies = nil, id = nil, non_default_inputs = nil, compute = nil, &task)
15
+ attr_accessor :path, :inputs, :dependencies, :id, :task, :tee_copies, :non_default_inputs, :provided_inputs, :compute, :overriden_task, :overriden_workflow
16
+ def initialize(path = nil, inputs = nil, dependencies = nil, id = nil, non_default_inputs = nil, provided_inputs = nil, compute = nil, &task)
16
17
  @path = path
17
18
  @inputs = inputs
18
19
  @dependencies = dependencies
19
20
  @id = id
20
21
  @non_default_inputs = non_default_inputs
22
+ @provided_inputs = provided_inputs
21
23
  @compute = compute
22
24
  @task = task
23
25
  @mutex = Mutex.new
@@ -31,7 +33,9 @@ class Step
31
33
  def inputs
32
34
  @inputs ||= begin
33
35
  if info_file && Open.exists?(info_file)
34
- info[:inputs]
36
+ inputs = info[:inputs]
37
+ NamedArray.setup(inputs, info[:input_names]) if inputs && info[:input_names]
38
+ inputs
35
39
  else
36
40
  []
37
41
  end
@@ -59,6 +63,10 @@ class Step
59
63
  @name ||= File.basename(@path)
60
64
  end
61
65
 
66
+ def short_path
67
+ [workflow.to_s, task_name, name] * "/"
68
+ end
69
+
62
70
  def clean_name
63
71
  return @id if @id
64
72
  return info[:clean_name] if info.include? :clean_name
@@ -120,9 +128,10 @@ class Step
120
128
  begin
121
129
  Persist.persist(name, type, :path => path, :tee_copies => tee_copies) do
122
130
  clear_info
131
+ input_names = (task.respond_to?(:inputs) && task.inputs) ? task.inputs.collect{|name,_| name} : []
123
132
  merge_info :status => :start, :start => Time.now,
124
133
  :pid => Process.pid, :pid_hostname => Misc.hostname,
125
- :inputs => MetaExtension.purge(inputs), :type => type,
134
+ :inputs => MetaExtension.purge(inputs), :input_names => input_names, :type => type,
126
135
  :dependencies => dependencies.collect{|d| d.path }
127
136
 
128
137
  @result = exec
@@ -209,7 +218,7 @@ class Step
209
218
  if done?
210
219
  Open.open(self.path)
211
220
  else
212
- if running?
221
+ if running? || waiting?
213
222
  join
214
223
  Open.open(self.path)
215
224
  else
@@ -251,7 +260,7 @@ class Step
251
260
 
252
261
  def join
253
262
  consume_all_streams
254
- while @result.nil? && (present? && ! terminated?)
263
+ while @result.nil? && (present? && ! (terminated? || done?))
255
264
  sleep 0.1
256
265
  end
257
266
  raise self.exception if self.exception
@@ -276,8 +285,10 @@ class Step
276
285
  end
277
286
 
278
287
  def step(task_name)
288
+ task_name = task_name.to_sym
279
289
  dependencies.each do |dep|
280
- return dep if dep.task_name == task_name
290
+ return dep if dep.task_name && dep.task_name.to_sym == task_name
291
+ return dep if dep.overriden_task && dep.overriden_task.to_sym == task_name
281
292
  rec_dep = dep.step(task_name)
282
293
  return rec_dep if rec_dep
283
294
  end
@@ -295,4 +306,12 @@ class Step
295
306
  def fingerprint
296
307
  digest_str
297
308
  end
309
+
310
+ def task_signature
311
+ [workflow.to_s, task_name] * "#"
312
+ end
313
+
314
+ def alias?
315
+ task.alias? if task
316
+ end
298
317
  end
@@ -54,7 +54,10 @@ module Task
54
54
  if provided_inputs.include?(overriden = [workflow.name, task] * "#")
55
55
  dep = provided_inputs[overriden]
56
56
  dep = Step.new dep unless Step === dep
57
+ dep = dep.dup
57
58
  dep.type = workflow.tasks[task].type
59
+ dep.overriden_task = task
60
+ dep.overriden_workflow = workflow
58
61
  dependencies << dep
59
62
  non_default_inputs << overriden
60
63
  next
@@ -20,7 +20,7 @@ module Task
20
20
  end
21
21
  end
22
22
 
23
- def assign_inputs(provided_inputs = {})
23
+ def assign_inputs(provided_inputs = {}, id = nil)
24
24
  if self.inputs.nil? || (self.inputs.empty? && Array === provided_inputs)
25
25
  case provided_inputs
26
26
  when Array
@@ -43,6 +43,8 @@ module Task
43
43
  if ! provided.nil? && provided != value
44
44
  non_default_inputs << name.to_sym
45
45
  input_array << provided
46
+ elsif options && options[:jobname]
47
+ input_array << id
46
48
  else
47
49
  input_array << value
48
50
  end
@@ -53,13 +55,14 @@ module Task
53
55
  [input_array, non_default_inputs]
54
56
  end
55
57
 
56
- def process_inputs(provided_inputs = {})
57
- input_array, non_default_inputs = assign_inputs provided_inputs
58
+ def process_inputs(provided_inputs = {}, id = nil)
59
+ input_array, non_default_inputs = assign_inputs provided_inputs, id
58
60
  digest_str = Misc.digest_str(input_array)
59
61
  [input_array, non_default_inputs, digest_str]
60
62
  end
61
63
 
62
64
  def save_file_input(orig_file, directory)
65
+ orig_file = orig_file.path if Step === orig_file
63
66
  basename = File.basename(orig_file)
64
67
  digest = Misc.digest(orig_file)
65
68
  if basename.include? '.'
@@ -81,7 +84,12 @@ module Task
81
84
  value = provided_inputs[name]
82
85
  input_file = File.join(directory, name.to_s)
83
86
 
84
- if type == :file
87
+ if Path.is_filename?(value)
88
+ relative_file = save_file_input(value, directory)
89
+ Open.write(input_file + ".as_file", relative_file)
90
+ elsif Step === value
91
+ Open.write(input_file + ".as_step", value.short_path)
92
+ elsif type == :file
85
93
  relative_file = save_file_input(value, directory)
86
94
  Persist.save(relative_file, input_file, :file)
87
95
  elsif type == :file_array
@@ -89,13 +97,10 @@ module Task
89
97
  save_file_input(orig_file, directory)
90
98
  end
91
99
  Persist.save(new_files, input_file, type)
92
- elsif Path.is_filename?(value)
93
- relative_file = save_file_input(value, directory)
94
- Open.write(input_file + ".as_file", relative_file)
95
100
  elsif Open.is_stream?(value)
96
- Persist.save(input_file, value, type)
101
+ Persist.save(value, input_file, type)
97
102
  elsif Open.has_stream?(value)
98
- Persist.save(input_file, value.stream, type)
103
+ Persist.save(value.stream, input_file, type)
99
104
  else
100
105
  Persist.save(value, input_file, type)
101
106
  end
@@ -112,6 +117,11 @@ module Task
112
117
  value = Open.read(filename).strip
113
118
  value.sub!(/^\./, File.dirname(filename)) if value.start_with?("./")
114
119
  inputs[name] = value
120
+ elsif filename.end_with?('.as_step')
121
+ value = Open.read(filename).strip
122
+ inputs[name] = Step.load value
123
+ elsif (options && (options[:noload] || options[:stream] || options[:nofile]))
124
+ inputs[name] = filename
115
125
  else
116
126
  inputs[name] = Persist.load(filename, type)
117
127
  end
@@ -42,16 +42,32 @@ module Task
42
42
  def job(id = DEFAULT_NAME, provided_inputs = nil )
43
43
  provided_inputs, id = id, DEFAULT_NAME if (provided_inputs.nil? || provided_inputs.empty?) && (Hash === id || Array === id)
44
44
  provided_inputs = {} if provided_inputs.nil?
45
+ IndiferentHash.setup(provided_inputs)
45
46
  id = DEFAULT_NAME if id.nil?
46
47
 
48
+
49
+ missing_inputs = []
50
+ self.inputs.each do |input,type,desc,val,options|
51
+ next unless options && options[:required]
52
+ missing_inputs << input unless provided_inputs.include?(input)
53
+ end if self.inputs
54
+
55
+ if missing_inputs.length == 1
56
+ raise ParameterException, "Input '#{missing_inputs.first}' is required but was not provided or is nil"
57
+ end
58
+
59
+ if missing_inputs.length > 1
60
+ raise ParameterException, "Inputs #{Misc.humanize_list(missing_inputs)} are required but were not provided or are nil"
61
+ end
62
+
47
63
  provided_inputs = load_inputs(provided_inputs[:load_inputs]) if Hash === provided_inputs && provided_inputs[:load_inputs]
48
64
 
49
- inputs, non_default_inputs, input_digest_str = process_inputs provided_inputs
65
+ inputs, non_default_inputs, input_digest_str = process_inputs provided_inputs, id
50
66
 
51
67
  compute = {}
52
68
  dependencies = dependencies(id, provided_inputs, non_default_inputs, compute)
53
69
 
54
- non_default_inputs.concat provided_inputs.keys.select{|k| String === k && k.include?("#") } if Hash === provided_inputs
70
+ #non_default_inputs.concat provided_inputs.keys.select{|k| String === k && k.include?("#") } if Hash === provided_inputs
55
71
 
56
72
  non_default_inputs.uniq!
57
73
 
@@ -90,7 +106,12 @@ module Task
90
106
  Log.debug "ID #{self.name} #{id} - Clean"
91
107
  end
92
108
  NamedArray.setup(inputs, @inputs.collect{|i| i[0] }) if @inputs
93
- Step.new path.find, inputs, dependencies, id, non_default_inputs, compute, &self
109
+ step_provided_inputs = Hash === provided_inputs ? provided_inputs.slice(*non_default_inputs) : provided_inputs
110
+ Step.new path.find, inputs, dependencies, id, non_default_inputs, step_provided_inputs, compute, &self
94
111
  end
95
112
  end
113
+
114
+ def alias?
115
+ @extension == :dep_task
116
+ end
96
117
  end
@@ -13,28 +13,30 @@ module Task
13
13
  if paragraph
14
14
  str.puts "\n" << Misc.format_paragraph(paragraph)
15
15
  end
16
- str.puts
17
16
  else
18
17
  title = self.name.to_s
19
18
  str.puts Log.color :yellow, title
20
19
  str.puts Log.color :yellow, "-" * title.length
21
20
  str.puts "\n" << Misc.format_paragraph(description)
22
- str.puts
23
21
  end
24
22
  else
25
23
  title = self.name.to_s
26
24
  str.puts Log.color :yellow, title
27
25
  str.puts Log.color :yellow, "-" * title.length
28
- str.puts
29
26
  end
30
27
 
31
28
 
32
29
  selects = []
30
+
33
31
  if inputs && inputs.any?
32
+ str.puts
34
33
  str.puts Log.color(:magenta, "Inputs")
35
34
  str.puts
36
35
  str.puts SOPT.input_array_doc(inputs)
37
- str.puts
36
+
37
+ inputs.select{|name,type, _| type == :select }.each do |name,_,_,_,options|
38
+ selects << [name, options[:select_options]] if options[:select_options]
39
+ end
38
40
  end
39
41
 
40
42
  deps = workflow ? workflow.recursive_deps(self.name) : self.deps if deps.nil?
@@ -44,14 +46,21 @@ module Task
44
46
  deps.each do |dep_workflow,task_name,options|
45
47
  next if task_name.nil?
46
48
  task = dep_workflow.tasks[task_name]
49
+
47
50
  next if task.inputs.nil?
51
+
48
52
  inputs = task.inputs.reject{|name, _| seen.include? name }
49
53
  inputs = task.inputs.reject{|name, _| options.include? name }
50
54
  next unless inputs.any?
55
+ task.inputs.select{|name, _| inputs.include? name }.each do |name,_,_,_,options|
56
+ selects << [i, options[:select_options]] if options[:select_options]
57
+ end
58
+
51
59
  dep = workflow.nil? || dep_workflow.name != workflow.name ? ["#{dep_workflow.name}", task_name.to_s] *"#" : task_name.to_s
52
60
  dep_inputs[dep] = inputs
53
61
  end
54
62
 
63
+ str.puts
55
64
  str.puts Log.color(:magenta, "Inputs from dependencies:") if dep_inputs.any?
56
65
  dep_inputs.each do |dep,inputs|
57
66
  str.puts
@@ -59,54 +68,31 @@ module Task
59
68
  str.puts
60
69
  str.puts SOPT.input_array_doc(inputs)
61
70
  end
62
-
63
- #task_inputs = dep_inputs deps, workflow
64
- #task_inputs.each do |task,new_inputs|
65
- # new_inputs.zip(task.input_types.values_at(*new_inputs)).select do |i,t|
66
- # t.to_sym == :select and task.input_options[i][:select_options]
67
- # end.each do |i,t|
68
- # selects << [i, task.input_options[i][:select_options]]
69
- # end
70
-
71
- # next if new_inputs.empty?
72
-
73
- # if task.workflow and task.workflow != workflow
74
- # puts " #{Log.color :yellow, ["[#{task.workflow.to_s}]", task.name.to_s] *" "}:"
75
- # else
76
- # puts " #{Log.color :yellow, task.name.to_s}:"
77
- # end
78
- # puts unless Log.compact
79
- # puts SOPT.input_doc(new_inputs, task.input_types, task.input_descriptions, task.input_defaults, true)
80
- # puts unless Log.compact
81
- #end
82
- #puts
83
71
  end
84
72
 
85
73
  case
86
74
  when inputs && inputs.select{|name,type| type == :array }.any?
75
+ str.puts
87
76
  str.puts Log.color(:green, Misc.format_paragraph("Lists are specified as arguments using ',' or '|'. When specified as files the '\\n'
88
77
  also works in addition to the others. You may use the '--array_separator' option
89
78
  the change this default. Whenever a file is specified it may also accept STDIN using
90
79
  the '-' character."))
91
- str.puts
92
80
 
93
81
  when inputs && inputs.select{|name,type| type == :file || type == :tsv }.any?
94
- str.puts Log.color(:green, Misc.format_paragraph("Whenever a file is specified it may also accept STDIN using the '-' character."))
95
82
  str.puts
83
+ str.puts Log.color(:green, Misc.format_paragraph("Whenever a file is specified it may also accept STDIN using the '-' character."))
96
84
  end
97
85
 
98
86
  str.puts
99
87
  str.puts Log.color(:magenta, "Returns: ") << Log.color(:blue, type.to_s) << "\n"
100
- str.puts
101
88
 
102
89
  if selects.any?
103
- str.puts Log.color(:magenta, "Input select options")
104
90
  str.puts
91
+ str.puts Log.color(:magenta, "Input select options")
105
92
  selects.collect{|p| p}.uniq.each do |input,options|
93
+ str.puts
106
94
  str.puts Log.color(:blue, input.to_s + ": ") << Misc.format_paragraph(options.collect{|o| Array === o ? o.first.to_s : o.to_s} * ", ") << "\n"
107
- str.puts unless Log.compact
108
95
  end
109
- str.puts
110
96
  end
111
97
  str.rewind
112
98
  str.read
@@ -150,6 +136,7 @@ module Workflow
150
136
 
151
137
  dep_tree = {}
152
138
  task = self.tasks[task_name]
139
+ raise "TaskNotFound: #{task_name}" if task.nil?
153
140
  task.deps.each do |workflow, task, options|
154
141
  next if seen.include? dep
155
142
  seen << [workflow, task, options.merge(seen_options)]
@@ -249,7 +236,9 @@ module Workflow
249
236
 
250
237
  str.puts
251
238
 
252
- if task.nil?
239
+ if tasks.nil?
240
+ str.puts Log.color(:title, "No tasks")
241
+ elsif task.nil?
253
242
 
254
243
  if self.documentation[:description] and not self.documentation[:description].empty?
255
244
  str.puts Misc.format_paragraph self.documentation[:description]
@@ -284,7 +273,7 @@ module Workflow
284
273
 
285
274
  prov_string = prov_string(dep_tree(name))
286
275
  str.puts Misc.format_paragraph Log.color(:blue, "->" + prov_string) if prov_string && ! prov_string.empty?
287
- end
276
+ end
288
277
 
289
278
  else
290
279