scout-gear 7.3.0 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +44 -16
  3. data/Rakefile +6 -1
  4. data/VERSION +1 -1
  5. data/bin/scout +21 -7
  6. data/doc/lib/scout/path.md +35 -0
  7. data/doc/lib/scout/workflow/task.md +13 -0
  8. data/lib/rbbt-scout.rb +1 -0
  9. data/lib/scout/cmd.rb +24 -25
  10. data/lib/scout/concurrent_stream.rb +59 -39
  11. data/lib/scout/config.rb +1 -1
  12. data/lib/scout/exceptions.rb +10 -0
  13. data/lib/scout/log/color.rb +15 -12
  14. data/lib/scout/log/progress/report.rb +8 -6
  15. data/lib/scout/log/progress/util.rb +61 -54
  16. data/lib/scout/log/progress.rb +1 -1
  17. data/lib/scout/log/trap.rb +107 -0
  18. data/lib/scout/log.rb +115 -52
  19. data/lib/scout/meta_extension.rb +47 -6
  20. data/lib/scout/misc/digest.rb +12 -3
  21. data/lib/scout/misc/format.rb +24 -7
  22. data/lib/scout/misc/insist.rb +1 -1
  23. data/lib/scout/misc/monitor.rb +22 -0
  24. data/lib/scout/misc/system.rb +58 -0
  25. data/lib/scout/named_array.rb +73 -3
  26. data/lib/scout/offsite/ssh.rb +171 -0
  27. data/lib/scout/offsite/step.rb +83 -0
  28. data/lib/scout/offsite/sync.rb +55 -0
  29. data/lib/scout/offsite.rb +3 -0
  30. data/lib/scout/open/lock/lockfile.rb +587 -0
  31. data/lib/scout/open/lock.rb +9 -2
  32. data/lib/scout/open/remote.rb +16 -1
  33. data/lib/scout/open/stream.rb +146 -83
  34. data/lib/scout/open/util.rb +22 -3
  35. data/lib/scout/open.rb +5 -4
  36. data/lib/scout/path/find.rb +24 -11
  37. data/lib/scout/path/util.rb +40 -0
  38. data/lib/scout/persist/serialize.rb +19 -6
  39. data/lib/scout/persist.rb +29 -13
  40. data/lib/scout/resource/path.rb +57 -0
  41. data/lib/scout/resource/produce.rb +0 -8
  42. data/lib/scout/resource/util.rb +12 -5
  43. data/lib/scout/tmpfile.rb +7 -8
  44. data/lib/scout/tsv/attach.rb +177 -0
  45. data/lib/scout/tsv/change_id.rb +40 -0
  46. data/lib/scout/tsv/dumper.rb +74 -46
  47. data/lib/scout/tsv/index.rb +85 -87
  48. data/lib/scout/tsv/open.rb +160 -85
  49. data/lib/scout/tsv/parser.rb +142 -80
  50. data/lib/scout/tsv/path.rb +1 -2
  51. data/lib/scout/tsv/persist/adapter.rb +15 -45
  52. data/lib/scout/tsv/persist/fix_width_table.rb +3 -0
  53. data/lib/scout/tsv/persist/tokyocabinet.rb +6 -1
  54. data/lib/scout/tsv/persist.rb +4 -0
  55. data/lib/scout/tsv/stream.rb +204 -0
  56. data/lib/scout/tsv/transformer.rb +152 -0
  57. data/lib/scout/tsv/traverse.rb +96 -92
  58. data/lib/scout/tsv/util/filter.rb +9 -0
  59. data/lib/scout/tsv/util/reorder.rb +81 -0
  60. data/lib/scout/tsv/util/select.rb +78 -33
  61. data/lib/scout/tsv/util/unzip.rb +86 -0
  62. data/lib/scout/tsv/util.rb +60 -11
  63. data/lib/scout/tsv.rb +34 -4
  64. data/lib/scout/work_queue/socket.rb +6 -1
  65. data/lib/scout/work_queue/worker.rb +5 -2
  66. data/lib/scout/work_queue.rb +51 -20
  67. data/lib/scout/workflow/definition.rb +23 -3
  68. data/lib/scout/workflow/deployment/orchestrator.rb +245 -0
  69. data/lib/scout/workflow/deployment.rb +1 -0
  70. data/lib/scout/workflow/step/dependencies.rb +56 -10
  71. data/lib/scout/workflow/step/file.rb +5 -0
  72. data/lib/scout/workflow/step/info.rb +40 -7
  73. data/lib/scout/workflow/step/load.rb +1 -1
  74. data/lib/scout/workflow/step/provenance.rb +9 -7
  75. data/lib/scout/workflow/step/status.rb +43 -0
  76. data/lib/scout/workflow/step.rb +160 -49
  77. data/lib/scout/workflow/task/dependencies.rb +114 -0
  78. data/lib/scout/workflow/task/inputs.rb +40 -32
  79. data/lib/scout/workflow/task.rb +38 -102
  80. data/lib/scout/workflow/usage.rb +48 -18
  81. data/lib/scout/workflow.rb +4 -2
  82. data/lib/scout-gear.rb +2 -0
  83. data/lib/scout.rb +6 -0
  84. data/scout-gear.gemspec +52 -23
  85. data/scout_commands/doc +37 -0
  86. data/scout_commands/find +1 -0
  87. data/scout_commands/offsite +30 -0
  88. data/scout_commands/update +29 -0
  89. data/scout_commands/workflow/info +15 -3
  90. data/scout_commands/workflow/install +102 -0
  91. data/scout_commands/workflow/task +57 -9
  92. data/test/scout/offsite/test_ssh.rb +15 -0
  93. data/test/scout/offsite/test_step.rb +33 -0
  94. data/test/scout/offsite/test_sync.rb +36 -0
  95. data/test/scout/offsite/test_task.rb +0 -0
  96. data/test/scout/open/test_stream.rb +60 -58
  97. data/test/scout/path/test_find.rb +10 -1
  98. data/test/scout/resource/test_path.rb +6 -0
  99. data/test/scout/resource/test_produce.rb +15 -0
  100. data/test/scout/test_meta_extension.rb +25 -0
  101. data/test/scout/test_named_array.rb +24 -0
  102. data/test/scout/test_persist.rb +9 -2
  103. data/test/scout/test_tsv.rb +229 -2
  104. data/test/scout/test_work_queue.rb +65 -41
  105. data/test/scout/tsv/persist/test_tokyocabinet.rb +29 -1
  106. data/test/scout/tsv/test_attach.rb +227 -0
  107. data/test/scout/tsv/test_change_id.rb +98 -0
  108. data/test/scout/tsv/test_dumper.rb +1 -1
  109. data/test/scout/tsv/test_index.rb +49 -3
  110. data/test/scout/tsv/test_open.rb +160 -2
  111. data/test/scout/tsv/test_parser.rb +33 -2
  112. data/test/scout/tsv/test_persist.rb +2 -0
  113. data/test/scout/tsv/test_stream.rb +200 -0
  114. data/test/scout/tsv/test_transformer.rb +120 -0
  115. data/test/scout/tsv/test_traverse.rb +88 -3
  116. data/test/scout/tsv/test_util.rb +1 -0
  117. data/test/scout/tsv/util/test_reorder.rb +94 -0
  118. data/test/scout/tsv/util/test_select.rb +25 -11
  119. data/test/scout/tsv/util/test_unzip.rb +112 -0
  120. data/test/scout/work_queue/test_socket.rb +0 -1
  121. data/test/scout/workflow/deployment/test_orchestrator.rb +272 -0
  122. data/test/scout/workflow/step/test_dependencies.rb +68 -0
  123. data/test/scout/workflow/step/test_info.rb +18 -0
  124. data/test/scout/workflow/step/test_status.rb +30 -0
  125. data/test/scout/workflow/task/test_dependencies.rb +355 -0
  126. data/test/scout/workflow/task/test_inputs.rb +67 -14
  127. data/test/scout/workflow/test_definition.rb +18 -0
  128. data/test/scout/workflow/test_documentation.rb +24 -0
  129. data/test/scout/workflow/test_step.rb +112 -3
  130. data/test/scout/workflow/test_task.rb +0 -151
  131. data/test/scout/workflow/test_usage.rb +33 -6
  132. data/test/test_scout.rb +9 -0
  133. metadata +100 -8
  134. data/scout_commands/workflow/task_old +0 -706
