process_executer 0.4.0 → 0.5.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: c6d2904b0fd36166eb05a23f8d208b27de0c19bc10284e71e63a1e7055034a7b
4
+ data.tar.gz: 5ed998bd38182774d63265bd6729c219918acf41755215a172c87b85e4873ebe
5
5
  SHA512:
6
- metadata.gz: 312c65d62033f2e3a13ed5094de32940b9923d775d45f3d90dc6cbe4f0a8d0dcf3e5e772e6add2999c97e2b2980b39d4fa3d4f7ddf0172e9877d7e94a1770d94
7
- data.tar.gz: 267da77563194721787fee28ff41cdf19159f083f5a0cb04d6d912e0271f2e2062b50da8d1655b36bea2ebb5327f3f3d618aa90453ad60ba61d72e4c779afe84
6
+ metadata.gz: 7aec2cdf98b06acccccfa449e57efbc3d6ea4b7bb9ee833a5f81ee77faa02490c55d5c3cea7c1235295d765eaf3b957b2577e3cf8fae0818d4e60e097218bcc3
7
+ data.tar.gz: a0a95d10b07111be5ca3bc094cfa4255629f43b723fb7ae44ead94615c11cd97af370da9a219e481c1d55eb55fd20f7f3a24e213a96834d91b3f18527a6af8ab
data/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ 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.5.0 (2022-12-12)
9
+
10
+ [Full Changelog](https://github.com/main-branch/process_executer/compare/v0.4.0...v0.5.0)
11
+
12
+ * c6d8de9 Workaround a problem with SimpleCov / JRuby
13
+ * c480b5f Increase time to wait for results from a writer throwing an exception
14
+ * 1934563 Handle exceptions from writers within MonitoredPipe
15
+ * e948ada Increase default chunk_size to 100_000 bytes
16
+ * 5eb2c24 Update documentation for ProcessExecuter#spawn
17
+ * a3a4217 Release v0.4.0
18
+
8
19
  ## v0.4.0 (2022-12-06)
9
20
 
10
21
  [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
@@ -271,7 +300,8 @@ module ProcessExecuter
271
300
  # Close the write end of the pipe so no more data can be written to it
272
301
  pipe_writer.close
273
302
  # Read remaining data from pipe_reader (if any)
274
- if pipe_reader.wait_readable(0.01)
303
+ # If an exception was already raised by the last call to #write, then don't try to read remaining data
304
+ if exception.nil? && pipe_reader.wait_readable(0.01)
275
305
  new_data = pipe_reader.read(chunk_size)
276
306
  writers.each { |w| w.write(new_data) }
277
307
  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.5.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
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.5.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: 2022-12-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bump