long-command-runner 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 732ddcf74afb4d4a3f7e5990e4761e4fabbea37824890c98e8cc236c3339a317
4
- data.tar.gz: 3209440517bcfcb93e020e01a3484e029487af1e61b95e86d932db6c27b30c7e
3
+ metadata.gz: 52ed0ae54876241ebf67b4fad432492782745247b45f69ac2815eb3f1a24ed3c
4
+ data.tar.gz: 5394c9c043976a7842aa1201a3e76d09dbd849212641181a8220b320efc2b604
5
5
  SHA512:
6
- metadata.gz: ac8cc65bf6dfc634469ca90d9312183aa17c7ec3d7d6649bddfc505879babbf0adadc503124a384d6fe3d78aa335d2d90e230bdfb7cf1d8670bfae04496efd6e
7
- data.tar.gz: 004ca4095874c144356593c7ceedef1c8a3983b2f6a5fbf18cfd0da5ca154fac670bc64cc81acf22644a5e5603e47a026fb1231c96b69c0cfdf1bf4644e86f19
6
+ metadata.gz: dde08e22ddb8396d3d3dd97dc115fad4b8034cf89c5d1042de489fd52fe4e392a72d00bfdb453039db70f247dd4bc008f58863bfa38e87239a3c756ce3ed063a
7
+ data.tar.gz: f89f3c02c3e055cb69714e2a267628dc9773d8d00eb061f774e9015527f631b0e479e8b737fac5471fc63b1368207b1f52c82a733ccd18ba890aa235948ac456
@@ -6,7 +6,7 @@ module LCR
6
6
  # This class aims to contain the runing script.
7
7
  class Container
8
8
  # The expected Language environment:
9
- ENVIRONMENT = 'LANGUAGE=en'
9
+ ENVIRONMENT = { 'LANGUAGE' => 'en' }.freeze
10
10
 
11
11
  # Get the standard input IO of the process.
12
12
  #
@@ -38,7 +38,7 @@ module LCR
38
38
  # @param [Hash] opts The option to pass to Open3.popen3.
39
39
  def initialize(command, opts = {})
40
40
  safe_command = quote_sanitize(command)
41
- @stdin, @stdout, @stderr, @wait_thr = Open3.popen3("#{ENVIRONMENT} bash -c \"#{safe_command}\"", opts)
41
+ @stdin, @stdout, @stderr, @wait_thr = Open3.popen3(ENVIRONMENT, "bash -c \"#{safe_command}\"", opts)
42
42
  end
43
43
 
44
44
  # Is the last launched command is still running.
@@ -69,6 +69,13 @@ module LCR
69
69
  @wait_thr.pid
70
70
  end
71
71
 
72
+ def children
73
+ get_children = lambda do |pid|
74
+ `pgrep -P #{pid}`.split("\n").map { |c_pid| [c_pid.to_i, get_children.call(c_pid)] }.to_h
75
+ end
76
+ get_children.call(pid)
77
+ end
78
+
72
79
  private
73
80
 
74
81
  def quote_sanitize(cmd)
@@ -29,11 +29,9 @@ module LCR
29
29
  end
30
30
 
31
31
  # access to the array of streams.
32
- # The intance variable is duplicated to prevent modification of the array.
32
+ # Be carefull accessing these, because you may prevent line_reader to run correctly.
33
33
  # @return [Array<IO>]
34
- def streams
35
- @streams.dup
36
- end
34
+ attr_reader :streams
37
35
 
38
36
  # Blocking method to read on the streams (you may call it in a dedicated thread).
39
37
  #
@@ -103,7 +101,7 @@ module LCR
103
101
  # puts "read_on(buffers:#{buffers}, streams_lines:#{stream_newlines}, io:#{io}, line:#{line})"
104
102
  if io.eof?
105
103
  stream_newlines[line] = [buffers[line]] unless buffers[line].nil?
106
- # buffers[line] = ''
104
+ buffers[line] = nil
107
105
  return false
108
106
  end
109
107
  r = io.read_nonblock(MAX_READ_CHAR)
@@ -16,12 +16,18 @@ module LCR
16
16
  # The constant used to get the percetange of completion of the command.
17
17
  PERCENT_INDICATOR = /(.*\s|^)((\d+)([,.]\d*)?)%.*/.freeze
18
18
 
19
- # Get the mesured progress in percentage.
19
+ # [Float] Get the mesured progress in percentage.
20
20
  # This is just the result of parsing stdout lines with {PERCENT_INDICATOR}.
21
21
  # So this percentage is comming from the thread not this library.
22
- attr_reader :progress, :tms
22
+ attr_reader :progress
23
+ # [Benchmark::Tms] The time mesured of execution in the runner thread.
24
+ attr_reader :tms
23
25
 
24
26
  # Initializer takes the command as a plain string.
27
+ # @param [String] command The command to run (can be a one-line shell script)
28
+ # @option opts [Boolean] :do_progress_on Set to `'stderr'`, if you want to use
29
+ # stderr to get the progression percentage instead of the default: `'stdout'`.
30
+ # Any other value will deactivate the feature.
25
31
  # You can optionaly pass a block that will be called when the command generate
26
32
  # outputs:
27
33
  # @yield [nl_stdout, nl_stderr] call when at least a line comes on stdout or stderr.
@@ -30,13 +36,14 @@ module LCR
30
36
  # @yieldparam [String | nil] nl_stderr The new line without the return line character.
31
37
  # This may be nil if only a new line has been found on stdout.
32
38
  # @yieldreturn [void] It is ignored.
33
- def initialize(command, &on_input)
39
+ def initialize(command, opts = {}, &on_input)
34
40
  @command = command
35
41
  @container = nil
36
42
  @launch_lock = Mutex.new
37
43
  @on_input = on_input
38
44
  @progress = 0.0
39
45
  @tms = nil
46
+ @do_progress_on = opts[:do_progress_on] || 'stdout'
40
47
  end
41
48
 
42
49
  # Is the last launched command is still running.
@@ -122,13 +129,14 @@ module LCR
122
129
  def kill(signal)
123
130
  return nil if @container.nil?
124
131
 
125
- Process.kill(signal, @container.pid)
132
+ children = @container.children
133
+ Runner.kill_children(signal, @container.pid, children)
126
134
  rescue Errno::ESRCH
127
135
  nil
128
136
  end
129
137
 
130
138
  # Old get the time really spend ('real' part of `time` command)
131
- # DEPRECATED: use tms access now.
139
+ # DEPRECATED: use tms.real access now.
132
140
  def time_real
133
141
  warn '[DEPRECATION] use :tms instead of :time_real'
134
142
  return nil if @tms.nil?
@@ -136,28 +144,46 @@ module LCR
136
144
  @tms.real
137
145
  end
138
146
 
139
- # Old get the time in user space spend ('user' part of `time` command)
140
- # DEPRECATED: use tms access now.
147
+ # Get the total time spent in user space (sum of tms.cutime + tms.utime)
141
148
  def time_user
142
- warn '[DEPRECATION] use :tms instead of :time_user'
143
149
  return nil if @tms.nil?
144
150
 
145
151
  @tms.cutime + @tms.utime
146
152
  end
147
153
 
148
- # old get the time system space spend ('sys' part of `time` command)
149
- # DEPRECATED: use tms access now.
154
+ # Get the total time spent in system space (sum of tms.cstime + tms.stime)
150
155
  def time_sys
151
- warn '[DEPRECATION] use :tms instead of :time_user'
152
156
  return nil if @tms.nil?
153
157
 
154
158
  @tms.cstime + @tms.stime
155
159
  end
156
160
 
161
+ def self.dead?(pid)
162
+ Process.kill(0, pid)
163
+ false
164
+ rescue Errno::ESRCH
165
+ true
166
+ end
167
+
168
+ def self.kill_children(signal, pid, children)
169
+ n = Process.kill(signal, pid)
170
+ sleep 0.1
171
+ children.keys.reject { |c_pid| dead? c_pid }.each do |c_pid|
172
+ # the not dead
173
+ n += kill_children(signal, c_pid, children[c_pid])
174
+ end
175
+ n
176
+ end
177
+
157
178
  private
158
179
 
159
180
  def on_newline(nl_stdout, nl_stderr)
160
- manage_progress(nl_stdout) unless nl_stdout.nil?
181
+ nl_progress = if @do_progress_on == 'stdout'
182
+ nl_stdout
183
+ elsif @do_progress_on == 'stderr'
184
+ nl_stderr
185
+ end
186
+ manage_progress(nl_progress) unless nl_progress.nil?
161
187
  @on_input&.call(nl_stdout, nl_stderr)
162
188
  end
163
189
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module LCR
4
4
  # Version of the library.
5
- VERSION = '0.2.0'
5
+ VERSION = '0.3.0'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: long-command-runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roland Laurès
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-07 00:00:00.000000000 Z
11
+ date: 2019-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry