rbbt-util 5.12.3 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/rbbt/persist.rb +127 -109
- data/lib/rbbt/tsv/dumper.rb +0 -1
- data/lib/rbbt/tsv/parallel/traverse.rb +101 -36
- data/lib/rbbt/util/concurrency/processes.rb +5 -2
- data/lib/rbbt/util/concurrency/threads.rb +6 -4
- data/lib/rbbt/util/log.rb +1 -0
- data/lib/rbbt/util/log/progress.rb +163 -0
- data/lib/rbbt/util/misc/options.rb +1 -0
- data/lib/rbbt/util/misc/pipes.rb +63 -50
- data/lib/rbbt/util/misc/progress.rb +0 -0
- data/lib/rbbt/util/open.rb +45 -11
- data/lib/rbbt/util/simpleopt/get.rb +1 -1
- data/lib/rbbt/workflow/accessor.rb +59 -38
- data/lib/rbbt/workflow/step/run.rb +2 -3
- data/share/rbbt_commands/workflow/task +14 -4
- data/test/rbbt/tsv/parallel/test_traverse.rb +35 -1
- data/test/rbbt/util/log/test_progress.rb +49 -0
- data/test/rbbt/util/misc/test_pipes.rb +4 -4
- data/test/rbbt/util/test_open.rb +0 -3
- metadata +6 -2
@@ -4,11 +4,12 @@ require 'rbbt/util/concurrency/processes/socket'
|
|
4
4
|
class RbbtProcessQueue
|
5
5
|
#{{{ RbbtProcessQueue
|
6
6
|
|
7
|
-
attr_accessor :num_processes, :processes, :queue, :process_monitor, :cleanup
|
8
|
-
def initialize(num_processes, cleanup = nil)
|
7
|
+
attr_accessor :num_processes, :processes, :queue, :process_monitor, :cleanup, :join
|
8
|
+
def initialize(num_processes, cleanup = nil, join = nil)
|
9
9
|
@num_processes = num_processes
|
10
10
|
@processes = []
|
11
11
|
@cleanup = cleanup
|
12
|
+
@join = join
|
12
13
|
@queue = RbbtProcessSocket.new
|
13
14
|
end
|
14
15
|
|
@@ -90,6 +91,8 @@ class RbbtProcessQueue
|
|
90
91
|
ensure
|
91
92
|
@queue.swrite.close
|
92
93
|
end
|
94
|
+
|
95
|
+
@join.call if @join
|
93
96
|
end
|
94
97
|
|
95
98
|
def clean
|
@@ -4,26 +4,28 @@ class RbbtThreadQueue
|
|
4
4
|
class RbbtThreadQueueWorker < Thread
|
5
5
|
def initialize(queue, mutex = nil, &block)
|
6
6
|
if mutex.nil?
|
7
|
-
super(Thread.current) do |
|
7
|
+
super(Thread.current) do |parent|
|
8
8
|
begin
|
9
9
|
loop do
|
10
10
|
p = queue.pop
|
11
11
|
block.call *p
|
12
12
|
end
|
13
|
+
rescue Aborted
|
13
14
|
rescue Exception
|
14
|
-
|
15
|
+
parent.raise $!
|
15
16
|
end
|
16
17
|
end
|
17
18
|
else
|
18
|
-
super(Thread.current) do |
|
19
|
+
super(Thread.current) do |parent|
|
19
20
|
begin
|
20
21
|
loop do
|
21
22
|
p = queue.pop
|
22
23
|
p = Array === p ? p << mutex : [p,mutex]
|
23
24
|
block.call *p
|
24
25
|
end
|
26
|
+
rescue Aborted
|
25
27
|
rescue Exception
|
26
|
-
|
28
|
+
parent.raise $!
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
data/lib/rbbt/util/log.rb
CHANGED
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'rbbt/util/log'
|
2
|
+
module Log
|
3
|
+
class ProgressBar
|
4
|
+
|
5
|
+
attr_accessor :depth, :num_reports, :desc, :io, :severity
|
6
|
+
|
7
|
+
# Creates a new instance. Max is the total number of iterations of the
|
8
|
+
# loop. The depth represents how many other loops are above this one,
|
9
|
+
# this information is used to find the place to print the progress
|
10
|
+
# report.
|
11
|
+
def initialize(max, options = {})
|
12
|
+
options = Misc.add_defaults options, :depth => 0, :num_reports => 100, :desc => "Progress", :io => STDERR, :severity => Log.severity
|
13
|
+
depth, num_reports, desc, io, severity = Misc.process_options options, :depth, :num_reports, :desc, :io, :severity
|
14
|
+
|
15
|
+
@max = max
|
16
|
+
@max = 1 if @max and @max < 1
|
17
|
+
@current = 0
|
18
|
+
@time = Time.now
|
19
|
+
@last_report = -1
|
20
|
+
@num_reports = num_reports
|
21
|
+
@severity = severity
|
22
|
+
@depth = depth
|
23
|
+
@desc = desc
|
24
|
+
end
|
25
|
+
|
26
|
+
# Used to register a new completed loop iteration.
|
27
|
+
def tick(step = nil)
|
28
|
+
|
29
|
+
if step.nil?
|
30
|
+
@current += 1
|
31
|
+
else
|
32
|
+
@current = step
|
33
|
+
end
|
34
|
+
|
35
|
+
if @max
|
36
|
+
if percent - @last_report > 1.to_f/@num_reports.to_f
|
37
|
+
report
|
38
|
+
@last_report=percent
|
39
|
+
end
|
40
|
+
else
|
41
|
+
@last_report = Time.now if @last_report == -1
|
42
|
+
if Time.now - @last_report >= 1.0
|
43
|
+
throughput
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def progress
|
52
|
+
@current.to_f/ @max
|
53
|
+
end
|
54
|
+
|
55
|
+
def percent
|
56
|
+
(self.progress * 100).to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
def eta
|
60
|
+
(Time.now - @time)/progress * (1-progress)
|
61
|
+
end
|
62
|
+
|
63
|
+
def used
|
64
|
+
(Time.now - @time).to_i
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def up_lines(depth)
|
69
|
+
"\033[#{depth + 2}F\033[2K"
|
70
|
+
end
|
71
|
+
|
72
|
+
def down_lines(depth)
|
73
|
+
"\n\033[#{depth + 3}E"
|
74
|
+
end
|
75
|
+
|
76
|
+
def report_msg
|
77
|
+
progress = self.progress
|
78
|
+
percent = self.percent
|
79
|
+
|
80
|
+
indicator = Log.color(:magenta, @desc) << " "
|
81
|
+
10.times{|i|
|
82
|
+
if i < progress * 10 then
|
83
|
+
indicator << Log.color(:yellow, ".")
|
84
|
+
else
|
85
|
+
indicator << " "
|
86
|
+
end
|
87
|
+
}
|
88
|
+
done = progress == 1
|
89
|
+
|
90
|
+
used = self.used
|
91
|
+
used = [used/3600, used/60 % 60, used % 60].map{|t| "%02i" % t }.join(':')
|
92
|
+
|
93
|
+
if progress == 1
|
94
|
+
indicator << Log.color(:green, " done ")
|
95
|
+
|
96
|
+
|
97
|
+
indicator << Log.color(:blue, " #{used}")
|
98
|
+
else
|
99
|
+
indicator << " done #{Log.color(:blue, percent.to_s << "%")}"
|
100
|
+
|
101
|
+
eta = self.eta
|
102
|
+
eta = [eta/3600, eta/60 % 60, eta % 60].map{|t| "%02i" % t }.join(':')
|
103
|
+
|
104
|
+
indicator << " (Time left #{eta} seconds) (Started #{used} seconds ago)"
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
def throughput_msg
|
110
|
+
indicator = Log.color(:magenta, @desc)
|
111
|
+
time = Time.now - @last_report
|
112
|
+
thr = (@current / time).to_i
|
113
|
+
indicator << " #{ Log.color :blue, thr } per second"
|
114
|
+
indicator
|
115
|
+
end
|
116
|
+
|
117
|
+
# Prints de progress report. It backs up as many lines as the meters
|
118
|
+
# depth. Prints the progress as a line of dots, a percentage, time
|
119
|
+
# spent, and time left. And then goes moves the cursor back to its
|
120
|
+
# original line. Everything is printed to stderr.
|
121
|
+
def report(io = STDERR)
|
122
|
+
io.print(up_lines(@depth) << report_msg << down_lines(@depth)) if severity >= Log.severity
|
123
|
+
end
|
124
|
+
|
125
|
+
def throughput(io = STDERR)
|
126
|
+
io.print(up_lines(@depth) << throughput_msg << down_lines(@depth)) if severity >= Log.severity
|
127
|
+
@last_report = Time.now
|
128
|
+
@current = 0
|
129
|
+
end
|
130
|
+
BAR_MUTEX = Mutex.new
|
131
|
+
BARS = []
|
132
|
+
def self.new_bar(max, options = {})
|
133
|
+
options = Misc.add_defaults options, :depth => BARS.length
|
134
|
+
BAR_MUTEX.synchronize do
|
135
|
+
BARS << (bar = ProgressBar.new(max, options))
|
136
|
+
bar
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.remove_bar(bar)
|
141
|
+
BAR_MUTEX.synchronize do
|
142
|
+
index = BARS.index bar
|
143
|
+
if index
|
144
|
+
(index+1..BARS.length-1).each do |pos|
|
145
|
+
bar = BARS[pos]
|
146
|
+
bar.depth = pos - 1
|
147
|
+
BARS[pos-1] = bar
|
148
|
+
end
|
149
|
+
BARS.pop
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.with_bar(max, options = {})
|
155
|
+
bar = new_bar(max, options)
|
156
|
+
begin
|
157
|
+
yield bar
|
158
|
+
ensure
|
159
|
+
remove_bar(bar)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
data/lib/rbbt/util/misc/pipes.rb
CHANGED
@@ -73,65 +73,74 @@ module Misc
|
|
73
73
|
sout
|
74
74
|
end
|
75
75
|
|
76
|
-
def self.tee_stream_fork(stream)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
76
|
+
#def self.tee_stream_fork(stream)
|
77
|
+
# stream_out1, stream_in1 = Misc.pipe
|
78
|
+
# stream_out2, stream_in2 = Misc.pipe
|
79
|
+
|
80
|
+
# splitter_pid = Process.fork do
|
81
|
+
# Misc.purge_pipes(stream_in1, stream_in2)
|
82
|
+
# stream_out1.close
|
83
|
+
# stream_out2.close
|
84
|
+
# begin
|
85
|
+
# filename = stream.respond_to?(:filename)? stream.filename : nil
|
86
|
+
# skip1 = skip2 = false
|
87
|
+
# while block = stream.read(2048)
|
88
|
+
# begin stream_in1.write block; rescue Exception; Log.exception $!; skip1 = true end unless skip1
|
89
|
+
# begin stream_in2.write block; rescue Exception; Log.exception $!; skip2 = true end unless skip2
|
90
|
+
# end
|
91
|
+
# raise "Error writing in stream_in1" if skip1
|
92
|
+
# raise "Error writing in stream_in2" if skip2
|
93
|
+
# stream.join if stream.respond_to? :join
|
94
|
+
# stream_in1.close
|
95
|
+
# stream_in2.close
|
96
|
+
# rescue Aborted
|
97
|
+
# stream.abort if stream.respond_to? :abort
|
98
|
+
# raise $!
|
99
|
+
# rescue IOError
|
100
|
+
# Log.exception $!
|
101
|
+
# rescue Exception
|
102
|
+
# Log.exception $!
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
# stream.close
|
106
|
+
# stream_in1.close
|
107
|
+
# stream_in2.close
|
108
|
+
|
109
|
+
# ConcurrentStream.setup stream_out1, :pids => [splitter_pid]
|
110
|
+
# ConcurrentStream.setup stream_out2, :pids => [splitter_pid]
|
111
|
+
|
112
|
+
# [stream_out1, stream_out2]
|
113
|
+
#end
|
114
114
|
|
115
115
|
def self.tee_stream_thread(stream)
|
116
116
|
stream_out1, stream_in1 = Misc.pipe
|
117
117
|
stream_out2, stream_in2 = Misc.pipe
|
118
118
|
|
119
|
-
splitter_thread = Thread.new(Thread.current
|
119
|
+
splitter_thread = Thread.new(Thread.current) do |parent|
|
120
120
|
begin
|
121
121
|
filename = stream.respond_to?(:filename)? stream.filename : nil
|
122
122
|
skip1 = skip2 = false
|
123
123
|
while block = stream.read(2048)
|
124
|
-
begin
|
125
|
-
|
124
|
+
begin
|
125
|
+
stream_in1.write block;
|
126
|
+
rescue IOError
|
127
|
+
Log.error("Tee stream 1 #{stream} IOError: #{$!.message}");
|
128
|
+
skip1 = true
|
129
|
+
end unless skip1
|
130
|
+
|
131
|
+
begin
|
132
|
+
stream_in2.write block
|
133
|
+
rescue IOError
|
134
|
+
Log.error("Tee stream 2 #{stream} IOError: #{$!.message}");
|
135
|
+
skip2 = true
|
136
|
+
end unless skip2
|
126
137
|
end
|
127
|
-
stream_in1.close
|
128
|
-
stream_in2.close
|
138
|
+
stream_in1.close unless stream_in1.closed?
|
139
|
+
stream_in2.close unless stream_in2.closed?
|
129
140
|
stream.join if stream.respond_to? :join
|
130
141
|
rescue Aborted
|
142
|
+
Log.error("Tee stream #{stream} Aborted");
|
131
143
|
stream.abort if stream.respond_to? :abort
|
132
|
-
parent.raise $!
|
133
|
-
rescue IOError
|
134
|
-
Log.exception $!
|
135
144
|
rescue Exception
|
136
145
|
Log.exception $!
|
137
146
|
parent.raise $!
|
@@ -207,10 +216,11 @@ module Misc
|
|
207
216
|
end
|
208
217
|
|
209
218
|
def self.sensiblewrite(path, content = nil, &block)
|
210
|
-
|
219
|
+
|
220
|
+
return if Open.exists? path
|
211
221
|
tmp_path = Persist.persistence_path(path, {:dir => Misc.sensiblewrite_dir})
|
212
222
|
Misc.lock tmp_path do
|
213
|
-
if not
|
223
|
+
if not Open.exists? path
|
214
224
|
FileUtils.rm_f tmp_path if File.exists? tmp_path
|
215
225
|
begin
|
216
226
|
case
|
@@ -227,10 +237,13 @@ module Misc
|
|
227
237
|
else
|
228
238
|
File.open(tmp_path, 'w') do |f| end
|
229
239
|
end
|
230
|
-
|
240
|
+
|
241
|
+
Open.mv tmp_path, path
|
242
|
+
rescue Aborted
|
243
|
+
Log.error "Aborted sensiblewrite -- #{ Log.color :blue, path }"
|
231
244
|
rescue Exception
|
232
245
|
Log.error "Exception in sensiblewrite: #{$!.message} -- #{ Log.color :blue, path }"
|
233
|
-
|
246
|
+
Open.rm_f path if File.exists? path
|
234
247
|
raise $!
|
235
248
|
ensure
|
236
249
|
FileUtils.rm_f tmp_path if File.exists? tmp_path
|
File without changes
|
data/lib/rbbt/util/open.rb
CHANGED
@@ -145,29 +145,34 @@ module Open
|
|
145
145
|
|
146
146
|
def self.get_stream_from_repo(dir, sub_path)
|
147
147
|
repo = get_repo_from_dir(dir)
|
148
|
-
repo.
|
149
|
-
|
148
|
+
repo.read_and_close do
|
149
|
+
StringIO.new repo[sub_path]
|
150
|
+
end
|
150
151
|
end
|
151
152
|
|
152
153
|
def self.save_content_in_repo(dir, sub_path, content)
|
153
154
|
repo = get_repo_from_dir(dir)
|
154
|
-
repo.
|
155
|
-
|
155
|
+
repo.write_and_close do
|
156
|
+
repo[sub_path] = content
|
157
|
+
end
|
156
158
|
end
|
157
159
|
|
158
160
|
def self.remove_from_repo(dir, sub_path, recursive = false)
|
159
161
|
repo = get_repo_from_dir(dir)
|
160
|
-
repo.
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
162
|
+
repo.write_and_close do
|
163
|
+
if recursive
|
164
|
+
repo.outlist repo.range sub_path, true, sub_path.sub(/.$/,('\1'.ord + 1).chr), false
|
165
|
+
else
|
166
|
+
repo.outlist sub_path
|
167
|
+
end
|
165
168
|
end
|
166
169
|
end
|
167
170
|
|
168
171
|
def self.exists_in_repo(dir, sub_path, content)
|
169
172
|
repo = get_repo_from_dir(dir)
|
170
|
-
repo.
|
173
|
+
repo.read_and_close do
|
174
|
+
repo.include? sub_path
|
175
|
+
end
|
171
176
|
end
|
172
177
|
|
173
178
|
def self.find_repo_dir(file)
|
@@ -198,7 +203,7 @@ module Open
|
|
198
203
|
|
199
204
|
def self.file_open(file, grep, mode = 'r', invert_grep = false)
|
200
205
|
if (dir_sub_path = find_repo_dir(file))
|
201
|
-
stream =
|
206
|
+
stream = get_stream_from_repo(*dir_sub_path)
|
202
207
|
else
|
203
208
|
stream = File.open(file, mode)
|
204
209
|
end
|
@@ -227,6 +232,34 @@ module Open
|
|
227
232
|
end
|
228
233
|
end
|
229
234
|
|
235
|
+
def self.mv(source, target)
|
236
|
+
dir_sub_path_source = find_repo_dir(source)
|
237
|
+
dir_sub_path_target = find_repo_dir(target)
|
238
|
+
|
239
|
+
return FileUtils.mv source, target if dir_sub_path_source.nil? and dir_sub_path_target.nil?
|
240
|
+
|
241
|
+
if dir_sub_path_source.nil?
|
242
|
+
save_content_in_repo(dir_sub_path_target[0], dir_sub_path_target[1], Open.read(source))
|
243
|
+
return nil
|
244
|
+
end
|
245
|
+
|
246
|
+
if dir_sub_path_target.nil?
|
247
|
+
Open.write(target, get_stream_from_repo(dir_sub_path_source))
|
248
|
+
return nil
|
249
|
+
end
|
250
|
+
|
251
|
+
repo_source = get_repo_from_dir(dir_sub_path_source[0])
|
252
|
+
repo_target = get_repo_from_dir(dir_sub_path_target[0])
|
253
|
+
|
254
|
+
repo_source.write do
|
255
|
+
repo_target.write do
|
256
|
+
repo_source[dir_sub_path_source[1]] = repo_target[dir_sub_path_target[1]]
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
return nil
|
261
|
+
end
|
262
|
+
|
230
263
|
def self.exists?(file)
|
231
264
|
if (dir_sub_path = find_repo_dir(file))
|
232
265
|
dir_sub_path.push file
|
@@ -358,6 +391,7 @@ module Open
|
|
358
391
|
end
|
359
392
|
|
360
393
|
io.filename = url.to_s
|
394
|
+
|
361
395
|
io
|
362
396
|
end
|
363
397
|
|