@@ -1,4 +1,70 @@
1
1
  module TSV
2
+ def self.select(key, values, method, fields: nil, field: nil, invert: false, type: nil, sep: nil, &block)
3
+ return ! select(key, values, method, field: field, invert: false, type: type, sep: sep, &block) if invert
4
+
5
+ return yield(key, values) if method.nil? && block_given
6
+
7
+ if Hash === method
8
+ if method.include?(:invert)
9
+ method = method.dup
10
+ invert = method.delete(:invert)
11
+ return select(key, values, method, fields: fields, field: field, invert: invert, type: type, sep: sep, &block)
12
+ end
13
+ field = method.keys.first
14
+ value = method[field]
15
+ return select(key, values, value, fields: fields, field: field, invert: invert, type: type, sep: sep, &block)
16
+ end
17
+
18
+ if field
19
+ field = fields.index(field) if fields && String === field
20
+ set = field == :key ? [key] : (type == :double ? values[field].split(sep) : values[field])
21
+ else
22
+ set = [key, (type == :double ? values.collect{|v| v.split(sep) } : values)]
23
+ end
24
+
25
+ if Array === set
26
+ set.flatten!
27
+ else
28
+ set = [set]
29
+ end
30
+
31
+ case method
32
+ when Array
33
+ (method & set).any?
34
+ when Regexp
35
+ set.select{|v| v =~ method }.any?
36
+ when Symbol
37
+ set.first.send(method)
38
+ when Numeric
39
+ set.size > method
40
+ when String
41
+ if block_given?
42
+ field = method
43
+ field = fields.index?(field) if fields && String === field
44
+ case
45
+ when block.arity == 1
46
+ if (method == key_field or method == :key)
47
+ yield(key)
48
+ else
49
+ yield(values[method])
50
+ end
51
+ when block.arity == 2
52
+ if (method == key_field or method == :key)
53
+ yield(key, key)
54
+ else
55
+ yield(key, values[method])
56
+ end
57
+ end
58
+ elsif m = method.match(/^([<>]=?)(.*)/)
59
+ set.select{|v| v.to_f.send($1, $2.to_f) }.any?
60
+ else
61
+ set.select{|v| v == method }.any?
62
+ end
63
+ when Proc
64
+ set.select{|v| method.call(v) }.any?
65
+ end
66
+ end
67
+
2
68
  def select(method = nil, invert = false, &block)
3
69
  new = TSV.setup({}, :key_field => key_field, :fields => fields, :type => type, :filename => filename, :identifiers => identifiers)
4
70
 
@@ -33,7 +99,7 @@ module TSV
33
99
  new[key] = values if invert ^ ([key,values].flatten.select{|v| v =~ method}.any?)
34
100
  end
35
101
  end
36
- when (String === method || Symbol === method)
102
+ when ((String === method) || (Symbol === method))
37
103
  if block_given?
38
104
  case
39
105
  when block.arity == 1
@@ -88,11 +154,11 @@ module TSV
88
154
  key = method.keys.first
89
155
  method = method.values.first
90
156
  case
91
- when (Array === method and (key == :key or key_field == key))
157
+ when ((Array === method) and (key == :key or key_field == key))
92
158
  with_unnamed do
93
- Annotated.purge(method).each{|key|
94
- new[key] = self[key] if invert ^ (self.include? key)
95
- }
159
+ keys.each do |key|
160
+ new[key] = self[key] if invert ^ (method.include? key)
161
+ end
96
162
  end
97
163
  when Array === method
98
164
  with_unnamed do
@@ -125,7 +191,7 @@ module TSV
125
191
  end
126
192
  end
127
193
 
128
- when (String === method and method =~ /name:(.*)/)
194
+ when ((String === method) and (method =~ /name:(.*)/))
129
195
  name = $1
130
196
  old_unnamed = self.unnamed
131
197
  self.unnamed = false
@@ -187,34 +253,13 @@ module TSV
187
253
  new
188
254
  end
189
255
 
190
- def reorder(key_field = nil, fields = nil, merge: true, one2one: :fill)
191
- res = self.annotate({})
192
- key_field_name, field_names = through key_field, fields, one2one: one2one do |k,v|
193
- if @type == :double && merge && res.include?(k)
194
- current = res[k]
195
- if merge == :concat
196
- v.each_with_index do |new,i|
197
- next if new.empty?
198
- current[i].concat(new)
199
- end
200
- else
201
- merged = []
202
- v.each_with_index do |new,i|
203
- next if new.empty?
204
- merged[i] = current[i] + new
205
- end
206
- res[k] = merged
207
- end
208
- else
209
- res[k] = v
256
+ def subset(keys)
257
+ new = self.annotate({})
258
+ self.with_unnamed do
259
+ keys.each do |k|
260
+ new[k] = self[k] if self.include?(k)
210
261
  end
211
262
  end
212
- res.key_field = key_field_name
213
- res.fields = field_names
214
- res
215
- end
216
-
217
- def slice(fields)
218
- reorder :key, fields
263
+ new
219
264
  end
220
265
  end
@@ -0,0 +1,86 @@
1
+ module TSV
2
+
3
+ def self.unzip(source, field, target: nil, sep: ":", delete: true, type: :list, merge: false, one2one: true, bar: nil)
4
+ source = TSV::Parser.new source if String === source
5
+
6
+ field_pos = source.identify_field(field)
7
+ new_fields = source.fields.dup
8
+ field_name = new_fields[field_pos]
9
+ new_fields.delete_at(field_pos) if delete
10
+ new_key_field = [source.key_field, field_name] * sep
11
+ type = :double if merge
12
+
13
+ stream = target == :stream
14
+
15
+ target = case target
16
+ when :stream
17
+ TSV::Dumper.new(source.options.merge(sep: "\t"))
18
+ when nil
19
+ TSV.setup({})
20
+ else
21
+ target
22
+ end
23
+
24
+ target.fields = new_fields
25
+ target.key_field = new_key_field
26
+ target.type = type
27
+
28
+ transformer = TSV::Transformer.new source, target, unnamed: true
29
+
30
+ bar = "Unzip #{new_key_field}" if TrueClass === bar
31
+
32
+ transformer.traverse unnamed: true, one2one: one2one, bar: bar do |k,v|
33
+ if source.type == :double
34
+ if one2one
35
+ res = NamedArray.zip_fields(v).collect do |_v|
36
+ field_value = _v[field_pos]
37
+
38
+ if delete
39
+ new_values = _v.dup
40
+ new_values.delete_at field_pos
41
+ else
42
+ new_values = _v
43
+ end
44
+
45
+ new_key = [k,field_value] * sep
46
+ new_values = new_values.collect{|e| [e] } if transformer.type == :double
47
+ [new_key, new_values]
48
+ end
49
+ else
50
+ all_values = v.collect{|e| e.dup }
51
+ all_values.delete_at field_pos if delete
52
+ res = NamedArray.zip_fields(v).collect do |_v|
53
+ field_value = _v[field_pos]
54
+
55
+ new_key = [k,field_value] * sep
56
+ new_values = all_values if transformer.type == :double
57
+ [new_key, new_values]
58
+ end
59
+ end
60
+
61
+ MultipleResult.setup(res)
62
+ else
63
+ field_value = v[field_pos]
64
+
65
+ if delete
66
+ new_values = v.dup
67
+ new_values.delete_at field_pos
68
+ else
69
+ new_values = v
70
+ end
71
+
72
+ new_key = [k,field_value] * sep
73
+
74
+ new_values = new_values.collect{|e| [e] } if transformer.type == :double
75
+
76
+ [new_key, new_values]
77
+ end
78
+ end
79
+
80
+ stream ? transformer : transformer.tsv(merge: merge)
81
+ end
82
+
83
+ def unzip(*args, **kwargs)
84
+ TSV.unzip(self, *args, **kwargs)
85
+ end
86
+ end
@@ -1,21 +1,57 @@
1
1
  #require_relative '../../../modules/rbbt-util/lib/rbbt/tsv/manipulate'
2
2
  #Log.warn "USING OLD RBBT CODE: #{__FILE__}"
3
3
  require_relative 'traverse'
4
+ require_relative 'util/filter'
4
5
  require_relative 'util/process'
5
6
  require_relative 'util/select'
7
+ require_relative 'util/reorder'
8
+ require_relative 'util/unzip'
6
9
  module TSV
7
- def [](*args)
8
- v = super(*args)
9
- NamedArray.setup(v, @fields) unless @unnamed || ! (Array === v)
10
+ def self.identify_field(key_field, fields, name, strict: nil)
11
+ return :key if name == :key || (! strict && NamedArray.field_match(key_field, name))
12
+ name.collect!{|n| key_field == n ? :key : n } if Array === name
13
+ NamedArray.identify_name(fields, name, strict: strict)
14
+ end
15
+
16
+ def identify_field(name, strict: nil)
17
+ TSV.identify_field(@key_field, @fields, name, strict: strict)
18
+ end
19
+
20
+ def [](key, *rest)
21
+ v = super(key, *rest)
22
+ NamedArray.setup(v, @fields, key) unless @unnamed || ! (Array === v)
10
23
  v
11
24
  end
12
25
 
26
+ def options
27
+ extension_attr_hash
28
+ end
29
+
30
+ def zip_new(key, values, insitu: :lax)
31
+ values = values.collect{|v| Array === v ? v : [v] } unless Array === values.first
32
+ if current_values = self[key]
33
+ if insitu == :lax
34
+ self[key] = NamedArray.add_zipped(current_values, values)
35
+ elsif insitu
36
+ NamedArray.add_zipped(current_values, values)
37
+ else
38
+ self[key] = NamedArray.add_zipped(current_values.dup, values)
39
+ end
40
+ else
41
+ if insitu && insitu != :lax
42
+ self[key] = values.dup
43
+ else
44
+ self[key] = values
45
+ end
46
+ end
47
+ end
48
+
13
49
  def each(*args, &block)
14
50
  if block_given?
15
51
  super(*args) do |k,v|
16
- NamedArray.setup(v, @fields) unless @unnamed || ! (Array === v)
17
- block.call(k, v)
18
- end
52
+ NamedArray.setup(v, @fields) unless @unnamed || ! (Array === v)
53
+ block.call(k, v)
54
+ end
19
55
  else
20
56
  super(*args)
21
57
  end
@@ -33,13 +69,13 @@ module TSV
33
69
  end
34
70
  end
35
71
 
36
- def with_unnamed
72
+ def with_unnamed(unnamed = true)
37
73
  begin
38
- old_unnamed = unnamed
39
- unnamed = true
74
+ old_unnamed = @unnamed
75
+ @unnamed = unnamed
40
76
  yield
41
77
  ensure
42
- unnamed = old_unnamed
78
+ @unnamed = old_unnamed
43
79
  end
44
80
  end
45
81
 
@@ -53,7 +89,7 @@ module TSV
53
89
  end
54
90
 
55
91
  filename = @filename
56
- filename = "No filename" if filename.nil? || filename.empty?
92
+ filename = "No filename" if filename.nil? || String === filename && filename.empty?
57
93
  filename.find if Path === filename
58
94
  filename = File.basename(filename) + " [" + File.basename(persistence_path) + "]" if respond_to?(:persistence_path) and persistence_path
59
95
 
@@ -73,10 +109,23 @@ Example:
73
109
  end
74
110
 
75
111
  def all_fields
112
+ return [] if @fields.nil?
76
113
  [@key_field] + @fields
77
114
  end
78
115
 
116
+ def options
117
+ self.extension_attr_hash
118
+ end
119
+
79
120
  def fingerprint
80
121
  "TSV:{"<< Log.fingerprint(self.all_fields|| []) << ";" << Log.fingerprint(self.keys) << "}"
81
122
  end
123
+
124
+ def digest_str
125
+ fingerprint
126
+ end
127
+
128
+ def inspect
129
+ fingerprint
130
+ end
82
131
  end
data/lib/scout/tsv.rb CHANGED
@@ -2,23 +2,53 @@ require_relative 'meta_extension'
2
2
  require_relative 'tsv/util'
3
3
  require_relative 'tsv/parser'
4
4
  require_relative 'tsv/dumper'
5
+ require_relative 'tsv/transformer'
5
6
  require_relative 'tsv/persist'
6
7
  require_relative 'tsv/index'
7
8
  require_relative 'tsv/path'
8
9
  require_relative 'tsv/traverse'
9
10
  require_relative 'tsv/open'
11
+ require_relative 'tsv/attach'
12
+ require_relative 'tsv/change_id'
13
+ require_relative 'tsv/stream'
10
14
 
11
15
  module TSV
12
16
  extend MetaExtension
13
- extension_attr :key_field, :fields, :type, :filename, :namespace, :unnamed, :identifiers
17
+ extension_attr :key_field, :fields, :type, :cast, :filename, :namespace, :unnamed, :identifiers
18
+
19
+ def self.str2options(str)
20
+ field_options,_sep, rest = str.partition("#")
21
+ key, fields_str = field_options.split("~")
22
+
23
+ fields = fields_str.nil? ? [] : fields_str.split(/,\s*/)
24
+
25
+ rest = ":type=" << rest if rest =~ /^:?\w+$/
26
+ rest_options = rest.nil? ? {} : IndiferentHash.string2hash(rest)
27
+
28
+ {:key_field => key, :fields => fields}.merge(rest_options)
29
+ end
30
+
31
+ def self.str_setup(option_str, obj)
32
+ options = TSV.str2options(option_str)
33
+ setup(obj, options)
34
+ end
14
35
 
15
36
  def self.open(file, options = {})
16
- persist, type = IndiferentHash.process_options options, :persist, :persist_type, :persist => false, :persist_type => "HDB"
17
- Persist.persist(file, type, options.merge(:persist => persist)) do |filename|
37
+ persist, type, grep, invert_grep = IndiferentHash.process_options options, :persist, :persist_type, :grep, :invert_grep, :persist => false, :persist_type => "HDB"
38
+ type = type.to_sym if type
39
+ file = StringIO.new file if String === file && ! (Path === file) && file.index("\n")
40
+ Persist.persist(file, type, options.merge(:persist => persist, :prefix => "Tsv", :other_options => options)) do |filename|
18
41
  data = filename ? ScoutCabinet.open(filename, true, type) : nil
19
42
  options[:data] = data if data
20
43
  options[:filename] = file
21
- Open.open(file) do |f|
44
+
45
+ if data
46
+ Log.debug "TSV open #{Log.fingerprint file} into #{Log.fingerprint data}"
47
+ else
48
+ Log.debug "TSV open #{Log.fingerprint file}"
49
+ end
50
+
51
+ Open.open(file, grep: grep, invert_grep: invert_grep) do |f|
22
52
  TSV.parse(f, **options)
23
53
  end
24
54
  end
@@ -3,7 +3,7 @@ require 'scout/semaphore'
3
3
  require 'scout/exceptions'
4
4
  class WorkQueue
5
5
  class Socket
6
- attr_accessor :sread, :swrite, :write_sem, :read_sem, :cleaned
6
+ attr_accessor :sread, :swrite, :write_sem, :read_sem, :cleaned, :exception
7
7
  def initialize(serializer = nil)
8
8
  @sread, @swrite = Open.pipe
9
9
 
