exeggutor 0.1.3 → 0.1.5

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/exeggutor.rb +65 -70
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f12efae289d93904fde9c9f64edda09e2b085805969648d3345c4054597032fc
4
- data.tar.gz: 35d7e49dd376fc0a03dc70c02b61c947bbbc0d27c0ba31c16250b4bce9332b74
3
+ metadata.gz: 22fa8f7a5c5a9cc9a28c6a05c358f9c1dd61ee296a28d210b964f40a49108d2d
4
+ data.tar.gz: e3158fe7f05f4085939025ac8eebecc824921e957b6bc3df82b07f5b07340ac0
5
5
  SHA512:
6
- metadata.gz: 9d627a0d4a40b7a235643f57a8a67836c9f016ff66c1473da7e629f30cbd2cb084ae91ed9a76fd8ac931f7fd95424331de6ebe8a5f918ac8eba48d425ccf261d
7
- data.tar.gz: 16e0d72b24bc2b0da0a4522c0e90720eefbcf18c28cdae17daea1d9ad2bdc76880769040517763bdc90c2fd62df85ddbd4a6a183f2c44acadd185fc8f9e7392c
6
+ metadata.gz: 7f50dfec6f6cb93b5b584529472d24839b269e14e685a6c4295143bfebd0994d2cab2f0f8828d79653178579754250d96ed5adf1317e8e706d1371de75b535d3
7
+ data.tar.gz: 66420f8bb2c69217c7dab572f9aa36ba637edaa31e4958e6130d774a765fc298ee8b1935cf20b63d4aaf74bc11b5a332c54736fa7b1c1a794e75ca094af153ae
data/lib/exeggutor.rb CHANGED
@@ -2,9 +2,10 @@ require 'open3'
2
2
  require 'shellwords'
3
3
 
4
4
  module Exeggutor
5
+
5
6
  # A handle to a process, with IO handles to communicate with it
6
7
  # and a {ProcessResult} object when it's done. It's largely similar to the array
7
- # of 4 values return by {Open3.popen3}. However, it doesn't suffer from that library's
8
+ # of 4 values return by Open3.popen3. However, it doesn't suffer from that library's
8
9
  # dead-locking issue. For example, even if lots of data has been written to stdout that hasn't been
9
10
  # read, the subprocess can still write to stdout and stderr without blocking
10
11
  class ProcessHandle
@@ -121,7 +122,7 @@ module Exeggutor
121
122
  # Represents an error that occurs during a process execution.
122
123
  # The error contains a {ProcessResult} object with details about the process.
123
124
  #
124
- # @attr_reader result [ProcessResult] The result of the process execution.
125
+ # @attr_reader result {ProcessResult} The result of the process execution.
125
126
  class ProcessError < StandardError
126
127
  attr_reader :result
127
128
 
@@ -143,65 +144,6 @@ module Exeggutor
143
144
  end
144
145
  end
145
146
 
146
- # @private
147
- def self.exeg(args, can_fail: false, show_stdout: false, show_stderr: false, env: nil, chdir: nil, stdin_data: nil)
148
- raise "args.size must be >= 1" if args.empty?
149
-
150
- stdin_io, stdout_io, stderr_io, wait_thr = Exeggutor::run_popen3(args, env, chdir)
151
- stdin_io.write(stdin_data) if stdin_data
152
- stdin_io.close
153
-
154
- # Make the streams as synchronous as possible, to minimize the possibility of a surprising lack
155
- # of output
156
- stdout_io.sync = true
157
- stderr_io.sync = true
158
-
159
- stdout = +''
160
- stderr = +''
161
-
162
- # Although there could be more code sharing between this and exeg_async, it would either complicate exeg_async's inner workings
163
- # or force us to pay the same performance cost that exeg_async does
164
- remaining_ios = [stdout_io, stderr_io]
165
- while remaining_ios.size > 0
166
- readable_ios, = IO.select(remaining_ios)
167
- for readable_io in readable_ios
168
- begin
169
- data = readable_io.read_nonblock(100_000)
170
- if readable_io == stdout_io
171
- stdout << data
172
- $stdout.print(data) if show_stdout
173
- else
174
- stderr << data
175
- $stderr.print(data) if show_stderr
176
- end
177
- rescue IO::WaitReadable
178
- # Shouldn't usually happen because IO.select indicated data is ready, but maybe due to EINTR or something
179
- next
180
- rescue EOFError
181
- remaining_ios.delete(readable_io)
182
- end
183
- end
184
- end
185
-
186
- result = ProcessResult.new(
187
- stdout: stdout,
188
- stderr: stderr,
189
- exit_code: wait_thr.value.exitstatus,
190
- pid: wait_thr.pid
191
- )
192
- if !can_fail && !result.success?
193
- error_str = <<~ERROR_STR
194
- Command failed: #{args.shelljoin}
195
- Exit code: #{result.exit_code}
196
- stdout: #{result.stdout}
197
- stderr: #{result.stderr}
198
- pid: #{result.pid}
199
- ERROR_STR
200
- raise ProcessError.new(result), error_str
201
- end
202
-
203
- result
204
- end
205
147
  end
206
148
 
207
149
  # Executes a command with the provided arguments and options. Waits for the process to finish.
@@ -215,11 +157,66 @@ end
215
157
  # @param env [Hash{String => String}, nil] A hashmap containing environment variable overrides,
216
158
  # or `nil` if no overrides are desired
217
159
  #
218
- # @return [ProcessResult] An object containing process info such as stdout, stderr, and exit code.
160
+ # @return {ProcessResult} An object containing process info such as stdout, stderr, and exit code.
219
161
  #
220
- # @raise [ProcessError] If the command fails and `can_fail` is false.
221
- def exeg(...)
222
- Exeggutor::exeg(...)
162
+ # @raise {ProcessError} If the command fails and `can_fail` is false.
163
+ def exeg(args, can_fail: false, show_stdout: false, show_stderr: false, env: nil, chdir: nil, stdin_data: nil)
164
+ raise "args.size must be >= 1" if args.empty?
165
+
166
+ stdin_io, stdout_io, stderr_io, wait_thr = Exeggutor::run_popen3(args, env, chdir)
167
+ stdin_io.write(stdin_data) if stdin_data
168
+ stdin_io.close
169
+
170
+ # Make the streams as synchronous as possible, to minimize the possibility of a surprising lack
171
+ # of output
172
+ stdout_io.sync = true
173
+ stderr_io.sync = true
174
+
175
+ stdout = +''
176
+ stderr = +''
177
+
178
+ # Although there could be more code sharing between this and exeg_async, it would either complicate exeg_async's inner workings
179
+ # or force us to pay the same performance cost that exeg_async does
180
+ remaining_ios = [stdout_io, stderr_io]
181
+ while remaining_ios.size > 0
182
+ readable_ios, = IO.select(remaining_ios)
183
+ for readable_io in readable_ios
184
+ begin
185
+ data = readable_io.read_nonblock(100_000)
186
+ if readable_io == stdout_io
187
+ stdout << data
188
+ $stdout.print(data) if show_stdout
189
+ else
190
+ stderr << data
191
+ $stderr.print(data) if show_stderr
192
+ end
193
+ rescue IO::WaitReadable
194
+ # Shouldn't usually happen because IO.select indicated data is ready, but maybe due to EINTR or something
195
+ next
196
+ rescue EOFError
197
+ remaining_ios.delete(readable_io)
198
+ end
199
+ end
200
+ end
201
+
202
+ result = Exeggutor::ProcessResult.new(
203
+ stdout: stdout,
204
+ stderr: stderr,
205
+ exit_code: wait_thr.value.exitstatus,
206
+ pid: wait_thr.pid
207
+ )
208
+ if !can_fail && !result.success?
209
+ error_str = <<~ERROR_STR
210
+ Command failed: #{args.shelljoin}
211
+ Exit code: #{result.exit_code}
212
+ stdout: #{result.stdout}
213
+ stderr: #{result.stderr}
214
+ pid: #{result.pid}
215
+ ERROR_STR
216
+ raise Exeggutor::ProcessError.new(result), error_str
217
+ end
218
+
219
+ result
223
220
  end
224
221
 
225
222
  # Executes a command with the provided arguments and options. Does not wait for the process to finish.
@@ -229,9 +226,7 @@ end
229
226
  # @param env [Hash{String => String}, nil] A hashmap containing environment variable overrides,
230
227
  # or `nil` if no overrides are desired
231
228
  #
232
- # @return [ProcessHandle]
233
- #
234
- # @raise [ProcessError] If the command fails and `can_fail` is false.
235
- def exeg_async(...)
236
- Exeggutor::ProcessHandle.new(...)
229
+ # @return {ProcessHandle}
230
+ def exeg_async(args, env: nil, chdir: nil)
231
+ Exeggutor::ProcessHandle.new(args, env: env, chdir: chdir)
237
232
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exeggutor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Eisel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-28 00:00:00.000000000 Z
11
+ date: 2025-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest