process_executer 0.4.0 → 0.6.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: fc6b432be7a421bc53561149ce4718a41fd7ff61b1b2213498000891c388a0c9
4
- data.tar.gz: ce582e04a3500b0b6c0e31af16d2d735e87f9c99c5d82279c591c9e5abc0a619
3
+ metadata.gz: 9ac0d1397bbe7c4f632a017f4b66ccdfba6860eed0bb616e65b385ef43bcd613
4
+ data.tar.gz: 3b4430d7c79739767bcf8f24ab644834eb4a43e89876ef27c1b40032621b72e9
5
5
  SHA512:
6
- metadata.gz: 312c65d62033f2e3a13ed5094de32940b9923d775d45f3d90dc6cbe4f0a8d0dcf3e5e772e6add2999c97e2b2980b39d4fa3d4f7ddf0172e9877d7e94a1770d94
7
- data.tar.gz: 267da77563194721787fee28ff41cdf19159f083f5a0cb04d6d912e0271f2e2062b50da8d1655b36bea2ebb5327f3f3d618aa90453ad60ba61d72e4c779afe84
6
+ metadata.gz: 63766977ee0f62f6f8ef705c5bc47ecc5358bf06bb9764245ca7c4d6114adf7d0cfef443fac5c89597b5f504ef0b52d8444cf03436c8aae00c0e6ed6fc1d22ad
7
+ data.tar.gz: 1e8d38c8b4a1169177cd41a8767da33c2b15e2c7a45c3d7ef042aeafc7ba46b19bebfc63ef1bc0b8cd781b312300fbf51363b2305207d0d752895dd0a0dca735
data/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ All notable changes to the process_executer gem will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## v0.6.0 (2023-02-12)
9
+
10
+ [Full Changelog](https://github.com/main-branch/process_executer/compare/v0.5.0..v0.6.0)
11
+
12
+ Changes since v0.5.0:
13
+
14
+ * 2a22dbd Fix intermittent test failures (#21)
15
+ * e3afaa3 Add build for MRI Ruby 3.2 on unbuntu-latest (#20)
16
+ * 17522ac Use latest create_release_version gem (#19)
17
+ * ba1fb2d Read remaining data from pipe_reader when closing a MonitoredPipe (#17)
18
+ * 8422aa9 Release v0.5.0
19
+
20
+ ## v0.5.0 (2022-12-12)
21
+
22
+ [Full Changelog](https://github.com/main-branch/process_executer/compare/v0.4.0...v0.5.0)
23
+
24
+ * c6d8de9 Workaround a problem with SimpleCov / JRuby
25
+ * c480b5f Increase time to wait for results from a writer throwing an exception
26
+ * 1934563 Handle exceptions from writers within MonitoredPipe
27
+ * e948ada Increase default chunk_size to 100_000 bytes
28
+ * 5eb2c24 Update documentation for ProcessExecuter#spawn
29
+ * a3a4217 Release v0.4.0
30
+
8
31
  ## v0.4.0 (2022-12-06)
9
32
 
10
33
  [Full Changelog](https://github.com/main-branch/process_executer/compare/v0.3.0...v0.4.0)
@@ -12,6 +12,9 @@ module ProcessExecuter
12
12
  # Data that is read from that pipe is written one or more writers passed to
13
13
  # `#initialize`.
14
14
  #
15
+ # If any of the writers raise an exception, the monitoring thread will exit, the
16
+ # pipe will be closed, and the exception will be saved in `#exception`.
17
+ #
15
18
  # `#close` must be called to ensure that (1) the pipe is closed, (2) all data is
16
19
  # read from the pipe and written to the writers, and (3) the monitoring thread is
17
20
  # killed.
@@ -52,12 +55,16 @@ module ProcessExecuter
52
55
  # @param writers [Array<#write>] as data is read from the pipe, it is written to these writers
53
56
  # @param chunk_size [Integer] the size of the chunks to read from the pipe
54
57
  #
55
- def initialize(*writers, chunk_size: 1000)
58
+ def initialize(*writers, chunk_size: 100_000)
56
59
  @writers = writers
57
60
  @chunk_size = chunk_size
58
61
  @pipe_reader, @pipe_writer = IO.pipe
59
62
  @state = :open
60
- @thread = Thread.new { monitor }
63
+ @thread = Thread.new do
64
+ Thread.current.report_on_exception = false
65
+ Thread.current.abort_on_exception = false
66
+ monitor
67
+ end
61
68
  end
62
69
 
63
70
  # Set the state to `:closing` and wait for the state to be set to `:closed`
@@ -76,6 +83,8 @@ module ProcessExecuter
76
83
  # @return [void]
77
84
  #
78
85
  def close
86
+ return unless state == :open
87
+
79
88
  @state = :closing
80
89
  sleep 0.01 until state == :closed
81
90
  end
@@ -233,6 +242,19 @@ module ProcessExecuter
233
242
  #
234
243
  attr_reader :state
235
244
 
245
+ # @!attribute [r]
246
+ #
247
+ # The exception raised by a writer
248
+ #
249
+ # If an exception is raised by a writer, it is stored here. Otherwise, it is `nil`.
250
+ #
251
+ # @example
252
+ # pipe.exception #=> nil
253
+ #
254
+ # @return [Exception, nil] the exception raised by a writer or `nil` if no exception was raised
255
+ #
256
+ attr_reader :exception
257
+
236
258
  private
237
259
 
238
260
  # Read data from the pipe until `#state` is changed to `:closing`
@@ -257,10 +279,17 @@ module ProcessExecuter
257
279
  # @api private
258
280
  def monitor_pipe
259
281
  new_data = pipe_reader.read_nonblock(chunk_size)
282
+ # SimpleCov under JRuby reports the begin statement as not covered, but it is
283
+ # :nocov:
284
+ begin
285
+ # :nocov:
286
+ writers.each { |w| w.write(new_data) }
287
+ rescue StandardError => e
288
+ @exception = e
289
+ @state = :closing
290
+ end
260
291
  rescue IO::WaitReadable
261
292
  pipe_reader.wait_readable(0.01)
262
- else
263
- writers.each { |w| w.write(new_data) }
264
293
  end
265
294
 
266
295
  # Read any remaining data from the pipe and close it
@@ -270,11 +299,11 @@ module ProcessExecuter
270
299
  def close_pipe
271
300
  # Close the write end of the pipe so no more data can be written to it
272
301
  pipe_writer.close
302
+
273
303
  # Read remaining data from pipe_reader (if any)
274
- if pipe_reader.wait_readable(0.01)
275
- new_data = pipe_reader.read(chunk_size)
276
- writers.each { |w| w.write(new_data) }
277
- end
304
+ # If an exception was already raised by the last call to #write, then don't try to read remaining data
305
+ monitor_pipe while exception.nil? && !pipe_reader.eof?
306
+
278
307
  # Close the read end of the pipe
279
308
  pipe_reader.close
280
309
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module ProcessExecuter
4
4
  # The current Gem version
5
- VERSION = '0.4.0'
5
+ VERSION = '0.6.0'
6
6
  end
@@ -10,15 +10,17 @@ require 'timeout'
10
10
  # @api public
11
11
  #
12
12
  module ProcessExecuter
13
- # Execute the specified command and return the exit status
13
+ # Execute the specified command as a subprocess and return the exit status
14
14
  #
15
- # This method blocks until the command has terminated or the timeout has been reached.
15
+ # This method blocks until the command has terminated.
16
+ #
17
+ # The command will be send the SIGKILL signal if it does not terminate within
18
+ # the specified timeout.
16
19
  #
17
20
  # @example
18
21
  # status = ProcessExecuter.spawn('echo hello')
19
22
  # status.exited? # => true
20
23
  # status.success? # => true
21
- # stdout.string # => "hello\n"
22
24
  #
23
25
  # @example with a timeout
24
26
  # status = ProcessExecuter.spawn('sleep 10', timeout: 0.01)
@@ -27,6 +29,11 @@ module ProcessExecuter
27
29
  # status.signaled? # => true
28
30
  # status.termsig # => 9
29
31
  #
32
+ # @example capturing stdout to a string
33
+ # stdout = StringIO.new
34
+ # status = ProcessExecuter.spawn('echo hello', out: stdout)
35
+ # stdout.string # => "hello"
36
+ #
30
37
  # @see https://ruby-doc.org/core-3.1.2/Kernel.html#method-i-spawn Kernel.spawn
31
38
  # documentation for valid command and options
32
39
  #
@@ -34,13 +41,13 @@ module ProcessExecuter
34
41
  # for additional options that may be specified
35
42
  #
36
43
  # @param command [Array<String>] the command to execute
37
- # @param options_hash [Hash] the options to use for this execution context
44
+ # @param options_hash [Hash] the options to use when exectuting the command
38
45
  #
39
- # @return [ProcessExecuter::ExecutionContext] the execution context that can run commands
46
+ # @return [Process::Status] the exit status of the proceess
40
47
  #
41
48
  def self.spawn(*command, **options_hash)
42
49
  options = ProcessExecuter::Options.new(**options_hash)
43
- pid = ::Process.spawn(*command, **options.spawn_options)
50
+ pid = Process.spawn(*command, **options.spawn_options)
44
51
  wait_for_process(pid, options)
45
52
  end
46
53
 
@@ -51,16 +58,16 @@ module ProcessExecuter
51
58
  # @param pid [Integer] the process id
52
59
  # @param options [ProcessExecuter::Options] the options used
53
60
  #
54
- # @return [ProcessExecuter::Status] the status of the process
61
+ # @return [Process::Status] the status of the process
55
62
  #
56
63
  # @api private
57
64
  #
58
65
  private_class_method def self.wait_for_process(pid, options)
59
66
  Timeout.timeout(options.timeout) do
60
- ::Process.wait2(pid).last
67
+ Process.wait2(pid).last
61
68
  end
62
69
  rescue Timeout::Error
63
- ::Process.kill('KILL', pid)
64
- ::Process.wait2(pid).last
70
+ Process.kill('KILL', pid)
71
+ Process.wait2(pid).last
65
72
  end
66
73
  end
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
35
35
  # spec.add_dependency "example-gem", "~> 1.0"
36
36
  spec.add_development_dependency 'bump', '~> 0.10'
37
37
  spec.add_development_dependency 'bundler-audit', '~> 0.9'
38
- spec.add_development_dependency 'create_github_release', '~> 0.2'
38
+ spec.add_development_dependency 'create_github_release', '~> 1.0'
39
39
  spec.add_development_dependency 'rake', '~> 13.0'
40
40
  spec.add_development_dependency 'rspec', '~> 3.10'
41
41
  spec.add_development_dependency 'rubocop', '~> 1.36'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: process_executer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Couball
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-07 00:00:00.000000000 Z
11
+ date: 2023-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bump
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.2'
47
+ version: '1.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.2'
54
+ version: '1.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -229,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
229
  - !ruby/object:Gem::Version
230
230
  version: '0'
231
231
  requirements: []
232
- rubygems_version: 3.3.7
232
+ rubygems_version: 3.4.1
233
233
  signing_key:
234
234
  specification_version: 4
235
235
  summary: An API for executing commands in a subprocess