@@ -112,6 +112,11 @@ class WorkQueue
112
112
  end
113
113
  end
114
114
 
115
+ def abort(exception)
116
+ @exception = exception
117
+ @swrite.close unless closed_write?
118
+ end
119
+
115
120
  alias write push
116
121
 
117
122
  alias read pop
@@ -27,8 +27,10 @@ class WorkQueue
27
27
  rescue Interrupt
28
28
  rescue Exception
29
29
  output.write WorkerException.new($!, Process.pid)
30
- Kernel.exit -1
30
+ exit -1
31
+ ensure
31
32
  end
33
+ exit 0
32
34
  end
33
35
  end
34
36
 
@@ -36,7 +38,8 @@ class WorkQueue
36
38
  begin
37
39
  Log.debug "Aborting worker #{@pid}"
38
40
  Process.kill "INT", @pid
39
- rescue Errno::ECHILD
41
+ rescue Errno::ECHILD
42
+ rescue Errno::ESRCH
40
43
  end
41
44
  end
42
45
 
@@ -1,5 +1,6 @@
1
1
  require_relative 'work_queue/socket'
2
2
  require_relative 'work_queue/worker'
3
+ require 'timeout'
3
4
 
4
5
  class WorkQueue
5
6
  attr_accessor :workers, :worker_proc, :callback
@@ -38,7 +39,7 @@ class WorkQueue
38
39
  @worker_mutex.synchronize do
39
40
  worker = @workers.index{|w| w.pid == pid}
40
41
  if worker
41
- Log.debug "Removed worker #{pid}"
42
+ Log.low "Removed worker #{pid}"
42
43
  @workers.delete_at(worker)
43
44
  @removed_workers << pid
44
45
  end
@@ -46,7 +47,11 @@ class WorkQueue
46
47
  end
47
48
 
48
49
  def process(&callback)
49
- @reader = Thread.new do |parent|
50
+ @workers.each do |w|
51
+ w.process @input, @output, &@worker_proc
52
+ end
53
+
54
+ @reader = Thread.new(Thread.current) do |parent|
50
55
  begin
51
56
  Thread.current.report_on_exception = false
52
57
  Thread.current["name"] = "Output reader #{Process.pid}"
@@ -71,8 +76,9 @@ class WorkQueue
71
76
  rescue DoneProcessing
72
77
  rescue Aborted
73
78
  rescue WorkerException
74
- Log.error "Exception in worker #{obj.pid} in queue #{Process.pid}: #{obj.message}"
79
+ Log.error "Exception in worker #{obj.pid} in queue #{Process.pid}: #{obj.worker_exception.message}"
75
80
  self.abort
81
+ @input.abort obj.worker_exception
76
82
  raise obj.worker_exception
77
83
  rescue
78
84
  Log.error "Exception processing output in queue #{Process.pid}: #{$!.message}"
@@ -81,30 +87,45 @@ class WorkQueue
81
87
  end
82
88
  end
83
89
 
84
- @workers.each do |w|
85
- w.process @input, @output, &@worker_proc
86
- end
87
-
88
90
  Thread.pass until @reader["name"]
89
91
 
92
+ Thread.pass until @worker_mutex.synchronize{ @workers.select{|w| w.pid.nil? }.empty? }
93
+
90
94
  @waiter = Thread.new do
91
- begin
92
- Thread.current.report_on_exception = false
93
- Thread.current["name"] = "Worker waiter #{Process.pid}"
94
- while true
95
- pid = Process.wait
96
- remove_worker(pid)
97
- break if @worker_mutex.synchronize{ @workers.empty? }
95
+ Thread.current.report_on_exception = false
96
+ Thread.current["name"] = "Worker waiter #{Process.pid}"
97
+ while true
98
+ break if @worker_mutex.synchronize{ @workers.empty? }
99
+ begin
100
+ Timeout.timeout(1) do
101
+ begin
102
+ pid, status = Process.wait2
103
+ remove_worker(pid) if pid
104
+ rescue Exception
105
+ Log.exception $!
106
+ end
107
+ end
108
+ rescue Timeout::Error
109
+ pids = @worker_mutex.synchronize{ @workers.collect{|w| w.pid } }
110
+ pids.each do |p|
111
+ pid, status = Process.wait2 p, Process::WNOHANG
112
+ remove_worker(pid) if pid
113
+ end
98
114
  end
