rbbt-util 5.13.1 → 5.13.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rbbt/persist.rb +10 -35
- data/lib/rbbt/tsv/parallel/traverse.rb +118 -78
- data/lib/rbbt/tsv/parser.rb +6 -4
- data/lib/rbbt/tsv/stream.rb +107 -2
- data/lib/rbbt/tsv/util.rb +7 -15
- data/lib/rbbt/util/concurrency/processes.rb +16 -17
- data/lib/rbbt/util/concurrency/processes/worker.rb +5 -3
- data/lib/rbbt/util/log.rb +2 -1
- data/lib/rbbt/util/log/progress.rb +46 -20
- data/lib/rbbt/util/misc/exceptions.rb +11 -1
- data/lib/rbbt/util/misc/inspect.rb +1 -1
- data/lib/rbbt/util/misc/lock.rb +1 -1
- data/lib/rbbt/util/misc/pipes.rb +66 -25
- data/lib/rbbt/util/semaphore.rb +3 -3
- data/lib/rbbt/workflow/accessor.rb +34 -12
- data/lib/rbbt/workflow/definition.rb +7 -3
- data/lib/rbbt/workflow/step.rb +4 -3
- data/lib/rbbt/workflow/step/run.rb +104 -30
- data/lib/rbbt/workflow/usage.rb +1 -1
- data/share/rbbt_commands/workflow/task +11 -1
- data/test/rbbt/tsv/parallel/test_traverse.rb +31 -34
- data/test/rbbt/tsv/test_filter.rb +6 -6
- data/test/rbbt/util/concurrency/test_processes.rb +0 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52c2b4f2b6f065da491646388575b22ffccddee4
|
4
|
+
data.tar.gz: 48c8543fb121288f15a998bf4ce0237e56bdc2a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b218fc2960eb7f021d8628fa08b5363362efdc6bbf4b04ea1002140dcdf93dd7556f39f6265d77c8d35c3c234a0ecedcfe94197499ea080c663249d748ef963e
|
7
|
+
data.tar.gz: 3562d3fb1ccbf97705e4923642814b2c4519942c89dcfc97d9cc74802a220e32c338f2bfce65d689dffc0b35e99982f817236a75b67a0ebc8faeda7b5b516024
|
data/lib/rbbt/persist.rb
CHANGED
@@ -116,7 +116,6 @@ module Persist
|
|
116
116
|
begin
|
117
117
|
Marshal.load(stream)
|
118
118
|
rescue
|
119
|
-
Log.exception $!
|
120
119
|
raise $!
|
121
120
|
end
|
122
121
|
else
|
@@ -135,7 +134,7 @@ module Persist
|
|
135
134
|
raise "Unknown persistence: #{ type }"
|
136
135
|
end
|
137
136
|
rescue
|
138
|
-
Log.
|
137
|
+
Log.warn "Exception loading #{ type } #{ path }: #{$!.message}"
|
139
138
|
raise $!
|
140
139
|
end
|
141
140
|
end
|
@@ -191,30 +190,6 @@ module Persist
|
|
191
190
|
end
|
192
191
|
end
|
193
192
|
|
194
|
-
#def self.tee_stream_fork(stream, path, type, callback = nil)
|
195
|
-
# file, out = Misc.tee_stream(stream)
|
196
|
-
|
197
|
-
# saver_pid = Process.fork do
|
198
|
-
# out.close
|
199
|
-
# stream.close
|
200
|
-
# Misc.purge_pipes
|
201
|
-
# begin
|
202
|
-
# Misc.lock(path) do
|
203
|
-
# save_file(path, type, file)
|
204
|
-
# end
|
205
|
-
# rescue Aborted
|
206
|
-
# stream.abort if stream.respond_to? :abort
|
207
|
-
# raise $!
|
208
|
-
# rescue Exception
|
209
|
-
# Log.exception $!
|
210
|
-
# Kernel.exit! -1
|
211
|
-
# end
|
212
|
-
# Kernel.exit! 0
|
213
|
-
# end
|
214
|
-
# file.close
|
215
|
-
# ConcurrentStream.setup(out, :pids => [saver_pid], :filename => path)
|
216
|
-
#end
|
217
|
-
|
218
193
|
def self.tee_stream_thread(stream, path, type, callback = nil)
|
219
194
|
file, out = Misc.tee_stream(stream)
|
220
195
|
|
@@ -225,11 +200,11 @@ module Persist
|
|
225
200
|
save_file(path, type, file)
|
226
201
|
end
|
227
202
|
rescue Aborted
|
228
|
-
Log.
|
203
|
+
Log.warn "Persist stream thread aborted: #{ Log.color :blue, path }"
|
229
204
|
file.abort if file.respond_to? :abort
|
205
|
+
parent.raise $!
|
230
206
|
rescue Exception
|
231
|
-
Log.
|
232
|
-
Log.exception $!
|
207
|
+
Log.warn "Persist stream thread exception: #{ Log.color :blue, path }"
|
233
208
|
file.abort if file.respond_to? :abort
|
234
209
|
parent.raise $!
|
235
210
|
end
|
@@ -250,10 +225,10 @@ module Persist
|
|
250
225
|
end
|
251
226
|
Log.high "Stream pipe saved: #{path}"
|
252
227
|
rescue Aborted
|
253
|
-
Log.
|
228
|
+
Log.warn "Persist stream pipe exception: #{ Log.color :blue, path }"
|
254
229
|
stream.abort if stream.respond_to? :abort
|
255
230
|
rescue Exception
|
256
|
-
Log.
|
231
|
+
Log.warn "Persist stream pipe exception: #{ Log.color :blue, path }"
|
257
232
|
Log.exception $!
|
258
233
|
stream.abort if stream.respond_to? :abort
|
259
234
|
parent.raise $!
|
@@ -266,7 +241,7 @@ module Persist
|
|
266
241
|
sin.write block
|
267
242
|
end
|
268
243
|
rescue Aborted
|
269
|
-
Log.
|
244
|
+
Log.warn "Tee stream thread aborted"
|
270
245
|
sout.abort if sout.respond_to? :abort
|
271
246
|
sin.abort if sin.respond_to? :abort
|
272
247
|
rescue Exception
|
@@ -328,7 +303,7 @@ module Persist
|
|
328
303
|
res = tee_stream(res.stream, path, type, res.respond_to?(:callback)? res.callback : nil)
|
329
304
|
ConcurrentStream.setup res do
|
330
305
|
begin
|
331
|
-
lockfile.unlock
|
306
|
+
lockfile.unlock if lockfile.locked?
|
332
307
|
rescue
|
333
308
|
Log.exception $!
|
334
309
|
Log.warn "Lockfile exception: " << $!.message
|
@@ -336,7 +311,7 @@ module Persist
|
|
336
311
|
end
|
337
312
|
res.abort_callback = Proc.new do
|
338
313
|
begin
|
339
|
-
lockfile.unlock
|
314
|
+
lockfile.unlock if lockfile.locked?
|
340
315
|
rescue
|
341
316
|
Log.exception $!
|
342
317
|
Log.warn "Lockfile exception: " << $!.message
|
@@ -406,7 +381,7 @@ module Persist
|
|
406
381
|
end
|
407
382
|
|
408
383
|
rescue
|
409
|
-
Log.
|
384
|
+
Log.warn "Error in persist: #{path}#{Open.exists?(path) ? Log.color(:red, " Erasing") : ""}"
|
410
385
|
FileUtils.rm path if Open.exists? path
|
411
386
|
raise $!
|
412
387
|
end
|
@@ -14,13 +14,13 @@ module TSV
|
|
14
14
|
filename_obj = obj.respond_to?(:filename) ? obj.filename : nil
|
15
15
|
filename_obj ||= obj.respond_to?(:path) ? obj.path : nil
|
16
16
|
stream_obj = obj_stream(obj) || obj
|
17
|
-
|
17
|
+
obj.class.to_s << "-" << Misc.fingerprint(stream_obj)
|
18
18
|
end
|
19
19
|
|
20
20
|
def self.report(msg, obj, into)
|
21
21
|
into = into[:into] if Hash === into and into.include? :into
|
22
22
|
|
23
|
-
Log.
|
23
|
+
Log.medium "#{ msg } #{stream_name(obj)} -> #{stream_name(into)}"
|
24
24
|
end
|
25
25
|
|
26
26
|
#{{{ TRAVERSE OBJECTS
|
@@ -96,13 +96,20 @@ module TSV
|
|
96
96
|
|
97
97
|
def self.traverse_io_array(io, options = {}, &block)
|
98
98
|
callback, bar, join = Misc.process_options options, :callback, :bar, :join
|
99
|
+
if File === io and io.closed?
|
100
|
+
begin
|
101
|
+
Log.medium "Rewinding stream #{stream_name(io)}"
|
102
|
+
io.reopen io.filename, "r"
|
103
|
+
rescue
|
104
|
+
Log.exception $!
|
105
|
+
raise "File closed and could not reopen #{stream_name(io)}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
99
109
|
if callback
|
100
110
|
while line = io.gets
|
101
|
-
|
102
|
-
|
103
|
-
ensure
|
104
|
-
bar.tick if bar
|
105
|
-
end
|
111
|
+
callback.call yield line.strip
|
112
|
+
bar.tick if bar
|
106
113
|
end
|
107
114
|
else
|
108
115
|
while line = io.gets
|
@@ -114,17 +121,22 @@ module TSV
|
|
114
121
|
|
115
122
|
def self.traverse_io(io, options = {}, &block)
|
116
123
|
callback, bar, join = Misc.process_options options, :callback, :bar, :join
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
+
if File === io and io.closed?
|
125
|
+
begin
|
126
|
+
Log.medium "Rewinding stream #{stream_name(io)}"
|
127
|
+
io.reopen io.filename, "r"
|
128
|
+
rescue
|
129
|
+
Log.exception $!
|
130
|
+
raise "File closed and could not reopen #{stream_name(io)}"
|
124
131
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
132
|
+
end
|
133
|
+
|
134
|
+
if callback
|
135
|
+
TSV::Parser.traverse(io, options) do |k,v|
|
136
|
+
callback.call yield k, v
|
137
|
+
end
|
138
|
+
else
|
139
|
+
TSV::Parser.traverse(io, options, &block)
|
128
140
|
end
|
129
141
|
join.call if join
|
130
142
|
end
|
@@ -135,64 +147,90 @@ module TSV
|
|
135
147
|
options[:type] = :single
|
136
148
|
end
|
137
149
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
when IO, File
|
153
|
-
begin
|
154
|
-
if options[:type] == :array
|
155
|
-
traverse_io_array(obj, options, &block)
|
150
|
+
Log.medium "Traversing #{stream_name(obj)} #{Log.color :green, "->"} #{stream_name(options[:into])}"
|
151
|
+
begin
|
152
|
+
sleep 1
|
153
|
+
case obj
|
154
|
+
when TSV
|
155
|
+
traverse_tsv(obj, options, &block)
|
156
|
+
when Hash
|
157
|
+
traverse_hash(obj, options, &block)
|
158
|
+
when TSV::Parser
|
159
|
+
callback = Misc.process_options options, :callback
|
160
|
+
if callback
|
161
|
+
obj.traverse(options) do |k,v|
|
162
|
+
callback.call yield k, v
|
163
|
+
end
|
156
164
|
else
|
157
|
-
|
165
|
+
obj.traverse(options, &block)
|
158
166
|
end
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
167
|
+
when IO, File
|
168
|
+
begin
|
169
|
+
if options[:type] == :array
|
170
|
+
traverse_io_array(obj, options, &block)
|
171
|
+
else
|
172
|
+
traverse_io(obj, options, &block)
|
173
|
+
end
|
174
|
+
rescue Aborted
|
175
|
+
obj.abort if obj.respond_to? :abort
|
176
|
+
raise $!
|
177
|
+
rescue Exception
|
178
|
+
obj.abort if obj.respond_to? :abort
|
179
|
+
raise $!
|
180
|
+
ensure
|
181
|
+
obj.close if obj.respond_to? :close and not obj.closed?
|
182
|
+
obj.join if obj.respond_to? :join
|
183
|
+
end
|
184
|
+
when Path
|
185
|
+
obj.open do |stream|
|
186
|
+
traverse_obj(stream, options, &block)
|
187
|
+
end
|
188
|
+
when TSV::Dumper
|
189
|
+
traverse_obj(obj.stream, options, &block)
|
190
|
+
when (defined? Step and Step)
|
173
191
|
|
174
|
-
|
192
|
+
stream = obj.get_stream
|
175
193
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
end
|
182
|
-
when Array
|
183
|
-
traverse_array(obj, options, &block)
|
184
|
-
when String
|
185
|
-
if Open.remote? obj or Misc.is_filename? obj
|
186
|
-
Open.open(obj) do |s|
|
187
|
-
traverse_obj(s, options, &block)
|
194
|
+
if stream
|
195
|
+
traverse_obj(stream, options, &block)
|
196
|
+
else
|
197
|
+
obj.join
|
198
|
+
traverse_obj(obj.path, options, &block)
|
188
199
|
end
|
200
|
+
when Array
|
201
|
+
traverse_array(obj, options, &block)
|
202
|
+
when String
|
203
|
+
if Open.remote? obj or Misc.is_filename? obj
|
204
|
+
Open.open(obj) do |s|
|
205
|
+
traverse_obj(s, options, &block)
|
206
|
+
end
|
207
|
+
else
|
208
|
+
raise "Can not open obj for traversal #{Misc.fingerprint obj}"
|
209
|
+
end
|
210
|
+
when nil
|
211
|
+
raise "Can not traverse nil object into #{stream_name(options[:into])}"
|
189
212
|
else
|
190
|
-
raise "
|
213
|
+
raise "Unknown object for traversal: #{Misc.fingerprint obj }"
|
191
214
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
215
|
+
rescue IOError
|
216
|
+
Log.warn "IOError traversing #{stream_name(obj)}: #{$!.message}"
|
217
|
+
stream = obj_stream(obj)
|
218
|
+
stream.abort if stream and stream.respond_to? :abort
|
219
|
+
raise $!
|
220
|
+
rescue Errno::EPIPE
|
221
|
+
Log.warn "Pipe closed while traversing #{stream_name(obj)}: #{$!.message}"
|
222
|
+
raise $!
|
223
|
+
rescue Aborted
|
224
|
+
Log.warn "Aborted traversing #{stream_name(obj)}"
|
225
|
+
stream = obj_stream(obj)
|
226
|
+
stream.abort if stream and stream.respond_to? :abort
|
227
|
+
raise $!
|
228
|
+
rescue Exception
|
229
|
+
Log.warn "Exception traversing #{stream_name(obj)}"
|
230
|
+
Log.exception $!
|
231
|
+
stream = obj_stream(obj)
|
232
|
+
stream.abort if stream and stream.respond_to? :abort
|
233
|
+
raise $!
|
196
234
|
end
|
197
235
|
end
|
198
236
|
|
@@ -236,9 +274,13 @@ module TSV
|
|
236
274
|
end
|
237
275
|
end
|
238
276
|
|
239
|
-
|
277
|
+
begin
|
278
|
+
thread.join
|
279
|
+
rescue
|
280
|
+
raise $!
|
281
|
+
end
|
240
282
|
rescue Interrupt, Aborted
|
241
|
-
Log.
|
283
|
+
Log.warn "Aborted traversal in CPUs for #{stream_name(obj) || Misc.fingerprint(obj)}"
|
242
284
|
stream = obj_stream(obj)
|
243
285
|
stream.abort if stream.respond_to? :abort
|
244
286
|
stream = obj_stream(options[:into])
|
@@ -246,9 +288,7 @@ module TSV
|
|
246
288
|
q.abort
|
247
289
|
raise $!
|
248
290
|
rescue Exception
|
249
|
-
Log.
|
250
|
-
Log.exception $!
|
251
|
-
|
291
|
+
Log.warn "Exception during traversal in CPUs for #{stream_name(obj) || Misc.fingerprint(obj)}"
|
252
292
|
stream = obj_stream(obj)
|
253
293
|
stream.abort if stream.respond_to? :abort
|
254
294
|
stream = obj_stream(options[:into])
|
@@ -350,11 +390,12 @@ module TSV
|
|
350
390
|
begin
|
351
391
|
traverse_run(obj, threads, cpus, options, &block)
|
352
392
|
into.close if into.respond_to? :close
|
353
|
-
rescue
|
354
|
-
|
355
|
-
|
393
|
+
rescue
|
394
|
+
stream = obj_stream(obj)
|
395
|
+
stream.abort if stream and stream.respond_to? :abort
|
356
396
|
stream = obj_stream(into)
|
357
397
|
stream.abort if stream and stream.respond_to? :abort
|
398
|
+
parent.raise $!
|
358
399
|
end
|
359
400
|
end
|
360
401
|
ConcurrentStream.setup(obj_stream(into), :threads => thread)
|
@@ -398,7 +439,7 @@ module TSV
|
|
398
439
|
|
399
440
|
if into
|
400
441
|
bar = Misc.process_options options, :bar
|
401
|
-
|
442
|
+
|
402
443
|
options[:join] = Proc.new do
|
403
444
|
Log::ProgressBar.remove_bar(bar)
|
404
445
|
end if bar
|
@@ -407,7 +448,6 @@ module TSV
|
|
407
448
|
begin
|
408
449
|
store_into into, e
|
409
450
|
rescue Aborted
|
410
|
-
Log.error "Traversal info #{stream_name into} aborted"
|
411
451
|
raise $!
|
412
452
|
rescue Exception
|
413
453
|
Log.exception $!
|
data/lib/rbbt/tsv/parser.rb
CHANGED
@@ -134,7 +134,6 @@ module TSV
|
|
134
134
|
return parts.shift.split(@sep2, -1).first, parts.collect{|value| value.split(@sep2, -1)}.flatten if
|
135
135
|
field_positions.nil? and (key_position.nil? or key_position == 0)
|
136
136
|
rescue
|
137
|
-
eee [:rescue, orig]
|
138
137
|
raise $!
|
139
138
|
end
|
140
139
|
|
@@ -536,11 +535,14 @@ module TSV
|
|
536
535
|
end
|
537
536
|
rescue END_PARSING
|
538
537
|
break
|
539
|
-
|
540
|
-
|
541
|
-
|
538
|
+
rescue Errno::EPIPE
|
539
|
+
Log.error "Pipe closed while parsing #{Misc.fingerprint stream}: #{$!.message}"
|
540
|
+
raise $!
|
542
541
|
rescue Exception
|
542
|
+
Log.error "Exception parsing #{Misc.fingerprint stream}: #{$!.message}"
|
543
|
+
Log.exception $!
|
543
544
|
stream.abort if stream.respond_to? :abort
|
545
|
+
stream.join if stream.respond_to? :join
|
544
546
|
raise $!
|
545
547
|
end
|
546
548
|
end
|
data/lib/rbbt/tsv/stream.rb
CHANGED
@@ -28,8 +28,8 @@ module TSV
|
|
28
28
|
input_options = []
|
29
29
|
|
30
30
|
input_source_streams = inputs.collect do |input|
|
31
|
-
stream = TSV.get_stream
|
32
|
-
stream
|
31
|
+
stream = sort ? Misc.sort_stream(input) : TSV.get_stream(input)
|
32
|
+
stream
|
33
33
|
end
|
34
34
|
|
35
35
|
input_source_streams.each do |stream|
|
@@ -52,4 +52,109 @@ module TSV
|
|
52
52
|
dumper.stream = Misc.paste_streams input_streams, input_lines, options[:sep], header
|
53
53
|
dumper
|
54
54
|
end
|
55
|
+
|
56
|
+
def self.paste_streams(streams, options = {})
|
57
|
+
options = Misc.add_defaults options, :sep => "\t", :sort => true
|
58
|
+
sort, sep = Misc.process_options options, :sort, :sep
|
59
|
+
|
60
|
+
Misc.open_pipe do |sin|
|
61
|
+
num_streams = streams.length
|
62
|
+
|
63
|
+
streams = streams.collect do |stream|
|
64
|
+
if defined? Step and Step === stream
|
65
|
+
stream.get_stream || stream.join.path.open
|
66
|
+
else
|
67
|
+
stream
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
streams = streams.collect do |stream|
|
72
|
+
Misc.sort_stream(stream)
|
73
|
+
end if sort
|
74
|
+
|
75
|
+
lines = []
|
76
|
+
fields = []
|
77
|
+
key_fields = []
|
78
|
+
input_options = []
|
79
|
+
empty = []
|
80
|
+
|
81
|
+
streams = streams.collect do |stream|
|
82
|
+
parser = TSV::Parser.new stream, options
|
83
|
+
lines << parser.first_line
|
84
|
+
empty << stream if parser.first_line.nil?
|
85
|
+
key_fields << parser.key_field
|
86
|
+
fields << parser.fields
|
87
|
+
input_options << parser.options
|
88
|
+
|
89
|
+
parser.stream
|
90
|
+
end
|
91
|
+
|
92
|
+
key_field = key_fields.compact.first
|
93
|
+
fields = fields.compact.flatten
|
94
|
+
options = options.merge(input_options.first)
|
95
|
+
|
96
|
+
sin.puts TSV.header_lines(key_field, fields, options)
|
97
|
+
|
98
|
+
empty.each do |stream|
|
99
|
+
i = streams.index stream
|
100
|
+
lines.delete_at i
|
101
|
+
fields.delete_at i
|
102
|
+
key_fields.delete_at i
|
103
|
+
input_options.delete_at i
|
104
|
+
end
|
105
|
+
|
106
|
+
begin
|
107
|
+
done_streams = []
|
108
|
+
|
109
|
+
keys = []
|
110
|
+
parts = []
|
111
|
+
lines.each_with_index do |line,i|
|
112
|
+
key, *p = line.strip.split(sep, -1)
|
113
|
+
keys[i] = key
|
114
|
+
parts[i] = p
|
115
|
+
end
|
116
|
+
sizes = parts.collect{|p| p.length }
|
117
|
+
last_min = nil
|
118
|
+
while lines.compact.any?
|
119
|
+
min = keys.compact.sort.first
|
120
|
+
str = []
|
121
|
+
keys.each_with_index do |key,i|
|
122
|
+
case key
|
123
|
+
when min
|
124
|
+
str << [parts[i] * sep]
|
125
|
+
line = lines[i] = begin
|
126
|
+
streams[i].gets
|
127
|
+
rescue
|
128
|
+
Log.exception $!
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
if line.nil?
|
132
|
+
stream = streams[i]
|
133
|
+
#stream.join if stream.respond_to? :join
|
134
|
+
keys[i] = nil
|
135
|
+
parts[i] = nil
|
136
|
+
else
|
137
|
+
k, *p = line.strip.split(sep, -1)
|
138
|
+
keys[i] = k
|
139
|
+
parts[i] = p
|
140
|
+
end
|
141
|
+
else
|
142
|
+
str << [sep * (sizes[i]-1)] if sizes[i] > 0
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
sin.puts [min, str*sep] * sep
|
147
|
+
end
|
148
|
+
streams.each do |stream|
|
149
|
+
#stream.join if stream.respond_to? :join
|
150
|
+
end
|
151
|
+
rescue
|
152
|
+
Log.exception $!
|
153
|
+
streams.each do |stream|
|
154
|
+
stream.abort if stream.respond_to? :abort
|
155
|
+
end
|
156
|
+
raise $!
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
55
160
|
end
|