rbbt-util 5.44.1 → 6.0.4

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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/bin/rbbt +67 -90
  4. data/bin/rbbt_exec.rb +2 -2
  5. data/etc/app.d/base.rb +2 -2
  6. data/etc/app.d/semaphores.rb +3 -3
  7. data/lib/rbbt/annotations/annotated_array.rb +207 -207
  8. data/lib/rbbt/annotations/refactor.rb +27 -0
  9. data/lib/rbbt/annotations/util.rb +282 -282
  10. data/lib/rbbt/annotations.rb +343 -320
  11. data/lib/rbbt/association/database.rb +200 -225
  12. data/lib/rbbt/association/index.rb +294 -291
  13. data/lib/rbbt/association/item.rb +227 -227
  14. data/lib/rbbt/association/open.rb +35 -34
  15. data/lib/rbbt/association/util.rb +0 -169
  16. data/lib/rbbt/association.rb +2 -4
  17. data/lib/rbbt/entity/identifiers.rb +119 -118
  18. data/lib/rbbt/entity/refactor.rb +12 -0
  19. data/lib/rbbt/entity.rb +319 -315
  20. data/lib/rbbt/hpc/batch.rb +72 -53
  21. data/lib/rbbt/hpc/lsf.rb +2 -2
  22. data/lib/rbbt/hpc/orchestrate/batches.rb +2 -2
  23. data/lib/rbbt/hpc/orchestrate/chains.rb +25 -5
  24. data/lib/rbbt/hpc/orchestrate/rules.rb +2 -2
  25. data/lib/rbbt/hpc/orchestrate.rb +19 -13
  26. data/lib/rbbt/hpc/slurm.rb +18 -18
  27. data/lib/rbbt/knowledge_base/entity.rb +13 -5
  28. data/lib/rbbt/knowledge_base/query.rb +2 -2
  29. data/lib/rbbt/knowledge_base/registry.rb +32 -31
  30. data/lib/rbbt/knowledge_base/traverse.rb +1 -1
  31. data/lib/rbbt/knowledge_base.rb +1 -1
  32. data/lib/rbbt/monitor.rb +36 -25
  33. data/lib/rbbt/persist/refactor.rb +166 -0
  34. data/lib/rbbt/persist/tsv/tokyocabinet.rb +105 -105
  35. data/lib/rbbt/persist/tsv.rb +187 -185
  36. data/lib/rbbt/persist.rb +556 -551
  37. data/lib/rbbt/refactor.rb +20 -0
  38. data/lib/rbbt/resource/path/refactor.rb +178 -0
  39. data/lib/rbbt/resource/path.rb +317 -497
  40. data/lib/rbbt/resource/util.rb +0 -48
  41. data/lib/rbbt/resource.rb +3 -390
  42. data/lib/rbbt/tsv/accessor.rb +2 -838
  43. data/lib/rbbt/tsv/attach.rb +303 -299
  44. data/lib/rbbt/tsv/change_id.rb +244 -245
  45. data/lib/rbbt/tsv/csv.rb +87 -85
  46. data/lib/rbbt/tsv/dumper.rb +2 -100
  47. data/lib/rbbt/tsv/excel.rb +26 -24
  48. data/lib/rbbt/tsv/field_index.rb +4 -1
  49. data/lib/rbbt/tsv/filter.rb +3 -2
  50. data/lib/rbbt/tsv/index.rb +2 -284
  51. data/lib/rbbt/tsv/manipulate.rb +750 -747
  52. data/lib/rbbt/tsv/marshal.rb +3 -3
  53. data/lib/rbbt/tsv/matrix.rb +2 -2
  54. data/lib/rbbt/tsv/parallel/through.rb +2 -1
  55. data/lib/rbbt/tsv/parallel/traverse.rb +783 -781
  56. data/lib/rbbt/tsv/parser.rb +678 -678
  57. data/lib/rbbt/tsv/refactor.rb +195 -0
  58. data/lib/rbbt/tsv/stream.rb +253 -251
  59. data/lib/rbbt/tsv/util.rb +420 -420
  60. data/lib/rbbt/tsv.rb +210 -208
  61. data/lib/rbbt/util/R/eval.rb +4 -4
  62. data/lib/rbbt/util/R/plot.rb +62 -166
  63. data/lib/rbbt/util/R.rb +21 -18
  64. data/lib/rbbt/util/cmd.rb +2 -318
  65. data/lib/rbbt/util/color.rb +269 -269
  66. data/lib/rbbt/util/colorize.rb +89 -89
  67. data/lib/rbbt/util/concurrency/processes/refactor.rb +22 -0
  68. data/lib/rbbt/util/concurrency/processes/worker.rb +2 -2
  69. data/lib/rbbt/util/concurrency/processes.rb +389 -386
  70. data/lib/rbbt/util/config.rb +169 -167
  71. data/lib/rbbt/util/filecache.rb +1 -1
  72. data/lib/rbbt/util/iruby.rb +20 -0
  73. data/lib/rbbt/util/log/progress/report.rb +241 -241
  74. data/lib/rbbt/util/log/progress/util.rb +99 -99
  75. data/lib/rbbt/util/log/progress.rb +102 -102
  76. data/lib/rbbt/util/log/refactor.rb +49 -0
  77. data/lib/rbbt/util/log.rb +486 -532
  78. data/lib/rbbt/util/migrate.rb +2 -2
  79. data/lib/rbbt/util/misc/concurrent_stream.rb +248 -246
  80. data/lib/rbbt/util/misc/development.rb +12 -11
  81. data/lib/rbbt/util/misc/exceptions.rb +117 -112
  82. data/lib/rbbt/util/misc/format.rb +2 -230
  83. data/lib/rbbt/util/misc/indiferent_hash.rb +2 -107
  84. data/lib/rbbt/util/misc/inspect.rb +2 -476
  85. data/lib/rbbt/util/misc/lock.rb +109 -106
  86. data/lib/rbbt/util/misc/omics.rb +9 -1
  87. data/lib/rbbt/util/misc/pipes.rb +765 -793
  88. data/lib/rbbt/util/misc/refactor.rb +20 -0
  89. data/lib/rbbt/util/misc/ssw.rb +27 -17
  90. data/lib/rbbt/util/misc/system.rb +92 -105
  91. data/lib/rbbt/util/misc.rb +39 -20
  92. data/lib/rbbt/util/named_array/refactor.rb +4 -0
  93. data/lib/rbbt/util/named_array.rb +3 -220
  94. data/lib/rbbt/util/open/refactor.rb +7 -0
  95. data/lib/rbbt/util/open.rb +3 -857
  96. data/lib/rbbt/util/procpath.rb +6 -6
  97. data/lib/rbbt/util/python/paths.rb +27 -0
  98. data/lib/rbbt/util/python/run.rb +115 -0
  99. data/lib/rbbt/util/python/script.rb +110 -0
  100. data/lib/rbbt/util/python/util.rb +3 -3
  101. data/lib/rbbt/util/python.rb +22 -81
  102. data/lib/rbbt/util/semaphore.rb +152 -148
  103. data/lib/rbbt/util/simpleopt.rb +9 -8
  104. data/lib/rbbt/util/ssh/refactor.rb +19 -0
  105. data/lib/rbbt/util/ssh.rb +122 -118
  106. data/lib/rbbt/util/tar.rb +117 -115
  107. data/lib/rbbt/util/tmpfile.rb +69 -67
  108. data/lib/rbbt/util/version.rb +2 -0
  109. data/lib/rbbt/workflow/refactor/entity.rb +11 -0
  110. data/lib/rbbt/workflow/refactor/export.rb +66 -0
  111. data/lib/rbbt/workflow/refactor/inputs.rb +24 -0
  112. data/lib/rbbt/workflow/refactor/recursive.rb +64 -0
  113. data/lib/rbbt/workflow/refactor/task_info.rb +66 -0
  114. data/lib/rbbt/workflow/refactor.rb +150 -0
  115. data/lib/rbbt/workflow/remote_workflow/driver/rest.rb +1 -2
  116. data/lib/rbbt/workflow/remote_workflow/driver/ssh.rb +55 -32
  117. data/lib/rbbt/workflow/remote_workflow/remote_step/rest.rb +3 -1
  118. data/lib/rbbt/workflow/remote_workflow/remote_step/ssh.rb +14 -5
  119. data/lib/rbbt/workflow/remote_workflow/remote_step.rb +19 -7
  120. data/lib/rbbt/workflow/remote_workflow.rb +6 -1
  121. data/lib/rbbt/workflow/step/run.rb +766 -766
  122. data/lib/rbbt/workflow/step/save_load_inputs.rb +254 -254
  123. data/lib/rbbt/workflow/step.rb +2 -362
  124. data/lib/rbbt/workflow/task.rb +118 -118
  125. data/lib/rbbt/workflow/usage.rb +289 -287
  126. data/lib/rbbt/workflow/util/archive.rb +6 -5
  127. data/lib/rbbt/workflow/util/data.rb +1 -1
  128. data/lib/rbbt/workflow/util/orchestrator.rb +249 -246
  129. data/lib/rbbt/workflow/util/trace.rb +79 -44
  130. data/lib/rbbt/workflow.rb +4 -882
  131. data/lib/rbbt-util.rb +21 -13
  132. data/lib/rbbt.rb +16 -3
  133. data/python/rbbt/__init__.py +96 -4
  134. data/python/rbbt/workflow/remote.py +104 -0
  135. data/python/rbbt/workflow.py +64 -0
  136. data/python/test.py +10 -0
  137. data/share/Rlib/plot.R +37 -37
  138. data/share/Rlib/svg.R +22 -5
  139. data/share/install/software/lib/install_helpers +1 -1
  140. data/share/rbbt_commands/hpc/list +2 -3
  141. data/share/rbbt_commands/hpc/orchestrate +4 -4
  142. data/share/rbbt_commands/hpc/tail +2 -0
  143. data/share/rbbt_commands/hpc/task +10 -7
  144. data/share/rbbt_commands/lsf/list +2 -3
  145. data/share/rbbt_commands/lsf/orchestrate +4 -4
  146. data/share/rbbt_commands/lsf/tail +2 -0
  147. data/share/rbbt_commands/lsf/task +10 -7
  148. data/share/rbbt_commands/migrate +1 -1
  149. data/share/rbbt_commands/pbs/list +2 -3
  150. data/share/rbbt_commands/pbs/orchestrate +4 -4
  151. data/share/rbbt_commands/pbs/tail +2 -0
  152. data/share/rbbt_commands/pbs/task +10 -7
  153. data/share/rbbt_commands/resource/produce +8 -1
  154. data/share/rbbt_commands/slurm/list +2 -3
  155. data/share/rbbt_commands/slurm/orchestrate +4 -4
  156. data/share/rbbt_commands/slurm/tail +2 -0
  157. data/share/rbbt_commands/slurm/task +10 -7
  158. data/share/rbbt_commands/system/clean +5 -5
  159. data/share/rbbt_commands/system/status +5 -5
  160. data/share/rbbt_commands/tsv/get +2 -3
  161. data/share/rbbt_commands/tsv/info +10 -13
  162. data/share/rbbt_commands/tsv/keys +18 -14
  163. data/share/rbbt_commands/tsv/slice +2 -2
  164. data/share/rbbt_commands/tsv/transpose +6 -2
  165. data/share/rbbt_commands/workflow/info +20 -24
  166. data/share/rbbt_commands/workflow/list +1 -1
  167. data/share/rbbt_commands/workflow/prov +20 -13
  168. data/share/rbbt_commands/workflow/retry +43 -0
  169. data/share/rbbt_commands/workflow/server +12 -2
  170. data/share/rbbt_commands/workflow/task +80 -73
  171. data/share/rbbt_commands/workflow/write_info +26 -9
  172. data/share/software/opt/ssw/ssw.c +861 -0
  173. data/share/software/opt/ssw/ssw.h +130 -0
  174. data/share/workflow_config.ru +3 -3
  175. metadata +45 -6
@@ -1,793 +1,765 @@
1
- require 'rbbt'
2
-
3
- module Misc
4
- class << self
5
- attr_accessor :sensiblewrite_lock_dir
6
-
7
- def sensiblewrite_lock_dir
8
- @sensiblewrite_lock_dir ||= Rbbt.tmp.sensiblewrite_locks.find
9
- end
10
- end
11
-
12
- class << self
13
- attr_accessor :sensiblewrite_dir
14
- def sensiblewrite_dir
15
- @sensiblewrite_dir = Rbbt.tmp.sensiblewrite.find
16
- end
17
- end
18
-
19
- BLOCK_SIZE=1024 * 8
20
-
21
- SKIP_TAG="[SKIP TAG]"
22
-
23
- PIPE_MUTEX = Mutex.new
24
-
25
- OPEN_PIPE_IN = []
26
- def self.pipe
27
- OPEN_PIPE_IN.delete_if{|pipe| pipe.closed? }
28
- res = PIPE_MUTEX.synchronize do
29
- sout, sin = IO.pipe
30
- OPEN_PIPE_IN << sin
31
-
32
- [sout, sin]
33
- end
34
- Log.debug{"Creating pipe #{[res.last.inspect, res.first.inspect] * " => "}"}
35
- res
36
- end
37
-
38
- def self.with_fifo(path = nil, clean = true, &block)
39
- begin
40
- erase = path.nil?
41
- path = TmpFile.tmp_file if path.nil?
42
- File.rm path if clean && File.exist?(path)
43
- File.mkfifo path
44
- yield path
45
- ensure
46
- FileUtils.rm path if erase && File.exist?(path)
47
- end
48
- end
49
-
50
- def self.release_pipes(*pipes)
51
- PIPE_MUTEX.synchronize do
52
- pipes.flatten.each do |pipe|
53
- pipe.close unless pipe.closed?
54
- end
55
- end
56
- end
57
-
58
- def self.purge_pipes(*save)
59
- PIPE_MUTEX.synchronize do
60
- OPEN_PIPE_IN.each do |pipe|
61
- next if save.include? pipe
62
- pipe.close unless pipe.closed?
63
- end
64
- end
65
- end
66
-
67
- def self.open_pipe(do_fork = false, close = true)
68
- raise "No block given" unless block_given?
69
-
70
- sout, sin = Misc.pipe
71
-
72
- if do_fork
73
-
74
- #parent_pid = Process.pid
75
- pid = Process.fork {
76
- purge_pipes(sin)
77
- sout.close
78
- begin
79
-
80
- yield sin
81
- sin.close if close and not sin.closed?
82
-
83
- rescue Exception
84
- Log.exception $!
85
- #Process.kill :INT, parent_pid
86
- Kernel.exit! -1
87
- end
88
- Kernel.exit! 0
89
- }
90
- sin.close
91
-
92
- ConcurrentStream.setup sout, :pids => [pid]
93
- else
94
-
95
- ConcurrentStream.setup sin, :pair => sout
96
- ConcurrentStream.setup sout, :pair => sin
97
-
98
- thread = Thread.new do
99
- begin
100
-
101
- yield sin
102
-
103
- sin.close if close and not sin.closed? and not sin.aborted?
104
-
105
- rescue Aborted
106
- Log.medium "Aborted open_pipe: #{$!.message}"
107
- raise $!
108
- rescue Exception
109
- Log.medium "Exception in open_pipe: #{$!.message}"
110
- Log.exception $!
111
- begin
112
- sin.raise($!) if sin.respond_to? :raise
113
- sin.join if sin.respond_to? :join
114
- ensure
115
- raise $!
116
- end
117
- end
118
- end
119
-
120
- sin.threads = [thread]
121
- sout.threads = [thread]
122
- end
123
-
124
- sout
125
- end
126
-
127
- #def self.tee_stream_thread(stream)
128
- # stream_out1, stream_in1 = Misc.pipe
129
- # stream_out2, stream_in2 = Misc.pipe
130
-
131
- # splitter_thread = Thread.new(Thread.current) do |parent|
132
- # begin
133
-
134
- # skip1 = skip2 = false
135
- # while block = stream.read(1024)
136
-
137
- # begin
138
- # stream_in1.write block;
139
- # rescue IOError
140
- # Log.medium("Tee stream 1 #{Misc.fingerprint stream} IOError: #{$!.message}");
141
- # skip1 = true
142
- # end unless skip1
143
-
144
- # begin
145
- # stream_in2.write block
146
- # rescue IOError
147
- # Log.medium("Tee stream 2 #{Misc.fingerprint stream} IOError: #{$!.message}");
148
- # skip2 = true
149
- # end unless skip2
150
-
151
- # end
152
-
153
- # stream_in1.close unless stream_in1.closed?
154
- # stream.join if stream.respond_to? :join
155
- # stream_in2.close unless stream_in2.closed?
156
- # rescue Aborted, Interrupt
157
- # stream_out1.abort if stream_out1.respond_to? :abort
158
- # stream.abort if stream.respond_to? :abort
159
- # stream_out2.abort if stream_out2.respond_to? :abort
160
- # Log.medium "Tee aborting #{Misc.fingerprint stream}"
161
- # raise $!
162
- # rescue Exception
163
- # stream_out1.abort if stream_out1.respond_to? :abort
164
- # stream.abort if stream.respond_to? :abort
165
- # stream_out2.abort if stream_out2.respond_to? :abort
166
- # Log.medium "Tee exception #{Misc.fingerprint stream}"
167
- # raise $!
168
- # end
169
- # end
170
-
171
- # ConcurrentStream.setup stream_out1, :threads => splitter_thread
172
- # ConcurrentStream.setup stream_out2, :threads => splitter_thread
173
-
174
- # [stream_out1, stream_out2]
175
- #end
176
-
177
- def self.tee_stream_thread_multiple(stream, num = 2)
178
- in_pipes = []
179
- out_pipes = []
180
- num.times do
181
- sout, sin = Misc.pipe
182
- in_pipes << sin
183
- out_pipes << sout
184
- end
185
-
186
- filename = stream.filename if stream.respond_to? :filename
187
-
188
- splitter_thread = Thread.new(Thread.current) do |parent|
189
- begin
190
-
191
- skip = [false] * num
192
- begin
193
- while block = stream.readpartial(BLOCK_SIZE)
194
-
195
- in_pipes.each_with_index do |sin,i|
196
- begin
197
- sin.write block
198
- rescue IOError
199
- Log.error("Tee stream #{i} #{Misc.fingerprint stream} IOError: #{$!.message} (#{Misc.fingerprint sin})");
200
- skip[i] = true
201
- rescue
202
- Log.error("Tee stream #{i} #{Misc.fingerprint stream} Exception: #{$!.message} (#{Misc.fingerprint sin})");
203
- raise $!
204
- end unless skip[i]
205
- end
206
- end
207
- rescue IOError
208
- end
209
-
210
- stream.close unless stream.closed?
211
- #stream.join if stream.respond_to? :join
212
- in_pipes.first.close unless in_pipes.first.closed?
213
- #Log.medium "Tee done #{Misc.fingerprint stream}"
214
- rescue Aborted, Interrupt
215
- stream.abort if stream.respond_to? :abort
216
- out_pipes.each do |sout|
217
- sout.abort if sout.respond_to? :abort
218
- end
219
- Log.medium "Tee aborting #{Misc.fingerprint stream}"
220
- raise $!
221
- rescue Exception
222
- stream.abort($!) if stream.respond_to? :abort
223
- out_pipes.each do |sout|
224
- sout.abort if sout.respond_to? :abort
225
- end
226
- Log.medium "Tee exception #{Misc.fingerprint stream}"
227
- raise $!
228
- end
229
- end
230
-
231
- out_pipes.each do |sout|
232
- ConcurrentStream.setup sout, :threads => splitter_thread, :filename => filename, :_pair => stream
233
- end
234
-
235
- main_pipe = out_pipes.first
236
- main_pipe.autojoin = true
237
-
238
- main_pipe.callback = Proc.new do
239
- stream.join if stream.respond_to? :join
240
- in_pipes[1..-1].each do |sin|
241
- sin.close unless sin.closed?
242
- end
243
- end
244
-
245
- out_pipes
246
- end
247
-
248
- def self.tee_stream_thread(stream)
249
- tee_stream_thread_multiple(stream, 2)
250
- end
251
-
252
- def self.dup_stream_multiple(stream, num = 1)
253
- stream_dup = stream.dup
254
- if stream.respond_to? :annotate
255
- stream.annotate stream_dup
256
- stream.clear
257
- end
258
- tee1, *rest = Misc.tee_stream stream_dup, num + 1
259
- stream.reopen(tee1)
260
-
261
- #ToDo: I can't explain why the @threads variable appears with the value of
262
- # @filename
263
- stream.instance_variable_set(:@threads, nil) if stream.instance_variables.include?(:@threads)
264
-
265
- tee1.annotate(stream)
266
- rest
267
- end
268
-
269
- def self.dup_stream(stream)
270
- dup_stream_multiple(stream, 1).first
271
- end
272
-
273
- class << self
274
- alias tee_stream tee_stream_thread_multiple
275
- end
276
-
277
- def self.read_full_stream(io)
278
- str = ""
279
- begin
280
- while block = io.read(BLOCK_SIZE)
281
- str << block
282
- end
283
- io.join if io.respond_to? :join
284
- rescue
285
- io.abort if io.respond_to? :abort
286
- end
287
- str
288
- end
289
-
290
- def self.consume_stream(io, in_thread = false, into = nil, into_close = true, &block)
291
- return if Path === io
292
- return unless io.respond_to? :read
293
-
294
- if io.respond_to? :closed? and io.closed?
295
- io.join if io.respond_to? :join
296
- return
297
- end
298
-
299
- if in_thread
300
- Thread.new(Thread.current) do |parent|
301
- begin
302
- consume_stream(io, false, into, into_close)
303
- rescue Exception
304
- parent.raise $!
305
- end
306
- end
307
- else
308
- if into
309
- Log.medium "Consuming stream #{Misc.fingerprint io} -> #{Misc.fingerprint into}"
310
- else
311
- Log.medium "Consuming stream #{Misc.fingerprint io}"
312
- end
313
-
314
- begin
315
- into = into.find if Path === into
316
- if String === into
317
- dir = File.dirname(into)
318
- Open.mkdir dir unless Open.exists?(dir)
319
- into_path, into = into, Open.open(into, :mode => 'w')
320
- end
321
- #into.sync = true if IO === into
322
- into_close = false unless into.respond_to? :close
323
- #io.sync = true
324
-
325
- begin
326
- while c = io.readpartial(BLOCK_SIZE)
327
- into << c if into
328
- end
329
- rescue EOFError
330
- end
331
-
332
- io.join if io.respond_to? :join
333
- io.close unless io.closed?
334
- into.close if into and into_close and not into.closed?
335
- into.join if into and into_close and into.respond_to?(:joined?) and not into.joined?
336
- block.call if block_given?
337
-
338
- #Log.medium "Done consuming stream #{Misc.fingerprint io}"
339
- rescue Aborted
340
- Log.medium "Consume stream aborted #{Misc.fingerprint io}"
341
- io.abort if io.respond_to? :abort
342
- #io.close unless io.closed?
343
- FileUtils.rm into_path if into_path and File.exist? into_path
344
- rescue Exception
345
- Log.medium "Exception consuming stream: #{Misc.fingerprint io}: #{$!.message}"
346
- io.abort $! if io.respond_to? :abort
347
- FileUtils.rm into_path if into_path and File.exist? into_path
348
- raise $!
349
- end
350
- end
351
- end
352
-
353
- def self.read_stream(stream, size)
354
- str = nil
355
- Thread.pass while IO.select([stream],nil,nil,1).nil?
356
- while not str = stream.read(size)
357
- IO.select([stream],nil,nil,1)
358
- Thread.pass
359
- raise ClosedStream if stream.eof?
360
- end
361
-
362
- while str.length < size
363
- raise ClosedStream if stream.eof?
364
- IO.select([stream],nil,nil,1)
365
- if new = stream.read(size-str.length)
366
- str << new
367
- end
368
- end
369
- str
370
- end
371
-
372
- def self.sensiblewrite(path, content = nil, options = {}, &block)
373
- force = Misc.process_options options, :force
374
-
375
- if Open.exists? path and not force
376
- Misc.consume_stream content
377
- return
378
- end
379
-
380
- lock_options = Misc.pull_keys options.dup, :lock
381
- lock_options = lock_options[:lock] if Hash === lock_options[:lock]
382
- tmp_path = Persist.persistence_path(path, {:dir => Misc.sensiblewrite_dir})
383
- tmp_path_lock = Persist.persistence_path(path, {:dir => Misc.sensiblewrite_lock_dir})
384
-
385
- tmp_path_lock = nil if FalseClass === options[:lock]
386
-
387
- Misc.lock tmp_path_lock, lock_options do
388
-
389
- if Open.exists? path and not force
390
- Log.warn "Path exists in sensiblewrite, not forcing update: #{ path }"
391
- Misc.consume_stream content
392
- else
393
- FileUtils.mkdir_p File.dirname(tmp_path) unless File.directory? File.dirname(tmp_path)
394
- FileUtils.rm_f tmp_path if File.exist? tmp_path
395
- begin
396
-
397
- case
398
- when block_given?
399
- File.open(tmp_path, 'wb', &block)
400
- when String === content
401
- File.open(tmp_path, 'wb') do |f| f.write content end
402
- when (IO === content or StringIO === content or File === content)
403
-
404
- Open.write(tmp_path) do |f|
405
- #f.sync = true
406
- while block = content.read(BLOCK_SIZE)
407
- f.write block
408
- end
409
- end
410
- else
411
- File.open(tmp_path, 'wb') do |f| end
412
- end
413
-
414
- begin
415
- Misc.insist do
416
- Open.mv tmp_path, path, lock_options
417
- end
418
- rescue Exception
419
- raise $! unless Open.exists? path
420
- end
421
-
422
- Open.touch path if Open.exists? path
423
- content.join if content.respond_to?(:join) and not Path === content and not (content.respond_to?(:joined?) && content.joined?)
424
-
425
- Open.notify_write(path)
426
- rescue Aborted
427
- Log.medium "Aborted sensiblewrite -- #{ Log.reset << Log.color(:blue, path) }"
428
- content.abort if content.respond_to? :abort
429
- Open.rm path if File.exist? path
430
- rescue Exception
431
- exception = (AbortedStream === content and content.exception) ? content.exception : $!
432
- Log.medium "Exception in sensiblewrite: [#{Process.pid}] #{exception.message} -- #{ Log.color :blue, path }"
433
- content.abort if content.respond_to? :abort
434
- Open.rm path if File.exist? path
435
- raise exception
436
- rescue
437
- Log.exception $!
438
- raise $!
439
- ensure
440
- FileUtils.rm_f tmp_path if File.exist? tmp_path
441
- if Lockfile === lock_options[:lock] and lock_options[:lock].locked?
442
- lock_options[:lock].unlock
443
- end
444
- end
445
- end
446
- end
447
- end
448
-
449
- def self.process_stream(s)
450
- begin
451
- yield s
452
- s.join if s.respond_to? :join
453
- rescue
454
- s.abort if s.respond_to? :abort
455
- raise $!
456
- end
457
- end
458
-
459
- def self.sort_stream(stream, header_hash = "#", cmd_args = "-u")
460
- Misc.open_pipe do |sin|
461
- stream = TSV.get_stream stream
462
-
463
- line = stream.gets
464
- while line =~ /^#{header_hash}/ do
465
- sin.puts line
466
- line = stream.gets
467
- end
468
-
469
- line_stream = Misc.open_pipe do |line_stream_in|
470
- line_stream_in.puts line
471
- begin
472
- Misc.consume_stream(stream, false, line_stream_in)
473
- rescue
474
- raise $!
475
- end
476
- end
477
-
478
- sorted = CMD.cmd("env LC_ALL=C sort #{cmd_args || ""}", :in => line_stream, :pipe => true)
479
-
480
- begin
481
- Misc.consume_stream(sorted, false, sin)
482
- rescue
483
- Log.exception $!
484
- begin
485
- sorted.raise($!) if sorted.respond_to? :raise
486
- stream.raise($!) if stream.respond_to? :raise
487
- ensure
488
- raise $!
489
- end
490
- end
491
- end
492
- end
493
-
494
- def self.collapse_stream(s, line = nil, sep = "\t", header = nil, &block)
495
- sep ||= "\t"
496
- Misc.open_pipe do |sin|
497
- sin.puts header if header
498
- process_stream(s) do |s|
499
- line ||= s.gets
500
-
501
- current_parts = []
502
- while line
503
- key, *parts = line.chomp.split(sep, -1)
504
- case
505
- when key.nil?
506
- when current_parts.nil?
507
- current_parts = parts
508
- current_key = key
509
- when current_key == key
510
- parts.each_with_index do |part,i|
511
- if current_parts[i].nil?
512
- current_parts[i] = "|" << part
513
- else
514
- current_parts[i] = current_parts[i] << "|" << part
515
- end
516
- end
517
-
518
- (parts.length..current_parts.length-1).to_a.each do |pos|
519
- current_parts[pos] = current_parts[pos] << "|" << ""
520
- end
521
- when current_key.nil?
522
- current_key = key
523
- current_parts = parts
524
- when current_key != key
525
- if block_given?
526
- res = block.call(current_parts)
527
- sin.puts [current_key, res] * sep
528
- else
529
- sin.puts [current_key, current_parts].flatten * sep
530
- end
531
- current_key = key
532
- current_parts = parts
533
- end
534
- line = s.gets
535
- end
536
-
537
- if block_given?
538
- res = block.call(current_parts)
539
- sin.puts [current_key, res] * sep
540
- else
541
- sin.puts [current_key, current_parts].flatten * sep
542
- end unless current_key.nil?
543
- end
544
- end
545
- end
546
-
547
- def self.buffer_stream(stream)
548
- sout, sin = Misc.pipe
549
- Misc.consume_stream(stream, true, sin)
550
- sout
551
- end
552
-
553
- def self._paste_streams(streams, output, lines = nil, sep = "\t", header = nil, &block)
554
- output.puts header if header
555
- streams = streams.collect do |stream|
556
- if defined? Step and Step === stream
557
- io = stream.get_stream
558
- if io
559
- buffer_stream(io)
560
- else
561
- stream.join.path.open
562
- end
563
- else
564
- stream
565
- end
566
- end
567
-
568
- begin
569
-
570
- lines ||= streams.collect{|s| s.gets }
571
- keys = []
572
- parts = []
573
- lines.each_with_index do |line,i|
574
- if line.nil?
575
- keys[i] = nil
576
- parts[i] = []
577
- else
578
- key, *p = line.chomp.split(sep, -1)
579
- keys[i] = key
580
- parts[i] = p
581
- end
582
- end
583
- sizes = parts.collect{|p| p.nil? ? 0 : p.length }
584
- last_min = nil
585
-
586
- while lines.compact.any?
587
- if block_given?
588
- min = keys.compact.sort(&block).first
589
- else
590
- min = keys.compact.sort.first
591
- end
592
- str = []
593
- threads = []
594
- keys.each_with_index do |key,i|
595
- case key
596
- when min
597
- if parts[i] == [SKIP_TAG]
598
- str << [sep * (sizes[i]-1)] if sizes[i] > 0
599
- else
600
- str << [parts[i] * sep]
601
- end
602
-
603
- line = lines[i] = streams[i].gets
604
-
605
- if line.nil?
606
- keys[i] = nil
607
- parts[i] = nil
608
- streams[i].close unless streams[i].closed?
609
- streams[i].join if streams[i].respond_to?(:join)
610
- else
611
- k, *p = line.chomp.split(sep, -1)
612
- keys[i] = k
613
- parts[i] = p
614
- end
615
- else
616
- str << [sep * (sizes[i]-1)] if sizes[i] > 0
617
- end
618
- end
619
-
620
- output.puts [min, str.flatten*sep] * sep
621
- end
622
-
623
- streams.each do |stream|
624
- stream.close unless stream.closed?
625
- stream.join if stream.respond_to?(:join)
626
- end
627
- rescue
628
- Log.exception $!
629
- streams.each do |stream|
630
- stream.abort if stream.respond_to? :abort
631
- end
632
- raise $!
633
- end
634
- end
635
-
636
- def self.paste_streams(streams, lines = nil, sep = "\t", header = nil, &block)
637
- sep ||= "\t"
638
- num_streams = streams.length
639
- Misc.open_pipe do |sin|
640
- self._paste_streams(streams, sin, lines, sep, header, &block)
641
- end
642
- end
643
-
644
- def self.save_stream(file, stream)
645
- save, out = Misc.tee_stream stream
646
- out.filename = file
647
- save.filename = file
648
-
649
- Thread.new(Thread.current) do |parent|
650
- begin
651
- Misc.sensiblewrite(file, save)
652
- rescue Exception
653
- save.abort if save.respond_to? :abort
654
- stream.abort if stream.respond_to? :abort
655
- stream.join
656
- Log.medium "Exception in save_stream: #{$!.message}"
657
- raise $!
658
- end
659
- end
660
-
661
- out
662
- end
663
-
664
- def self.intercalate_streams(streams)
665
- Misc.open_pipe do |sin|
666
- continue = true
667
- while continue
668
- lines = streams.collect{|stream| stream.eof? ? nil : stream.gets }.compact
669
- lines.each do |line|
670
- sin.puts line
671
- end
672
- continue = false if lines.empty?
673
- end
674
- streams.each do |stream|
675
- stream.join if stream.respond_to? :join
676
- stream.close if stream.respond_to? :close and not stream.closed?
677
- end
678
- end
679
- end
680
-
681
- def self.compare_lines(stream1, stream2, args, sort = false)
682
- if sort
683
- stream1 = Misc.sort_stream stream1
684
- stream2 = Misc.sort_stream stream2
685
- compare_lines(stream1, stream2, args, false)
686
- else
687
- erase = []
688
-
689
- if Path === stream1 or (String === stream1 and File.exist? stream1)
690
- file1 = stream1
691
- else
692
- file1 = TmpFile.tmp_file
693
- erase << file1
694
- Misc.consume_stream(TSV.get_stream(stream1), false, file1)
695
- end
696
-
697
- if Path === stream2 or (String === stream2 and File.exist? stream2)
698
- file2 = stream2
699
- else
700
- file2 = TmpFile.tmp_file
701
- erase << file2
702
- Misc.consume_stream(TSV.get_stream(stream2), false, file2)
703
- end
704
-
705
- CMD.cmd("env LC_ALL=C comm #{args} '#{file1}' '#{file2}'", :pipe => true, :post => Proc.new{ erase.each{|f| FileUtils.rm f } })
706
- end
707
- end
708
-
709
- def self.remove_lines(stream1, stream2, sort)
710
- self.compare_lines(stream1, stream2, '-2 -3', sort)
711
- end
712
-
713
- def self.select_lines(stream1, stream2, sort)
714
- self.compare_lines(stream1, stream2, '-1 -2', sort)
715
- end
716
-
717
-
718
- def self.add_stream_filename(io, filename)
719
- if ! io.respond_to? :filename
720
- class << io
721
- attr_accessor :filename
722
- end
723
- io.filename = filename
724
- end
725
- end
726
-
727
- def self.sort_mutation_stream_strict(stream, sep=":")
728
- CMD.cmd("grep '#{sep}' | sort -u | sed 's/^M:/MT:/' | env LC_ALL=C sort -V -k1,1 -k2,2n -k3,3n -t'#{sep}'", :in => stream, :pipe => true, :no_fail => true)
729
- end
730
-
731
- def self.sort_mutation_stream(stream, sep=":")
732
- CMD.cmd("grep '#{sep}' | sort -u | sed 's/^M:/MT:/' | env LC_ALL=C sort -k1,1 -k2,2n -k3,3n -t'#{sep}'", :in => stream, :pipe => true, :no_fail => true)
733
- end
734
-
735
- def self.swap_quoted_character(stream, charout="\n", charin=" ", quote='"')
736
- io = Misc.open_pipe do |sin|
737
- begin
738
- quoted = false
739
- prev = nil
740
- while c = stream.getc
741
- if c == quote and not prev == "\\"
742
- quoted = ! quoted
743
- end
744
- c = charin if c == charout and quoted
745
- sin << c
746
- prev = c
747
- end
748
- rescue
749
- stream.abort if stream.respond_to? :abort
750
- raise $!
751
- ensure
752
- stream.join if stream.respond_to? :join
753
- end
754
- end
755
- end
756
-
757
- def self.remove_quoted_new_line(stream, quote = '"')
758
- swap_quoted_character(stream, "\n", " ", quote)
759
- end
760
-
761
- def self.line_monitor_stream(stream, &block)
762
- monitor, out = tee_stream stream
763
- monitor_thread = Thread.new do
764
- begin
765
- while line = monitor.gets
766
- block.call line
767
- end
768
- rescue
769
- Log.exception $!
770
- monitor.raise $!
771
- monitor.close unless monitor.closed?
772
- monitor.join if monitor.respond_to?(:join) && ! monitor.aborted?
773
- out.raise $! if out.respond_to?(:raise)
774
- ensure
775
- monitor.close unless monitor.closed?
776
- monitor.join if monitor.respond_to?(:join) && ! monitor.aborted?
777
- end
778
- end
779
-
780
- stream.annotate out if stream.respond_to? :annotate
781
- ConcurrentStream.setup out, :threads => monitor_thread
782
- end
783
-
784
- def self.open_gz_pipe
785
- sout = Misc.open_pipe do |sin|
786
- yield sin
787
- sin.close
788
- end
789
-
790
- Open.gzip(sout)
791
- end
792
-
793
- end
1
+ require_relative '../../refactor'
2
+ Rbbt.require_instead 'scout/open/stream'
3
+
4
+ #require 'rbbt'
5
+ #
6
+ #module Misc
7
+ # class << self
8
+ # attr_accessor :sensiblewrite_lock_dir
9
+ #
10
+ # def sensiblewrite_lock_dir
11
+ # @sensiblewrite_lock_dir ||= Rbbt.tmp.sensiblewrite_locks.find
12
+ # end
13
+ # end
14
+ #
15
+ # class << self
16
+ # attr_accessor :sensiblewrite_dir
17
+ # def sensiblewrite_dir
18
+ # @sensiblewrite_dir = Rbbt.tmp.sensiblewrite.find
19
+ # end
20
+ # end
21
+ #
22
+ # BLOCK_SIZE=1024 * 8
23
+ #
24
+ # SKIP_TAG="[SKIP TAG]"
25
+ #
26
+ # PIPE_MUTEX = Mutex.new
27
+ #
28
+ # OPEN_PIPE_IN = []
29
+ # def self.pipe
30
+ # OPEN_PIPE_IN.delete_if{|pipe| pipe.closed? }
31
+ # res = PIPE_MUTEX.synchronize do
32
+ # sout, sin = IO.pipe
33
+ # OPEN_PIPE_IN << sin
34
+ #
35
+ # [sout, sin]
36
+ # end
37
+ # Log.debug{"Creating pipe #{[res.last.inspect, res.first.inspect] * " => "}"}
38
+ # res
39
+ # end
40
+ #
41
+ # def self.with_fifo(path = nil, clean = true, &block)
42
+ # begin
43
+ # erase = path.nil?
44
+ # path = TmpFile.tmp_file if path.nil?
45
+ # File.rm path if clean && File.exist?(path)
46
+ # File.mkfifo path
47
+ # yield path
48
+ # ensure
49
+ # FileUtils.rm path if erase && File.exist?(path)
50
+ # end
51
+ # end
52
+ #
53
+ # def self.release_pipes(*pipes)
54
+ # PIPE_MUTEX.synchronize do
55
+ # pipes.flatten.each do |pipe|
56
+ # pipe.close unless pipe.closed?
57
+ # end
58
+ # end
59
+ # end
60
+ #
61
+ # def self.purge_pipes(*save)
62
+ # PIPE_MUTEX.synchronize do
63
+ # OPEN_PIPE_IN.each do |pipe|
64
+ # next if save.include? pipe
65
+ # pipe.close unless pipe.closed?
66
+ # end
67
+ # end
68
+ # end
69
+ #
70
+ # def self.open_pipe(do_fork = false, close = true)
71
+ # raise "No block given" unless block_given?
72
+ #
73
+ # sout, sin = Misc.pipe
74
+ #
75
+ # if do_fork
76
+ #
77
+ # #parent_pid = Process.pid
78
+ # pid = Process.fork {
79
+ # purge_pipes(sin)
80
+ # sout.close
81
+ # begin
82
+ #
83
+ # yield sin
84
+ # sin.close if close and not sin.closed?
85
+ #
86
+ # rescue Exception
87
+ # Log.exception $!
88
+ # #Process.kill :INT, parent_pid
89
+ # Kernel.exit! -1
90
+ # end
91
+ # Kernel.exit! 0
92
+ # }
93
+ # sin.close
94
+ #
95
+ # ConcurrentStream.setup sout, :pids => [pid]
96
+ # else
97
+ #
98
+ # ConcurrentStream.setup sin, :pair => sout
99
+ # ConcurrentStream.setup sout, :pair => sin
100
+ #
101
+ # thread = Thread.new do
102
+ # begin
103
+ #
104
+ # yield sin
105
+ #
106
+ # sin.close if close and not sin.closed? and not sin.aborted?
107
+ #
108
+ # rescue Aborted
109
+ # Log.medium "Aborted open_pipe: #{$!.message}"
110
+ # raise $!
111
+ # rescue Exception
112
+ # Log.medium "Exception in open_pipe: #{$!.message}"
113
+ # Log.exception $!
114
+ # begin
115
+ # sin.raise($!) if sin.respond_to? :raise
116
+ # sin.join if sin.respond_to? :join
117
+ # ensure
118
+ # raise $!
119
+ # end
120
+ # end
121
+ # end
122
+ #
123
+ # sin.threads = [thread]
124
+ # sout.threads = [thread]
125
+ # end
126
+ #
127
+ # sout
128
+ # end
129
+ #
130
+ # #def self.tee_stream_thread(stream)
131
+ # # stream_out1, stream_in1 = Misc.pipe
132
+ # # stream_out2, stream_in2 = Misc.pipe
133
+ #
134
+ # # splitter_thread = Thread.new(Thread.current) do |parent|
135
+ # # begin
136
+ #
137
+ # # skip1 = skip2 = false
138
+ # # while block = stream.read(1024)
139
+ #
140
+ # # begin
141
+ # # stream_in1.write block;
142
+ # # rescue IOError
143
+ # # Log.medium("Tee stream 1 #{Misc.fingerprint stream} IOError: #{$!.message}");
144
+ # # skip1 = true
145
+ # # end unless skip1
146
+ #
147
+ # # begin
148
+ # # stream_in2.write block
149
+ # # rescue IOError
150
+ # # Log.medium("Tee stream 2 #{Misc.fingerprint stream} IOError: #{$!.message}");
151
+ # # skip2 = true
152
+ # # end unless skip2
153
+ #
154
+ # # end
155
+ #
156
+ # # stream_in1.close unless stream_in1.closed?
157
+ # # stream.join if stream.respond_to? :join
158
+ # # stream_in2.close unless stream_in2.closed?
159
+ # # rescue Aborted, Interrupt
160
+ # # stream_out1.abort if stream_out1.respond_to? :abort
161
+ # # stream.abort if stream.respond_to? :abort
162
+ # # stream_out2.abort if stream_out2.respond_to? :abort
163
+ # # Log.medium "Tee aborting #{Misc.fingerprint stream}"
164
+ # # raise $!
165
+ # # rescue Exception
166
+ # # stream_out1.abort if stream_out1.respond_to? :abort
167
+ # # stream.abort if stream.respond_to? :abort
168
+ # # stream_out2.abort if stream_out2.respond_to? :abort
169
+ # # Log.medium "Tee exception #{Misc.fingerprint stream}"
170
+ # # raise $!
171
+ # # end
172
+ # # end
173
+ #
174
+ # # ConcurrentStream.setup stream_out1, :threads => splitter_thread
175
+ # # ConcurrentStream.setup stream_out2, :threads => splitter_thread
176
+ #
177
+ # # [stream_out1, stream_out2]
178
+ # #end
179
+ #
180
+ # def self.tee_stream_thread_multiple(stream, num = 2)
181
+ # in_pipes = []
182
+ # out_pipes = []
183
+ # num.times do
184
+ # sout, sin = Misc.pipe
185
+ # in_pipes << sin
186
+ # out_pipes << sout
187
+ # end
188
+ #
189
+ # filename = stream.filename if stream.respond_to? :filename
190
+ #
191
+ # splitter_thread = Thread.new(Thread.current) do |parent|
192
+ # begin
193
+ #
194
+ # skip = [false] * num
195
+ # begin
196
+ # while block = stream.readpartial(BLOCK_SIZE)
197
+ #
198
+ # in_pipes.each_with_index do |sin,i|
199
+ # begin
200
+ # sin.write block
201
+ # rescue IOError
202
+ # Log.error("Tee stream #{i} #{Misc.fingerprint stream} IOError: #{$!.message} (#{Misc.fingerprint sin})");
203
+ # skip[i] = true
204
+ # rescue
205
+ # Log.error("Tee stream #{i} #{Misc.fingerprint stream} Exception: #{$!.message} (#{Misc.fingerprint sin})");
206
+ # raise $!
207
+ # end unless skip[i]
208
+ # end
209
+ # end
210
+ # rescue IOError
211
+ # end
212
+ #
213
+ # stream.close unless stream.closed?
214
+ # #stream.join if stream.respond_to? :join
215
+ # in_pipes.first.close unless in_pipes.first.closed?
216
+ # #Log.medium "Tee done #{Misc.fingerprint stream}"
217
+ # rescue Aborted, Interrupt
218
+ # stream.abort if stream.respond_to? :abort
219
+ # out_pipes.each do |sout|
220
+ # sout.abort if sout.respond_to? :abort
221
+ # end
222
+ # Log.medium "Tee aborting #{Misc.fingerprint stream}"
223
+ # raise $!
224
+ # rescue Exception
225
+ # stream.abort($!) if stream.respond_to? :abort
226
+ # out_pipes.each do |sout|
227
+ # sout.abort if sout.respond_to? :abort
228
+ # end
229
+ # Log.medium "Tee exception #{Misc.fingerprint stream}"
230
+ # raise $!
231
+ # end
232
+ # end
233
+ #
234
+ # out_pipes.each do |sout|
235
+ # ConcurrentStream.setup sout, :threads => splitter_thread, :filename => filename, :_pair => stream
236
+ # end
237
+ #
238
+ # main_pipe = out_pipes.first
239
+ # main_pipe.autojoin = true
240
+ #
241
+ # main_pipe.callback = Proc.new do
242
+ # stream.join if stream.respond_to? :join
243
+ # in_pipes[1..-1].each do |sin|
244
+ # sin.close unless sin.closed?
245
+ # end
246
+ # end
247
+ #
248
+ # out_pipes
249
+ # end
250
+ #
251
+ # def self.tee_stream_thread(stream)
252
+ # tee_stream_thread_multiple(stream, 2)
253
+ # end
254
+ #
255
+ # def self.dup_stream_multiple(stream, num = 1)
256
+ # stream_dup = stream.dup
257
+ # if stream.respond_to? :annotate
258
+ # stream.annotate stream_dup
259
+ # stream.clear
260
+ # end
261
+ # tee1, *rest = Misc.tee_stream stream_dup, num + 1
262
+ # stream.reopen(tee1)
263
+ #
264
+ # #ToDo: I can't explain why the @threads variable appears with the value of
265
+ # # @filename
266
+ # stream.instance_variable_set(:@threads, nil) if stream.instance_variables.include?(:@threads)
267
+ #
268
+ # tee1.annotate(stream)
269
+ # rest
270
+ # end
271
+ #
272
+ # def self.dup_stream(stream)
273
+ # dup_stream_multiple(stream, 1).first
274
+ # end
275
+ #
276
+ # class << self
277
+ # alias tee_stream tee_stream_thread_multiple
278
+ # end
279
+ #
280
+ # def self.read_full_stream(io)
281
+ # str = ""
282
+ # begin
283
+ # while block = io.read(BLOCK_SIZE)
284
+ # str << block
285
+ # end
286
+ # io.join if io.respond_to? :join
287
+ # rescue
288
+ # io.abort if io.respond_to? :abort
289
+ # end
290
+ # str
291
+ # end
292
+ #
293
+ # def self.consume_stream(io, in_thread = false, into = nil, into_close = true, &block)
294
+ # return if Path === io
295
+ # return unless io.respond_to? :read
296
+ #
297
+ # if io.respond_to? :closed? and io.closed?
298
+ # io.join if io.respond_to? :join
299
+ # return
300
+ # end
301
+ #
302
+ # if in_thread
303
+ # Thread.new(Thread.current) do |parent|
304
+ # begin
305
+ # consume_stream(io, false, into, into_close)
306
+ # rescue Exception
307
+ # parent.raise $!
308
+ # end
309
+ # end
310
+ # else
311
+ # if into
312
+ # Log.medium "Consuming stream #{Misc.fingerprint io} -> #{Misc.fingerprint into}"
313
+ # else
314
+ # Log.medium "Consuming stream #{Misc.fingerprint io}"
315
+ # end
316
+ #
317
+ # begin
318
+ # into = into.find if Path === into
319
+ # if String === into
320
+ # dir = File.dirname(into)
321
+ # Open.mkdir dir unless Open.exists?(dir)
322
+ # into_path, into = into, Open.open(into, :mode => 'w')
323
+ # end
324
+ # into.sync = true if IO === into
325
+ # into_close = false unless into.respond_to? :close
326
+ # io.sync = true
327
+ #
328
+ # begin
329
+ # while c = io.readpartial(BLOCK_SIZE)
330
+ # into << c if into
331
+ # end
332
+ # rescue EOFError
333
+ # end
334
+ #
335
+ # io.join if io.respond_to? :join
336
+ # io.close unless io.closed?
337
+ # into.close if into and into_close and not into.closed?
338
+ # into.join if into and into_close and into.respond_to?(:joined?) and not into.joined?
339
+ # block.call if block_given?
340
+ #
341
+ # #Log.medium "Done consuming stream #{Misc.fingerprint io}"
342
+ # rescue Aborted
343
+ # Log.medium "Consume stream aborted #{Misc.fingerprint io}"
344
+ # io.abort if io.respond_to? :abort
345
+ # #io.close unless io.closed?
346
+ # FileUtils.rm into_path if into_path and File.exist? into_path
347
+ # rescue Exception
348
+ # Log.medium "Exception consuming stream: #{Misc.fingerprint io}: #{$!.message}"
349
+ # io.abort $! if io.respond_to? :abort
350
+ # FileUtils.rm into_path if into_path and File.exist? into_path
351
+ # raise $!
352
+ # end
353
+ # end
354
+ # end
355
+ #
356
+ # def self.read_stream(stream, size)
357
+ # str = nil
358
+ # Thread.pass while IO.select([stream],nil,nil,1).nil?
359
+ # while not str = stream.read(size)
360
+ # IO.select([stream],nil,nil,1)
361
+ # Thread.pass
362
+ # raise ClosedStream if stream.eof?
363
+ # end
364
+ #
365
+ # while str.length < size
366
+ # raise ClosedStream if stream.eof?
367
+ # IO.select([stream],nil,nil,1)
368
+ # if new = stream.read(size-str.length)
369
+ # str << new
370
+ # end
371
+ # end
372
+ # str
373
+ # end
374
+ #
375
+ # def self.sensiblewrite(*args, &block)
376
+ # Open.sensible_write(*args, &block)
377
+ # end
378
+ #
379
+ # #def self.sensiblewrite(path, content = nil, options = {}, &block)
380
+ # # force = Misc.process_options options, :force
381
+ #
382
+ # # if Open.exists? path and not force
383
+ # # Misc.consume_stream content
384
+ # # return
385
+ # # end
386
+ #
387
+ # # lock_options = Misc.pull_keys options.dup, :lock
388
+ # # lock_options = lock_options[:lock] if Hash === lock_options[:lock]
389
+ # # tmp_path = Persist.persistence_path(path, {:dir => Misc.sensiblewrite_dir})
390
+ # # tmp_path_lock = Persist.persistence_path(path, {:dir => Misc.sensiblewrite_lock_dir})
391
+ #
392
+ # # tmp_path_lock = nil if FalseClass === options[:lock]
393
+ #
394
+ # # Misc.lock tmp_path_lock, lock_options do
395
+ #
396
+ # # if Open.exists? path and not force
397
+ # # Log.warn "Path exists in sensiblewrite, not forcing update: #{ path }"
398
+ # # Misc.consume_stream content
399
+ # # else
400
+ # # FileUtils.mkdir_p File.dirname(tmp_path) unless File.directory? File.dirname(tmp_path)
401
+ # # FileUtils.rm_f tmp_path if File.exist? tmp_path
402
+ # # begin
403
+ #
404
+ # # case
405
+ # # when block_given?
406
+ # # File.open(tmp_path, 'wb', &block)
407
+ # # when String === content
408
+ # # File.open(tmp_path, 'wb') do |f| f.write content end
409
+ # # when (IO === content or StringIO === content or File === content)
410
+ #
411
+ # # Open.write(tmp_path) do |f|
412
+ # # f.sync = true
413
+ # # while block = content.read(BLOCK_SIZE)
414
+ # # f.write block
415
+ # # end
416
+ # # end
417
+ # # else
418
+ # # File.open(tmp_path, 'wb') do |f| end
419
+ # # end
420
+ #
421
+ # # begin
422
+ # # Misc.insist do
423
+ # # Open.mv tmp_path, path, lock_options
424
+ # # end
425
+ # # rescue Exception
426
+ # # raise $! unless Open.exists? path
427
+ # # end
428
+ #
429
+ # # Open.touch path if Open.exists? path
430
+ # # content.join if content.respond_to?(:join) and not Path === content and not (content.respond_to?(:joined?) && content.joined?)
431
+ #
432
+ # # Open.notify_write(path)
433
+ # # rescue Aborted
434
+ # # Log.medium "Aborted sensiblewrite -- #{ Log.reset << Log.color(:blue, path) }"
435
+ # # content.abort if content.respond_to? :abort
436
+ # # Open.rm path if File.exist? path
437
+ # # rescue Exception
438
+ # # exception = (AbortedStream === content and content.exception) ? content.exception : $!
439
+ # # Log.medium "Exception in sensiblewrite: [#{Process.pid}] #{exception.message} -- #{ Log.color :blue, path }"
440
+ # # content.abort if content.respond_to? :abort
441
+ # # Open.rm path if File.exist? path
442
+ # # raise exception
443
+ # # rescue
444
+ # # Log.exception $!
445
+ # # raise $!
446
+ # # ensure
447
+ # # FileUtils.rm_f tmp_path if File.exist? tmp_path
448
+ # # if Lockfile === lock_options[:lock] and lock_options[:lock].locked?
449
+ # # lock_options[:lock].unlock
450
+ # # end
451
+ # # end
452
+ # # end
453
+ # # end
454
+ # #end
455
+ #
456
+ # def self.process_stream(s)
457
+ # begin
458
+ # yield s
459
+ # s.join if s.respond_to? :join
460
+ # rescue
461
+ # s.abort if s.respond_to? :abort
462
+ # raise $!
463
+ # end
464
+ # end
465
+ #
466
+ # def self.collapse_stream(s, line = nil, sep = "\t", header = nil, &block)
467
+ # sep ||= "\t"
468
+ # Misc.open_pipe do |sin|
469
+ # sin.puts header if header
470
+ # process_stream(s) do |s|
471
+ # line ||= s.gets
472
+ #
473
+ # current_parts = []
474
+ # while line
475
+ # key, *parts = line.chomp.split(sep, -1)
476
+ # case
477
+ # when key.nil?
478
+ # when current_parts.nil?
479
+ # current_parts = parts
480
+ # current_key = key
481
+ # when current_key == key
482
+ # parts.each_with_index do |part,i|
483
+ # if current_parts[i].nil?
484
+ # current_parts[i] = "|" << part
485
+ # else
486
+ # current_parts[i] = current_parts[i] << "|" << part
487
+ # end
488
+ # end
489
+ #
490
+ # (parts.length..current_parts.length-1).to_a.each do |pos|
491
+ # current_parts[pos] = current_parts[pos] << "|" << ""
492
+ # end
493
+ # when current_key.nil?
494
+ # current_key = key
495
+ # current_parts = parts
496
+ # when current_key != key
497
+ # if block_given?
498
+ # res = block.call(current_parts)
499
+ # sin.puts [current_key, res] * sep
500
+ # else
501
+ # sin.puts [current_key, current_parts].flatten * sep
502
+ # end
503
+ # current_key = key
504
+ # current_parts = parts
505
+ # end
506
+ # line = s.gets
507
+ # end
508
+ #
509
+ # if block_given?
510
+ # res = block.call(current_parts)
511
+ # sin.puts [current_key, res] * sep
512
+ # else
513
+ # sin.puts [current_key, current_parts].flatten * sep
514
+ # end unless current_key.nil?
515
+ # end
516
+ # end
517
+ # end
518
+ #
519
+ # def self.buffer_stream(stream)
520
+ # sout, sin = Misc.pipe
521
+ # Misc.consume_stream(stream, true, sin)
522
+ # sout
523
+ # end
524
+ #
525
+ # def self._paste_streams(streams, output, lines = nil, sep = "\t", header = nil, &block)
526
+ # output.puts header if header
527
+ # streams = streams.collect do |stream|
528
+ # if defined? Step and Step === stream
529
+ # io = stream.get_stream
530
+ # if io
531
+ # buffer_stream(io)
532
+ # else
533
+ # stream.join.path.open
534
+ # end
535
+ # else
536
+ # stream
537
+ # end
538
+ # end
539
+ #
540
+ # begin
541
+ #
542
+ # lines ||= streams.collect{|s| s.gets }
543
+ # keys = []
544
+ # parts = []
545
+ # lines.each_with_index do |line,i|
546
+ # if line.nil?
547
+ # keys[i] = nil
548
+ # parts[i] = []
549
+ # else
550
+ # key, *p = line.chomp.split(sep, -1)
551
+ # keys[i] = key
552
+ # parts[i] = p
553
+ # end
554
+ # end
555
+ # sizes = parts.collect{|p| p.nil? ? 0 : p.length }
556
+ # last_min = nil
557
+ #
558
+ # while lines.compact.any?
559
+ # if block_given?
560
+ # min = keys.compact.sort(&block).first
561
+ # else
562
+ # min = keys.compact.sort.first
563
+ # end
564
+ # str = []
565
+ # threads = []
566
+ # keys.each_with_index do |key,i|
567
+ # case key
568
+ # when min
569
+ # if parts[i] == [SKIP_TAG]
570
+ # str << [sep * (sizes[i]-1)] if sizes[i] > 0
571
+ # else
572
+ # str << [parts[i] * sep]
573
+ # end
574
+ #
575
+ # line = lines[i] = streams[i].gets
576
+ #
577
+ # if line.nil?
578
+ # keys[i] = nil
579
+ # parts[i] = nil
580
+ # streams[i].close unless streams[i].closed?
581
+ # streams[i].join if streams[i].respond_to?(:join)
582
+ # else
583
+ # k, *p = line.chomp.split(sep, -1)
584
+ # keys[i] = k
585
+ # parts[i] = p
586
+ # end
587
+ # else
588
+ # str << [sep * (sizes[i]-1)] if sizes[i] > 0
589
+ # end
590
+ # end
591
+ #
592
+ # output.puts [min, str.flatten*sep] * sep
593
+ # end
594
+ #
595
+ # streams.each do |stream|
596
+ # stream.close unless stream.closed?
597
+ # stream.join if stream.respond_to?(:join)
598
+ # end
599
+ # rescue
600
+ # Log.exception $!
601
+ # streams.each do |stream|
602
+ # stream.abort if stream.respond_to? :abort
603
+ # end
604
+ # raise $!
605
+ # end
606
+ # end
607
+ #
608
+ # def self.paste_streams(streams, lines = nil, sep = "\t", header = nil, &block)
609
+ # sep ||= "\t"
610
+ # num_streams = streams.length
611
+ # Misc.open_pipe do |sin|
612
+ # self._paste_streams(streams, sin, lines, sep, header, &block)
613
+ # end
614
+ # end
615
+ #
616
+ # def self.save_stream(file, stream)
617
+ # save, out = Misc.tee_stream stream
618
+ # out.filename = file
619
+ # save.filename = file
620
+ #
621
+ # Thread.new(Thread.current) do |parent|
622
+ # begin
623
+ # Misc.sensiblewrite(file, save)
624
+ # rescue Exception
625
+ # save.abort if save.respond_to? :abort
626
+ # stream.abort if stream.respond_to? :abort
627
+ # stream.join
628
+ # Log.medium "Exception in save_stream: #{$!.message}"
629
+ # raise $!
630
+ # end
631
+ # end
632
+ #
633
+ # out
634
+ # end
635
+ #
636
+ # def self.intercalate_streams(streams)
637
+ # Misc.open_pipe do |sin|
638
+ # continue = true
639
+ # while continue
640
+ # lines = streams.collect{|stream| stream.eof? ? nil : stream.gets }.compact
641
+ # lines.each do |line|
642
+ # sin.puts line
643
+ # end
644
+ # continue = false if lines.empty?
645
+ # end
646
+ # streams.each do |stream|
647
+ # stream.join if stream.respond_to? :join
648
+ # stream.close if stream.respond_to? :close and not stream.closed?
649
+ # end
650
+ # end
651
+ # end
652
+ #
653
+ # def self.compare_lines(stream1, stream2, args, sort = false)
654
+ # if sort
655
+ # stream1 = Misc.sort_stream stream1
656
+ # stream2 = Misc.sort_stream stream2
657
+ # compare_lines(stream1, stream2, args, false)
658
+ # else
659
+ # erase = []
660
+ #
661
+ # if Path === stream1 or (String === stream1 and File.exist? stream1)
662
+ # file1 = stream1
663
+ # else
664
+ # file1 = TmpFile.tmp_file
665
+ # erase << file1
666
+ # Misc.consume_stream(TSV.get_stream(stream1), false, file1)
667
+ # end
668
+ #
669
+ # if Path === stream2 or (String === stream2 and File.exist? stream2)
670
+ # file2 = stream2
671
+ # else
672
+ # file2 = TmpFile.tmp_file
673
+ # erase << file2
674
+ # Misc.consume_stream(TSV.get_stream(stream2), false, file2)
675
+ # end
676
+ #
677
+ # CMD.cmd("env LC_ALL=C comm #{args} '#{file1}' '#{file2}'", :pipe => true, :post => Proc.new{ erase.each{|f| FileUtils.rm f } })
678
+ # end
679
+ # end
680
+ #
681
+ # def self.remove_lines(stream1, stream2, sort)
682
+ # self.compare_lines(stream1, stream2, '-2 -3', sort)
683
+ # end
684
+ #
685
+ # def self.select_lines(stream1, stream2, sort)
686
+ # self.compare_lines(stream1, stream2, '-1 -2', sort)
687
+ # end
688
+ #
689
+ #
690
+ # def self.add_stream_filename(io, filename)
691
+ # if ! io.respond_to? :filename
692
+ # class << io
693
+ # attr_accessor :filename
694
+ # end
695
+ # io.filename = filename
696
+ # end
697
+ # end
698
+ #
699
+ # def self.sort_mutation_stream_strict(stream, sep=":")
700
+ # CMD.cmd("grep '#{sep}' | sort -u | sed 's/^M:/MT:/' | env LC_ALL=C sort -V -k1,1 -k2,2n -k3,3n -t'#{sep}'", :in => stream, :pipe => true, :no_fail => true)
701
+ # end
702
+ #
703
+ # def self.sort_mutation_stream(stream, sep=":")
704
+ # CMD.cmd("grep '#{sep}' | sort -u | sed 's/^M:/MT:/' | env LC_ALL=C sort -k1,1 -k2,2n -k3,3n -t'#{sep}'", :in => stream, :pipe => true, :no_fail => true)
705
+ # end
706
+ #
707
+ # def self.swap_quoted_character(stream, charout="\n", charin=" ", quote='"')
708
+ # io = Misc.open_pipe do |sin|
709
+ # begin
710
+ # quoted = false
711
+ # prev = nil
712
+ # while c = stream.getc
713
+ # if c == quote and not prev == "\\"
714
+ # quoted = ! quoted
715
+ # end
716
+ # c = charin if c == charout and quoted
717
+ # sin << c
718
+ # prev = c
719
+ # end
720
+ # rescue
721
+ # stream.abort if stream.respond_to? :abort
722
+ # raise $!
723
+ # ensure
724
+ # stream.join if stream.respond_to? :join
725
+ # end
726
+ # end
727
+ # end
728
+ #
729
+ # def self.remove_quoted_new_line(stream, quote = '"')
730
+ # swap_quoted_character(stream, "\n", " ", quote)
731
+ # end
732
+ #
733
+ # def self.line_monitor_stream(stream, &block)
734
+ # monitor, out = tee_stream stream
735
+ # monitor_thread = Thread.new do
736
+ # begin
737
+ # while line = monitor.gets
738
+ # block.call line
739
+ # end
740
+ # rescue
741
+ # Log.exception $!
742
+ # monitor.raise $!
743
+ # monitor.close unless monitor.closed?
744
+ # monitor.join if monitor.respond_to?(:join) && ! monitor.aborted?
745
+ # out.raise $! if out.respond_to?(:raise)
746
+ # ensure
747
+ # monitor.close unless monitor.closed?
748
+ # monitor.join if monitor.respond_to?(:join) && ! monitor.aborted?
749
+ # end
750
+ # end
751
+ #
752
+ # stream.annotate out if stream.respond_to? :annotate
753
+ # ConcurrentStream.setup out, :threads => monitor_thread
754
+ # end
755
+ #
756
+ # def self.open_gz_pipe
757
+ # sout = Misc.open_pipe do |sin|
758
+ # yield sin
759
+ # sin.close
760
+ # end
761
+ #
762
+ # Open.gzip(sout)
763
+ # end
764
+ #
765
+ #end