99
115
  end
100
116
  end
101
117
 
102
- Thread.pass until @worker_mutex.synchronize{ @workers.select{|w| w.pid.nil? }.empty? }
103
118
  Thread.pass until @waiter["name"]
104
119
  end
105
120
 
106
121
  def write(obj)
107
- @input.write obj
122
+ begin
123
+ @input.write obj
124
+ rescue Exception
125
+ raise $! unless @input.exception
126
+ ensure
127
+ raise @input.exception if @input.exception
128
+ end
108
129
  end
109
130
 
110
131
  def abort
@@ -117,12 +138,22 @@ class WorkQueue
117
138
  def close
118
139
  @closed = true
119
140
  @worker_mutex.synchronize{ @workers.length }.times do
120
- @input.write DoneProcessing.new()
141
+ @input.write DoneProcessing.new() unless @input.closed_write?
121
142
  end
122
143
  end
123
144
 
124
- def join
125
- @waiter.join if @waiter
126
- @reader.join if @reader
145
+ def clean
146
+ @waiter.join if @waiter
147
+ @input.clean
148
+ @output.clean
149
+ end
150
+
151
+ def join(clean = true)
152
+ begin
153
+ @waiter.join if @waiter
154
+ @reader.join if @reader
155
+ ensure
156
+ self.clean if clean
157
+ end
127
158
  end
128
159
  end
@@ -50,7 +50,7 @@ module Workflow
50
50
 
51
51
  def directory=(directory)
52
52
  @directory = directory
53
- @tasks.each{|name,d| d.directory = directory[name] } if @tasks
53
+ @tasks.each{|name,d| d.directory = Path === directory ? directory[name] : File.join(directory, name.to_s) } if @tasks
54
54
  end
55
55
 
56
56
  def annotate_next_task(type, obj)
@@ -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
 
@@ -101,8 +103,23 @@ module Workflow
101
103
  def task(name_and_type, &block)
102
104
  name, type = name_and_type.collect.first
103
105
  @tasks ||= IndiferentHash.setup({})
106
+ block = self.method(name) if block.nil?
104
107
  begin
105
108
  @annotate_next_task ||= {}
109
+ @annotate_next_task[:extension] ||=
110
+ case type
111
+ when :tsv
112
+ "tsv"
113
+ when :yaml
114
+ "yaml"
115
+ when :marshal
116
+ "marshal"
117
+ when :json
118
+ "json"
119
+ else
120
+ nil
121
+ end
122
+
106
123
  task = Task.setup(block, @annotate_next_task.merge(name: name, type: type, directory: directory[name], workflow: self))
107
124
  @tasks[name] = task
108
125
  ensure
@@ -110,11 +127,14 @@ module Workflow
110
127
  end
111
128
  end
112
129
 
130
+ FORGET_DEP_TASKS = ENV["SCOUT_FORGET_DEP_TASKS"] == "true"
131
+ REMOVE_DEP_TASKS = ENV["SCOUT_REMOVE_DEP_TASKS"] == "true"
113
132
  def task_alias(name, workflow, oname, *rest, &block)
114
133
  dep(workflow, oname, *rest, &block)
115
134
  extension :dep_task unless @extension
116
- returns workflow.tasks[oname].returns if workflow.tasks.include?(oname) unless @returns
117
- task name => nil do
135
+ returns workflow.tasks[oname].returns if @returns.nil?
136
+ type = workflow.tasks[oname].type
137
+ task name => type do
118
138
  raise RbbtException, "dep_task does not have any dependencies" if dependencies.empty?
119
139
  Step.wait_for_jobs dependencies.select{|d| d.streaming? }
120
140
  dep = dependencies.last